diff options
Diffstat (limited to 'vendor/github.com/cpuguy83')
| -rw-r--r-- | vendor/github.com/cpuguy83/dockercfg/LICENSE | 21 | ||||
| -rw-r--r-- | vendor/github.com/cpuguy83/dockercfg/README.md | 8 | ||||
| -rw-r--r-- | vendor/github.com/cpuguy83/dockercfg/auth.go | 215 | ||||
| -rw-r--r-- | vendor/github.com/cpuguy83/dockercfg/config.go | 65 | ||||
| -rw-r--r-- | vendor/github.com/cpuguy83/dockercfg/load.go | 55 |
5 files changed, 364 insertions, 0 deletions
diff --git a/vendor/github.com/cpuguy83/dockercfg/LICENSE b/vendor/github.com/cpuguy83/dockercfg/LICENSE new file mode 100644 index 0000000..8ed6818 --- /dev/null +++ b/vendor/github.com/cpuguy83/dockercfg/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Brian Goff + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.
\ No newline at end of file diff --git a/vendor/github.com/cpuguy83/dockercfg/README.md b/vendor/github.com/cpuguy83/dockercfg/README.md new file mode 100644 index 0000000..880ab80 --- /dev/null +++ b/vendor/github.com/cpuguy83/dockercfg/README.md @@ -0,0 +1,8 @@ +### github.com/cpuguy83/dockercfg +Go library to load docker CLI configs, auths, etc. with minimal deps. +So far the only deps are on the stdlib. + +### Usage +See the [godoc](https://godoc.org/github.com/cpuguy83/dockercfg) for API details. + +I'm currently using this in [zapp](https://github.com/cpuguy83/zapp/blob/d25c43d4cd7ccf29fba184aafbc720a753e1a15d/main.go#L58-L83) to handle registry auth instead of always asking the user to enter it.
\ No newline at end of file diff --git a/vendor/github.com/cpuguy83/dockercfg/auth.go b/vendor/github.com/cpuguy83/dockercfg/auth.go new file mode 100644 index 0000000..106ab84 --- /dev/null +++ b/vendor/github.com/cpuguy83/dockercfg/auth.go @@ -0,0 +1,215 @@ +package dockercfg + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "io/fs" + "os/exec" + "runtime" + "strings" +) + +// This is used by the docker CLI in cases where an oauth identity token is used. +// In that case the username is stored literally as `<token>` +// When fetching the credentials we check for this value to determine if. +const tokenUsername = "<token>" + +// GetRegistryCredentials gets registry credentials for the passed in registry host. +// +// This will use [LoadDefaultConfig] to read registry auth details from the config. +// If the config doesn't exist, it will attempt to load registry credentials using the default credential helper for the platform. +func GetRegistryCredentials(hostname string) (string, string, error) { + cfg, err := LoadDefaultConfig() + if err != nil { + if !errors.Is(err, fs.ErrNotExist) { + return "", "", fmt.Errorf("load default config: %w", err) + } + + return GetCredentialsFromHelper("", hostname) + } + + return cfg.GetRegistryCredentials(hostname) +} + +// ResolveRegistryHost can be used to transform a docker registry host name into what is used for the docker config/cred helpers +// +// This is useful for using with containerd authorizers. +// Naturally this only transforms docker hub URLs. +func ResolveRegistryHost(host string) string { + switch host { + case "index.docker.io", "docker.io", "https://index.docker.io/v1/", "registry-1.docker.io": + return "https://index.docker.io/v1/" + } + return host +} + +// GetRegistryCredentials gets credentials, if any, for the provided hostname. +// +// Hostnames should already be resolved using [ResolveRegistryHost]. +// +// If the returned username string is empty, the password is an identity token. +func (c *Config) GetRegistryCredentials(hostname string) (string, string, error) { + h, ok := c.CredentialHelpers[hostname] + if ok { + return GetCredentialsFromHelper(h, hostname) + } + + if c.CredentialsStore != "" { + username, password, err := GetCredentialsFromHelper(c.CredentialsStore, hostname) + if err != nil { + return "", "", fmt.Errorf("get credentials from store: %w", err) + } + + if username != "" || password != "" { + return username, password, nil + } + } + + auth, ok := c.AuthConfigs[hostname] + if !ok { + return GetCredentialsFromHelper("", hostname) + } + + if auth.IdentityToken != "" { + return "", auth.IdentityToken, nil + } + + if auth.Username != "" && auth.Password != "" { + return auth.Username, auth.Password, nil + } + + return DecodeBase64Auth(auth) +} + +// DecodeBase64Auth decodes the legacy file-based auth storage from the docker CLI. +// It takes the "Auth" filed from AuthConfig and decodes that into a username and password. +// +// If "Auth" is empty, an empty user/pass will be returned, but not an error. +func DecodeBase64Auth(auth AuthConfig) (string, string, error) { + if auth.Auth == "" { + return "", "", nil + } + + decLen := base64.StdEncoding.DecodedLen(len(auth.Auth)) + decoded := make([]byte, decLen) + n, err := base64.StdEncoding.Decode(decoded, []byte(auth.Auth)) + if err != nil { + return "", "", fmt.Errorf("decode auth: %w", err) + } + + decoded = decoded[:n] + + const sep = ":" + user, pass, found := strings.Cut(string(decoded), sep) + if !found { + return "", "", fmt.Errorf("invalid auth: missing %q separator", sep) + } + + return user, pass, nil +} + +// Errors from credential helpers. +var ( + ErrCredentialsNotFound = errors.New("credentials not found in native keychain") + ErrCredentialsMissingServerURL = errors.New("no credentials server URL") +) + +//nolint:gochecknoglobals // These are used to mock exec in tests. +var ( + // execLookPath is a variable that can be used to mock exec.LookPath in tests. + execLookPath = exec.LookPath + // execCommand is a variable that can be used to mock exec.Command in tests. + execCommand = exec.Command +) + +// GetCredentialsFromHelper attempts to lookup credentials from the passed in docker credential helper. +// +// The credential helper should just be the suffix name (no "docker-credential-"). +// If the passed in helper program is empty this will look up the default helper for the platform. +// +// If the credentials are not found, no error is returned, only empty credentials. +// +// Hostnames should already be resolved using [ResolveRegistryHost] +// +// If the username string is empty, the password string is an identity token. +func GetCredentialsFromHelper(helper, hostname string) (string, string, error) { + if helper == "" { + helper, helperErr := getCredentialHelper() + if helperErr != nil { + return "", "", fmt.Errorf("get credential helper: %w", helperErr) + } + + if helper == "" { + return "", "", nil + } + } + + helper = "docker-credential-" + helper + p, err := execLookPath(helper) + if err != nil { + if !errors.Is(err, exec.ErrNotFound) { + return "", "", fmt.Errorf("look up %q: %w", helper, err) + } + + return "", "", nil + } + + var outBuf, errBuf bytes.Buffer + cmd := execCommand(p, "get") + cmd.Stdin = strings.NewReader(hostname) + cmd.Stdout = &outBuf + cmd.Stderr = &errBuf + + if err = cmd.Run(); err != nil { + out := strings.TrimSpace(outBuf.String()) + switch out { + case ErrCredentialsNotFound.Error(): + return "", "", nil + case ErrCredentialsMissingServerURL.Error(): + return "", "", ErrCredentialsMissingServerURL + default: + return "", "", fmt.Errorf("execute %q stdout: %q stderr: %q: %w", + helper, out, strings.TrimSpace(errBuf.String()), err, + ) + } + } + + var creds struct { + Username string `json:"Username"` + Secret string `json:"Secret"` + } + + if err = json.Unmarshal(outBuf.Bytes(), &creds); err != nil { + return "", "", fmt.Errorf("unmarshal credentials from: %q: %w", helper, err) + } + + // When tokenUsername is used, the output is an identity token and the username is garbage. + if creds.Username == tokenUsername { + creds.Username = "" + } + + return creds.Username, creds.Secret, nil +} + +// getCredentialHelper gets the default credential helper name for the current platform. +func getCredentialHelper() (string, error) { + switch runtime.GOOS { + case "linux": + if _, err := exec.LookPath("pass"); err != nil { + if errors.Is(err, exec.ErrNotFound) { + return "secretservice", nil + } + return "", fmt.Errorf(`look up "pass": %w`, err) + } + return "pass", nil + case "darwin": + return "osxkeychain", nil + case "windows": + return "wincred", nil + default: + return "", nil + } +} diff --git a/vendor/github.com/cpuguy83/dockercfg/config.go b/vendor/github.com/cpuguy83/dockercfg/config.go new file mode 100644 index 0000000..5e53907 --- /dev/null +++ b/vendor/github.com/cpuguy83/dockercfg/config.go @@ -0,0 +1,65 @@ +package dockercfg + +// Config represents the on disk format of the docker CLI's config file. +type Config struct { + AuthConfigs map[string]AuthConfig `json:"auths"` + HTTPHeaders map[string]string `json:"HttpHeaders,omitempty"` + PsFormat string `json:"psFormat,omitempty"` + ImagesFormat string `json:"imagesFormat,omitempty"` + NetworksFormat string `json:"networksFormat,omitempty"` + PluginsFormat string `json:"pluginsFormat,omitempty"` + VolumesFormat string `json:"volumesFormat,omitempty"` + StatsFormat string `json:"statsFormat,omitempty"` + DetachKeys string `json:"detachKeys,omitempty"` + CredentialsStore string `json:"credsStore,omitempty"` + CredentialHelpers map[string]string `json:"credHelpers,omitempty"` + Filename string `json:"-"` // Note: for internal use only. + ServiceInspectFormat string `json:"serviceInspectFormat,omitempty"` + ServicesFormat string `json:"servicesFormat,omitempty"` + TasksFormat string `json:"tasksFormat,omitempty"` + SecretFormat string `json:"secretFormat,omitempty"` + ConfigFormat string `json:"configFormat,omitempty"` + NodesFormat string `json:"nodesFormat,omitempty"` + PruneFilters []string `json:"pruneFilters,omitempty"` + Proxies map[string]ProxyConfig `json:"proxies,omitempty"` + Experimental string `json:"experimental,omitempty"` + StackOrchestrator string `json:"stackOrchestrator,omitempty"` + Kubernetes *KubernetesConfig `json:"kubernetes,omitempty"` + CurrentContext string `json:"currentContext,omitempty"` + CLIPluginsExtraDirs []string `json:"cliPluginsExtraDirs,omitempty"` + Aliases map[string]string `json:"aliases,omitempty"` +} + +// ProxyConfig contains proxy configuration settings. +type ProxyConfig struct { + HTTPProxy string `json:"httpProxy,omitempty"` + HTTPSProxy string `json:"httpsProxy,omitempty"` + NoProxy string `json:"noProxy,omitempty"` + FTPProxy string `json:"ftpProxy,omitempty"` +} + +// AuthConfig contains authorization information for connecting to a Registry. +type AuthConfig struct { + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + Auth string `json:"auth,omitempty"` + + // Email is an optional value associated with the username. + // This field is deprecated and will be removed in a later + // version of docker. + Email string `json:"email,omitempty"` + + ServerAddress string `json:"serveraddress,omitempty"` + + // IdentityToken is used to authenticate the user and get + // an access token for the registry. + IdentityToken string `json:"identitytoken,omitempty"` + + // RegistryToken is a bearer token to be sent to a registry. + RegistryToken string `json:"registrytoken,omitempty"` +} + +// KubernetesConfig contains Kubernetes orchestrator settings. +type KubernetesConfig struct { + AllNamespaces string `json:"allNamespaces,omitempty"` +} diff --git a/vendor/github.com/cpuguy83/dockercfg/load.go b/vendor/github.com/cpuguy83/dockercfg/load.go new file mode 100644 index 0000000..a1c4dca --- /dev/null +++ b/vendor/github.com/cpuguy83/dockercfg/load.go @@ -0,0 +1,55 @@ +package dockercfg + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" +) + +// UserHomeConfigPath returns the path to the docker config in the current user's home dir. +func UserHomeConfigPath() (string, error) { + home, err := os.UserHomeDir() + if err != nil { + return "", fmt.Errorf("user home dir: %w", err) + } + + return filepath.Join(home, ".docker", "config.json"), nil +} + +// ConfigPath returns the path to the docker cli config. +// +// It will either use the DOCKER_CONFIG env var if set, or the value from [UserHomeConfigPath] +// DOCKER_CONFIG would be the dir path where `config.json` is stored, this returns the path to config.json. +func ConfigPath() (string, error) { + if p := os.Getenv("DOCKER_CONFIG"); p != "" { + return filepath.Join(p, "config.json"), nil + } + return UserHomeConfigPath() +} + +// LoadDefaultConfig loads the docker cli config from the path returned from [ConfigPath]. +func LoadDefaultConfig() (Config, error) { + var cfg Config + p, err := ConfigPath() + if err != nil { + return cfg, fmt.Errorf("config path: %w", err) + } + + return cfg, FromFile(p, &cfg) +} + +// FromFile loads config from the specified path into cfg. +func FromFile(configPath string, cfg *Config) error { + f, err := os.Open(configPath) + if err != nil { + return fmt.Errorf("open config: %w", err) + } + defer f.Close() + + if err = json.NewDecoder(f).Decode(&cfg); err != nil { + return fmt.Errorf("decode config: %w", err) + } + + return nil +} |
