diff --git a/vendor/github.com/containers/image/docker/docker_client.go b/vendor/github.com/containers/image/docker/docker_client.go index 51149040..217e9dcb 100644 --- a/vendor/github.com/containers/image/docker/docker_client.go +++ b/vendor/github.com/containers/image/docker/docker_client.go @@ -3,23 +3,20 @@ package docker import ( "context" "crypto/tls" - "encoding/base64" "encoding/json" "fmt" "io" "io/ioutil" "net/http" - "os" "path/filepath" "strings" "time" "github.com/containers/image/docker/reference" + "github.com/containers/image/pkg/docker/config" "github.com/containers/image/pkg/tlsclientconfig" "github.com/containers/image/types" - "github.com/containers/storage/pkg/homedir" "github.com/docker/distribution/registry/client" - helperclient "github.com/docker/docker-credential-helpers/client" "github.com/docker/go-connections/tlsconfig" "github.com/opencontainers/go-digest" "github.com/pkg/errors" @@ -27,13 +24,8 @@ import ( ) const ( - dockerHostname = "docker.io" - dockerRegistry = "registry-1.docker.io" - dockerAuthRegistry = "https://index.docker.io/v1/" - - dockerCfg = ".docker" - dockerCfgFileName = "config.json" - dockerCfgObsolete = ".dockercfg" + dockerHostname = "docker.io" + dockerRegistry = "registry-1.docker.io" systemPerHostCertDirPath = "/etc/docker/certs.d" @@ -51,9 +43,13 @@ const ( extensionSignatureTypeAtomic = "atomic" // extensionSignature.Type ) -// ErrV1NotSupported is returned when we're trying to talk to a -// docker V1 registry. -var ErrV1NotSupported = errors.New("can't talk to a V1 docker registry") +var ( + // ErrV1NotSupported is returned when we're trying to talk to a + // docker V1 registry. + ErrV1NotSupported = errors.New("can't talk to a V1 docker registry") + // ErrUnauthorizedForCredentials is returned when the status code returned is 401 + ErrUnauthorizedForCredentials = errors.New("unable to retrieve auth token: invalid username/password") +) // extensionSignature and extensionSignatureList come from github.com/openshift/origin/pkg/dockerregistry/server/signaturedispatcher.go: // signature represents a Docker image signature. @@ -128,52 +124,84 @@ func dockerCertDir(ctx *types.SystemContext, hostPort string) string { return filepath.Join(hostCertDir, hostPort) } -// newDockerClient returns a new dockerClient instance for refHostname (a host a specified in the Docker image reference, not canonicalized to dockerRegistry) +// newDockerClientFromRef returns a new dockerClient instance for refHostname (a host a specified in the Docker image reference, not canonicalized to dockerRegistry) // “write” specifies whether the client will be used for "write" access (in particular passed to lookaside.go:toplevelFromSection) -func newDockerClient(ctx *types.SystemContext, ref dockerReference, write bool, actions string) (*dockerClient, error) { +func newDockerClientFromRef(ctx *types.SystemContext, ref dockerReference, write bool, actions string) (*dockerClient, error) { registry := reference.Domain(ref.ref) - if registry == dockerHostname { - registry = dockerRegistry + username, password, err := config.GetAuthentication(ctx, reference.Domain(ref.ref)) + if err != nil { + return nil, errors.Wrapf(err, "error getting username and password") } - username, password, err := getAuth(ctx, reference.Domain(ref.ref)) + sigBase, err := configuredSignatureStorageBase(ctx, ref, write) if err != nil { return nil, err } + remoteName := reference.Path(ref.ref) + + return newDockerClientWithDetails(ctx, registry, username, password, actions, sigBase, remoteName) +} + +// newDockerClientWithDetails returns a new dockerClient instance for the given parameters +func newDockerClientWithDetails(ctx *types.SystemContext, registry, username, password, actions string, sigBase signatureStorageBase, remoteName string) (*dockerClient, error) { + hostName := registry + if registry == dockerHostname { + registry = dockerRegistry + } tr := tlsclientconfig.NewTransport() tr.TLSClientConfig = serverDefault() + // It is undefined whether the host[:port] string for dockerHostname should be dockerHostname or dockerRegistry, // because docker/docker does not read the certs.d subdirectory at all in that case. We use the user-visible // dockerHostname here, because it is more symmetrical to read the configuration in that case as well, and because // generally the UI hides the existence of the different dockerRegistry. But note that this behavior is // undocumented and may change if docker/docker changes. - certDir := dockerCertDir(ctx, reference.Domain(ref.ref)) + certDir := dockerCertDir(ctx, hostName) if err := tlsclientconfig.SetupCertificates(certDir, tr.TLSClientConfig); err != nil { return nil, err } + if ctx != nil && ctx.DockerInsecureSkipTLSVerify { tr.TLSClientConfig.InsecureSkipVerify = true } - client := &http.Client{Transport: tr} - - sigBase, err := configuredSignatureStorageBase(ctx, ref, write) - if err != nil { - return nil, err - } return &dockerClient{ ctx: ctx, registry: registry, username: username, password: password, - client: client, + client: &http.Client{Transport: tr}, signatureBase: sigBase, scope: authScope{ actions: actions, - remoteName: reference.Path(ref.ref), + remoteName: remoteName, }, }, nil } +// CheckAuth validates the credentials by attempting to log into the registry +// returns an error if an error occcured while making the http request or the status code received was 401 +func CheckAuth(ctx context.Context, sCtx *types.SystemContext, username, password, registry string) error { + newLoginClient, err := newDockerClientWithDetails(sCtx, registry, username, password, "", nil, "") + if err != nil { + return errors.Wrapf(err, "error creating new docker client") + } + + resp, err := newLoginClient.makeRequest(ctx, "GET", "/v2/", nil, nil) + if err != nil { + return err + } + defer resp.Body.Close() + + switch resp.StatusCode { + case http.StatusOK: + return nil + case http.StatusUnauthorized: + return ErrUnauthorizedForCredentials + default: + return errors.Errorf("error occured with status code %q", resp.StatusCode) + } +} + // makeRequest creates and executes a http.Request with the specified parameters, adding authentication and TLS options for the Docker client. // The host name and schema is taken from the client or autodetected, and the path is relative to it, i.e. the path usually starts with /v2/. func (c *dockerClient) makeRequest(ctx context.Context, method, path string, headers map[string][]string, stream io.Reader) (*http.Response, error) { @@ -245,7 +273,10 @@ func (c *dockerClient) setupRequestAuth(req *http.Request) error { return errors.Errorf("missing realm in bearer auth challenge") } service, _ := challenge.Parameters["service"] // Will be "" if not present - scope := fmt.Sprintf("repository:%s:%s", c.scope.remoteName, c.scope.actions) + var scope string + if c.scope.remoteName != "" && c.scope.actions != "" { + scope = fmt.Sprintf("repository:%s:%s", c.scope.remoteName, c.scope.actions) + } token, err := c.getBearerToken(req.Context(), realm, service, scope) if err != nil { return err @@ -291,7 +322,7 @@ func (c *dockerClient) getBearerToken(ctx context.Context, realm, service, scope defer res.Body.Close() switch res.StatusCode { case http.StatusUnauthorized: - return nil, errors.Errorf("unable to retrieve auth token: 401 unauthorized") + return nil, ErrUnauthorizedForCredentials case http.StatusOK: break default: @@ -315,65 +346,6 @@ func (c *dockerClient) getBearerToken(ctx context.Context, realm, service, scope return &token, nil } -func getAuth(ctx *types.SystemContext, registry string) (string, string, error) { - if ctx != nil && ctx.DockerAuthConfig != nil { - return ctx.DockerAuthConfig.Username, ctx.DockerAuthConfig.Password, nil - } - var dockerAuth dockerConfigFile - dockerCfgPath := filepath.Join(getDefaultConfigDir(".docker"), dockerCfgFileName) - if _, err := os.Stat(dockerCfgPath); err == nil { - j, err := ioutil.ReadFile(dockerCfgPath) - if err != nil { - return "", "", err - } - if err := json.Unmarshal(j, &dockerAuth); err != nil { - return "", "", err - } - - } else if os.IsNotExist(err) { - // try old config path - oldDockerCfgPath := filepath.Join(getDefaultConfigDir(dockerCfgObsolete)) - if _, err := os.Stat(oldDockerCfgPath); err != nil { - if os.IsNotExist(err) { - return "", "", nil - } - return "", "", errors.Wrap(err, oldDockerCfgPath) - } - - j, err := ioutil.ReadFile(oldDockerCfgPath) - if err != nil { - return "", "", err - } - if err := json.Unmarshal(j, &dockerAuth.AuthConfigs); err != nil { - return "", "", err - } - - } else if err != nil { - return "", "", errors.Wrap(err, dockerCfgPath) - } - - // First try cred helpers. They should always be normalized. - if ch, exists := dockerAuth.CredHelpers[registry]; exists { - return getAuthFromCredHelper(ch, registry) - } - - // I'm feeling lucky. - if c, exists := dockerAuth.AuthConfigs[registry]; exists { - return decodeDockerAuth(c.Auth) - } - - // bad luck; let's normalize the entries first - registry = normalizeRegistry(registry) - normalizedAuths := map[string]dockerAuthConfig{} - for k, v := range dockerAuth.AuthConfigs { - normalizedAuths[normalizeRegistry(k)] = v - } - if c, exists := normalizedAuths[registry]; exists { - return decodeDockerAuth(c.Auth) - } - return "", "", nil -} - // detectProperties detects various properties of the registry. // See the dockerClient documentation for members which are affected by this. func (c *dockerClient) detectProperties(ctx context.Context) error { @@ -456,67 +428,3 @@ func (c *dockerClient) getExtensionsSignatures(ctx context.Context, ref dockerRe } return &parsedBody, nil } - -func getDefaultConfigDir(confPath string) string { - return filepath.Join(homedir.Get(), confPath) -} - -type dockerAuthConfig struct { - Auth string `json:"auth,omitempty"` -} - -type dockerConfigFile struct { - AuthConfigs map[string]dockerAuthConfig `json:"auths"` - CredHelpers map[string]string `json:"credHelpers,omitempty"` -} - -func getAuthFromCredHelper(credHelper, registry string) (string, string, error) { - helperName := fmt.Sprintf("docker-credential-%s", credHelper) - p := helperclient.NewShellProgramFunc(helperName) - creds, err := helperclient.Get(p, registry) - if err != nil { - return "", "", err - } - - return creds.Username, creds.Secret, nil -} - -func decodeDockerAuth(s string) (string, string, error) { - decoded, err := base64.StdEncoding.DecodeString(s) - if err != nil { - return "", "", err - } - parts := strings.SplitN(string(decoded), ":", 2) - if len(parts) != 2 { - // if it's invalid just skip, as docker does - return "", "", nil - } - user := parts[0] - password := strings.Trim(parts[1], "\x00") - return user, password, nil -} - -// convertToHostname converts a registry url which has http|https prepended -// to just an hostname. -// Copied from github.com/docker/docker/registry/auth.go -func convertToHostname(url string) string { - stripped := url - if strings.HasPrefix(url, "http://") { - stripped = strings.TrimPrefix(url, "http://") - } else if strings.HasPrefix(url, "https://") { - stripped = strings.TrimPrefix(url, "https://") - } - - nameParts := strings.SplitN(stripped, "/", 2) - - return nameParts[0] -} - -func normalizeRegistry(registry string) string { - normalized := convertToHostname(registry) - switch normalized { - case "registry-1.docker.io", "docker.io": - return "index.docker.io" - } - return normalized -} diff --git a/vendor/github.com/containers/image/docker/docker_image_dest.go b/vendor/github.com/containers/image/docker/docker_image_dest.go index ee2af92b..32d5a18b 100644 --- a/vendor/github.com/containers/image/docker/docker_image_dest.go +++ b/vendor/github.com/containers/image/docker/docker_image_dest.go @@ -34,7 +34,7 @@ type dockerImageDestination struct { // newImageDestination creates a new ImageDestination for the specified image reference. func newImageDestination(ctx *types.SystemContext, ref dockerReference) (types.ImageDestination, error) { - c, err := newDockerClient(ctx, ref, true, "pull,push") + c, err := newDockerClientFromRef(ctx, ref, true, "pull,push") if err != nil { return nil, err } diff --git a/vendor/github.com/containers/image/docker/docker_image_src.go b/vendor/github.com/containers/image/docker/docker_image_src.go index 88e3853a..232c3cf9 100644 --- a/vendor/github.com/containers/image/docker/docker_image_src.go +++ b/vendor/github.com/containers/image/docker/docker_image_src.go @@ -31,7 +31,7 @@ type dockerImageSource struct { // newImageSource creates a new ImageSource for the specified image reference. // The caller must call .Close() on the returned ImageSource. func newImageSource(ctx *types.SystemContext, ref dockerReference) (*dockerImageSource, error) { - c, err := newDockerClient(ctx, ref, false, "pull") + c, err := newDockerClientFromRef(ctx, ref, false, "pull") if err != nil { return nil, err } @@ -298,7 +298,7 @@ func (s *dockerImageSource) getSignaturesFromAPIExtension(ctx context.Context) ( // deleteImage deletes the named image from the registry, if supported. func deleteImage(ctx *types.SystemContext, ref dockerReference) error { - c, err := newDockerClient(ctx, ref, true, "push") + c, err := newDockerClientFromRef(ctx, ref, true, "push") if err != nil { return err } diff --git a/vendor/github.com/containers/image/image/docker_schema1.go b/vendor/github.com/containers/image/image/docker_schema1.go index 4152b3cd..0000a048 100644 --- a/vendor/github.com/containers/image/image/docker_schema1.go +++ b/vendor/github.com/containers/image/image/docker_schema1.go @@ -161,14 +161,17 @@ func (m *manifestSchema1) imageInspectInfo() (*types.ImageInspectInfo, error) { if err := json.Unmarshal([]byte(m.History[0].V1Compatibility), v1); err != nil { return nil, err } - return &types.ImageInspectInfo{ + i := &types.ImageInspectInfo{ Tag: m.Tag, DockerVersion: v1.DockerVersion, Created: v1.Created, - Labels: v1.Config.Labels, Architecture: v1.Architecture, Os: v1.OS, - }, nil + } + if v1.Config != nil { + i.Labels = v1.Config.Labels + } + return i, nil } // UpdatedImageNeedsLayerDiffIDs returns true iff UpdatedImage(options) needs InformationOnly.LayerDiffIDs. diff --git a/vendor/github.com/containers/image/image/docker_schema2.go b/vendor/github.com/containers/image/image/docker_schema2.go index 8cc3c495..848e8743 100644 --- a/vendor/github.com/containers/image/image/docker_schema2.go +++ b/vendor/github.com/containers/image/image/docker_schema2.go @@ -157,13 +157,16 @@ func (m *manifestSchema2) imageInspectInfo() (*types.ImageInspectInfo, error) { if err := json.Unmarshal(config, v1); err != nil { return nil, err } - return &types.ImageInspectInfo{ + i := &types.ImageInspectInfo{ DockerVersion: v1.DockerVersion, Created: v1.Created, - Labels: v1.Config.Labels, Architecture: v1.Architecture, Os: v1.OS, - }, nil + } + if v1.Config != nil { + i.Labels = v1.Config.Labels + } + return i, nil } // UpdatedImageNeedsLayerDiffIDs returns true iff UpdatedImage(options) needs InformationOnly.LayerDiffIDs. diff --git a/vendor/github.com/containers/image/image/oci.go b/vendor/github.com/containers/image/image/oci.go index 5f7c0728..ce84bac2 100644 --- a/vendor/github.com/containers/image/image/oci.go +++ b/vendor/github.com/containers/image/image/oci.go @@ -130,13 +130,16 @@ func (m *manifestOCI1) imageInspectInfo() (*types.ImageInspectInfo, error) { if err := json.Unmarshal(config, v1); err != nil { return nil, err } - return &types.ImageInspectInfo{ + i := &types.ImageInspectInfo{ DockerVersion: v1.DockerVersion, Created: v1.Created, - Labels: v1.Config.Labels, Architecture: v1.Architecture, Os: v1.OS, - }, nil + } + if v1.Config != nil { + i.Labels = v1.Config.Labels + } + return i, nil } // UpdatedImageNeedsLayerDiffIDs returns true iff UpdatedImage(options) needs InformationOnly.LayerDiffIDs. diff --git a/vendor/github.com/containers/image/oci/archive/oci_dest.go b/vendor/github.com/containers/image/oci/archive/oci_dest.go index ba5a9e8c..52e99a43 100644 --- a/vendor/github.com/containers/image/oci/archive/oci_dest.go +++ b/vendor/github.com/containers/image/oci/archive/oci_dest.go @@ -106,7 +106,6 @@ func (d *ociArchiveImageDestination) Commit() error { src := d.tempDirRef.tempDirectory // path to save tarred up file dst := d.ref.resolvedFile - return tarDirectory(src, dst) } diff --git a/vendor/github.com/containers/image/pkg/docker/config/config.go b/vendor/github.com/containers/image/pkg/docker/config/config.go new file mode 100644 index 00000000..fd0ae7d8 --- /dev/null +++ b/vendor/github.com/containers/image/pkg/docker/config/config.go @@ -0,0 +1,295 @@ +package config + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" + + "github.com/containers/image/types" + helperclient "github.com/docker/docker-credential-helpers/client" + "github.com/docker/docker-credential-helpers/credentials" + "github.com/docker/docker/pkg/homedir" + "github.com/pkg/errors" +) + +type dockerAuthConfig struct { + Auth string `json:"auth,omitempty"` +} + +type dockerConfigFile struct { + AuthConfigs map[string]dockerAuthConfig `json:"auths"` + CredHelpers map[string]string `json:"credHelpers,omitempty"` +} + +const ( + defaultPath = "/run/user" + authCfg = "containers" + authCfgFileName = "auth.json" + dockerCfg = ".docker" + dockerCfgFileName = "config.json" + dockerLegacyCfg = ".dockercfg" +) + +var ( + // ErrNotLoggedIn is returned for users not logged into a registry + // that they are trying to logout of + ErrNotLoggedIn = errors.New("not logged in") +) + +// SetAuthentication stores the username and password in the auth.json file +func SetAuthentication(ctx *types.SystemContext, registry, username, password string) error { + return modifyJSON(ctx, func(auths *dockerConfigFile) (bool, error) { + if ch, exists := auths.CredHelpers[registry]; exists { + return false, setAuthToCredHelper(ch, registry, username, password) + } + + creds := base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) + newCreds := dockerAuthConfig{Auth: creds} + auths.AuthConfigs[registry] = newCreds + return true, nil + }) +} + +// GetAuthentication returns the registry credentials stored in +// either auth.json file or .docker/config.json +// If an entry is not found empty strings are returned for the username and password +func GetAuthentication(ctx *types.SystemContext, registry string) (string, string, error) { + if ctx != nil && ctx.DockerAuthConfig != nil { + return ctx.DockerAuthConfig.Username, ctx.DockerAuthConfig.Password, nil + } + + dockerLegacyPath := filepath.Join(homedir.Get(), dockerLegacyCfg) + paths := [3]string{getPathToAuth(ctx), filepath.Join(homedir.Get(), dockerCfg, dockerCfgFileName), dockerLegacyPath} + + for _, path := range paths { + legacyFormat := path == dockerLegacyPath + username, password, err := findAuthentication(registry, path, legacyFormat) + if err != nil { + return "", "", err + } + if username != "" && password != "" { + return username, password, nil + } + } + return "", "", nil +} + +// GetUserLoggedIn returns the username logged in to registry from either +// auth.json or XDG_RUNTIME_DIR +// Used to tell the user if someone is logged in to the registry when logging in +func GetUserLoggedIn(ctx *types.SystemContext, registry string) string { + path := getPathToAuth(ctx) + username, _, _ := findAuthentication(registry, path, false) + if username != "" { + return username + } + return "" +} + +// RemoveAuthentication deletes the credentials stored in auth.json +func RemoveAuthentication(ctx *types.SystemContext, registry string) error { + return modifyJSON(ctx, func(auths *dockerConfigFile) (bool, error) { + // First try cred helpers. + if ch, exists := auths.CredHelpers[registry]; exists { + return false, deleteAuthFromCredHelper(ch, registry) + } + + if _, ok := auths.AuthConfigs[registry]; ok { + delete(auths.AuthConfigs, registry) + } else if _, ok := auths.AuthConfigs[normalizeRegistry(registry)]; ok { + delete(auths.AuthConfigs, normalizeRegistry(registry)) + } else { + return false, ErrNotLoggedIn + } + return true, nil + }) +} + +// RemoveAllAuthentication deletes all the credentials stored in auth.json +func RemoveAllAuthentication(ctx *types.SystemContext) error { + return modifyJSON(ctx, func(auths *dockerConfigFile) (bool, error) { + auths.CredHelpers = make(map[string]string) + auths.AuthConfigs = make(map[string]dockerAuthConfig) + return true, nil + }) +} + +// getPath gets the path of the auth.json file +// The path can be overriden by the user if the overwrite-path flag is set +// If the flag is not set and XDG_RUNTIME_DIR is ser, the auth.json file is saved in XDG_RUNTIME_DIR/containers +// Otherwise, the auth.json file is stored in /run/user/UID/containers +func getPathToAuth(ctx *types.SystemContext) string { + if ctx != nil { + if ctx.AuthFilePath != "" { + return ctx.AuthFilePath + } + if ctx.RootForImplicitAbsolutePaths != "" { + return filepath.Join(ctx.RootForImplicitAbsolutePaths, defaultPath, strconv.Itoa(os.Getuid()), authCfg, authCfgFileName) + } + } + runtimeDir := os.Getenv("XDG_RUNTIME_DIR") + if runtimeDir == "" { + runtimeDir = filepath.Join(defaultPath, strconv.Itoa(os.Getuid())) + } + return filepath.Join(runtimeDir, authCfg, authCfgFileName) +} + +// readJSONFile unmarshals the authentications stored in the auth.json file and returns it +// or returns an empty dockerConfigFile data structure if auth.json does not exist +// if the file exists and is empty, readJSONFile returns an error +func readJSONFile(path string, legacyFormat bool) (dockerConfigFile, error) { + var auths dockerConfigFile + + raw, err := ioutil.ReadFile(path) + if os.IsNotExist(err) { + auths.AuthConfigs = map[string]dockerAuthConfig{} + return auths, nil + } + + if legacyFormat { + if err = json.Unmarshal(raw, &auths.AuthConfigs); err != nil { + return dockerConfigFile{}, errors.Wrapf(err, "error unmarshaling JSON at %q", path) + } + return auths, nil + } + + if err = json.Unmarshal(raw, &auths); err != nil { + return dockerConfigFile{}, errors.Wrapf(err, "error unmarshaling JSON at %q", path) + } + + return auths, nil +} + +// modifyJSON writes to auth.json if the dockerConfigFile has been updated +func modifyJSON(ctx *types.SystemContext, editor func(auths *dockerConfigFile) (bool, error)) error { + path := getPathToAuth(ctx) + dir := filepath.Dir(path) + if _, err := os.Stat(dir); os.IsNotExist(err) { + if err = os.Mkdir(dir, 0700); err != nil { + return errors.Wrapf(err, "error creating directory %q", dir) + } + } + + auths, err := readJSONFile(path, false) + if err != nil { + return errors.Wrapf(err, "error reading JSON file %q", path) + } + + updated, err := editor(&auths) + if err != nil { + return errors.Wrapf(err, "error updating %q", path) + } + if updated { + newData, err := json.MarshalIndent(auths, "", "\t") + if err != nil { + return errors.Wrapf(err, "error marshaling JSON %q", path) + } + + if err = ioutil.WriteFile(path, newData, 0755); err != nil { + return errors.Wrapf(err, "error writing to file %q", path) + } + } + + return nil +} + +func getAuthFromCredHelper(credHelper, registry string) (string, string, error) { + helperName := fmt.Sprintf("docker-credential-%s", credHelper) + p := helperclient.NewShellProgramFunc(helperName) + creds, err := helperclient.Get(p, registry) + if err != nil { + return "", "", err + } + return creds.Username, creds.Secret, nil +} + +func setAuthToCredHelper(credHelper, registry, username, password string) error { + helperName := fmt.Sprintf("docker-credential-%s", credHelper) + p := helperclient.NewShellProgramFunc(helperName) + creds := &credentials.Credentials{ + ServerURL: registry, + Username: username, + Secret: password, + } + return helperclient.Store(p, creds) +} + +func deleteAuthFromCredHelper(credHelper, registry string) error { + helperName := fmt.Sprintf("docker-credential-%s", credHelper) + p := helperclient.NewShellProgramFunc(helperName) + return helperclient.Erase(p, registry) +} + +// findAuthentication looks for auth of registry in path +func findAuthentication(registry, path string, legacyFormat bool) (string, string, error) { + auths, err := readJSONFile(path, legacyFormat) + if err != nil { + return "", "", errors.Wrapf(err, "error reading JSON file %q", path) + } + + // First try cred helpers. They should always be normalized. + if ch, exists := auths.CredHelpers[registry]; exists { + return getAuthFromCredHelper(ch, registry) + } + + // I'm feeling lucky + if val, exists := auths.AuthConfigs[registry]; exists { + return decodeDockerAuth(val.Auth) + } + + // bad luck; let's normalize the entries first + registry = normalizeRegistry(registry) + normalizedAuths := map[string]dockerAuthConfig{} + for k, v := range auths.AuthConfigs { + normalizedAuths[normalizeRegistry(k)] = v + } + if val, exists := normalizedAuths[registry]; exists { + return decodeDockerAuth(val.Auth) + } + return "", "", nil +} + +func decodeDockerAuth(s string) (string, string, error) { + decoded, err := base64.StdEncoding.DecodeString(s) + if err != nil { + return "", "", err + } + parts := strings.SplitN(string(decoded), ":", 2) + if len(parts) != 2 { + // if it's invalid just skip, as docker does + return "", "", nil + } + user := parts[0] + password := strings.Trim(parts[1], "\x00") + return user, password, nil +} + +// convertToHostname converts a registry url which has http|https prepended +// to just an hostname. +// Copied from github.com/docker/docker/registry/auth.go +func convertToHostname(url string) string { + stripped := url + if strings.HasPrefix(url, "http://") { + stripped = strings.TrimPrefix(url, "http://") + } else if strings.HasPrefix(url, "https://") { + stripped = strings.TrimPrefix(url, "https://") + } + + nameParts := strings.SplitN(stripped, "/", 2) + + return nameParts[0] +} + +func normalizeRegistry(registry string) string { + normalized := convertToHostname(registry) + switch normalized { + case "registry-1.docker.io", "docker.io": + return "index.docker.io" + } + return normalized +} diff --git a/vendor/github.com/containers/image/types/types.go b/vendor/github.com/containers/image/types/types.go index e955c338..4ede907b 100644 --- a/vendor/github.com/containers/image/types/types.go +++ b/vendor/github.com/containers/image/types/types.go @@ -304,6 +304,8 @@ type SystemContext struct { RegistriesDirPath string // Path to the system-wide registries configuration file SystemRegistriesConfPath string + // If not "", overrides the default path for the authentication file + AuthFilePath string // === OCI.Transport overrides === // If not "", a directory containing a CA certificate (ending with ".crt"), diff --git a/vendor/github.com/containers/storage/pkg/archive/example_changes.go b/vendor/github.com/containers/storage/pkg/archive/example_changes.go deleted file mode 100644 index 70f9c556..00000000 --- a/vendor/github.com/containers/storage/pkg/archive/example_changes.go +++ /dev/null @@ -1,97 +0,0 @@ -// +build ignore - -// Simple tool to create an archive stream from an old and new directory -// -// By default it will stream the comparison of two temporary directories with junk files -package main - -import ( - "flag" - "fmt" - "io" - "io/ioutil" - "os" - "path" - - "github.com/containers/storage/pkg/archive" - "github.com/sirupsen/logrus" -) - -var ( - flDebug = flag.Bool("D", false, "debugging output") - flNewDir = flag.String("newdir", "", "") - flOldDir = flag.String("olddir", "", "") - log = logrus.New() -) - -func main() { - flag.Usage = func() { - fmt.Println("Produce a tar from comparing two directory paths. By default a demo tar is created of around 200 files (including hardlinks)") - fmt.Printf("%s [OPTIONS]\n", os.Args[0]) - flag.PrintDefaults() - } - flag.Parse() - log.Out = os.Stderr - if (len(os.Getenv("DEBUG")) > 0) || *flDebug { - logrus.SetLevel(logrus.DebugLevel) - } - var newDir, oldDir string - - if len(*flNewDir) == 0 { - var err error - newDir, err = ioutil.TempDir("", "storage-test-newDir") - if err != nil { - log.Fatal(err) - } - defer os.RemoveAll(newDir) - if _, err := prepareUntarSourceDirectory(100, newDir, true); err != nil { - log.Fatal(err) - } - } else { - newDir = *flNewDir - } - - if len(*flOldDir) == 0 { - oldDir, err := ioutil.TempDir("", "storage-test-oldDir") - if err != nil { - log.Fatal(err) - } - defer os.RemoveAll(oldDir) - } else { - oldDir = *flOldDir - } - - changes, err := archive.ChangesDirs(newDir, oldDir) - if err != nil { - log.Fatal(err) - } - - a, err := archive.ExportChanges(newDir, changes) - if err != nil { - log.Fatal(err) - } - defer a.Close() - - i, err := io.Copy(os.Stdout, a) - if err != nil && err != io.EOF { - log.Fatal(err) - } - fmt.Fprintf(os.Stderr, "wrote archive of %d bytes", i) -} - -func prepareUntarSourceDirectory(numberOfFiles int, targetPath string, makeLinks bool) (int, error) { - fileData := []byte("fooo") - for n := 0; n < numberOfFiles; n++ { - fileName := fmt.Sprintf("file-%d", n) - if err := ioutil.WriteFile(path.Join(targetPath, fileName), fileData, 0700); err != nil { - return 0, err - } - if makeLinks { - if err := os.Link(path.Join(targetPath, fileName), path.Join(targetPath, fileName+"-link")); err != nil { - return 0, err - } - } - } - totalSize := numberOfFiles * len(fileData) - return totalSize, nil -}