diff --git a/sources/host-ctr/cmd/host-ctr/main.go b/sources/host-ctr/cmd/host-ctr/main.go index 7985a9806f2..6ec3cc4b7cc 100644 --- a/sources/host-ctr/cmd/host-ctr/main.go +++ b/sources/host-ctr/cmd/host-ctr/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "encoding/base64" "io/ioutil" "math/rand" "os" @@ -11,6 +12,9 @@ import ( "syscall" "time" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/ecrpublic" "github.com/awslabs/amazon-ecr-containerd-resolver/ecr" "github.com/containerd/containerd" "github.com/containerd/containerd/cio" @@ -20,6 +24,7 @@ import ( "github.com/containerd/containerd/log" "github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/oci" + "github.com/containerd/containerd/remotes/docker" runtimespec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -664,19 +669,68 @@ func tagImage(ctx context.Context, imageName string, newImageName string, client // withDynamicResolver provides an initialized resolver for use with ref. func withDynamicResolver(ctx context.Context, ref string) containerd.RemoteOpt { - if !strings.HasPrefix(ref, "ecr.aws/") { - // not handled here - return func(_ *containerd.Client, _ *containerd.RemoteContext) error { return nil } - } - - return func(_ *containerd.Client, c *containerd.RemoteContext) error { - // Create the ECR resolver - resolver, err := ecr.NewResolver() + noOp := func(_ *containerd.Client, _ *containerd.RemoteContext) error { return nil } + + switch { + // For ECR registries, we need to use the Amazon ECR resolver + case strings.HasPrefix(ref, "ecr.aws/"): + return func(_ *containerd.Client, c *containerd.RemoteContext) error { + // Create the Amazon ECR resolver + resolver, err := ecr.NewResolver() + if err != nil { + return errors.Wrap(err, "Failed to create ECR resolver") + } + log.G(ctx).WithField("ref", ref).Info("pulling with Amazon ECR Resolver") + c.Resolver = resolver + return nil + } + // For Amazon ECR Public registries, we should try and fetch credentials before resolving the image reference + case strings.HasPrefix(ref, "public.ecr.aws/"): + // Try to get credentials for authenticated pulls from ECR Public + session := session.Must(session.NewSession()) + // The ECR Public API is only available in us-east-1 today + publicConfig := aws.NewConfig().WithRegion("us-east-1") + client := ecrpublic.New(session, publicConfig) + output, err := client.GetAuthorizationToken(&ecrpublic.GetAuthorizationTokenInput{}) if err != nil { - return errors.Wrap(err, "Failed to create ECR resolver") + log.G(ctx).Warn("ecr-public: failed to get authorization token, falling back to default resolver (unauthenticated pull)") + return noOp } - log.G(ctx).WithField("ref", ref).Info("pulling with Amazon ECR Resolver") - c.Resolver = resolver - return nil + if output == nil || output.AuthorizationData == nil { + log.G(ctx).Warn("ecr-public: missing AuthorizationData in ECR Public GetAuthorizationToken response, falling back to default resolver (unauthenticated pull)") + return noOp + } + authToken, err := base64.StdEncoding.DecodeString(aws.StringValue(output.AuthorizationData.AuthorizationToken)) + if err != nil { + log.G(ctx).Warn("ecr-public: unable to decode authorization token, falling back to default resolver (unauthenticated pull)") + return noOp + } + tokens := strings.SplitN(string(authToken), ":", 2) + if len(tokens) != 2 { + log.G(ctx).Warn("ecr-public: invalid credentials decoded from authorization token, falling back to default resolver (unauthenticated pull)") + return noOp + } + // Use the fetched authorization credentials to resolve the image + authOpt := docker.WithAuthCreds(func(host string) (string, string, error) { + // Double-check to make sure the we're doing this for an ECR Public registry + if host != "public.ecr.aws" { + return "", "", errors.New("ecr-public: expected image to start with public.ecr.aws") + } + return tokens[0], tokens[1], nil + }) + authorizer := docker.NewDockerAuthorizer(authOpt) + resolverOpt := docker.ResolverOptions{ + Hosts: docker.ConfigureDefaultRegistries(docker.WithAuthorizer(authorizer)), + } + + return func(_ *containerd.Client, c *containerd.RemoteContext) error { + resolver := docker.NewResolver(resolverOpt) + log.G(ctx).WithField("ref", ref).Info("pulling from ECR Public") + c.Resolver = resolver + return nil + } + default: + // For all other registries + return noOp } } diff --git a/sources/host-ctr/go.mod b/sources/host-ctr/go.mod index 69aadf1835f..127c8cebc1f 100644 --- a/sources/host-ctr/go.mod +++ b/sources/host-ctr/go.mod @@ -5,7 +5,7 @@ go 1.12 require ( github.com/Microsoft/go-winio v0.4.15 // indirect github.com/Microsoft/hcsshim v0.8.10 // indirect - github.com/aws/aws-sdk-go v1.35.25 // indirect + github.com/aws/aws-sdk-go v1.37.0 github.com/awslabs/amazon-ecr-containerd-resolver v0.0.0-20200922205237-bbd7175f7bd0 github.com/containerd/cgroups v0.0.0-20201109155418-13abef5d31ec // indirect github.com/containerd/containerd v1.3.7 @@ -24,7 +24,6 @@ require ( github.com/willf/bitset v1.1.11 // indirect go.etcd.io/bbolt v1.3.5 // indirect go.opencensus.io v0.22.5 // indirect - golang.org/x/net v0.0.0-20201110031124-69a78807bb2b // indirect golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 // indirect golang.org/x/sys v0.0.0-20201109165425-215b40eba54c // indirect golang.org/x/text v0.3.4 // indirect diff --git a/sources/host-ctr/go.sum b/sources/host-ctr/go.sum index ff4341534a6..d71556c6da5 100644 --- a/sources/host-ctr/go.sum +++ b/sources/host-ctr/go.sum @@ -12,8 +12,8 @@ github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg3 github.com/Microsoft/hcsshim v0.8.10 h1:k5wTrpnVU2/xv8ZuzGkbXVd3js5zJ8RnumPo5RxiIxU= github.com/Microsoft/hcsshim v0.8.10/go.mod h1:g5uw8EV2mAlzqe94tfNBNdr89fnbD/n3HV0OhsddkmM= github.com/aws/aws-sdk-go v1.32.13/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= -github.com/aws/aws-sdk-go v1.35.25 h1:0+UC6ZquMOLvYABoz0olShCAe+M9oKllgPfr2hnv9zE= -github.com/aws/aws-sdk-go v1.35.25/go.mod h1:tlPOdRjfxPBpNIwqDj61rmsnA85v9jc0Ps9+muhnW+k= +github.com/aws/aws-sdk-go v1.37.0 h1:GzFnhOIsrGyQ69s7VgqtrG2BG8v7X7vwB3Xpbd/DBBk= +github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/awslabs/amazon-ecr-containerd-resolver v0.0.0-20200922205237-bbd7175f7bd0 h1:mxzIxXHpy2nmQH1FG/5y0Mr1LRk7RqzwBB+cKe0YwmQ= github.com/awslabs/amazon-ecr-containerd-resolver v0.0.0-20200922205237-bbd7175f7bd0/go.mod h1:KuTNnZvgS7awNYUpeTkWpvz3GZoj6GNkwr/Mn2TCF8Y= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=