summaryrefslogtreecommitdiff
path: root/vendor/github.com/cpuguy83
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/cpuguy83')
-rw-r--r--vendor/github.com/cpuguy83/dockercfg/LICENSE21
-rw-r--r--vendor/github.com/cpuguy83/dockercfg/README.md8
-rw-r--r--vendor/github.com/cpuguy83/dockercfg/auth.go215
-rw-r--r--vendor/github.com/cpuguy83/dockercfg/config.go65
-rw-r--r--vendor/github.com/cpuguy83/dockercfg/load.go55
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
+}