summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--go.mod4
-rw-r--r--go.sum9
-rw-r--r--test/e2e/images_test.go6
-rw-r--r--vendor/github.com/containers/common/libimage/filters.go56
-rw-r--r--vendor/github.com/containers/common/libimage/image.go20
-rw-r--r--vendor/github.com/containers/common/pkg/auth/auth.go108
-rw-r--r--vendor/github.com/containers/common/pkg/retry/retry.go11
-rw-r--r--vendor/github.com/containers/image/v5/manifest/manifest.go9
-rw-r--r--vendor/github.com/containers/image/v5/manifest/oci.go1
-rw-r--r--vendor/github.com/containers/image/v5/manifest/oci_index.go2
-rw-r--r--vendor/github.com/containers/image/v5/pkg/docker/config/config.go165
-rw-r--r--vendor/modules.txt4
12 files changed, 228 insertions, 167 deletions
diff --git a/go.mod b/go.mod
index 2bdb97244..081bf8f91 100644
--- a/go.mod
+++ b/go.mod
@@ -12,9 +12,9 @@ require (
github.com/containernetworking/cni v1.0.1
github.com/containernetworking/plugins v1.0.1
github.com/containers/buildah v1.23.1
- github.com/containers/common v0.46.1-0.20211202172647-e77d74bd1976
+ github.com/containers/common v0.46.1-0.20211205182721-515a2805e7b9
github.com/containers/conmon v2.0.20+incompatible
- github.com/containers/image/v5 v5.17.1-0.20211129144953-4f6d0b45be6c
+ github.com/containers/image/v5 v5.17.1-0.20211201214147-603ec1341d58
github.com/containers/ocicrypt v1.1.2
github.com/containers/psgo v1.7.1
github.com/containers/storage v1.37.1-0.20211130181259-1a158c89a518
diff --git a/go.sum b/go.sum
index 1bda27de7..217a3295d 100644
--- a/go.sum
+++ b/go.sum
@@ -262,14 +262,13 @@ github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNB
github.com/containers/buildah v1.23.1 h1:Tpc9DsRuU+0Oofewpxb6OJVNQjCu7yloN/obUqzfDTY=
github.com/containers/buildah v1.23.1/go.mod h1:4WnrN0yrA7ab0ppgunixu2WM1rlD2rG8QLJAKbEkZlQ=
github.com/containers/common v0.44.2/go.mod h1:7sdP4vmI5Bm6FPFxb3lvAh1Iktb6tiO1MzjUzhxdoGo=
-github.com/containers/common v0.46.1-0.20211202172647-e77d74bd1976 h1:xVOGL69ge+1RirZvnrEl9nATL75udt/Hy2BN8qcmeNY=
-github.com/containers/common v0.46.1-0.20211202172647-e77d74bd1976/go.mod h1:J8MxXan58zAWbNgpj4ODPlzsuJnYvNc2zKJCZPIMHYQ=
+github.com/containers/common v0.46.1-0.20211205182721-515a2805e7b9 h1:BoPxjWIPX+cn3CGNxd1FC10jcq9TBMk1uvGQEWTXWto=
+github.com/containers/common v0.46.1-0.20211205182721-515a2805e7b9/go.mod h1:cxAKmvKoYBl/iLZ1YD/SKnJF7wPR9H6xM/Hu75ZN/oA=
github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg=
github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
github.com/containers/image/v5 v5.16.0/go.mod h1:XgTpfAPLRGOd1XYyCU5cISFr777bLmOerCSpt/v7+Q4=
-github.com/containers/image/v5 v5.17.0/go.mod h1:GnYVusVRFPMMTAAUkrcS8NNSpBp8oyrjOUe04AAmRr4=
-github.com/containers/image/v5 v5.17.1-0.20211129144953-4f6d0b45be6c h1:WfMOQlq3CDvVe5ONUGfj9/MajskqUHnbo24j24Xg2ZM=
-github.com/containers/image/v5 v5.17.1-0.20211129144953-4f6d0b45be6c/go.mod h1:boW5ckkT0wu9obDEiOIxrtWQmz1znMuHiVMQPcpHnk0=
+github.com/containers/image/v5 v5.17.1-0.20211201214147-603ec1341d58 h1:DI6d+6aRBC14mbfnh0eYlHeFBSZQ4adDykrS8F/Awrg=
+github.com/containers/image/v5 v5.17.1-0.20211201214147-603ec1341d58/go.mod h1:iUA6fv9NnqIhEaP3+dqo22nKMNkSWCj8d5o8Dju0j1Q=
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE=
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc=
diff --git a/test/e2e/images_test.go b/test/e2e/images_test.go
index efa9f399b..a3cc1ce99 100644
--- a/test/e2e/images_test.go
+++ b/test/e2e/images_test.go
@@ -141,12 +141,12 @@ var _ = Describe("Podman images", func() {
})
It("podman images filter reference", func() {
- result := podmanTest.Podman([]string{"images", "-q", "-f", "reference=quay.io*"})
+ result := podmanTest.Podman([]string{"images", "-q", "-f", "reference=quay.io/libpod/*"})
result.WaitWithDefaultTimeout()
Expect(result).Should(Exit(0))
Expect(result.OutputToStringArray()).To(HaveLen(7))
- retalpine := podmanTest.Podman([]string{"images", "-f", "reference=a*pine"})
+ retalpine := podmanTest.Podman([]string{"images", "-f", "reference=*lpine*"})
retalpine.WaitWithDefaultTimeout()
Expect(retalpine).Should(Exit(0))
Expect(retalpine.OutputToStringArray()).To(HaveLen(6))
@@ -155,7 +155,7 @@ var _ = Describe("Podman images", func() {
retalpine = podmanTest.Podman([]string{"images", "-f", "reference=alpine"})
retalpine.WaitWithDefaultTimeout()
Expect(retalpine).Should(Exit(0))
- Expect(retalpine.OutputToStringArray()).To(HaveLen(6))
+ Expect(retalpine.OutputToStringArray()).To(HaveLen(2))
Expect(retalpine.OutputToString()).To(ContainSubstring("alpine"))
retnone := podmanTest.Podman([]string{"images", "-q", "-f", "reference=bogus"})
diff --git a/vendor/github.com/containers/common/libimage/filters.go b/vendor/github.com/containers/common/libimage/filters.go
index 521af5d06..dfa34dd44 100644
--- a/vendor/github.com/containers/common/libimage/filters.go
+++ b/vendor/github.com/containers/common/libimage/filters.go
@@ -3,13 +3,14 @@ package libimage
import (
"context"
"fmt"
- "path/filepath"
+ "path"
"strconv"
"strings"
"time"
filtersPkg "github.com/containers/common/pkg/filters"
"github.com/containers/common/pkg/timetype"
+ "github.com/containers/image/v5/docker/reference"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -136,7 +137,7 @@ func (r *Runtime) compileImageFilters(ctx context.Context, options *ListImagesOp
filterFuncs = append(filterFuncs, filterReadOnly(readOnly))
case "reference":
- filterFuncs = append(filterFuncs, filterReference(value))
+ filterFuncs = append(filterFuncs, filterReferences(value))
case "until":
ts, err := timetype.GetTimestamp(value, time.Now())
@@ -158,24 +159,43 @@ func (r *Runtime) compileImageFilters(ctx context.Context, options *ListImagesOp
return filterFuncs, nil
}
-// filterReference creates a reference filter for matching the specified value.
-func filterReference(value string) filterFunc {
- // Replacing all '/' with '|' so that filepath.Match() can work '|'
- // character is not valid in image name, so this is safe.
- //
- // TODO: this has been copied from Podman and requires some more review
- // and especially tests.
- filter := fmt.Sprintf("*%s*", value)
- filter = strings.ReplaceAll(filter, "/", "|")
+// filterReferences creates a reference filter for matching the specified value.
+func filterReferences(value string) filterFunc {
return func(img *Image) (bool, error) {
- if len(value) < 1 {
- return true, nil
+ refs, err := img.NamesReferences()
+ if err != nil {
+ return false, err
}
- for _, name := range img.Names() {
- newName := strings.ReplaceAll(name, "/", "|")
- match, _ := filepath.Match(filter, newName)
- if match {
- return true, nil
+
+ for _, ref := range refs {
+ refString := ref.String() // FQN with tag/digest
+ candidates := []string{refString}
+
+ // Split the reference into 3 components (twice if diggested/tagged):
+ // 1) Fully-qualified reference
+ // 2) Without domain
+ // 3) Without domain and path
+ if named, isNamed := ref.(reference.Named); isNamed {
+ candidates = append(candidates,
+ reference.Path(named), // path/name without tag/digest (Path() removes it)
+ refString[strings.LastIndex(refString, "/")+1:]) // name with tag/digest
+
+ trimmedString := reference.TrimNamed(named).String()
+ if refString != trimmedString {
+ tagOrDigest := refString[len(trimmedString):]
+ candidates = append(candidates,
+ trimmedString, // FQN without tag/digest
+ reference.Path(named)+tagOrDigest, // path/name with tag/digest
+ trimmedString[strings.LastIndex(trimmedString, "/")+1:]) // name without tag/digest
+ }
+ }
+
+ for _, candidate := range candidates {
+ // path.Match() is also used by Docker's reference.FamiliarMatch().
+ matched, _ := path.Match(value, candidate)
+ if matched {
+ return true, nil
+ }
}
}
return false, nil
diff --git a/vendor/github.com/containers/common/libimage/image.go b/vendor/github.com/containers/common/libimage/image.go
index 50dbc9725..661ca159b 100644
--- a/vendor/github.com/containers/common/libimage/image.go
+++ b/vendor/github.com/containers/common/libimage/image.go
@@ -44,6 +44,8 @@ type Image struct {
completeInspectData *ImageData
// Corresponding OCI image.
ociv1Image *ociv1.Image
+ // Names() parsed into references.
+ namesReferences []reference.Reference
}
}
@@ -59,6 +61,7 @@ func (i *Image) reload() error {
i.cached.partialInspectData = nil
i.cached.completeInspectData = nil
i.cached.ociv1Image = nil
+ i.cached.namesReferences = nil
return nil
}
@@ -89,6 +92,23 @@ func (i *Image) Names() []string {
return i.storageImage.Names
}
+// NamesReferences returns Names() as references.
+func (i *Image) NamesReferences() ([]reference.Reference, error) {
+ if i.cached.namesReferences != nil {
+ return i.cached.namesReferences, nil
+ }
+ refs := make([]reference.Reference, 0, len(i.Names()))
+ for _, name := range i.Names() {
+ ref, err := reference.Parse(name)
+ if err != nil {
+ return nil, err
+ }
+ refs = append(refs, ref)
+ }
+ i.cached.namesReferences = refs
+ return refs, nil
+}
+
// StorageImage returns the underlying storage.Image.
func (i *Image) StorageImage() *storage.Image {
return i.storageImage
diff --git a/vendor/github.com/containers/common/pkg/auth/auth.go b/vendor/github.com/containers/common/pkg/auth/auth.go
index 0934b155f..ff52b028e 100644
--- a/vendor/github.com/containers/common/pkg/auth/auth.go
+++ b/vendor/github.com/containers/common/pkg/auth/auth.go
@@ -70,13 +70,10 @@ func Login(ctx context.Context, systemContext *types.SystemContext, opts *LoginO
systemContext = systemContextWithOptions(systemContext, opts.AuthFile, opts.CertDir)
var (
- authConfig types.DockerAuthConfig
key, registry string
- ref reference.Named
err error
)
- l := len(args)
- switch l {
+ switch len(args) {
case 0:
if !opts.AcceptUnspecifiedRegistry {
return errors.New("please provide a registry to login to")
@@ -88,26 +85,18 @@ func Login(ctx context.Context, systemContext *types.SystemContext, opts *LoginO
logrus.Debugf("registry not specified, default to the first registry %q from registries.conf", key)
case 1:
- key, registry, ref, err = parseRegistryArgument(args[0], opts.AcceptRepositories)
+ key, registry, err = parseCredentialsKey(args[0], opts.AcceptRepositories)
if err != nil {
return err
}
default:
return errors.New("login accepts only one registry to login to")
-
}
- if ref != nil {
- authConfig, err = config.GetCredentialsForRef(systemContext, ref)
- if err != nil {
- return errors.Wrap(err, "get credentials for repository")
- }
- } else {
- authConfig, err = config.GetCredentials(systemContext, registry)
- if err != nil {
- return errors.Wrap(err, "get credentials")
- }
+ authConfig, err := config.GetCredentials(systemContext, key)
+ if err != nil {
+ return errors.Wrap(err, "get credentials")
}
if opts.GetLoginSet {
@@ -173,39 +162,44 @@ func Login(ctx context.Context, systemContext *types.SystemContext, opts *LoginO
return errors.Wrapf(err, "authenticating creds for %q", key)
}
-// parseRegistryArgument verifies the provided arg depending if we accept
-// repositories or not.
-func parseRegistryArgument(arg string, acceptRepositories bool) (key, registry string, maybeRef reference.Named, err error) {
+// parseCredentialsKey turns the provided argument into a valid credential key
+// and computes the registry part.
+func parseCredentialsKey(arg string, acceptRepositories bool) (key, registry string, err error) {
if !acceptRepositories {
registry = getRegistryName(arg)
key = registry
- return key, registry, maybeRef, nil
+ return key, registry, nil
}
key = trimScheme(arg)
if key != arg {
- return key, registry, nil, errors.New("credentials key has https[s]:// prefix")
+ return "", "", errors.New("credentials key has https[s]:// prefix")
}
registry = getRegistryName(key)
if registry == key {
- // We cannot parse a reference from a registry, so we stop here
- return key, registry, nil, nil
+ // The key is not namespaced
+ return key, registry, nil
}
- ref, parseErr := reference.ParseNamed(key)
- if parseErr != nil {
- return key, registry, nil, errors.Wrapf(parseErr, "parse reference from %q", key)
+ // Sanity-check that the key looks reasonable (e.g. doesn't use invalid characters),
+ // and does not contain a tag or digest.
+ // WARNING: ref.Named() MUST NOT be used to compute key, because
+ // reference.ParseNormalizedNamed() turns docker.io/vendor to docker.io/library/vendor
+ // Ideally c/image should provide dedicated validation functionality.
+ ref, err := reference.ParseNormalizedNamed(key)
+ if err != nil {
+ return "", "", errors.Wrapf(err, "parse reference from %q", key)
}
-
if !reference.IsNameOnly(ref) {
- return key, registry, nil, errors.Errorf("reference %q contains tag or digest", ref.String())
+ return "", "", errors.Errorf("reference %q contains tag or digest", ref.String())
+ }
+ refRegistry := reference.Domain(ref)
+ if refRegistry != registry { // This should never happen, check just to make sure
+ return "", "", fmt.Errorf("internal error: key %q registry mismatch, %q vs. %q", key, ref, refRegistry)
}
- maybeRef = ref
- registry = reference.Domain(ref)
-
- return key, registry, maybeRef, nil
+ return key, registry, nil
}
// getRegistryName scrubs and parses the input to get the server name
@@ -271,15 +265,23 @@ func Logout(systemContext *types.SystemContext, opts *LogoutOptions, args []stri
}
systemContext = systemContextWithOptions(systemContext, opts.AuthFile, "")
+ if opts.All {
+ if len(args) != 0 {
+ return errors.New("--all takes no arguments")
+ }
+ if err := config.RemoveAllAuthentication(systemContext); err != nil {
+ return err
+ }
+ fmt.Fprintln(opts.Stdout, "Removed login credentials for all registries")
+ return nil
+ }
+
var (
key, registry string
- ref reference.Named
err error
)
- if len(args) > 1 {
- return errors.New("logout accepts only one registry to logout from")
- }
- if len(args) == 0 && !opts.All {
+ switch len(args) {
+ case 0:
if !opts.AcceptUnspecifiedRegistry {
return errors.New("please provide a registry to logout from")
}
@@ -288,23 +290,15 @@ func Logout(systemContext *types.SystemContext, opts *LogoutOptions, args []stri
}
registry = key
logrus.Debugf("registry not specified, default to the first registry %q from registries.conf", key)
- }
- if len(args) != 0 {
- if opts.All {
- return errors.New("--all takes no arguments")
- }
- key, registry, ref, err = parseRegistryArgument(args[0], opts.AcceptRepositories)
+
+ case 1:
+ key, registry, err = parseCredentialsKey(args[0], opts.AcceptRepositories)
if err != nil {
return err
}
- }
- if opts.All {
- if err := config.RemoveAllAuthentication(systemContext); err != nil {
- return err
- }
- fmt.Fprintln(opts.Stdout, "Removed login credentials for all registries")
- return nil
+ default:
+ return errors.New("logout accepts only one registry to logout from")
}
err = config.RemoveAuthentication(systemContext, key)
@@ -313,17 +307,9 @@ func Logout(systemContext *types.SystemContext, opts *LogoutOptions, args []stri
fmt.Fprintf(opts.Stdout, "Removed login credentials for %s\n", key)
return nil
case config.ErrNotLoggedIn:
- var authConfig types.DockerAuthConfig
- if ref != nil {
- authConfig, err = config.GetCredentialsForRef(systemContext, ref)
- if err != nil {
- return errors.Wrap(err, "get credentials for repository")
- }
- } else {
- authConfig, err = config.GetCredentials(systemContext, registry)
- if err != nil {
- return errors.Wrap(err, "get credentials")
- }
+ authConfig, err := config.GetCredentials(systemContext, key)
+ if err != nil {
+ return errors.Wrap(err, "get credentials")
}
authInvalid := docker.CheckAuth(context.Background(), systemContext, authConfig.Username, authConfig.Password, registry)
diff --git a/vendor/github.com/containers/common/pkg/retry/retry.go b/vendor/github.com/containers/common/pkg/retry/retry.go
index 43e3a6688..a9573e4e8 100644
--- a/vendor/github.com/containers/common/pkg/retry/retry.go
+++ b/vendor/github.com/containers/common/pkg/retry/retry.go
@@ -45,8 +45,12 @@ func RetryIfNecessary(ctx context.Context, operation func() error, retryOptions
func isRetryable(err error) bool {
err = errors.Cause(err)
- if err == context.Canceled || err == context.DeadlineExceeded {
+ switch err {
+ case nil:
return false
+ case context.Canceled, context.DeadlineExceeded:
+ return false
+ default: // continue
}
type unwrapper interface {
@@ -57,7 +61,8 @@ func isRetryable(err error) bool {
case errcode.Error:
switch e.Code {
- case errcode.ErrorCodeUnauthorized, errcodev2.ErrorCodeNameUnknown, errcodev2.ErrorCodeManifestUnknown:
+ case errcode.ErrorCodeUnauthorized, errcode.ErrorCodeDenied,
+ errcodev2.ErrorCodeNameUnknown, errcodev2.ErrorCodeManifestUnknown:
return false
}
return true
@@ -86,7 +91,7 @@ func isRetryable(err error) bool {
}
}
return true
- case unwrapper:
+ case unwrapper: // Test this last, because various error types might implement .Unwrap()
err = e.Unwrap()
return isRetryable(err)
}
diff --git a/vendor/github.com/containers/image/v5/manifest/manifest.go b/vendor/github.com/containers/image/v5/manifest/manifest.go
index 4b644f253..2e3e5da15 100644
--- a/vendor/github.com/containers/image/v5/manifest/manifest.go
+++ b/vendor/github.com/containers/image/v5/manifest/manifest.go
@@ -110,7 +110,8 @@ func GuessMIMEType(manifest []byte) string {
}
switch meta.MediaType {
- case DockerV2Schema2MediaType, DockerV2ListMediaType: // A recognized type.
+ case DockerV2Schema2MediaType, DockerV2ListMediaType,
+ imgspecv1.MediaTypeImageManifest, imgspecv1.MediaTypeImageIndex: // A recognized type.
return meta.MediaType
}
// this is the only way the function can return DockerV2Schema1MediaType, and recognizing that is essential for stripping the JWS signatures = computing the correct manifest digest.
@@ -121,9 +122,9 @@ func GuessMIMEType(manifest []byte) string {
}
return DockerV2Schema1MediaType
case 2:
- // best effort to understand if this is an OCI image since mediaType
- // isn't in the manifest for OCI anymore
- // for docker v2s2 meta.MediaType should have been set. But given the data, this is our best guess.
+ // Best effort to understand if this is an OCI image since mediaType
+ // wasn't in the manifest for OCI image-spec < 1.0.2.
+ // For docker v2s2 meta.MediaType should have been set. But given the data, this is our best guess.
ociMan := struct {
Config struct {
MediaType string `json:"mediaType"`
diff --git a/vendor/github.com/containers/image/v5/manifest/oci.go b/vendor/github.com/containers/image/v5/manifest/oci.go
index c4616b965..5892184df 100644
--- a/vendor/github.com/containers/image/v5/manifest/oci.go
+++ b/vendor/github.com/containers/image/v5/manifest/oci.go
@@ -66,6 +66,7 @@ func OCI1FromComponents(config imgspecv1.Descriptor, layers []imgspecv1.Descript
return &OCI1{
imgspecv1.Manifest{
Versioned: specs.Versioned{SchemaVersion: 2},
+ MediaType: imgspecv1.MediaTypeImageManifest,
Config: config,
Layers: layers,
},
diff --git a/vendor/github.com/containers/image/v5/manifest/oci_index.go b/vendor/github.com/containers/image/v5/manifest/oci_index.go
index 5bec43ff9..c4f11e09c 100644
--- a/vendor/github.com/containers/image/v5/manifest/oci_index.go
+++ b/vendor/github.com/containers/image/v5/manifest/oci_index.go
@@ -119,6 +119,7 @@ func OCI1IndexFromComponents(components []imgspecv1.Descriptor, annotations map[
index := OCI1Index{
imgspecv1.Index{
Versioned: imgspec.Versioned{SchemaVersion: 2},
+ MediaType: imgspecv1.MediaTypeImageIndex,
Manifests: make([]imgspecv1.Descriptor, len(components)),
Annotations: dupStringStringMap(annotations),
},
@@ -195,6 +196,7 @@ func OCI1IndexFromManifest(manifest []byte) (*OCI1Index, error) {
index := OCI1Index{
Index: imgspecv1.Index{
Versioned: imgspec.Versioned{SchemaVersion: 2},
+ MediaType: imgspecv1.MediaTypeImageIndex,
Manifests: []imgspecv1.Descriptor{},
Annotations: make(map[string]string),
},
diff --git a/vendor/github.com/containers/image/v5/pkg/docker/config/config.go b/vendor/github.com/containers/image/v5/pkg/docker/config/config.go
index 63f5bd53e..642057cd7 100644
--- a/vendor/github.com/containers/image/v5/pkg/docker/config/config.go
+++ b/vendor/github.com/containers/image/v5/pkg/docker/config/config.go
@@ -54,8 +54,8 @@ var (
// SetCredentials stores the username and password in a location
// appropriate for sys and the users’ configuration.
-// A valid key can be either a registry hostname or additionally a namespace if
-// the AuthenticationFileHelper is being unsed.
+// A valid key is a repository, a namespace within a registry, or a registry hostname;
+// using forms other than just a registry may fail depending on configuration.
// Returns a human-redable description of the location that was updated.
// NOTE: The return value is only intended to be read by humans; its form is not an API,
// it may change (or new forms can be added) any time.
@@ -128,11 +128,15 @@ func GetAllCredentials(sys *types.SystemContext) (map[string]types.DockerAuthCon
// possible sources, and then call `GetCredentials` on them. That
// prevents us from having to reverse engineer the logic in
// `GetCredentials`.
- allRegistries := make(map[string]bool)
- addRegistry := func(s string) {
- allRegistries[s] = true
+ allKeys := make(map[string]bool)
+ addKey := func(s string) {
+ allKeys[s] = true
}
+ // To use GetCredentials, we must at least convert the URL forms into host names.
+ // While we're at it, we’ll also canonicalize docker.io to the standard format.
+ normalizedDockerIORegistry := normalizeRegistry("docker.io")
+
helpers, err := sysregistriesv2.CredentialHelpers(sys)
if err != nil {
return nil, err
@@ -151,10 +155,14 @@ func GetAllCredentials(sys *types.SystemContext) (map[string]types.DockerAuthCon
// direct mapping to a registry, so we can just
// walk the map.
for registry := range auths.CredHelpers {
- addRegistry(registry)
+ addKey(registry)
}
- for registry := range auths.AuthConfigs {
- addRegistry(registry)
+ for key := range auths.AuthConfigs {
+ key := normalizeAuthFileKey(key, path.legacyFormat)
+ if key == normalizedDockerIORegistry {
+ key = "docker.io"
+ }
+ addKey(key)
}
}
// External helpers.
@@ -166,7 +174,7 @@ func GetAllCredentials(sys *types.SystemContext) (map[string]types.DockerAuthCon
switch errors.Cause(err) {
case nil:
for registry := range creds {
- addRegistry(registry)
+ addKey(registry)
}
case exec.ErrNotFound:
// It's okay if the helper doesn't exist.
@@ -179,8 +187,8 @@ func GetAllCredentials(sys *types.SystemContext) (map[string]types.DockerAuthCon
// Now use `GetCredentials` to the specific auth configs for each
// previously listed registry.
authConfigs := make(map[string]types.DockerAuthConfig)
- for registry := range allRegistries {
- authConf, err := GetCredentials(sys, registry)
+ for key := range allKeys {
+ authConf, err := GetCredentials(sys, key)
if err != nil {
if credentials.IsErrCredentialsNotFoundMessage(err.Error()) {
// Ignore if the credentials could not be found (anymore).
@@ -189,7 +197,7 @@ func GetAllCredentials(sys *types.SystemContext) (map[string]types.DockerAuthCon
// Note: we rely on the logging in `GetCredentials`.
return nil, err
}
- authConfigs[registry] = authConf
+ authConfigs[key] = authConf
}
return authConfigs, nil
@@ -230,16 +238,14 @@ func getAuthFilePaths(sys *types.SystemContext, homeDir string) []authPath {
return paths
}
-// GetCredentials returns the registry credentials stored in the
-// registry-specific credential helpers or in the default global credentials
-// helpers with falling back to using either auth.json
-// file or .docker/config.json, including support for OAuth2 and IdentityToken.
+// GetCredentials returns the registry credentials matching key, appropriate for
+// sys and the users’ configuration.
// If an entry is not found, an empty struct is returned.
+// A valid key is a repository, a namespace within a registry, or a registry hostname.
//
-// GetCredentialsForRef should almost always be used in favor of this API to
-// allow different credentials for different repositories on the same registry.
-func GetCredentials(sys *types.SystemContext, registry string) (types.DockerAuthConfig, error) {
- return getCredentialsWithHomeDir(sys, nil, registry, homedir.Get())
+// GetCredentialsForRef should almost always be used in favor of this API.
+func GetCredentials(sys *types.SystemContext, key string) (types.DockerAuthConfig, error) {
+ return getCredentialsWithHomeDir(sys, key, homedir.Get())
}
// GetCredentialsForRef returns the registry credentials necessary for
@@ -247,30 +253,34 @@ func GetCredentials(sys *types.SystemContext, registry string) (types.DockerAuth
// appropriate for sys and the users’ configuration.
// If an entry is not found, an empty struct is returned.
func GetCredentialsForRef(sys *types.SystemContext, ref reference.Named) (types.DockerAuthConfig, error) {
- return getCredentialsWithHomeDir(sys, ref, reference.Domain(ref), homedir.Get())
+ return getCredentialsWithHomeDir(sys, ref.Name(), homedir.Get())
}
// getCredentialsWithHomeDir is an internal implementation detail of
// GetCredentialsForRef and GetCredentials. It exists only to allow testing it
// with an artificial home directory.
-func getCredentialsWithHomeDir(sys *types.SystemContext, ref reference.Named, registry, homeDir string) (types.DockerAuthConfig, error) {
- // consistency check of the ref and registry arguments
- if ref != nil && reference.Domain(ref) != registry {
- return types.DockerAuthConfig{}, errors.Errorf(
- "internal error: provided reference domain %q name does not match registry %q",
- reference.Domain(ref), registry,
- )
+func getCredentialsWithHomeDir(sys *types.SystemContext, key, homeDir string) (types.DockerAuthConfig, error) {
+ _, err := validateKey(key)
+ if err != nil {
+ return types.DockerAuthConfig{}, err
}
if sys != nil && sys.DockerAuthConfig != nil {
- logrus.Debugf("Returning credentials for %s from DockerAuthConfig", registry)
+ logrus.Debugf("Returning credentials for %s from DockerAuthConfig", key)
return *sys.DockerAuthConfig, nil
}
+ var registry string // We compute this once because it is used in several places.
+ if firstSlash := strings.IndexRune(key, '/'); firstSlash != -1 {
+ registry = key[:firstSlash]
+ } else {
+ registry = key
+ }
+
// Anonymous function to query credentials from auth files.
getCredentialsFromAuthFiles := func() (types.DockerAuthConfig, string, error) {
for _, path := range getAuthFilePaths(sys, homeDir) {
- authConfig, err := findAuthentication(ref, registry, path.path, path.legacyFormat)
+ authConfig, err := findCredentialsInFile(key, registry, path.path, path.legacyFormat)
if err != nil {
return types.DockerAuthConfig{}, "", err
}
@@ -291,57 +301,61 @@ func getCredentialsWithHomeDir(sys *types.SystemContext, ref reference.Named, re
for _, helper := range helpers {
var (
creds types.DockerAuthConfig
+ helperKey string
credHelperPath string
err error
)
switch helper {
// Special-case the built-in helper for auth files.
case sysregistriesv2.AuthenticationFileHelper:
+ helperKey = key
creds, credHelperPath, err = getCredentialsFromAuthFiles()
// External helpers.
default:
+ // This intentionally uses "registry", not "key"; we don't support namespaced
+ // credentials in helpers, but a "registry" is a valid parent of "key".
+ helperKey = registry
creds, err = getAuthFromCredHelper(helper, registry)
}
if err != nil {
- logrus.Debugf("Error looking up credentials for %s in credential helper %s: %v", registry, helper, err)
+ logrus.Debugf("Error looking up credentials for %s in credential helper %s: %v", helperKey, helper, err)
multiErr = multierror.Append(multiErr, err)
continue
}
- if len(creds.Username)+len(creds.Password)+len(creds.IdentityToken) == 0 {
- continue
- }
- msg := fmt.Sprintf("Found credentials for %s in credential helper %s", registry, helper)
- if credHelperPath != "" {
- msg = fmt.Sprintf("%s in file %s", msg, credHelperPath)
+ if creds != (types.DockerAuthConfig{}) {
+ msg := fmt.Sprintf("Found credentials for %s in credential helper %s", helperKey, helper)
+ if credHelperPath != "" {
+ msg = fmt.Sprintf("%s in file %s", msg, credHelperPath)
+ }
+ logrus.Debug(msg)
+ return creds, nil
}
- logrus.Debug(msg)
- return creds, nil
}
if multiErr != nil {
return types.DockerAuthConfig{}, multiErr
}
- logrus.Debugf("No credentials for %s found", registry)
+ logrus.Debugf("No credentials for %s found", key)
return types.DockerAuthConfig{}, nil
}
-// GetAuthentication returns the registry credentials stored in the
-// registry-specific credential helpers or in the default global credentials
-// helpers with falling back to using either auth.json file or
-// .docker/config.json
+// GetAuthentication returns the registry credentials matching key, appropriate for
+// sys and the users’ configuration.
+// If an entry is not found, an empty struct is returned.
+// A valid key is a repository, a namespace within a registry, or a registry hostname.
//
// Deprecated: This API only has support for username and password. To get the
// support for oauth2 in container registry authentication, we added the new
-// GetCredentials API. The new API should be used and this API is kept to
+// GetCredentialsForRef and GetCredentials API. The new API should be used and this API is kept to
// maintain backward compatibility.
-func GetAuthentication(sys *types.SystemContext, registry string) (string, string, error) {
- return getAuthenticationWithHomeDir(sys, registry, homedir.Get())
+func GetAuthentication(sys *types.SystemContext, key string) (string, string, error) {
+ return getAuthenticationWithHomeDir(sys, key, homedir.Get())
}
// getAuthenticationWithHomeDir is an internal implementation detail of GetAuthentication,
// it exists only to allow testing it with an artificial home directory.
-func getAuthenticationWithHomeDir(sys *types.SystemContext, registry, homeDir string) (string, string, error) {
- auth, err := getCredentialsWithHomeDir(sys, nil, registry, homeDir)
+func getAuthenticationWithHomeDir(sys *types.SystemContext, key, homeDir string) (string, string, error) {
+ auth, err := getCredentialsWithHomeDir(sys, key, homeDir)
if err != nil {
return "", "", err
}
@@ -353,8 +367,8 @@ func getAuthenticationWithHomeDir(sys *types.SystemContext, registry, homeDir st
// RemoveAuthentication removes credentials for `key` from all possible
// sources such as credential helpers and auth files.
-// A valid key can be either a registry hostname or additionally a namespace if
-// the AuthenticationFileHelper is being unsed.
+// A valid key is a repository, a namespace within a registry, or a registry hostname;
+// using forms other than just a registry may fail depending on configuration.
func RemoveAuthentication(sys *types.SystemContext, key string) error {
isNamespaced, err := validateKey(key)
if err != nil {
@@ -639,26 +653,27 @@ func deleteAuthFromCredHelper(credHelper, registry string) error {
return helperclient.Erase(p, registry)
}
-// findAuthentication looks for auth of registry in path. If ref is
-// not nil, then it will be taken into account when looking up the
-// authentication credentials.
-func findAuthentication(ref reference.Named, registry, path string, legacyFormat bool) (types.DockerAuthConfig, error) {
+// findCredentialsInFile looks for credentials matching "key"
+// (which is "registry" or a namespace in "registry") in "path".
+func findCredentialsInFile(key, registry, path string, legacyFormat bool) (types.DockerAuthConfig, error) {
auths, err := readJSONFile(path, legacyFormat)
if err != nil {
return types.DockerAuthConfig{}, errors.Wrapf(err, "reading JSON file %q", path)
}
// First try cred helpers. They should always be normalized.
+ // This intentionally uses "registry", not "key"; we don't support namespaced
+ // credentials in helpers.
if ch, exists := auths.CredHelpers[registry]; exists {
return getAuthFromCredHelper(ch, registry)
}
- // Support for different paths in auth.
+ // Support sub-registry namespaces in auth.
// (This is not a feature of ~/.docker/config.json; we support it even for
// those files as an extension.)
var keys []string
- if !legacyFormat && ref != nil {
- keys = authKeysForRef(ref)
+ if !legacyFormat {
+ keys = authKeysForKey(key)
} else {
keys = []string{registry}
}
@@ -689,23 +704,22 @@ func findAuthentication(ref reference.Named, registry, path string, legacyFormat
return types.DockerAuthConfig{}, nil
}
-// authKeysForRef returns the valid paths for a provided reference. For example,
-// when given a reference "quay.io/repo/ns/image:tag", then it would return
+// authKeysForKey returns the keys matching a provided auth file key, in order
+// from the best match to worst. For example,
+// when given a repository key "quay.io/repo/ns/image", it returns
// - quay.io/repo/ns/image
// - quay.io/repo/ns
// - quay.io/repo
// - quay.io
-func authKeysForRef(ref reference.Named) (res []string) {
- name := ref.Name()
-
+func authKeysForKey(key string) (res []string) {
for {
- res = append(res, name)
+ res = append(res, key)
- lastSlash := strings.LastIndex(name, "/")
+ lastSlash := strings.LastIndex(key, "/")
if lastSlash == -1 {
break
}
- name = name[:lastSlash]
+ key = key[:lastSlash]
}
return res
@@ -759,11 +773,24 @@ func normalizeRegistry(registry string) string {
// validateKey verifies that the input key does not have a prefix that is not
// allowed and returns an indicator if the key is namespaced.
-func validateKey(key string) (isNamespaced bool, err error) {
+func validateKey(key string) (bool, error) {
if strings.HasPrefix(key, "http://") || strings.HasPrefix(key, "https://") {
- return isNamespaced, errors.Errorf("key %s contains http[s]:// prefix", key)
+ return false, errors.Errorf("key %s contains http[s]:// prefix", key)
+ }
+
+ // Ideally this should only accept explicitly valid keys, compare
+ // validateIdentityRemappingPrefix. For now, just reject values that look
+ // like tagged or digested values.
+ if strings.ContainsRune(key, '@') {
+ return false, fmt.Errorf(`key %s contains a '@' character`, key)
}
+ firstSlash := strings.IndexRune(key, '/')
+ isNamespaced := firstSlash != -1
+ // Reject host/repo:tag, but allow localhost:5000 and localhost:5000/foo.
+ if isNamespaced && strings.ContainsRune(key[firstSlash+1:], ':') {
+ return false, fmt.Errorf(`key %s contains a ':' character after host[:port]`, key)
+ }
// check if the provided key contains one or more subpaths.
- return strings.ContainsRune(key, '/'), nil
+ return isNamespaced, nil
}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 04f15620c..df8cf40c8 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -106,7 +106,7 @@ github.com/containers/buildah/pkg/rusage
github.com/containers/buildah/pkg/sshagent
github.com/containers/buildah/pkg/util
github.com/containers/buildah/util
-# github.com/containers/common v0.46.1-0.20211202172647-e77d74bd1976
+# github.com/containers/common v0.46.1-0.20211205182721-515a2805e7b9
## explicit
github.com/containers/common/libimage
github.com/containers/common/libimage/manifests
@@ -142,7 +142,7 @@ github.com/containers/common/version
# github.com/containers/conmon v2.0.20+incompatible
## explicit
github.com/containers/conmon/runner/config
-# github.com/containers/image/v5 v5.17.1-0.20211129144953-4f6d0b45be6c
+# github.com/containers/image/v5 v5.17.1-0.20211201214147-603ec1341d58
## explicit
github.com/containers/image/v5/copy
github.com/containers/image/v5/directory