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:
@@ -8,7 +8,6 @@ import (
|
|||||||
|
|
||||||
"github.com/docker/buildx/store"
|
"github.com/docker/buildx/store"
|
||||||
"github.com/docker/buildx/util/progress"
|
"github.com/docker/buildx/util/progress"
|
||||||
clitypes "github.com/docker/cli/cli/config/types"
|
|
||||||
controlapi "github.com/moby/buildkit/api/services/control"
|
controlapi "github.com/moby/buildkit/api/services/control"
|
||||||
"github.com/moby/buildkit/client"
|
"github.com/moby/buildkit/client"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@@ -58,10 +57,6 @@ type Info struct {
|
|||||||
DynamicNodes []store.Node
|
DynamicNodes []store.Node
|
||||||
}
|
}
|
||||||
|
|
||||||
type Auth interface {
|
|
||||||
GetAuthConfig(registryHostname string) (clitypes.AuthConfig, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Driver interface {
|
type Driver interface {
|
||||||
Factory() Factory
|
Factory() Factory
|
||||||
Bootstrap(context.Context, progress.Logger) error
|
Bootstrap(context.Context, progress.Logger) error
|
||||||
|
|||||||
+2
-1
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/docker/cli/cli/context/store"
|
"github.com/docker/cli/cli/context/store"
|
||||||
"github.com/moby/buildkit/client"
|
"github.com/moby/buildkit/client"
|
||||||
|
"github.com/moby/buildkit/session/auth/authprovider"
|
||||||
"github.com/moby/buildkit/util/tracing/delegated"
|
"github.com/moby/buildkit/util/tracing/delegated"
|
||||||
dockerclient "github.com/moby/moby/client"
|
dockerclient "github.com/moby/moby/client"
|
||||||
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
@@ -35,7 +36,7 @@ type InitConfig struct {
|
|||||||
BuildkitdFlags []string
|
BuildkitdFlags []string
|
||||||
Files map[string][]byte
|
Files map[string][]byte
|
||||||
DriverOpts map[string]string
|
DriverOpts map[string]string
|
||||||
Auth Auth
|
Auth authprovider.AuthConfigProvider
|
||||||
Platforms []ocispecs.Platform
|
Platforms []ocispecs.Platform
|
||||||
ContextPathHash string
|
ContextPathHash string
|
||||||
DialMeta map[string][]string
|
DialMeta map[string][]string
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/docker/buildx/store"
|
"github.com/docker/buildx/store"
|
||||||
"github.com/docker/buildx/util/confutil"
|
"github.com/docker/buildx/util/confutil"
|
||||||
"github.com/docker/buildx/util/dockerutil"
|
"github.com/docker/buildx/util/dockerutil"
|
||||||
|
"github.com/docker/buildx/util/dockerutil/dockerconfig"
|
||||||
"github.com/docker/buildx/util/imagetools"
|
"github.com/docker/buildx/util/imagetools"
|
||||||
"github.com/docker/buildx/util/resolver"
|
"github.com/docker/buildx/util/resolver"
|
||||||
"github.com/docker/cli/cli/command"
|
"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) {
|
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 {
|
if ng == nil || len(ng.Nodes) == 0 {
|
||||||
return opt, nil
|
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) {
|
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)
|
ac, err := ap.loadHost(ctx, host, scopes, cacheExpireCheck)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.AuthConfig{}, err
|
return types.AuthConfig{}, err
|
||||||
|
|||||||
+6
-60
@@ -5,68 +5,14 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/containerd/containerd/v2/core/remotes/docker"
|
"github.com/containerd/containerd/v2/core/remotes/docker"
|
||||||
"github.com/distribution/reference"
|
"github.com/distribution/reference"
|
||||||
"github.com/docker/cli/cli/config/types"
|
"github.com/moby/buildkit/session/auth/authprovider"
|
||||||
)
|
)
|
||||||
|
|
||||||
type authConfig struct {
|
func RegistryAuthForRef(ref string, auth authprovider.AuthConfigProvider) (string, error) {
|
||||||
mu sync.Mutex
|
if auth == nil {
|
||||||
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 {
|
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
r, err := parseRef(ref)
|
r, err := parseRef(ref)
|
||||||
@@ -77,7 +23,7 @@ func RegistryAuthForRef(ref string, a Auth) (string, error) {
|
|||||||
if host == "docker.io" {
|
if host == "docker.io" {
|
||||||
host = "https://index.docker.io/v1/"
|
host = "https://index.docker.io/v1/"
|
||||||
}
|
}
|
||||||
ac, err := a.GetAuthConfig(host)
|
ac, err := auth(context.TODO(), host, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -90,11 +36,11 @@ func RegistryAuthForRef(ref string, a Auth) (string, error) {
|
|||||||
|
|
||||||
type withBearerAuthorizer struct {
|
type withBearerAuthorizer struct {
|
||||||
docker.Authorizer
|
docker.Authorizer
|
||||||
AuthConfig *authConfig
|
AuthConfig authprovider.AuthConfigProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *withBearerAuthorizer) Authorize(ctx context.Context, req *http.Request) error {
|
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 != "" {
|
if err == nil && ac.RegistryToken != "" {
|
||||||
req.Header.Set("Authorization", "Bearer "+ac.RegistryToken)
|
req.Header.Set("Authorization", "Bearer "+ac.RegistryToken)
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -12,19 +12,15 @@ import (
|
|||||||
"github.com/distribution/reference"
|
"github.com/distribution/reference"
|
||||||
"github.com/docker/buildx/util/resolver"
|
"github.com/docker/buildx/util/resolver"
|
||||||
"github.com/docker/buildx/util/resolver/auth"
|
"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/contentutil"
|
||||||
"github.com/moby/buildkit/util/tracing"
|
"github.com/moby/buildkit/util/tracing"
|
||||||
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Auth interface {
|
|
||||||
GetAuthConfig(registryHostname string) (clitypes.AuthConfig, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Opt struct {
|
type Opt struct {
|
||||||
Auth Auth
|
Auth authprovider.AuthConfigProvider
|
||||||
RegistryConfig map[string]resolver.RegistryConfig
|
RegistryConfig map[string]resolver.RegistryConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,11 +31,10 @@ type Resolver struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func New(opt Opt) *Resolver {
|
func New(opt Opt) *Resolver {
|
||||||
ac := newAuthConfig(opt.Auth)
|
dockerAuth := auth.NewDockerAuthorizer(auth.WithAuthProvider(opt.Auth), auth.WithAuthClient(http.DefaultClient))
|
||||||
dockerAuth := auth.NewDockerAuthorizer(auth.WithAuthCreds(ac.credentials), auth.WithAuthClient(http.DefaultClient))
|
|
||||||
auth := &withBearerAuthorizer{
|
auth := &withBearerAuthorizer{
|
||||||
Authorizer: dockerAuth,
|
Authorizer: dockerAuth,
|
||||||
AuthConfig: ac,
|
AuthConfig: opt.Auth,
|
||||||
}
|
}
|
||||||
return &Resolver{
|
return &Resolver{
|
||||||
auth: auth,
|
auth: auth,
|
||||||
|
|||||||
@@ -14,13 +14,15 @@ import (
|
|||||||
"github.com/containerd/containerd/v2/core/remotes/docker/auth"
|
"github.com/containerd/containerd/v2/core/remotes/docker/auth"
|
||||||
remoteerrors "github.com/containerd/containerd/v2/core/remotes/errors"
|
remoteerrors "github.com/containerd/containerd/v2/core/remotes/errors"
|
||||||
"github.com/containerd/errdefs"
|
"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/moby/buildkit/util/bklog"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type dockerAuthorizer struct {
|
type dockerAuthorizer struct {
|
||||||
credentials func(string) (string, string, error)
|
credentials authprovider.AuthConfigProvider
|
||||||
|
|
||||||
client *http.Client
|
client *http.Client
|
||||||
header http.Header
|
header http.Header
|
||||||
@@ -31,7 +33,7 @@ type dockerAuthorizer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type authorizerConfig struct {
|
type authorizerConfig struct {
|
||||||
credentials func(string) (string, string, error)
|
credentials authprovider.AuthConfigProvider
|
||||||
client *http.Client
|
client *http.Client
|
||||||
header http.Header
|
header http.Header
|
||||||
}
|
}
|
||||||
@@ -47,9 +49,9 @@ func WithAuthClient(client *http.Client) AuthorizerOpt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WithAuthCreds provides a credential function to the authorizer
|
// 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) {
|
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
|
var username, secret string
|
||||||
if a.credentials != nil {
|
if a.credentials != nil {
|
||||||
var err error
|
var err error
|
||||||
username, secret, err = a.credentials(host)
|
ac, err := a.credentials(ctx, host, strings.Split(c.Parameters["scope"], " "), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
username, secret = parseAuthConfig(ac)
|
||||||
}
|
}
|
||||||
|
|
||||||
common, err := auth.GenerateTokenOptions(ctx, host, username, secret, c)
|
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)
|
a.handlers[host] = newAuthHandler(a.client, a.header, c.Scheme, common)
|
||||||
return nil
|
return nil
|
||||||
} else if c.Scheme == auth.BasicAuth && a.credentials != 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
username, secret := parseAuthConfig(ac)
|
||||||
|
|
||||||
if username == "" || secret == "" {
|
if username == "" || secret == "" {
|
||||||
return errors.Wrap(err, "no basic auth credentials")
|
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")
|
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.
|
// authResult is used to control limit rate.
|
||||||
type authResult struct {
|
type authResult struct {
|
||||||
sync.WaitGroup
|
sync.WaitGroup
|
||||||
|
|||||||
Reference in New Issue
Block a user