imagetools: use dockerconfig for auth

Enables fallback for DHI and Scout registries and
repo/scope specific credentials like supported for builds.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
Tonis Tiigi
2026-01-26 10:15:14 -08:00
parent e254cf1985
commit 27dde04ab5
7 changed files with 39 additions and 82 deletions
-5
View File
@@ -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
+2 -1
View File
@@ -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
+2 -1
View File
@@ -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
@@ -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
+6 -60
View File
@@ -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
+4 -9
View File
@@ -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,
+18 -6
View File
@@ -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