From 13a585240ab863f0d4e2bbb31b4eb7d2065326e3 Mon Sep 17 00:00:00 2001 From: Rafael Matias Date: Wed, 2 Oct 2024 15:18:20 +0200 Subject: [PATCH] feat: support auth from docker config --- .../docker/docker_manager/docker_auth.go | 98 +++++++++++++++++++ .../docker/docker_manager/docker_manager.go | 15 +++ 2 files changed, 113 insertions(+) create mode 100644 container-engine-lib/lib/backend_impls/docker/docker_manager/docker_auth.go diff --git a/container-engine-lib/lib/backend_impls/docker/docker_manager/docker_auth.go b/container-engine-lib/lib/backend_impls/docker/docker_manager/docker_auth.go new file mode 100644 index 0000000000..bffda4d18c --- /dev/null +++ b/container-engine-lib/lib/backend_impls/docker/docker_manager/docker_auth.go @@ -0,0 +1,98 @@ +package docker_manager + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "os/exec" + "strings" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/registry" + dockerregistry "github.com/docker/docker/registry" +) + +// RegistryAuthConfig holds authentication configuration for a container registry +type RegistryAuthConfig struct { + Auths map[string]registry.AuthConfig `json:"auths"` + CredHelpers map[string]string `json:"credHelpers"` + CredsStore string `json:"credsStore"` +} + +// loadDockerAuth loads the authentication configuration from the config.json file located in $DOCKER_CONFIG or ~/.docker +func loadDockerAuth() (RegistryAuthConfig, error) { + configFilePath := os.Getenv("DOCKER_CONFIG") + if configFilePath == "" { + configFilePath = os.Getenv("HOME") + "/.docker/config.json" + } else { + configFilePath = configFilePath + "/config.json" + } + + file, err := os.ReadFile(configFilePath) + if err != nil { + return RegistryAuthConfig{}, fmt.Errorf("error reading Docker config file: %v", err) + } + + var authConfig RegistryAuthConfig + if err := json.Unmarshal(file, &authConfig); err != nil { + return RegistryAuthConfig{}, fmt.Errorf("error unmarshalling Docker config file: %v", err) + } + + return authConfig, nil +} + +// getCredentialsFromStore fetches credentials from a Docker credential helper (credStore) +func getCredentialsFromStore(credHelper string, registryURL string) (types.AuthConfig, error) { + // Prepare the helper command (docker-credential-) + credHelperCmd := "docker-credential-" + credHelper + + // Execute the credential helper to get credentials for the registry + cmd := exec.Command(credHelperCmd, "get") + cmd.Stdin = strings.NewReader(registryURL) + + var out bytes.Buffer + cmd.Stdout = &out + var stderr bytes.Buffer + cmd.Stderr = &stderr + + if err := cmd.Run(); err != nil { + return registry.AuthConfig{}, fmt.Errorf("error executing credential helper %s: %v, %s", credHelperCmd, err, stderr.String()) + } + + // Parse the output (it should return JSON containing "Username" and "Secret") + var creds registry.AuthConfig + if err := json.Unmarshal(out.Bytes(), &creds); err != nil { + return registry.AuthConfig{}, fmt.Errorf("error parsing credentials from store: %v", err) + } + + return creds, nil +} + +// getAuthFromDockerConfig retrieves the auth configuration for a given repository +func getAuthFromDockerConfig(repo string) (registry.AuthConfig, error) { + authConfig, err := loadDockerAuth() + if err != nil { + return registry.AuthConfig{}, err + } + + registryHost := dockerregistry.ConvertToHostname(repo) + + // 1. Check if there is a credHelper for this specific registry + if credHelper, exists := authConfig.CredHelpers[registryHost]; exists { + return getCredentialsFromStore(credHelper, registryHost) + } + + // 2. Check if there is a default credStore for all registries + if authConfig.CredsStore != "" { + return getCredentialsFromStore(authConfig.CredsStore, registryHost) + } + + // 3. Fallback to credentials in "auths" if no credStore is available + if auth, exists := authConfig.Auths[registryHost]; exists { + return auth, nil + } + + // Return an empty AuthConfig if no credentials were found + return registry.AuthConfig{}, nil +} diff --git a/container-engine-lib/lib/backend_impls/docker/docker_manager/docker_manager.go b/container-engine-lib/lib/backend_impls/docker/docker_manager/docker_manager.go index bd4e771c04..a38cfa0984 100644 --- a/container-engine-lib/lib/backend_impls/docker/docker_manager/docker_manager.go +++ b/container-engine-lib/lib/backend_impls/docker/docker_manager/docker_manager.go @@ -2279,6 +2279,21 @@ func pullImage(dockerClient *client.Client, imageName string, registrySpec *imag PrivilegeFunc: nil, Platform: platform, } + + // Try to obtain the auth configuration from the docker config file + authConfig, err := getAuthFromDockerConfig(imageName) + if err != nil { + logrus.Errorf("An error occurred while getting auth config for image: %s: %s", imageName, err.Error()) + } + + authFromConfig, err := registry.EncodeAuthConfig(authConfig) + if err != nil { + logrus.Errorf("An error occurred while encoding auth config for image: %s: %s", imageName, err.Error()) + } else { + imagePullOptions.RegistryAuth = authFromConfig + } + + // If the registry spec is defined, use that for authentication if registrySpec != nil { authConfig := registry.AuthConfig{ Username: registrySpec.GetUsername(),