diff --git a/driver/driver.go b/driver/driver.go index c82852cd8..2e3d9c4d0 100644 --- a/driver/driver.go +++ b/driver/driver.go @@ -8,7 +8,6 @@ import ( "github.com/docker/buildx/store" "github.com/docker/buildx/util/progress" - clitypes "github.com/docker/cli/cli/config/types" controlapi "github.com/moby/buildkit/api/services/control" "github.com/moby/buildkit/client" "github.com/pkg/errors" @@ -58,10 +57,6 @@ type Info struct { DynamicNodes []store.Node } -type Auth interface { - GetAuthConfig(registryHostname string) (clitypes.AuthConfig, error) -} - type Driver interface { Factory() Factory Bootstrap(context.Context, progress.Logger) error diff --git a/driver/manager.go b/driver/manager.go index 54bf860ec..f09d22d57 100644 --- a/driver/manager.go +++ b/driver/manager.go @@ -7,6 +7,7 @@ import ( "github.com/docker/cli/cli/context/store" "github.com/moby/buildkit/client" + "github.com/moby/buildkit/session/auth/authprovider" "github.com/moby/buildkit/util/tracing/delegated" dockerclient "github.com/moby/moby/client" ocispecs "github.com/opencontainers/image-spec/specs-go/v1" @@ -35,7 +36,7 @@ type InitConfig struct { BuildkitdFlags []string Files map[string][]byte DriverOpts map[string]string - Auth Auth + Auth authprovider.AuthConfigProvider Platforms []ocispecs.Platform ContextPathHash string DialMeta map[string][]string diff --git a/store/storeutil/storeutil.go b/store/storeutil/storeutil.go index e9b2a14fa..1ad4da6a1 100644 --- a/store/storeutil/storeutil.go +++ b/store/storeutil/storeutil.go @@ -8,6 +8,7 @@ import ( "github.com/docker/buildx/store" "github.com/docker/buildx/util/confutil" "github.com/docker/buildx/util/dockerutil" + "github.com/docker/buildx/util/dockerutil/dockerconfig" "github.com/docker/buildx/util/imagetools" "github.com/docker/buildx/util/resolver" "github.com/docker/cli/cli/command" @@ -109,7 +110,7 @@ func GetNodeGroup(txn *store.Txn, dockerCli command.Cli, name string) (*store.No } func GetImageConfig(dockerCli command.Cli, ng *store.NodeGroup) (opt imagetools.Opt, err error) { - opt.Auth = dockerCli.ConfigFile() + opt.Auth = dockerconfig.LoadAuthConfig(dockerCli) if ng == nil || len(ng.Nodes) == 0 { return opt, nil diff --git a/util/dockerutil/dockerconfig/configprovider.go b/util/dockerutil/dockerconfig/configprovider.go index 7c465dc95..1aa257460 100644 --- a/util/dockerutil/dockerconfig/configprovider.go +++ b/util/dockerutil/dockerconfig/configprovider.go @@ -43,6 +43,13 @@ type authConfigProvider struct { } func (ap *authConfigProvider) load(ctx context.Context, host string, scopes []string, cacheExpireCheck authprovider.ExpireCachedAuthCheck) (types.AuthConfig, error) { + if cacheExpireCheck == nil { + cacheExpireCheck = func(created time.Time, _ string) bool { + // Tokens for Google Artifact Registry via Workload Identity expire after 5 minutes. + return time.Since(created) > 4*time.Minute+50*time.Second + } + } + ac, err := ap.loadHost(ctx, host, scopes, cacheExpireCheck) if err != nil { return types.AuthConfig{}, err diff --git a/util/imagetools/auth.go b/util/imagetools/auth.go index 0ddeac8bc..f917fbdc8 100644 --- a/util/imagetools/auth.go +++ b/util/imagetools/auth.go @@ -5,68 +5,14 @@ import ( "encoding/base64" "encoding/json" "net/http" - "sync" - "time" "github.com/containerd/containerd/v2/core/remotes/docker" "github.com/distribution/reference" - "github.com/docker/cli/cli/config/types" + "github.com/moby/buildkit/session/auth/authprovider" ) -type authConfig struct { - mu sync.Mutex - authConfigCache map[string]authConfigCacheEntry - cfg Auth -} - -type authConfigCacheEntry struct { - Created time.Time - Auth types.AuthConfig -} - -func newAuthConfig(a Auth) *authConfig { - return &authConfig{ - authConfigCache: map[string]authConfigCacheEntry{}, - cfg: a, - } -} - -func (a *authConfig) credentials(host string) (string, string, error) { - ac, err := a.authConfig(host) - if err != nil { - return "", "", err - } - if ac.IdentityToken != "" { - return "", ac.IdentityToken, nil - } - return ac.Username, ac.Password, nil -} - -func (a *authConfig) authConfig(host string) (types.AuthConfig, error) { - const defaultExpiration = 2 * time.Minute - - if host == "registry-1.docker.io" { - host = "https://index.docker.io/v1/" - } - a.mu.Lock() - defer a.mu.Unlock() - - if c, ok := a.authConfigCache[host]; ok && time.Since(c.Created) <= defaultExpiration { - return c.Auth, nil - } - ac, err := a.cfg.GetAuthConfig(host) - if err != nil { - return types.AuthConfig{}, err - } - a.authConfigCache[host] = authConfigCacheEntry{ - Created: time.Now(), - Auth: ac, - } - return ac, nil -} - -func RegistryAuthForRef(ref string, a Auth) (string, error) { - if a == nil { +func RegistryAuthForRef(ref string, auth authprovider.AuthConfigProvider) (string, error) { + if auth == nil { return "", nil } r, err := parseRef(ref) @@ -77,7 +23,7 @@ func RegistryAuthForRef(ref string, a Auth) (string, error) { if host == "docker.io" { host = "https://index.docker.io/v1/" } - ac, err := a.GetAuthConfig(host) + ac, err := auth(context.TODO(), host, nil, nil) if err != nil { return "", err } @@ -90,11 +36,11 @@ func RegistryAuthForRef(ref string, a Auth) (string, error) { type withBearerAuthorizer struct { docker.Authorizer - AuthConfig *authConfig + AuthConfig authprovider.AuthConfigProvider } func (a *withBearerAuthorizer) Authorize(ctx context.Context, req *http.Request) error { - ac, err := a.AuthConfig.authConfig(req.Host) + ac, err := a.AuthConfig(ctx, req.Host, nil, nil) if err == nil && ac.RegistryToken != "" { req.Header.Set("Authorization", "Bearer "+ac.RegistryToken) return nil diff --git a/util/imagetools/inspect.go b/util/imagetools/inspect.go index e49e2418d..656654dc0 100644 --- a/util/imagetools/inspect.go +++ b/util/imagetools/inspect.go @@ -12,19 +12,15 @@ import ( "github.com/distribution/reference" "github.com/docker/buildx/util/resolver" "github.com/docker/buildx/util/resolver/auth" - clitypes "github.com/docker/cli/cli/config/types" + "github.com/moby/buildkit/session/auth/authprovider" "github.com/moby/buildkit/util/contentutil" "github.com/moby/buildkit/util/tracing" ocispecs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/sirupsen/logrus" ) -type Auth interface { - GetAuthConfig(registryHostname string) (clitypes.AuthConfig, error) -} - type Opt struct { - Auth Auth + Auth authprovider.AuthConfigProvider RegistryConfig map[string]resolver.RegistryConfig } @@ -35,11 +31,10 @@ type Resolver struct { } func New(opt Opt) *Resolver { - ac := newAuthConfig(opt.Auth) - dockerAuth := auth.NewDockerAuthorizer(auth.WithAuthCreds(ac.credentials), auth.WithAuthClient(http.DefaultClient)) + dockerAuth := auth.NewDockerAuthorizer(auth.WithAuthProvider(opt.Auth), auth.WithAuthClient(http.DefaultClient)) auth := &withBearerAuthorizer{ Authorizer: dockerAuth, - AuthConfig: ac, + AuthConfig: opt.Auth, } return &Resolver{ auth: auth, diff --git a/util/resolver/auth/authorizer.go b/util/resolver/auth/authorizer.go index 128dda560..77c3bffca 100644 --- a/util/resolver/auth/authorizer.go +++ b/util/resolver/auth/authorizer.go @@ -14,13 +14,15 @@ import ( "github.com/containerd/containerd/v2/core/remotes/docker/auth" remoteerrors "github.com/containerd/containerd/v2/core/remotes/errors" "github.com/containerd/errdefs" + "github.com/docker/cli/cli/config/types" + "github.com/moby/buildkit/session/auth/authprovider" "github.com/moby/buildkit/util/bklog" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) type dockerAuthorizer struct { - credentials func(string) (string, string, error) + credentials authprovider.AuthConfigProvider client *http.Client header http.Header @@ -31,7 +33,7 @@ type dockerAuthorizer struct { } type authorizerConfig struct { - credentials func(string) (string, string, error) + credentials authprovider.AuthConfigProvider client *http.Client header http.Header } @@ -47,9 +49,9 @@ func WithAuthClient(client *http.Client) AuthorizerOpt { } // WithAuthCreds provides a credential function to the authorizer -func WithAuthCreds(creds func(string) (string, string, error)) AuthorizerOpt { +func WithAuthProvider(provider authprovider.AuthConfigProvider) AuthorizerOpt { return func(opt *authorizerConfig) { - opt.credentials = creds + opt.credentials = provider } } @@ -143,10 +145,11 @@ func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.R var username, secret string if a.credentials != nil { var err error - username, secret, err = a.credentials(host) + ac, err := a.credentials(ctx, host, strings.Split(c.Parameters["scope"], " "), nil) if err != nil { return err } + username, secret = parseAuthConfig(ac) } common, err := auth.GenerateTokenOptions(ctx, host, username, secret, c) @@ -157,11 +160,13 @@ func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.R a.handlers[host] = newAuthHandler(a.client, a.header, c.Scheme, common) return nil } else if c.Scheme == auth.BasicAuth && a.credentials != nil { - username, secret, err := a.credentials(host) + ac, err := a.credentials(ctx, host, nil, nil) if err != nil { return err } + username, secret := parseAuthConfig(ac) + if username == "" || secret == "" { return errors.Wrap(err, "no basic auth credentials") } @@ -176,6 +181,13 @@ func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.R return errors.Wrap(errdefs.ErrNotImplemented, "failed to find supported auth scheme") } +func parseAuthConfig(ac types.AuthConfig) (string, string) { + if ac.IdentityToken != "" { + return "", ac.IdentityToken + } + return ac.Username, ac.Password +} + // authResult is used to control limit rate. type authResult struct { sync.WaitGroup