Docker auth (#891)

* feat: read docker credentials from local docker config

* fix: url.Parse requires protocol

Co-authored-by: Björn Brauer <zaubernerd@zaubernerd.de>

* fix: docker decides by the existence of . or : if...

... the image is in a custom registry or not.

Co-authored-by: Björn Brauer <zaubernerd@zaubernerd.de>

* fix: make docker hostname detection more robust

* test: mock docker config for getImagePullOptions test

By default github actions have a docker config set with a token to pull
images from docker hub.

Co-authored-by: Markus Wolf <markus.wolf@new-work.se>

Co-authored-by: Markus Wolf <markus.wolf@new-work.se>
This commit is contained in:
Björn Brauer 2021-11-27 19:21:32 +01:00 committed by GitHub
parent 5bdb9ed0fd
commit b910a42edf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 74 additions and 2 deletions

1
go.sum
View file

@ -408,6 +408,7 @@ github.com/docker/docker v20.10.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05b
github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.10+incompatible h1:GKkP0T7U4ks6X3lmmHKC2QDprnpRJor2Z5a8m62R9ZM=
github.com/docker/docker v20.10.10+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ=
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=

View file

@ -0,0 +1,36 @@
package container
import (
"strings"
"github.com/docker/cli/cli/config"
"github.com/docker/cli/cli/config/credentials"
"github.com/docker/docker/api/types"
log "github.com/sirupsen/logrus"
)
func LoadDockerAuthConfig(image string) (types.AuthConfig, error) {
config, err := config.Load(config.Dir())
if err != nil {
log.Warnf("Could not load docker config: %v", err)
return types.AuthConfig{}, err
}
if !config.ContainsAuth() {
config.CredentialsStore = credentials.DetectDefaultStore(config.CredentialsStore)
}
hostName := "index.docker.io"
index := strings.IndexRune(image, '/')
if index > -1 && (strings.ContainsAny(image[:index], ".:") || image[:index] == "localhost") {
hostName = image[:index]
}
authConfig, err := config.GetAuthConfig(hostName)
if err != nil {
log.Warnf("Could not get auth config from docker config: %v", err)
return types.AuthConfig{}, err
}
return types.AuthConfig(authConfig), nil
}

View file

@ -77,6 +77,7 @@ func getImagePullOptions(ctx context.Context, input NewDockerPullExecutorInput)
imagePullOptions := types.ImagePullOptions{
Platform: input.Platform,
}
if input.Username != "" && input.Password != "" {
logger := common.Logger(ctx)
logger.Debugf("using authentication for docker pull")
@ -91,6 +92,21 @@ func getImagePullOptions(ctx context.Context, input NewDockerPullExecutorInput)
return imagePullOptions, err
}
imagePullOptions.RegistryAuth = base64.URLEncoding.EncodeToString(encodedJSON)
} else {
authConfig, err := LoadDockerAuthConfig(input.Image)
if err != nil {
return imagePullOptions, err
}
if authConfig.Username == "" && authConfig.Password == "" {
return imagePullOptions, nil
}
encodedJSON, err := json.Marshal(authConfig)
if err != nil {
return imagePullOptions, err
}
imagePullOptions.RegistryAuth = base64.URLEncoding.EncodeToString(encodedJSON)
}

View file

@ -4,6 +4,8 @@ import (
"context"
"testing"
"github.com/docker/cli/cli/config"
log "github.com/sirupsen/logrus"
assert "github.com/stretchr/testify/assert"
)
@ -35,9 +37,11 @@ func TestCleanImage(t *testing.T) {
func TestGetImagePullOptions(t *testing.T) {
ctx := context.Background()
config.SetDir("/non-existent/docker")
options, err := getImagePullOptions(ctx, NewDockerPullExecutorInput{})
assert.Nil(t, err, "Failed to create ImagePullOptions")
assert.Equal(t, options.RegistryAuth, "", "RegistryAuth should be empty if no username or password is set")
assert.Equal(t, "", options.RegistryAuth, "RegistryAuth should be empty if no username or password is set")
options, err = getImagePullOptions(ctx, NewDockerPullExecutorInput{
Image: "",
@ -45,5 +49,13 @@ func TestGetImagePullOptions(t *testing.T) {
Password: "password",
})
assert.Nil(t, err, "Failed to create ImagePullOptions")
assert.Equal(t, options.RegistryAuth, "eyJ1c2VybmFtZSI6InVzZXJuYW1lIiwicGFzc3dvcmQiOiJwYXNzd29yZCJ9", "Username and Password should be provided")
assert.Equal(t, "eyJ1c2VybmFtZSI6InVzZXJuYW1lIiwicGFzc3dvcmQiOiJwYXNzd29yZCJ9", options.RegistryAuth, "Username and Password should be provided")
config.SetDir("testdata/docker-pull-options")
options, err = getImagePullOptions(ctx, NewDockerPullExecutorInput{
Image: "nektos/act",
})
assert.Nil(t, err, "Failed to create ImagePullOptions")
assert.Equal(t, "eyJ1c2VybmFtZSI6InVzZXJuYW1lIiwicGFzc3dvcmQiOiJwYXNzd29yZFxuIiwic2VydmVyYWRkcmVzcyI6Imh0dHBzOi8vaW5kZXguZG9ja2VyLmlvL3YxLyJ9", options.RegistryAuth, "RegistryAuth should be taken from local docker config")
}

View file

@ -0,0 +1,7 @@
{
"auths": {
"https://index.docker.io/v1/": {
"auth": "dXNlcm5hbWU6cGFzc3dvcmQK"
}
}
}