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:
parent
5bdb9ed0fd
commit
b910a42edf
5 changed files with 74 additions and 2 deletions
1
go.sum
1
go.sum
|
@ -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.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 h1:GKkP0T7U4ks6X3lmmHKC2QDprnpRJor2Z5a8m62R9ZM=
|
||||||
github.com/docker/docker v20.10.10+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
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/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 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||||
|
|
36
pkg/container/docker_auth.go
Normal file
36
pkg/container/docker_auth.go
Normal 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
|
||||||
|
}
|
|
@ -77,6 +77,7 @@ func getImagePullOptions(ctx context.Context, input NewDockerPullExecutorInput)
|
||||||
imagePullOptions := types.ImagePullOptions{
|
imagePullOptions := types.ImagePullOptions{
|
||||||
Platform: input.Platform,
|
Platform: input.Platform,
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.Username != "" && input.Password != "" {
|
if input.Username != "" && input.Password != "" {
|
||||||
logger := common.Logger(ctx)
|
logger := common.Logger(ctx)
|
||||||
logger.Debugf("using authentication for docker pull")
|
logger.Debugf("using authentication for docker pull")
|
||||||
|
@ -91,6 +92,21 @@ func getImagePullOptions(ctx context.Context, input NewDockerPullExecutorInput)
|
||||||
return imagePullOptions, err
|
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)
|
imagePullOptions.RegistryAuth = base64.URLEncoding.EncodeToString(encodedJSON)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/cli/cli/config"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
assert "github.com/stretchr/testify/assert"
|
assert "github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
@ -35,9 +37,11 @@ func TestCleanImage(t *testing.T) {
|
||||||
func TestGetImagePullOptions(t *testing.T) {
|
func TestGetImagePullOptions(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
config.SetDir("/non-existent/docker")
|
||||||
|
|
||||||
options, err := getImagePullOptions(ctx, NewDockerPullExecutorInput{})
|
options, err := getImagePullOptions(ctx, NewDockerPullExecutorInput{})
|
||||||
assert.Nil(t, err, "Failed to create ImagePullOptions")
|
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{
|
options, err = getImagePullOptions(ctx, NewDockerPullExecutorInput{
|
||||||
Image: "",
|
Image: "",
|
||||||
|
@ -45,5 +49,13 @@ func TestGetImagePullOptions(t *testing.T) {
|
||||||
Password: "password",
|
Password: "password",
|
||||||
})
|
})
|
||||||
assert.Nil(t, err, "Failed to create ImagePullOptions")
|
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")
|
||||||
}
|
}
|
||||||
|
|
7
pkg/container/testdata/docker-pull-options/config.json
vendored
Normal file
7
pkg/container/testdata/docker-pull-options/config.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"auths": {
|
||||||
|
"https://index.docker.io/v1/": {
|
||||||
|
"auth": "dXNlcm5hbWU6cGFzc3dvcmQK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue