Skip to content

Commit b59e9b3

Browse files
committed
feat: default to ADC when gcloud cred helper is configured in docker/config.json when using docker go library
1 parent e976e81 commit b59e9b3

File tree

2 files changed

+118
-17
lines changed

2 files changed

+118
-17
lines changed

pkg/skaffold/docker/auth.go

+70-13
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@ package docker
1818

1919
import (
2020
"context"
21-
"encoding/base64"
2221
"encoding/json"
2322
"fmt"
2423
"os"
2524
"path/filepath"
2625

26+
"github.com/distribution/reference"
2727
"github.com/docker/cli/cli/config"
2828
"github.com/docker/cli/cli/config/configfile"
29-
"github.com/docker/distribution/reference"
29+
clitypes "github.com/docker/cli/cli/config/types"
3030
types "github.com/docker/docker/api/types/registry"
3131
"github.com/docker/docker/pkg/homedir"
3232
"github.com/docker/docker/registry"
@@ -79,14 +79,67 @@ func (h credsHelper) GetAuthConfig(registry string) (types.AuthConfig, error) {
7979
return types.AuthConfig{}, err
8080
}
8181

82+
return h.loadCredentials(cf, registry)
83+
}
84+
85+
func (h credsHelper) loadCredentials(cf *configfile.ConfigFile, registry string) (types.AuthConfig, error) {
86+
if helper := cf.CredentialHelpers[registry]; helper == "gcloud" {
87+
authCfg, err := h.getGoogleAuthConfig(registry)
88+
if err == nil {
89+
return authCfg, nil
90+
}
91+
log.Entry(context.TODO()).Debugf("error getting google authenticator, falling back to docker auth: %v", err)
92+
}
93+
94+
var anonymous clitypes.AuthConfig
8295
auth, err := cf.GetAuthConfig(registry)
8396
if err != nil {
8497
return types.AuthConfig{}, err
8598
}
8699

100+
// From go-containerrergistry logic, the ServerAddress is not considered when determining if returned auth is anonymous.
101+
anonymous.ServerAddress = auth.ServerAddress
102+
if auth != anonymous {
103+
return types.AuthConfig(auth), nil
104+
}
105+
106+
if isGoogleRegistry(registry) {
107+
authCfg, err := h.getGoogleAuthConfig(registry)
108+
if err == nil {
109+
return authCfg, nil
110+
}
111+
}
112+
87113
return types.AuthConfig(auth), nil
88114
}
89115

