Skip to content

Commit bf712cc

Browse files
committed
allow k8snoclient for fetcher auth
1 parent b093afd commit bf712cc

2 files changed

Lines changed: 47 additions & 24 deletions

File tree

cmd/crossplane/core/core.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,12 @@ func (c *Command) Run() error {
9292
type startCommand struct {
9393
Profile string `help:"Serve runtime profiling data via HTTP at /debug/pprof." placeholder:"host:port"`
9494

95-
Namespace string `default:"crossplane-system" env:"POD_NAMESPACE" help:"Namespace used to unpack and run packages." short:"n"`
96-
ServiceAccount string `default:"crossplane" env:"POD_SERVICE_ACCOUNT" help:"Name of the Crossplane Service Account."`
97-
LeaderElection bool `default:"false" env:"LEADER_ELECTION" help:"Use leader election for the controller manager." short:"l"`
98-
CABundlePath string `env:"CA_BUNDLE_PATH" help:"Additional CA bundle to use when fetching packages from registry."`
99-
UserAgent string `default:"${default_user_agent}" env:"USER_AGENT" help:"The User-Agent header that will be set on all package requests."`
95+
Namespace string `default:"crossplane-system" env:"POD_NAMESPACE" help:"Namespace used to unpack and run packages." short:"n"`
96+
ServiceAccount string `default:"crossplane" env:"POD_SERVICE_ACCOUNT" help:"Name of the Crossplane Service Account."`
97+
LeaderElection bool `default:"false" env:"LEADER_ELECTION" help:"Use leader election for the controller manager." short:"l"`
98+
CABundlePath string `env:"CA_BUNDLE_PATH" help:"Additional CA bundle to use when fetching packages from registry."`
99+
UserAgent string `default:"${default_user_agent}" env:"USER_AGENT" help:"The User-Agent header that will be set on all package requests."`
100+
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."`
100101

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

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

503+
fetcherOpts := []xpkg.FetcherOpt{xpkg.WithUserAgent(c.UserAgent)}
504+
if c.RegistryAuthCloudNative {
505+
fetcherOpts = append(fetcherOpts, xpkg.WithCloudNativeAuth(true))
506+
log.Info("Using cloud-native authentication for registry access (IMDS/workload identity)")
507+
}
508+
502509
po := pkgcontroller.Options{
503510
Options: o,
504511
Cache: xpkg.NewFsPackageCache(c.XpkgCacheDir, afero.NewOsFs()),
505512
Namespace: c.Namespace,
506513
ServiceAccount: c.ServiceAccount,
507-
FetcherOptions: []xpkg.FetcherOpt{xpkg.WithUserAgent(c.UserAgent)},
514+
FetcherOptions: fetcherOpts,
508515
PackageRuntime: pr,
509516
MaxConcurrentPackageEstablishers: c.MaxConcurrentPackageEstablishers,
510517
}

internal/xpkg/fetch.go

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"io"
2424
"net/http"
2525

26+
"github.com/google/go-containerregistry/pkg/authn"
2627
"github.com/google/go-containerregistry/pkg/authn/k8schain"
2728
"github.com/google/go-containerregistry/pkg/name"
2829
v1 "github.com/google/go-containerregistry/pkg/v1"
@@ -52,11 +53,12 @@ type Fetcher interface {
5253

5354
// K8sFetcher uses kubernetes credentials to fetch package images.
5455
type K8sFetcher struct {
55-
client kubernetes.Interface
56-
namespace string
57-
serviceAccount string
58-
transport http.RoundTripper
59-
userAgent string
56+
client kubernetes.Interface
57+
namespace string
58+
serviceAccount string
59+
transport http.RoundTripper
60+
userAgent string
61+
useCloudNativeAuth bool
6062
}
6163

6264
// FetcherOpt can be used to add optional parameters to NewK8sFetcher.
@@ -107,6 +109,17 @@ func WithServiceAccount(sa string) FetcherOpt {
107109
}
108110
}
109111

112+
// WithCloudNativeAuth is a FetcherOpt that enables cloud-native authentication
113+
// (via IMDS/workload identity) without requiring Kubernetes API access to pull
114+
// secrets. This uses k8schain.NewNoClient which provides GCR/AR, ACR, and ECR
115+
// authentication via node/workload-identity style auth.
116+
func WithCloudNativeAuth(enabled bool) FetcherOpt {
117+
return func(k *K8sFetcher) error {
118+
k.useCloudNativeAuth = enabled
119+
return nil
120+
}
121+
}
122+
110123
// NewK8sFetcher creates a new K8sFetcher.
111124
func NewK8sFetcher(client kubernetes.Interface, opts ...FetcherOpt) (*K8sFetcher, error) {
112125
dt, ok := remote.DefaultTransport.(*http.Transport)
@@ -128,13 +141,24 @@ func NewK8sFetcher(client kubernetes.Interface, opts ...FetcherOpt) (*K8sFetcher
128141
return k, nil
129142
}
130143

131-
// Fetch fetches a package image.
132-
func (i *K8sFetcher) Fetch(ctx context.Context, ref name.Reference, secrets ...string) (v1.Image, error) {
133-
auth, err := k8schain.New(ctx, i.client, k8schain.Options{
144+
// keychain returns the appropriate keychain based on the fetcher configuration.
145+
// If useCloudNativeAuth is enabled, it uses k8schain.NewNoClient for cloud-native
146+
// authentication (GCR/AR, ACR, ECR via IMDS/workload identity).
147+
// Otherwise, it uses k8schain.New which reads ImagePullSecrets from Kubernetes.
148+
func (i *K8sFetcher) keychain(ctx context.Context, secrets ...string) (authn.Keychain, error) {
149+
if i.useCloudNativeAuth {
150+
return k8schain.NewNoClient(ctx)
151+
}
152+
return k8schain.New(ctx, i.client, k8schain.Options{
134153
Namespace: i.namespace,
135154
ServiceAccountName: i.serviceAccount,
136155
ImagePullSecrets: secrets,
137156
})
157+
}
158+
159+
// Fetch fetches a package image.
160+
func (i *K8sFetcher) Fetch(ctx context.Context, ref name.Reference, secrets ...string) (v1.Image, error) {
161+
auth, err := i.keychain(ctx, secrets...)
138162
if err != nil {
139163
return nil, err
140164
}
@@ -149,11 +173,7 @@ func (i *K8sFetcher) Fetch(ctx context.Context, ref name.Reference, secrets ...s
149173

150174
// Head fetches a package descriptor.
151175
func (i *K8sFetcher) Head(ctx context.Context, ref name.Reference, secrets ...string) (*v1.Descriptor, error) {
152-
auth, err := k8schain.New(ctx, i.client, k8schain.Options{
153-
Namespace: i.namespace,
154-
ServiceAccountName: i.serviceAccount,
155-
ImagePullSecrets: secrets,
156-
})
176+
auth, err := i.keychain(ctx, secrets...)
157177
if err != nil {
158178
return nil, err
159179
}
@@ -183,11 +203,7 @@ func (i *K8sFetcher) Head(ctx context.Context, ref name.Reference, secrets ...st
183203

184204
// Tags fetches a package's tags.
185205
func (i *K8sFetcher) Tags(ctx context.Context, ref name.Reference, secrets ...string) ([]string, error) {
186-
auth, err := k8schain.New(ctx, i.client, k8schain.Options{
187-
Namespace: i.namespace,
188-
ServiceAccountName: i.serviceAccount,
189-
ImagePullSecrets: secrets,
190-
})
206+
auth, err := i.keychain(ctx, secrets...)
191207
if err != nil {
192208
return nil, err
193209
}

0 commit comments

Comments
 (0)