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:
+69
-13
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user