Skip to content
Closed
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
19 changes: 13 additions & 6 deletions cmd/crossplane/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,12 @@ func (c *Command) Run() error {
type startCommand struct {
Profile string `help:"Serve runtime profiling data via HTTP at /debug/pprof." placeholder:"host:port"`

Namespace string `default:"crossplane-system" env:"POD_NAMESPACE" help:"Namespace used to unpack and run packages." short:"n"`
ServiceAccount string `default:"crossplane" env:"POD_SERVICE_ACCOUNT" help:"Name of the Crossplane Service Account."`
LeaderElection bool `default:"false" env:"LEADER_ELECTION" help:"Use leader election for the controller manager." short:"l"`
CABundlePath string `env:"CA_BUNDLE_PATH" help:"Additional CA bundle to use when fetching packages from registry."`
UserAgent string `default:"${default_user_agent}" env:"USER_AGENT" help:"The User-Agent header that will be set on all package requests."`
Namespace string `default:"crossplane-system" env:"POD_NAMESPACE" help:"Namespace used to unpack and run packages." short:"n"`
ServiceAccount string `default:"crossplane" env:"POD_SERVICE_ACCOUNT" help:"Name of the Crossplane Service Account."`
LeaderElection bool `default:"false" env:"LEADER_ELECTION" help:"Use leader election for the controller manager." short:"l"`
CABundlePath string `env:"CA_BUNDLE_PATH" help:"Additional CA bundle to use when fetching packages from registry."`
UserAgent string `default:"${default_user_agent}" env:"USER_AGENT" help:"The User-Agent header that will be set on all package requests."`
RegistryAuthCloudNative bool `default:"false" env:"REGISTRY_AUTH_CLOUD_NATIVE" help:"Use cloud-native authentication (IMDS/workload identity) for registry access instead of Kubernetes ImagePullSecrets. Enables GCR/AR, ACR, ECR authentication via node/workload-identity."`

XpkgCacheDir string `aliases:"cache-dir" default:"/cache/xpkg" env:"XPKG_CACHE_DIR,CACHE_DIR" help:"Directory used for caching package images." short:"c"`

Expand Down Expand Up @@ -499,12 +500,18 @@ func (c *startCommand) Run(s *runtime.Scheme, log logging.Logger) error { //noli
log.Info("Package Runtime for Provider: " + string(pr.For(pkgv1.ProviderKind)))
log.Info("Package Runtime for Function: " + string(pr.For(pkgv1.FunctionKind)))

fetcherOpts := []xpkg.FetcherOpt{xpkg.WithUserAgent(c.UserAgent)}
if c.RegistryAuthCloudNative {
fetcherOpts = append(fetcherOpts, xpkg.WithCloudNativeAuth(true))
log.Info("Using cloud-native authentication for registry access (IMDS/workload identity)")
}

po := pkgcontroller.Options{
Options: o,
Cache: xpkg.NewFsPackageCache(c.XpkgCacheDir, afero.NewOsFs()),
Namespace: c.Namespace,
ServiceAccount: c.ServiceAccount,
FetcherOptions: []xpkg.FetcherOpt{xpkg.WithUserAgent(c.UserAgent)},
FetcherOptions: fetcherOpts,
PackageRuntime: pr,
MaxConcurrentPackageEstablishers: c.MaxConcurrentPackageEstablishers,
}
Expand Down
52 changes: 34 additions & 18 deletions internal/xpkg/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"io"
"net/http"

"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/authn/k8schain"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
Expand Down Expand Up @@ -52,11 +53,12 @@ type Fetcher interface {

// K8sFetcher uses kubernetes credentials to fetch package images.
type K8sFetcher struct {
client kubernetes.Interface
namespace string
serviceAccount string
transport http.RoundTripper
userAgent string
client kubernetes.Interface
namespace string
serviceAccount string
transport http.RoundTripper
userAgent string
useCloudNativeAuth bool
}

// FetcherOpt can be used to add optional parameters to NewK8sFetcher.
Expand Down Expand Up @@ -107,6 +109,17 @@ func WithServiceAccount(sa string) FetcherOpt {
}
}

// WithCloudNativeAuth is a FetcherOpt that enables cloud-native authentication
// (via IMDS/workload identity) without requiring Kubernetes API access to pull
// secrets. This uses k8schain.NewNoClient which provides GCR/AR, ACR, and ECR
// authentication via node/workload-identity style auth.
func WithCloudNativeAuth(enabled bool) FetcherOpt {
return func(k *K8sFetcher) error {
k.useCloudNativeAuth = enabled
return nil
}
}

// NewK8sFetcher creates a new K8sFetcher.
func NewK8sFetcher(client kubernetes.Interface, opts ...FetcherOpt) (*K8sFetcher, error) {
dt, ok := remote.DefaultTransport.(*http.Transport)
Expand All @@ -128,13 +141,24 @@ func NewK8sFetcher(client kubernetes.Interface, opts ...FetcherOpt) (*K8sFetcher
return k, nil
}

// Fetch fetches a package image.
func (i *K8sFetcher) Fetch(ctx context.Context, ref name.Reference, secrets ...string) (v1.Image, error) {
auth, err := k8schain.New(ctx, i.client, k8schain.Options{
// keychain returns the appropriate keychain based on the fetcher configuration.
// If useCloudNativeAuth is enabled, it uses k8schain.NewNoClient for cloud-native
// authentication (GCR/AR, ACR, ECR via IMDS/workload identity).
// Otherwise, it uses k8schain.New which reads ImagePullSecrets from Kubernetes.
func (i *K8sFetcher) keychain(ctx context.Context, secrets ...string) (authn.Keychain, error) {
if i.useCloudNativeAuth {
return k8schain.NewNoClient(ctx)
}
return k8schain.New(ctx, i.client, k8schain.Options{
Namespace: i.namespace,
ServiceAccountName: i.serviceAccount,
ImagePullSecrets: secrets,
})
}

// Fetch fetches a package image.
func (i *K8sFetcher) Fetch(ctx context.Context, ref name.Reference, secrets ...string) (v1.Image, error) {
auth, err := i.keychain(ctx, secrets...)
if err != nil {
return nil, err
}
Expand All @@ -149,11 +173,7 @@ func (i *K8sFetcher) Fetch(ctx context.Context, ref name.Reference, secrets ...s

// Head fetches a package descriptor.
func (i *K8sFetcher) Head(ctx context.Context, ref name.Reference, secrets ...string) (*v1.Descriptor, error) {
auth, err := k8schain.New(ctx, i.client, k8schain.Options{
Namespace: i.namespace,
ServiceAccountName: i.serviceAccount,
ImagePullSecrets: secrets,
})
auth, err := i.keychain(ctx, secrets...)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -183,11 +203,7 @@ func (i *K8sFetcher) Head(ctx context.Context, ref name.Reference, secrets ...st

// Tags fetches a package's tags.
func (i *K8sFetcher) Tags(ctx context.Context, ref name.Reference, secrets ...string) ([]string, error) {
auth, err := k8schain.New(ctx, i.client, k8schain.Options{
Namespace: i.namespace,
ServiceAccountName: i.serviceAccount,
ImagePullSecrets: secrets,
})
auth, err := i.keychain(ctx, secrets...)
if err != nil {
return nil, err
}
Expand Down