forked from testcontainers/testcontainers-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
docker_auth.go
149 lines (122 loc) · 3.95 KB
/
docker_auth.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package testcontainers
import (
"context"
"encoding/base64"
"encoding/json"
"net/url"
"os"
"github.com/cpuguy83/dockercfg"
"github.com/docker/docker/api/types/registry"
"github.com/testcontainers/testcontainers-go/internal/core"
)
// defaultRegistryFn is variable overwritten in tests to check for behaviour with different default values.
var defaultRegistryFn = defaultRegistry
// DockerImageAuth returns the auth config for the given Docker image, extracting first its Docker registry.
// Finally, it will use the credential helpers to extract the information from the docker config file
// for that registry, if it exists.
func DockerImageAuth(ctx context.Context, image string) (string, registry.AuthConfig, error) {
defaultRegistry := defaultRegistryFn(ctx)
reg := core.ExtractRegistry(image, defaultRegistry)
cfgs, err := getDockerAuthConfigs()
if err != nil {
return reg, registry.AuthConfig{}, err
}
if cfg, ok := getRegistryAuth(reg, cfgs); ok {
return reg, cfg, nil
}
return reg, registry.AuthConfig{}, dockercfg.ErrCredentialsNotFound
}
func getRegistryAuth(reg string, cfgs map[string]registry.AuthConfig) (registry.AuthConfig, bool) {
if cfg, ok := cfgs[reg]; ok {
return cfg, true
}
// fallback match using authentication key host
for k, cfg := range cfgs {
keyURL, err := url.Parse(k)
if err != nil {
continue
}
host := keyURL.Host
if keyURL.Scheme == "" {
// url.Parse: The url may be relative (a path, without a host) [...]
host = keyURL.Path
}
if host == reg {
return cfg, true
}
}
return registry.AuthConfig{}, false
}
// defaultRegistry returns the default registry to use when pulling images
// It will use the docker daemon to get the default registry, returning "https://index.docker.io/v1/" if
// it fails to get the information from the daemon
func defaultRegistry(ctx context.Context) string {
client, err := NewDockerClientWithOpts(ctx)
if err != nil {
return core.IndexDockerIO
}
defer client.Close()
info, err := client.Info(ctx)
if err != nil {
return core.IndexDockerIO
}
return info.IndexServerAddress
}
// getDockerAuthConfigs returns a map with the auth configs from the docker config file
// using the registry as the key
func getDockerAuthConfigs() (map[string]registry.AuthConfig, error) {
cfg, err := getDockerConfig()
if err != nil {
return nil, err
}
cfgs := map[string]registry.AuthConfig{}
for k, v := range cfg.AuthConfigs {
ac := registry.AuthConfig{
Auth: v.Auth,
Email: v.Email,
IdentityToken: v.IdentityToken,
Password: v.Password,
RegistryToken: v.RegistryToken,
ServerAddress: v.ServerAddress,
Username: v.Username,
}
if v.Username == "" && v.Password == "" {
u, p, _ := dockercfg.GetRegistryCredentials(k)
ac.Username = u
ac.Password = p
}
if v.Auth == "" {
ac.Auth = base64.StdEncoding.EncodeToString([]byte(ac.Username + ":" + ac.Password))
}
cfgs[k] = ac
}
// in the case where the auth field in the .docker/conf.json is empty, and the user has credential helpers registered
// the auth comes from there
for k := range cfg.CredentialHelpers {
ac := registry.AuthConfig{}
u, p, _ := dockercfg.GetRegistryCredentials(k)
ac.Username = u
ac.Password = p
cfgs[k] = ac
}
return cfgs, nil
}
// getDockerConfig returns the docker config file. It will internally check, in this particular order:
// 1. the DOCKER_AUTH_CONFIG environment variable, unmarshalling it into a dockercfg.Config
// 2. the DOCKER_CONFIG environment variable, as the path to the config file
// 3. else it will load the default config file, which is ~/.docker/config.json
func getDockerConfig() (dockercfg.Config, error) {
dockerAuthConfig := os.Getenv("DOCKER_AUTH_CONFIG")
if dockerAuthConfig != "" {
cfg := dockercfg.Config{}
err := json.Unmarshal([]byte(dockerAuthConfig), &cfg)
if err == nil {
return cfg, nil
}
}
cfg, err := dockercfg.LoadDefaultConfig()
if err != nil {
return cfg, err
}
return cfg, nil
}