116+
func (h credsHelper) getGoogleAuthConfig(registry string) (types.AuthConfig, error) {
117+
auth := getGoogleAuthenticator()
118+
if auth == nil {
119+
return types.AuthConfig{}, fmt.Errorf("error getting google authenticator")
120+
}
121+
cfg, err := auth.Authorization()
122+
if err != nil {
123+
return types.AuthConfig{}, err
124+
}
125+
126+
bCfg, err := cfg.MarshalJSON()
127+
if err != nil {
128+
return types.AuthConfig{}, err
129+
}
130+
131+
var authCfg types.AuthConfig
132+
err = json.Unmarshal(bCfg, &authCfg)
133+
if err != nil {
134+
return types.AuthConfig{}, err
135+
}
136+
137+
// The docker library does the same when we request the credentials
138+
authCfg.ServerAddress = registry
139+
140+
return authCfg, nil
141+
}
142+
90143
// GetAllAuthConfigs retrieves all the auth configs.
91144
// Because this can take a long time, we make sure it can be interrupted by the user.
92145
func (h credsHelper) GetAllAuthConfigs(ctx context.Context) (map[string]types.AuthConfig, error) {
@@ -111,22 +164,31 @@ func (h credsHelper) GetAllAuthConfigs(ctx context.Context) (map[string]types.Au
111164
}
112165

113166
func (h credsHelper) doGetAllAuthConfigs() (map[string]types.AuthConfig, error) {
167+
credentials := make(map[string]types.AuthConfig)
114168
cf, err := loadDockerConfig()
115169
if err != nil {
116170
return nil, err
117171
}
118172

119-
credentials, err := cf.GetAllCredentials()
173+
defaultCreds, err := cf.GetCredentialsStore("").GetAll()
120174
if err != nil {
121175
return nil, err
122176
}
123177

124-
authConfigs := make(map[string]types.AuthConfig, len(credentials))
125-
for k, auth := range credentials {
126-
authConfigs[k] = types.AuthConfig(auth)
178+
for registry, cred := range defaultCreds {
179+
credentials[registry] = types.AuthConfig(cred)
180+
}
181+
182+
for registry := range cf.CredentialHelpers {
183+
authCfg, err := h.loadCredentials(cf, registry)
184+
if err != nil {
185+
log.Entry(context.TODO()).Debugf("failed to get credentials for registry %v: %v", registry, err)
186+
continue
187+
}
188+
credentials[registry] = authCfg
127189
}
128190

129-
return authConfigs, nil
191+
return credentials, nil
130192
}
131193

132194
func (l *localDaemon) encodedRegistryAuth(ctx context.Context, a AuthConfigHelper, image string) (string, error) {
@@ -150,12 +212,7 @@ func (l *localDaemon) encodedRegistryAuth(ctx context.Context, a AuthConfigHelpe
150212
return "", fmt.Errorf("getting auth config: %w", err)
151213
}
152214

153-
buf, err := json.Marshal(ac)
154-
if err != nil {
155-
return "", err
156-
}
157-
158-
return base64.URLEncoding.EncodeToString(buf), nil
215+
return types.EncodeAuthConfig(ac)
159216
}
160217

161218
func (l *localDaemon) officialRegistry(ctx context.Context) string {

pkg/skaffold/docker/auth_test.go

+48-4
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,18 @@ func TestGetAllAuthConfigs(t *testing.T) {
5858
tmpDir := t.NewTempDir().
5959
Write("config.json", `{"credHelpers":{"my.registry":"helper"}}`).
6060
Write("docker-credential-gcloud", `#!/bin/sh
61-
read server
62-
echo "{\"Username\":\"<token>\",\"Secret\":\"TOKEN_$server\"}"`).
61+
read server
62+
echo "{\"Username\":\"<token>\",\"Secret\":\"TOKEN_$server\"}"`).
6363
Write("docker-credential-helper", `#!/bin/sh
64-
read server
65-
echo "{\"Username\":\"<token>\",\"Secret\":\"TOKEN_$server\"}"`)
64+
read server
65+
echo "{\"Username\":\"<token>\",\"Secret\":\"TOKEN_$server\"}"`)
6666
t.Override(&configDir, tmpDir.Root())
6767
t.Setenv("PATH", tmpDir.Root())
6868

69+
// These env values will prevent the authenticator to use the Google authenticator, it will use docker logic instead.
70+
t.Setenv("HOME", tmpDir.Root())
71+
t.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "")
72+
6973
auth, err := DefaultAuthHelper.GetAllAuthConfigs(context.Background())
7074

7175
t.CheckNoError(err)
@@ -89,6 +93,46 @@ echo "{\"Username\":\"<token>\",\"Secret\":\"TOKEN_$server\"}"`)
8993
t.CheckError(true, err)
9094
t.CheckEmpty(auth)
9195
})
96+
97+
testutil.Run(t, "Application Default Credentials authentication", func(t *testutil.T) {
98+
if runtime.GOOS == "windows" {
99+
t.Skip("test doesn't work on windows")
100+
}
101+
102+
authToken := `{"access_token":"TOKEN","expires_in": 3599}`
103+
authServerURL := startTokenServer(t, authToken)
104+
credentialsFile := getCredentialsFile(t, map[string]string{
105+
"client_id": "123456.apps.googleusercontent.com",
106+
"client_secret": "THE-SECRET",
107+
"refresh_token": "REFRESH-TOKEN",
108+
"type": "authorized_user",
109+
}, authServerURL)
110+
111+
tmpDir := t.NewTempDir().Write("credentials.json", credentialsFile)
112+
tmpDir.Write("config.json", `{"credHelpers":{"my.registry1":"helper","my.registry2":"missinghelper","gcr.io":"gcloud","us-central1-docker.pkg.dev":"otherhelper","us-east1-docker.pkg.dev":"gcloud"}}`)
113+
tmpDir.Write("docker-credential-helper", `#!/bin/sh
114+
read server
115+
echo "{\"Username\":\"<token>\",\"Secret\":\"TOKEN_$server\"}"`)
116+
tmpDir.Write("docker-credential-otherhelper", `#!/bin/sh
117+
read server
118+
echo "{\"Username\":\"<token>\",\"Secret\":\"TOKEN_$server\"}"`)
119+
120+
t.Override(&configDir, tmpDir.Root())
121+
t.SetEnvs(map[string]string{
122+
"PATH": tmpDir.Root(),
123+
"HOME": tmpDir.Root(), // This is to prevent the go-containerregistry library from using ADCs that are already present on the computer.
124+
"GOOGLE_APPLICATION_CREDENTIALS": tmpDir.Path("credentials.json"),
125+
})
126+
127+
auth, err := DefaultAuthHelper.GetAllAuthConfigs(context.Background())
128+
t.CheckNoError(err)
129+
t.CheckDeepEqual(map[string]registry.AuthConfig{
130+
"gcr.io": {Username: "_token", Password: "TOKEN", Auth: "X3Rva2VuOlRPS0VO", ServerAddress: "gcr.io"},
131+
"us-east1-docker.pkg.dev": {Username: "_token", Password: "TOKEN", Auth: "X3Rva2VuOlRPS0VO", ServerAddress: "us-east1-docker.pkg.dev"},
132+
"us-central1-docker.pkg.dev": {IdentityToken: "TOKEN_us-central1-docker.pkg.dev", ServerAddress: "us-central1-docker.pkg.dev"},
133+
"my.registry1": {IdentityToken: "TOKEN_my.registry1", ServerAddress: "my.registry1"},
134+
}, auth)
135+
})
92136
}
93137

94138
func TestGetEncodedRegistryAuth(t *testing.T) {

0 commit comments

Comments
 (0)