imagetools: support registrytoken auth in docker config

This is not supported by the Authorizer from containerd and
needs to be added manually. Build authentication happens through
BuildKit session that already supports this.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
Tonis Tiigi
2025-06-10 23:20:08 -07:00
parent 10618d4c73
commit da426ecd3a
2 changed files with 76 additions and 14 deletions
+69 -13
View File
@@ -1,28 +1,70 @@
package imagetools
import (
"context"
"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"
)
func toCredentialsFunc(a Auth) func(string) (string, string, error) {
return func(host string) (string, string, error) {
if host == "registry-1.docker.io" {
host = "https://index.docker.io/v1/"
}
ac, err := a.GetAuthConfig(host)
if err != nil {
return "", "", err
}
if ac.IdentityToken != "" {
return "", ac.IdentityToken, nil
}
return ac.Username, ac.Password, nil
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 {
return "", nil
@@ -45,3 +87,17 @@ func RegistryAuthForRef(ref string, a Auth) (string, error) {
}
return base64.URLEncoding.EncodeToString(buf), nil
}
type withBearerAuthorizer struct {
docker.Authorizer
AuthConfig *authConfig
}
func (a *withBearerAuthorizer) Authorize(ctx context.Context, req *http.Request) error {
ac, err := a.AuthConfig.authConfig(req.Host)
if err == nil && ac.RegistryToken != "" {
req.Header.Set("Authorization", "Bearer "+ac.RegistryToken)
return nil
}
return a.Authorizer.Authorize(ctx, req)
}
+7 -1
View File
@@ -34,8 +34,14 @@ type Resolver struct {
}
func New(opt Opt) *Resolver {
ac := newAuthConfig(opt.Auth)
dockerAuth := docker.NewDockerAuthorizer(docker.WithAuthCreds(ac.credentials), docker.WithAuthClient(http.DefaultClient))
auth := &withBearerAuthorizer{
Authorizer: dockerAuth,
AuthConfig: ac,
}
return &Resolver{
auth: docker.NewDockerAuthorizer(docker.WithAuthCreds(toCredentialsFunc(opt.Auth)), docker.WithAuthClient(http.DefaultClient)),
auth: auth,
hosts: resolver.NewRegistryConfig(opt.RegistryConfig),
buffer: contentutil.NewBuffer(),
}