Skip to content

Commit da426ec

Browse files
committed
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 <[email protected]>
1 parent 10618d4 commit da426ec

File tree

2 files changed

+76
-14
lines changed

2 files changed

+76
-14
lines changed

util/imagetools/auth.go

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,68 @@
11
package imagetools
22

33
import (
4+
"context"
45
"encoding/base64"
56
"encoding/json"
7+
"net/http"
8+
"sync"
9+
"time"
610

11+
"github.com/containerd/containerd/v2/core/remotes/docker"
712
"github.com/distribution/reference"
13+
"github.com/docker/cli/cli/config/types"
814
)
915

10-
func toCredentialsFunc(a Auth) func(string) (string, string, error) {
11-
return func(host string) (string, string, error) {
12-
if host == "registry-1.docker.io" {
13-
host = "https://index.docker.io/v1/"
14-
}
15-
ac, err := a.GetAuthConfig(host)
16-
if err != nil {
17-
return "", "", err
18-
}
19-
if ac.IdentityToken != "" {
20-
return "", ac.IdentityToken, nil
21-
}
22-
return ac.Username, ac.Password, nil
16+
type authConfig struct {
17+
mu sync.Mutex
18+
authConfigCache map[string]authConfigCacheEntry
19+
cfg Auth
20+
}
21+
22+
type authConfigCacheEntry struct {
23+
Created time.Time
24+
Auth types.AuthConfig
25+
}
26+
27+
func newAuthConfig(a Auth) *authConfig {
28+
return &authConfig{
29+
authConfigCache: map[string]authConfigCacheEntry{},
30+
cfg: a,
31+
}
32+
}
33+
34+
func (a *authConfig) credentials(host string) (string, string, error) {
35+
ac, err := a.authConfig(host)
36+
if err != nil {
37+
return "", "", err
38+
}
39+
if ac.IdentityToken != "" {
40+
return "", ac.IdentityToken, nil
41+
}
42+
return ac.Username, ac.Password, nil
43+
}
44+
45+
func (a *authConfig) authConfig(host string) (types.AuthConfig, error) {
46+
const defaultExpiration = 2 * time.Minute
47+
48+
if host == "registry-1.docker.io" {
49+
host = "https://index.docker.io/v1/"
50+
}
51+
a.mu.Lock()
52+
defer a.mu.Unlock()
53+
54+
if c, ok := a.authConfigCache[host]; ok && time.Since(c.Created) <= defaultExpiration {
55+
return c.Auth, nil
56+
}
57+
ac, err := a.cfg.GetAuthConfig(host)
58+
if err != nil {
59+
return types.AuthConfig{}, err
2360
}
61+
a.authConfigCache[host] = authConfigCacheEntry{
62+
Created: time.Now(),
63+
Auth: ac,
64+
}
65+
return ac, nil
2466
}
2567

2668
func RegistryAuthForRef(ref string, a Auth) (string, error) {
@@ -45,3 +87,17 @@ func RegistryAuthForRef(ref string, a Auth) (string, error) {
4587
}
4688
return base64.URLEncoding.EncodeToString(buf), nil
4789
}
90+
91+
type withBearerAuthorizer struct {
92+
docker.Authorizer
93+
AuthConfig *authConfig
94+
}
95+
96+
func (a *withBearerAuthorizer) Authorize(ctx context.Context, req *http.Request) error {
97+
ac, err := a.AuthConfig.authConfig(req.Host)
98+
if err == nil && ac.RegistryToken != "" {
99+
req.Header.Set("Authorization", "Bearer "+ac.RegistryToken)
100+
return nil
101+
}
102+
return a.Authorizer.Authorize(ctx, req)
103+
}

util/imagetools/inspect.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,14 @@ type Resolver struct {
3434
}
3535

3636
func New(opt Opt) *Resolver {
37+
ac := newAuthConfig(opt.Auth)
38+
dockerAuth := docker.NewDockerAuthorizer(docker.WithAuthCreds(ac.credentials), docker.WithAuthClient(http.DefaultClient))
39+
auth := &withBearerAuthorizer{
40+
Authorizer: dockerAuth,
41+
AuthConfig: ac,
42+
}
3743
return &Resolver{
38-
auth: docker.NewDockerAuthorizer(docker.WithAuthCreds(toCredentialsFunc(opt.Auth)), docker.WithAuthClient(http.DefaultClient)),
44+
auth: auth,
3945
hosts: resolver.NewRegistryConfig(opt.RegistryConfig),
4046
buffer: contentutil.NewBuffer(),
4147
}

0 commit comments

Comments
 (0)