package registry import ( "encoding/base64" "encoding/json" "errors" "os" "github.com/containrrr/watchtower/pkg/registry/helpers" cliconfig "github.com/docker/cli/cli/config" "github.com/docker/cli/cli/config/configfile" "github.com/docker/cli/cli/config/credentials" "github.com/docker/cli/cli/config/types" log "github.com/sirupsen/logrus" ) // EncodedAuth returns an encoded auth config for the given registry // loaded from environment variables or docker config // as available in that order func EncodedAuth(ref string) (string, error) { auth, err := EncodedEnvAuth() if err != nil { auth, err = EncodedConfigAuth(ref) } return auth, err } // EncodedEnvAuth returns an encoded auth config for the given registry // loaded from environment variables // Returns an error if authentication environment variables have not been set func EncodedEnvAuth() (string, error) { username := os.Getenv("REPO_USER") password := os.Getenv("REPO_PASS") if username != "" && password != "" { auth := types.AuthConfig{ Username: username, Password: password, } log.Debugf("Loaded auth credentials for registry user %s from environment", auth.Username) // CREDENTIAL: Uncomment to log REPO_PASS environment variable // log.Tracef("Using auth password %s", auth.Password) return EncodeAuth(auth) } return "", errors.New("registry auth environment variables (REPO_USER, REPO_PASS) not set") } // EncodedConfigAuth returns an encoded auth config for the given registry // loaded from the docker config // Returns an empty string if credentials cannot be found for the referenced server // The docker config must be mounted on the container func EncodedConfigAuth(imageRef string) (string, error) { server, err := helpers.GetRegistryAddress(imageRef) if err != nil { log.Errorf("Could not get registry from image ref %s", imageRef) return "", err } configDir := os.Getenv("DOCKER_CONFIG") if configDir == "" { configDir = "/" } configFile, err := cliconfig.Load(configDir) if err != nil { log.Errorf("Unable to find default config file: %s", err) return "", err } credStore := CredentialsStore(*configFile) auth, _ := credStore.Get(server) // returns (types.AuthConfig{}) if server not in credStore if auth == (types.AuthConfig{}) { log.WithField("config_file", configFile.Filename).Debugf("No credentials for %s found", server) return "", nil } log.Debugf("Loaded auth credentials for user %s, on registry %s, from file %s", auth.Username, server, configFile.Filename) // CREDENTIAL: Uncomment to log docker config password // log.Tracef("Using auth password %s", auth.Password) return EncodeAuth(auth) } // CredentialsStore returns a new credentials store based // on the settings provided in the configuration file. func CredentialsStore(configFile configfile.ConfigFile) credentials.Store { if configFile.CredentialsStore != "" { return credentials.NewNativeStore(&configFile, configFile.CredentialsStore) } return credentials.NewFileStore(&configFile) } // EncodeAuth Base64 encode an AuthConfig struct for transmission over HTTP func EncodeAuth(authConfig types.AuthConfig) (string, error) { buf, err := json.Marshal(authConfig) if err != nil { return "", err } return base64.URLEncoding.EncodeToString(buf), nil }