Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions cmd/caib/auth/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,17 @@ import (
)

// GetOIDCConfigFromAPI fetches OIDC configuration from the Build API server.
func GetOIDCConfigFromAPI(serverURL string) (*OIDCConfig, error) {
func GetOIDCConfigFromAPI(serverURL string, insecureSkipTLS bool) (*OIDCConfig, error) {
pool, err := x509.SystemCertPool()
if err != nil {
pool = x509.NewCertPool()
}
tlsConfig := &tls.Config{RootCAs: pool}
if insecureSkipTLS {
tlsConfig.InsecureSkipVerify = true
}
transport := &http.Transport{
TLSClientConfig: &tls.Config{RootCAs: pool},
TLSClientConfig: tlsConfig,
}
client := &http.Client{
Timeout: 30 * time.Second,
Expand Down
14 changes: 7 additions & 7 deletions cmd/caib/auth/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ var _ = Describe("GetOIDCConfigFromAPI", func() {
_ = json.NewEncoder(w).Encode(response)
}))

config, err := GetOIDCConfigFromAPI(server.URL)
config, err := GetOIDCConfigFromAPI(server.URL, false)
Expect(err).NotTo(HaveOccurred())
Expect(config).NotTo(BeNil())
Expect(config.IssuerURL).To(Equal("https://issuer.example.com"))
Expand All @@ -56,7 +56,7 @@ var _ = Describe("GetOIDCConfigFromAPI", func() {
w.WriteHeader(http.StatusNotFound)
}))

config, err := GetOIDCConfigFromAPI(server.URL)
config, err := GetOIDCConfigFromAPI(server.URL, false)
Expect(err).NotTo(HaveOccurred())
Expect(config).To(BeNil())
})
Expand All @@ -66,7 +66,7 @@ var _ = Describe("GetOIDCConfigFromAPI", func() {
w.WriteHeader(http.StatusInternalServerError)
}))

config, err := GetOIDCConfigFromAPI(server.URL)
config, err := GetOIDCConfigFromAPI(server.URL, false)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("HTTP 500"))
Expect(config).To(BeNil())
Expand All @@ -82,7 +82,7 @@ var _ = Describe("GetOIDCConfigFromAPI", func() {
_ = json.NewEncoder(w).Encode(response)
}))

config, err := GetOIDCConfigFromAPI(server.URL)
config, err := GetOIDCConfigFromAPI(server.URL, false)
Expect(err).NotTo(HaveOccurred())
Expect(config).To(BeNil())
})
Expand All @@ -103,14 +103,14 @@ var _ = Describe("GetOIDCConfigFromAPI", func() {
_ = json.NewEncoder(w).Encode(response)
}))

config, err := GetOIDCConfigFromAPI(server.URL)
config, err := GetOIDCConfigFromAPI(server.URL, false)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("client ID is required"))
Expect(config).To(BeNil())
})

It("should return error when network request fails", func() {
config, err := GetOIDCConfigFromAPI("http://invalid-host-that-does-not-exist:9999")
config, err := GetOIDCConfigFromAPI("http://invalid-host-that-does-not-exist:9999", false)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("failed to fetch OIDC config from API"))
Expect(config).To(BeNil())
Expand All @@ -122,7 +122,7 @@ var _ = Describe("GetOIDCConfigFromAPI", func() {
_, _ = w.Write([]byte("invalid json"))
}))

