Skip to content

Commit 2393d9d

Browse files
authored
feat: default to ADC when gcloud cred helper is configured in docker/config.json when using docker go library (#9469)
1 parent 9d68211 commit 2393d9d

File tree

2 files changed

+125
-17
lines changed

2 files changed

+125
-17
lines changed

pkg/skaffold/docker/auth.go

+77-13
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,20 @@ 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"
33+
"github.com/google/go-containerregistry/pkg/authn"
34+
"github.com/google/go-containerregistry/pkg/v1/google"
3335

3436
"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/gcp"
3537
"github.com/GoogleContainerTools/skaffold/v2/pkg/skaffold/output/log"
@@ -79,14 +81,72 @@ func (h credsHelper) GetAuthConfig(registry string) (types.AuthConfig, error) {
7981
return types.AuthConfig{}, err
8082
}
8183

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

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

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

113173
func (h credsHelper) doGetAllAuthConfigs() (map[string]types.AuthConfig, error) {
174+
credentials := make(map[string]types.AuthConfig)
114175
cf, err := loadDockerConfig()
115176
if err != nil {
116177
return nil, err
117178
}
118179

119-
credentials, err := cf.GetAllCredentials()
180+
defaultCreds, err := cf.GetCredentialsStore("").GetAll()
120181
if err != nil {
121182
return nil, err
122183
}
123184

124-
authConfigs := make(map[string]types.AuthConfig, len(credentials))
125-
for k, auth := range credentials {
126-
authConfigs[k] = types.AuthConfig(auth)
185+
for registry, cred := range defaultCreds {
186+
credentials[registry] = types.AuthConfig(cred)
187+
}
188+
189+
for registry := range cf.CredentialHelpers {
190+
authCfg, err := h.loadCredentials(cf, registry)
191+
if err != nil {
192+
log.Entry(context.TODO()).Debugf("failed to get credentials for registry %v: %v", registry, err)
193+
continue
194+
}
195+
credentials[registry] = authCfg
127196
}
128197

129-
return authConfigs, nil
198+
return credentials, nil
130199
}
131200

132201
func (l *localDaemon) encodedRegistryAuth(ctx context.Context, a AuthConfigHelper, image string) (string, error) {
@@ -150,12 +219,7 @@ func (l *localDaemon) encodedRegistryAuth(ctx context.Context, a AuthConfigHelpe
150219
return "", fmt.Errorf("getting auth config: %w", err)
151220
}
152221

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

161225
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)