From 2002204ce9c27f67fa459d438d836ad66a0db671 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 17 Apr 2025 11:24:29 +0200 Subject: [PATCH 1/2] cli/command/container: createContainer: move fn closer to where used The "use-api-socket" code got in between, putting a lot of distance between the declaration and use. Signed-off-by: Sebastiaan van Stijn --- cli/command/container/create.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/cli/command/container/create.go b/cli/command/container/create.go index 8d29cbf2a5..1dbc8a69eb 100644 --- a/cli/command/container/create.go +++ b/cli/command/container/create.go @@ -240,16 +240,6 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c } } - pullAndTagImage := func() error { - if err := pullImage(ctx, dockerCli, config.Image, options); err != nil { - return err - } - if taggedRef, ok := namedRef.(reference.NamedTagged); ok && trustedRef != nil { - return trust.TagTrusted(ctx, dockerCli.Client(), dockerCli.Err(), trustedRef, taggedRef) - } - return nil - } - const dockerConfigPathInContainer = "/run/secrets/docker/config.json" var apiSocketCreds map[string]types.AuthConfig @@ -331,6 +321,16 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c platform = &p } + pullAndTagImage := func() error { + if err := pullImage(ctx, dockerCli, config.Image, options); err != nil { + return err + } + if taggedRef, ok := namedRef.(reference.NamedTagged); ok && trustedRef != nil { + return trust.TagTrusted(ctx, dockerCli.Client(), dockerCli.Err(), trustedRef, taggedRef) + } + return nil + } + if options.pull == PullImageAlways { if err := pullAndTagImage(); err != nil { return "", err From 73be7342a6ba0f8d9fdbb8e8b687c3ede9ec4ce2 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 17 Apr 2025 12:15:45 +0200 Subject: [PATCH 2/2] cli/command/container: --use-api-socket: support DOCKER_AUTH_CONFIG With this patch, the `--use-api-socket` flag can obtain credentials from a validly formatted `DOCKER_AUTH_CONFIG` environment-variable. If the env-var is not set, or doesn't contain credentials, it falls back to attempting to read credentials from the CLI's configured credentials store. With this patch: # Make sure there's no auth on disk first mkdir -p tmpConfig export DOCKER_CONFIG=$PWD/tmpConfig rm -f $PWD/tmpConfig/config.json # no credentials docker run --rm --use-api-socket alpine cat /run/secrets/docker/config.json cat: can't open '/run/secrets/docker/config.json': No such file or directory # pass credentials through DOCKER_AUTH_CONFIG DOCKER_AUTH_CONFIG='{"auths": {"https://index.docker.io/v1/": {"auth": "am9lam9lOmhlbGxv"}}}' docker run --rm --use-api-socket alpine cat /run/secrets/docker/config.json { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv" } } } # credentials from file if no DOCKER_AUTH_CONFIG is set echo '{"auths": {"https://index.docker.io/v1/": {"auth": "am9lam9lOmhlbGxv"}}}' > "${DOCKER_CONFIG}/config.json" docker run --rm --use-api-socket alpine cat /run/secrets/docker/config.json { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv" } } } # same if DOCKER_AUTH_CONFIG is set, but doesn't contain credentials DOCKER_AUTH_CONFIG='{}' docker run --rm --use-api-socket alpine cat /run/secrets/docker/config.json { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv" } } } DOCKER_AUTH_CONFIG='{"auths": {}}' docker run --rm --use-api-socket alpine cat /run/secrets/docker/config.json { "auths": { "https://index.docker.io/v1/": { "auth": "am9lam9lOmhlbGxv" } } } Signed-off-by: Sebastiaan van Stijn --- cli/command/container/auth_config_utils.go | 46 ++++++++++++++++++++++ cli/command/container/create.go | 2 +- 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 cli/command/container/auth_config_utils.go diff --git a/cli/command/container/auth_config_utils.go b/cli/command/container/auth_config_utils.go new file mode 100644 index 0000000000..f908bd8498 --- /dev/null +++ b/cli/command/container/auth_config_utils.go @@ -0,0 +1,46 @@ +package container + +import ( + "fmt" + "os" + "strings" + + "github.com/docker/cli/cli/config" + "github.com/docker/cli/cli/config/configfile" + "github.com/docker/cli/cli/config/types" +) + +// readCredentials resolves auth-config from the current environment to be +// applied to the container if the `--use-api-socket` flag is set. +// +// - If a valid "DOCKER_AUTH_CONFIG" env-var is found, and it contains +// credentials, it's value is used. +// - If no "DOCKER_AUTH_CONFIG" env-var is found, or it does not contain +// credentials, it attempts to read from the CLI's credentials store. +// +// It returns an error if either the "DOCKER_AUTH_CONFIG" is incorrectly +// formatted, or when failing to read from the credentials store. +// +// A nil value is returned if neither option contained any credentials. +func readCredentials(dockerCLI config.Provider) (creds map[string]types.AuthConfig, _ error) { + if v, ok := os.LookupEnv("DOCKER_AUTH_CONFIG"); ok && v != "" { + // The results are expected to have been unmarshaled the same as + // when reading from a config-file, which includes decoding the + // base64-encoded "username:password" into the "UserName" and + // "Password" fields. + ac := &configfile.ConfigFile{} + if err := ac.LoadFromReader(strings.NewReader(v)); err != nil { + return nil, fmt.Errorf("failed to read credentials from DOCKER_AUTH_CONFIG: %w", err) + } + if len(ac.AuthConfigs) > 0 { + return ac.AuthConfigs, nil + } + } + + // Resolve this here for later, ensuring we error our before we create the container. + creds, err := dockerCLI.ConfigFile().GetAllCredentials() + if err != nil { + return nil, fmt.Errorf("resolving credentials failed: %w", err) + } + return creds, nil +} diff --git a/cli/command/container/create.go b/cli/command/container/create.go index 1dbc8a69eb..25647e811d 100644 --- a/cli/command/container/create.go +++ b/cli/command/container/create.go @@ -295,7 +295,7 @@ func createContainer(ctx context.Context, dockerCli command.Cli, containerCfg *c // what they're doing and don't inject the creds. if !envvarPresent { // Resolve this here for later, ensuring we error our before we create the container. - creds, err := dockerCli.ConfigFile().GetAllCredentials() + creds, err := readCredentials(dockerCli) if err != nil { return "", fmt.Errorf("resolving credentials failed: %w", err) }