config, err := GetOIDCConfigFromAPI(server.URL)
config, err := GetOIDCConfigFromAPI(server.URL, false)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("failed to decode"))
Expect(config).To(BeNil())
Expand Down
21 changes: 14 additions & 7 deletions cmd/caib/auth/oidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"context"
"crypto/rand"
"crypto/sha256"
"crypto/tls"
"encoding/base64"
"encoding/json"
"fmt"
Expand Down Expand Up @@ -43,13 +44,14 @@ type OIDCConfig struct {

// OIDCAuth handles OIDC authentication flow and token management.
type OIDCAuth struct {
config OIDCConfig
tokenCache *TokenCache
cachePath string
config OIDCConfig
tokenCache *TokenCache
cachePath string
insecureSkipTLS bool
}

// NewOIDCAuth creates a new OIDC authenticator instance.
func NewOIDCAuth(issuerURL, clientID string, scopes []string) *OIDCAuth {
func NewOIDCAuth(issuerURL, clientID string, scopes []string, insecureSkipTLS bool) *OIDCAuth {
if issuerURL == "" || clientID == "" {
return nil
}
Expand All @@ -70,7 +72,8 @@ func NewOIDCAuth(issuerURL, clientID string, scopes []string) *OIDCAuth {
ClientID: clientID,
Scopes: scopes,
},
cachePath: cachePath,
cachePath: cachePath,
insecureSkipTLS: insecureSkipTLS,
}
}

Expand Down Expand Up @@ -275,7 +278,9 @@ func (a *OIDCAuth) exchangeCodeForToken(ctx context.Context, tokenEndpoint, code
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

transport := &http.Transport{}
// Use default TLS settings
if a.insecureSkipTLS {
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}

client := &http.Client{
Timeout: 30 * time.Second,
Expand Down Expand Up @@ -323,7 +328,9 @@ type DiscoveryDocument struct {

func (a *OIDCAuth) getDiscovery(discoveryURL string) (*DiscoveryDocument, error) {
transport := &http.Transport{}
// Use default TLS settings
if a.insecureSkipTLS {
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}

client := &http.Client{
Timeout: 10 * time.Second,
Expand Down
13 changes: 8 additions & 5 deletions cmd/caib/auth/wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ func IsAuthError(err error) bool {
// Returns empty string if no OIDC config is available (auth is optional).
// The boolean return indicates whether a fresh auth flow was performed.
// Returns an error if OIDC is configured but config fetch fails (network/server errors).
func GetTokenWithReauth(ctx context.Context, serverURL string, currentToken string) (string, bool, error) {
func GetTokenWithReauth(ctx context.Context, serverURL string, currentToken string, insecureSkipTLS bool) (string, bool, error) {
// Prefer API config over local: server is source of truth (OperatorConfig).
// When server has OIDC disabled or init failed, API returns empty JWT and we should not use local OIDC.
config, err := GetOIDCConfigFromAPI(serverURL)
config, err := GetOIDCConfigFromAPI(serverURL, insecureSkipTLS)
if err != nil {
// Error fetching config - this is a real error, not "not configured"
return "", false, fmt.Errorf("failed to fetch OIDC configuration: %w", err)
Expand All @@ -37,7 +37,7 @@ func GetTokenWithReauth(ctx context.Context, serverURL string, currentToken stri
return "", false, nil
}

oidcAuth := NewOIDCAuth(config.IssuerURL, config.ClientID, config.Scopes)
oidcAuth := NewOIDCAuth(config.IssuerURL, config.ClientID, config.Scopes, insecureSkipTLS)
if oidcAuth == nil {
return "", false, fmt.Errorf("failed to initialize OIDC authenticator")
}
Expand All @@ -60,7 +60,7 @@ func GetTokenWithReauth(ctx context.Context, serverURL string, currentToken stri
// CreateClientWithReauth creates a client and handles re-authentication on auth errors.
// If authToken is nil, it will be treated as empty and OIDC will be attempted.
// OIDC errors are logged but do not prevent client creation (auth is optional).
func CreateClientWithReauth(ctx context.Context, serverURL string, authToken *string) (*buildapiclient.Client, error) {
func CreateClientWithReauth(ctx context.Context, serverURL string, authToken *string, insecureSkipTLS bool) (*buildapiclient.Client, error) {
// Guard against nil pointer
tokenValue := ""
if authToken != nil {
Expand All @@ -70,7 +70,7 @@ func CreateClientWithReauth(ctx context.Context, serverURL string, authToken *st
// Try to get token from OIDC if needed
if tokenValue == "" {
// Try OIDC auth
token, _, err := GetTokenWithReauth(ctx, serverURL, "")
token, _, err := GetTokenWithReauth(ctx, serverURL, "", insecureSkipTLS)
if err != nil {
// OIDC fetch failed - log but continue (auth is optional, kubeconfig may work)
fmt.Printf("Warning: OIDC authentication failed: %v\n", err)
Expand All @@ -85,6 +85,9 @@ func CreateClientWithReauth(ctx context.Context, serverURL string, authToken *st
// Configure TLS options
var opts []buildapiclient.Option
opts = append(opts, buildapiclient.WithAuthToken(tokenValue))
if insecureSkipTLS {
opts = append(opts, buildapiclient.WithInsecureTLS())
}

return buildapiclient.New(serverURL, opts...)
}
8 changes: 4 additions & 4 deletions cmd/caib/auth/wrapper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,23 @@ import (
var _ = Describe("CreateClientWithReauth", func() {
It("should handle nil authToken pointer safely", func() {
ctx := context.Background()
client, err := CreateClientWithReauth(ctx, "https://api.example.com", nil)
client, err := CreateClientWithReauth(ctx, "https://api.example.com", nil, false)
Expect(err).NotTo(HaveOccurred())
Expect(client).NotTo(BeNil())
})

It("should create client with empty token when authToken is empty string", func() {
ctx := context.Background()
emptyToken := ""
client, err := CreateClientWithReauth(ctx, "https://api.example.com", &emptyToken)
client, err := CreateClientWithReauth(ctx, "https://api.example.com", &emptyToken, false)
Expect(err).NotTo(HaveOccurred())
Expect(client).NotTo(BeNil())
})

It("should create client with provided token", func() {
ctx := context.Background()
token := "test-token"
client, err := CreateClientWithReauth(ctx, "https://api.example.com", &token)
client, err := CreateClientWithReauth(ctx, "https://api.example.com", &token, false)
Expect(err).NotTo(HaveOccurred())
Expect(client).NotTo(BeNil())
})
Expand All @@ -35,7 +35,7 @@ var _ = Describe("CreateClientWithReauth", func() {
ctx := context.Background()
emptyToken := ""
// Use invalid server URL to trigger OIDC error
client, err := CreateClientWithReauth(ctx, "http://invalid-server:9999", &emptyToken)
client, err := CreateClientWithReauth(ctx, "http://invalid-server:9999", &emptyToken, false)
// Should still create client even if OIDC fails (auth is optional)
Expect(err).NotTo(HaveOccurred())
Expect(client).NotTo(BeNil())
Expand Down
4 changes: 2 additions & 2 deletions cmd/caib/catalog/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ type targetInfo struct {
Verified bool `json:"verified"`
}

func runAdd(_ *cobra.Command, args []string) error {
func runAdd(cmd *cobra.Command, args []string) error {
name := args[0]
registryURL := args[1]

Expand Down Expand Up @@ -147,7 +147,7 @@ func runAdd(_ *cobra.Command, args []string) error {
req.Header.Set("Authorization", "Bearer "+token)
}

client := &http.Client{}
client := newHTTPClient(getInsecureSkipTLS(cmd))
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("failed to make request: %w", err)
Expand Down
46 changes: 46 additions & 0 deletions cmd/caib/catalog/catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ limitations under the License.
package catalog

import (
"crypto/tls"
"net/http"
"os"
"strconv"
"strings"

"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -57,3 +63,43 @@ func addCommonFlags(cmd *cobra.Command) {
cmd.Flags().StringVarP(&namespace, "namespace", "n", "", "Kubernetes namespace")
cmd.Flags().StringVarP(&outputFormat, "output", "o", "table", "Output format (table, json, yaml)")
}

// getInsecureSkipTLS returns whether to skip TLS verification
// Checks the --insecure flag from the root command or CAIB_INSECURE env var
func getInsecureSkipTLS(cmd *cobra.Command) bool {
// Try to get from root command's persistent flag
if cmd.Root() != nil {
if flag := cmd.Root().PersistentFlags().Lookup("insecure"); flag != nil {
if val, err := strconv.ParseBool(flag.Value.String()); err == nil {
return val
}
}
}
// Fall back to env var
return envBool("CAIB_INSECURE")
}

// envBool parses a boolean from environment variable
func envBool(key string) bool {
v := strings.TrimSpace(os.Getenv(key))
if v == "" {
return false
}
b, err := strconv.ParseBool(v)
if err != nil {
return false
}
return b
}

// newHTTPClient creates an HTTP client with optional insecure TLS
func newHTTPClient(insecureSkipTLS bool) *http.Client {
if insecureSkipTLS {
return &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}
}
return &http.Client{}
}
4 changes: 2 additions & 2 deletions cmd/caib/catalog/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func newGetCmd() *cobra.Command {
return cmd
}

func runGet(_ *cobra.Command, args []string) error {
func runGet(cmd *cobra.Command, args []string) error {
name := args[0]

server := serverURL
Expand Down Expand Up @@ -73,7 +73,7 @@ func runGet(_ *cobra.Command, args []string) error {
req.Header.Set("Authorization", "Bearer "+token)
}

client := &http.Client{}
client := newHTTPClient(getInsecureSkipTLS(cmd))
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("failed to make request: %w", err)
Expand Down
4 changes: 2 additions & 2 deletions cmd/caib/catalog/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ type Target struct {
Name string `json:"name"`
}

func runList(_ *cobra.Command, _ []string) error {
func runList(cmd *cobra.Command, _ []string) error {
// Get server URL
server := serverURL
if server == "" {
Expand Down Expand Up @@ -144,7 +144,7 @@ func runList(_ *cobra.Command, _ []string) error {
req.Header.Set("Authorization", "Bearer "+token)
}

client := &http.Client{}
client := newHTTPClient(getInsecureSkipTLS(cmd))
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("failed to make request: %w", err)
Expand Down
4 changes: 2 additions & 2 deletions cmd/caib/catalog/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ type publishRequest struct {
Tags []string `json:"tags,omitempty"`
}

func runPublish(_ *cobra.Command, args []string) error {
func runPublish(cmd *cobra.Command, args []string) error {
imageBuildName := args[0]

server := serverURL
Expand Down Expand Up @@ -104,7 +104,7 @@ func runPublish(_ *cobra.Command, args []string) error {
req.Header.Set("Authorization", "Bearer "+token)
}

client := &http.Client{}
client := newHTTPClient(getInsecureSkipTLS(cmd))
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("failed to make request: %w", err)
Expand Down
4 changes: 2 additions & 2 deletions cmd/caib/catalog/remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func newRemoveCmd() *cobra.Command {
return cmd
}

func runRemove(_ *cobra.Command, args []string) error {
func runRemove(cmd *cobra.Command, args []string) error {
name := args[0]

server := serverURL
Expand Down Expand Up @@ -91,7 +91,7 @@ func runRemove(_ *cobra.Command, args []string) error {
req.Header.Set("Authorization", "Bearer "+token)
}

client := &http.Client{}
client := newHTTPClient(getInsecureSkipTLS(cmd))
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("failed to make request: %w", err)
Expand Down
4 changes: 2 additions & 2 deletions cmd/caib/catalog/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ type verifyResponse struct {
Triggered bool `json:"triggered"`
}

func runVerify(_ *cobra.Command, args []string) error {
func runVerify(cmd *cobra.Command, args []string) error {
name := args[0]

server := serverURL
Expand Down Expand Up @@ -84,7 +84,7 @@ func runVerify(_ *cobra.Command, args []string) error {
req.Header.Set("Authorization", "Bearer "+token)
}

client := &http.Client{}
client := newHTTPClient(getInsecureSkipTLS(cmd))
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("failed to make request: %w", err)
Expand Down
Loading
Loading