diff --git a/Dockerfile b/Dockerfile index ecf8ae73..703fe71f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Build the manager binary -FROM golang:1.17 as builder +FROM golang:1.20 as builder WORKDIR /workspace # Copy the Go Modules manifests @@ -15,15 +15,17 @@ COPY api/ api/ COPY controllers/ controllers/ COPY config/ config/ COPY pkg/ pkg/ +COPY cmd/ cmd/ # Build RUN GOPROXY=direct CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o package-manager main.go # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details -FROM gcr.io/distroless/static:nonroot +FROM gcr.io/distroless/static:debug-nonroot WORKDIR / COPY --from=builder /workspace/package-manager . +# https://github.com/GoogleContainerTools/distroless/blob/e3c79deb1c576afd2e7e77b96996e2e1abdc6937/base/base.bzl#L8 USER 65532:65532 ENTRYPOINT ["/package-manager"] diff --git a/charts/eks-anywhere-packages/templates/deployment.yaml b/charts/eks-anywhere-packages/templates/deployment.yaml index ac9f2c13..c18b69b7 100644 --- a/charts/eks-anywhere-packages/templates/deployment.yaml +++ b/charts/eks-anywhere-packages/templates/deployment.yaml @@ -101,6 +101,8 @@ spec: - mountPath: /tmp/ecr-token name: ecr-token readOnly: true + - name: aws-secret + mountPath: /tmp/aws-secret initContainers: - name: init-job image: {{.Values.sourceRegistry}}{{ template "template.image" .Values.cronjob }} @@ -146,6 +148,10 @@ spec: defaultMode: 420 secretName: ecr-token optional: true + - name: aws-secret + secret: + secretName: aws-secret + optional: true {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} diff --git a/cmd/server.go b/cmd/server.go index 50f242fe..fd5138bd 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -31,6 +31,7 @@ import ( "github.com/aws/eks-anywhere-packages/api/v1alpha1" "github.com/aws/eks-anywhere-packages/controllers" pkgConfig "github.com/aws/eks-anywhere-packages/pkg/config" + "github.com/aws/eks-anywhere-packages/pkg/registry" "github.com/aws/eks-anywhere-packages/pkg/webhook" ) @@ -87,6 +88,12 @@ func server() error { return fmt.Errorf("unable to start manager: %v", err) } + ecrCredAdapter, err := registry.NewECRCredInjector(rootCmd.Context(), mgr.GetClient(), packageLog) + if err != nil { + return fmt.Errorf("unable to create ecrCredAdapter: %v", err) + } + go ecrCredAdapter.Run(rootCmd.Context()) + if err = controllers.RegisterPackageBundleReconciler(mgr); err != nil { return fmt.Errorf("unable to register package bundle controller: %v", err) } diff --git a/ecrtokenrefresher/Dockerfile b/ecrtokenrefresher/Dockerfile new file mode 100644 index 00000000..37ade0fb --- /dev/null +++ b/ecrtokenrefresher/Dockerfile @@ -0,0 +1,25 @@ +# Build the manager binary +FROM golang:1.20 as builder + +WORKDIR /workspace +# Copy the Go Modules manifests +COPY go.mod go.mod +COPY go.sum go.sum +# cache deps before building and copying source so that we don't need to re-download as much +# and so that source changes don't invalidate our downloaded layer +RUN go mod download + +# Copy the go source +COPY pkg/ pkg/ +COPY cmd/ cmd/ + +# Build +RUN GOPROXY=direct CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o ecr-token-refresh ./cmd/ecr-token-refresher + +FROM gcr.io/distroless/static:debug-nonroot +WORKDIR / +COPY --from=builder /workspace/ecr-token-refresh . +# https://github.com/GoogleContainerTools/distroless/blob/e3c79deb1c576afd2e7e77b96996e2e1abdc6937/base/base.bzl#L8 +USER 65532:65532 + +ENTRYPOINT ["/ecr-token-refresh"] diff --git a/ecrtokenrefresher/pkg/secrets/registrymirror/registrymirror.go b/ecrtokenrefresher/pkg/secrets/registrymirror/registrymirror.go index 32735217..e81e887d 100644 --- a/ecrtokenrefresher/pkg/secrets/registrymirror/registrymirror.go +++ b/ecrtokenrefresher/pkg/secrets/registrymirror/registrymirror.go @@ -66,12 +66,8 @@ func (mirror *RegistryMirrorSecret) GetClusterCredentials(clientSets secrets.Clu CA: string(secret.Data[caKey]), Insecure: string(secret.Data[insecureKey]), } - if credential.Registry != "" && credential.Username != "" && credential.Password != "" { - clusterCredentials[clusterName] = []*secrets.Credential{credential} - utils.InfoLogger.Println("success.") - } else { - utils.InfoLogger.Println("empty credential.") - } + clusterCredentials[clusterName] = []*secrets.Credential{credential} + utils.InfoLogger.Println("success.") } else { utils.ErrorLogger.Println(err) return nil, err @@ -94,7 +90,9 @@ func (mirror *RegistryMirrorSecret) BroadcastCredentials() error { if clusterName == mirror.mgmtClusterName { data[corev1.DockerConfigJsonKey] = configJson } - data[clusterName+"_ca.crt"] = []byte(creds[0].CA) + if len(creds[0].CA) > 0 { // when "" ca is used, no tls verification will succeed + data[clusterName+"_ca.crt"] = []byte(creds[0].CA) + } data["config.json"] = configJson if creds[0].Insecure == "true" { data[clusterName+"_insecure"] = []byte(creds[0].Insecure) @@ -109,11 +107,13 @@ func (mirror *RegistryMirrorSecret) BroadcastCredentials() error { } secret, _ := k8s.GetSecret(defaultClientSet, credName, constants.PackagesNamespace) if secret == nil { + utils.InfoLogger.Printf("Create secret %s in namespace %s", credName, constants.PackagesNamespace) _, err := k8s.CreateSecret(defaultClientSet, credName, constants.PackagesNamespace, data) if err != nil { return err } } else { + utils.InfoLogger.Printf("Update secret %s in namespace %s", credName, constants.PackagesNamespace) _, err := k8s.UpdateSecret(defaultClientSet, constants.PackagesNamespace, secret, data) if err != nil { return err diff --git a/generatebundlefile/go.mod b/generatebundlefile/go.mod index 6b5a5f4d..4d81be64 100644 --- a/generatebundlefile/go.mod +++ b/generatebundlefile/go.mod @@ -5,13 +5,13 @@ go 1.20 replace github.com/aws/eks-anywhere-packages => ../ require ( - github.com/aws/aws-sdk-go-v2 v1.17.8 - github.com/aws/aws-sdk-go-v2/config v1.13.1 + github.com/aws/aws-sdk-go-v2 v1.21.0 + github.com/aws/aws-sdk-go-v2/config v1.18.39 github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.22.1 - github.com/aws/aws-sdk-go-v2/service/ecr v1.17.3 + github.com/aws/aws-sdk-go-v2/service/ecr v1.19.5 github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.13.3 github.com/aws/aws-sdk-go-v2/service/kms v1.17.1 - github.com/aws/aws-sdk-go-v2/service/sts v1.14.0 + github.com/aws/aws-sdk-go-v2/service/sts v1.21.5 github.com/aws/eks-anywhere-packages v0.0.0-00010101000000-000000000000 github.com/go-logr/logr v1.2.3 github.com/jinzhu/copier v0.3.5 @@ -32,14 +32,15 @@ require ( github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/Masterminds/squirrel v1.5.3 // indirect github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.8.0 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.10.0 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.26 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.20 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.5 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.7.0 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.9.0 // indirect - github.com/aws/smithy-go v1.13.5 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.13.37 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.13.6 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.6 // indirect + github.com/aws/smithy-go v1.14.2 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bshuster-repo/logrus-logstash-hook v1.0.2 // indirect github.com/bugsnag/bugsnag-go v2.1.2+incompatible // indirect diff --git a/generatebundlefile/go.sum b/generatebundlefile/go.sum index fc712770..832fc536 100644 --- a/generatebundlefile/go.sum +++ b/generatebundlefile/go.sum @@ -72,48 +72,49 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY= github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= -github.com/aws/aws-sdk-go-v2 v1.13.0/go.mod h1:L6+ZpqHaLbAaxsqV0L4cvxZY7QupWJB4fhkf8LXvC7w= github.com/aws/aws-sdk-go-v2 v1.16.2/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU= github.com/aws/aws-sdk-go-v2 v1.16.3/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU= github.com/aws/aws-sdk-go-v2 v1.17.2/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2 v1.17.8 h1:GMupCNNI7FARX27L7GjCJM8NgivWbRgpjNI/hOQjFS8= -github.com/aws/aws-sdk-go-v2 v1.17.8/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2/config v1.13.1 h1:yLv8bfNoT4r+UvUKQKqRtdnvuWGMK5a82l4ru9Jvnuo= -github.com/aws/aws-sdk-go-v2/config v1.13.1/go.mod h1:Ba5Z4yL/UGbjQUzsiaN378YobhFo0MLfueXGiOsYtEs= -github.com/aws/aws-sdk-go-v2/credentials v1.8.0 h1:8Ow0WcyDesGNL0No11jcgb1JAtE+WtubqXjgxau+S0o= -github.com/aws/aws-sdk-go-v2/credentials v1.8.0/go.mod h1:gnMo58Vwx3Mu7hj1wpcG8DI0s57c9o42UQ6wgTQT5to= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.10.0 h1:NITDuUZO34mqtOwFWZiXo7yAHj7kf+XPE+EiKuCBNUI= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.10.0/go.mod h1:I6/fHT/fH460v09eg2gVrd8B/IqskhNdpcLH0WNO3QI= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.4/go.mod h1:XHgQ7Hz2WY2GAn//UXHofLfPXWh+s62MbMOijrg12Lw= +github.com/aws/aws-sdk-go-v2 v1.21.0 h1:gMT0IW+03wtYJhRqTVYn0wLzwdnK9sRMcxmtfGzRdJc= +github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pfHvDagGEp0M= +github.com/aws/aws-sdk-go-v2/config v1.18.39 h1:oPVyh6fuu/u4OiW4qcuQyEtk7U7uuNBmHmJSLg1AJsQ= +github.com/aws/aws-sdk-go-v2/config v1.18.39/go.mod h1:+NH/ZigdPckFpgB1TRcRuWCB/Kbbvkxc/iNAKTq5RhE= +github.com/aws/aws-sdk-go-v2/credentials v1.13.37 h1:BvEdm09+ZEh2XtN+PVHPcYwKY3wIeB6pw7vPRM4M9/U= +github.com/aws/aws-sdk-go-v2/credentials v1.13.37/go.mod h1:ACLrdkd4CLZyXOghZ8IYumQbcooAcp2jo/s2xsFH8IM= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11 h1:uDZJF1hu0EVT/4bogChk8DyjSF6fof6uL/0Y26Ma7Fg= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11/go.mod h1:TEPP4tENqBGO99KwVpV9MlOX4NSrSLP8u3KRy2CDwA8= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.9/go.mod h1:AnVH5pvai0pAF4lXRq0bmhbes1u9R8wTE+g+183bZNM= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.10/go.mod h1:F+EZtuIwjlv35kRJPyBGcsA4f7bnSoz15zOQ2lJq1Z4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.26 h1:5WU31cY7m0tG+AiaXuXGoMzo2GBQ1IixtWa8Yywsgco= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.26/go.mod h1:2E0LdbJW6lbeU4uxjum99GZzI0ZjDpAb0CoSCM0oeEY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.2.0/go.mod h1:BsCSJHx5DnDXIrOcqB8KN1/B+hXLG/bi4Y6Vjcx/x9E= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 h1:22dGT7PneFMx4+b3pz7lMTRyN8ZKH7M2cW4GP9yUS2g= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41/go.mod h1:CrObHAuPneJBlfEJ5T3szXOUkLEThaGfvnhTf33buas= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.3/go.mod h1:ssOhaLpRlh88H3UmEcsBoVKq309quMvm3Ds8e9d4eJM= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.4/go.mod h1:8glyUqVIM4AmeenIsPo0oVh3+NUwnsQml2OFupfQW+0= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.20 h1:WW0qSzDWoiWU2FS5DbKpxGilFVlCEJPwx4YtjdfI0Jw= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.20/go.mod h1:/+6lSiby8TBFpTVXZgKiN/rCfkYXEGvhlM4zCgPpt7w= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.5 h1:ixotxbfTCFpqbuwFv/RcZwyzhkxPSYDYEMcj4niB5Uk= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.5/go.mod h1:R3sWUqPcfXSiF/LSFJhjyJmpg9uV6yP2yv3YZZjldVI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 h1:SijA0mgjV8E+8G45ltVHs0fvKpTj8xmZJ3VwhGKtUSI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35/go.mod h1:SJC1nEVVva1g3pHAIdCp7QsRIkMmLAgoDquQ9Rr8kYw= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42 h1:GPUcE/Yq7Ur8YSUk6lVkoIMWnJNO0HT18GUzCWCgCI0= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42/go.mod h1:rzfdUlfA+jdgLDmPKjd3Chq9V7LVLYo1Nz++Wb91aRo= github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.22.1 h1:X5wW/v/Lk++7ZKIee9wCs1uT4ENIJyuancNe5hHY7rg= github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.22.1/go.mod h1:HL0nIKWDYBdEhBDS4bldwL3NWi2++f8l1n+mQnTrr9Q= -github.com/aws/aws-sdk-go-v2/service/ecr v1.17.3 h1:izPPh0CPwbJMF+KkiOG30+Ptm90VXw15CI4Ipj5cP8M= -github.com/aws/aws-sdk-go-v2/service/ecr v1.17.3/go.mod h1:Yf1qbCbx9ds6+R5R7rXj5c04FSRjpTYEewce6nG9TIc= +github.com/aws/aws-sdk-go-v2/service/ecr v1.19.5 h1:hg2/a7rE9dwYr+/DPNzHQ+IsHXLNt1NsQVUecBtA8os= +github.com/aws/aws-sdk-go-v2/service/ecr v1.19.5/go.mod h1:pGwmNL8hN0jpBfKfTbmu+Rl0bJkDhaGl+9PQLrZ4KLo= github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.13.3 h1:2XpcXse156FZfnvnrzqTb8uwJuWUcT1ryiU7dZOzBYc= github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.13.3/go.mod h1:JojDs/ei43SWG9m059FtaOBJK607XPF5RuRJZ8NTWTk= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.7.0 h1:4QAOB3KrvI1ApJK14sliGr3Ie2pjyvNypn/lfzDHfUw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.7.0/go.mod h1:K/qPe6AP2TGYv4l6n7c88zh9jWBDf6nHhvg1fx/EWfU= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 h1:CdzPW9kKitgIiLV1+MHobfR5Xg25iYnyzWZhyQuSlDI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35/go.mod h1:QGF2Rs33W5MaN9gYdEQOBBFPLwTZkEhRwI33f7KIG0o= github.com/aws/aws-sdk-go-v2/service/kms v1.17.1 h1:8T0uFw+t/+uP0ukowdDQ2fxhh5jh07bM4WI8/KRGtv8= github.com/aws/aws-sdk-go-v2/service/kms v1.17.1/go.mod h1:0B58/BshOoe7rhRRRtHWVGcXqlJn7gQZmNLyKucFhCU= -github.com/aws/aws-sdk-go-v2/service/sso v1.9.0 h1:1qLJeQGBmNQW3mBNzK2CFmrQNmoXWrscPqsrAaU1aTA= -github.com/aws/aws-sdk-go-v2/service/sso v1.9.0/go.mod h1:vCV4glupK3tR7pw7ks7Y4jYRL86VvxS+g5qk04YeWrU= -github.com/aws/aws-sdk-go-v2/service/sts v1.14.0 h1:ksiDXhvNYg0D2/UFkLejsaz3LqpW5yjNQ8Nx9Sn2c0E= -github.com/aws/aws-sdk-go-v2/service/sts v1.14.0/go.mod h1:u0xMJKDvvfocRjiozsoZglVNXRG19043xzp3r2ivLIk= -github.com/aws/smithy-go v1.10.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= +github.com/aws/aws-sdk-go-v2/service/sso v1.13.6 h1:2PylFCfKCEDv6PeSN09pC/VUiRd10wi1VfHG5FrW0/g= +github.com/aws/aws-sdk-go-v2/service/sso v1.13.6/go.mod h1:fIAwKQKBFu90pBxx07BFOMJLpRUGu8VOzLJakeY+0K4= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.6 h1:pSB560BbVj9ZlJZF4WYj5zsytWHWKxg+NgyGV4B2L58= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.6/go.mod h1:yygr8ACQRY2PrEcy3xsUI357stq2AxnFM6DIsR9lij4= +github.com/aws/aws-sdk-go-v2/service/sts v1.21.5 h1:CQBFElb0LS8RojMJlxRSo/HXipvTZW2S44Lt9Mk2aYQ= +github.com/aws/aws-sdk-go-v2/service/sts v1.21.5/go.mod h1:VC7JDqsqiwXukYEDjoHh9U0fOJtNWh04FPQz4ct4GGU= github.com/aws/smithy-go v1.11.2/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM= -github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/aws/smithy-go v1.14.2 h1:MJU9hqBGbvWZdApzpvoF2WAIJDbtjK2NDJSiJP7HblQ= +github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -303,7 +304,6 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= diff --git a/go.mod b/go.mod index 953251d9..6feae088 100644 --- a/go.mod +++ b/go.mod @@ -40,6 +40,7 @@ require ( github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/Masterminds/squirrel v1.5.3 // indirect github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect + github.com/aws/aws-sdk-go-v2 v1.21.0 github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect @@ -163,3 +164,23 @@ require ( sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect ) + +require ( + github.com/aws/aws-sdk-go-v2/config v1.18.39 + github.com/aws/smithy-go v1.14.2 // indirect +) + +require ( + github.com/aws/aws-sdk-go-v2/credentials v1.13.37 + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42 // indirect + github.com/aws/aws-sdk-go-v2/service/ecr v1.19.5 + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.13.6 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.6 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.21.5 // indirect +) + +require github.com/jmespath/go-jmespath v0.4.0 // indirect diff --git a/go.sum b/go.sum index 9e28d70e..98cd86b7 100644 --- a/go.sum +++ b/go.sum @@ -75,6 +75,32 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY= github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= +github.com/aws/aws-sdk-go-v2 v1.21.0 h1:gMT0IW+03wtYJhRqTVYn0wLzwdnK9sRMcxmtfGzRdJc= +github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pfHvDagGEp0M= +github.com/aws/aws-sdk-go-v2/config v1.18.39 h1:oPVyh6fuu/u4OiW4qcuQyEtk7U7uuNBmHmJSLg1AJsQ= +github.com/aws/aws-sdk-go-v2/config v1.18.39/go.mod h1:+NH/ZigdPckFpgB1TRcRuWCB/Kbbvkxc/iNAKTq5RhE= +github.com/aws/aws-sdk-go-v2/credentials v1.13.37 h1:BvEdm09+ZEh2XtN+PVHPcYwKY3wIeB6pw7vPRM4M9/U= +github.com/aws/aws-sdk-go-v2/credentials v1.13.37/go.mod h1:ACLrdkd4CLZyXOghZ8IYumQbcooAcp2jo/s2xsFH8IM= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11 h1:uDZJF1hu0EVT/4bogChk8DyjSF6fof6uL/0Y26Ma7Fg= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11/go.mod h1:TEPP4tENqBGO99KwVpV9MlOX4NSrSLP8u3KRy2CDwA8= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 h1:22dGT7PneFMx4+b3pz7lMTRyN8ZKH7M2cW4GP9yUS2g= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41/go.mod h1:CrObHAuPneJBlfEJ5T3szXOUkLEThaGfvnhTf33buas= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 h1:SijA0mgjV8E+8G45ltVHs0fvKpTj8xmZJ3VwhGKtUSI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35/go.mod h1:SJC1nEVVva1g3pHAIdCp7QsRIkMmLAgoDquQ9Rr8kYw= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42 h1:GPUcE/Yq7Ur8YSUk6lVkoIMWnJNO0HT18GUzCWCgCI0= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42/go.mod h1:rzfdUlfA+jdgLDmPKjd3Chq9V7LVLYo1Nz++Wb91aRo= +github.com/aws/aws-sdk-go-v2/service/ecr v1.19.5 h1:hg2/a7rE9dwYr+/DPNzHQ+IsHXLNt1NsQVUecBtA8os= +github.com/aws/aws-sdk-go-v2/service/ecr v1.19.5/go.mod h1:pGwmNL8hN0jpBfKfTbmu+Rl0bJkDhaGl+9PQLrZ4KLo= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 h1:CdzPW9kKitgIiLV1+MHobfR5Xg25iYnyzWZhyQuSlDI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35/go.mod h1:QGF2Rs33W5MaN9gYdEQOBBFPLwTZkEhRwI33f7KIG0o= +github.com/aws/aws-sdk-go-v2/service/sso v1.13.6 h1:2PylFCfKCEDv6PeSN09pC/VUiRd10wi1VfHG5FrW0/g= +github.com/aws/aws-sdk-go-v2/service/sso v1.13.6/go.mod h1:fIAwKQKBFu90pBxx07BFOMJLpRUGu8VOzLJakeY+0K4= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.6 h1:pSB560BbVj9ZlJZF4WYj5zsytWHWKxg+NgyGV4B2L58= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.6/go.mod h1:yygr8ACQRY2PrEcy3xsUI357stq2AxnFM6DIsR9lij4= +github.com/aws/aws-sdk-go-v2/service/sts v1.21.5 h1:CQBFElb0LS8RojMJlxRSo/HXipvTZW2S44Lt9Mk2aYQ= +github.com/aws/aws-sdk-go-v2/service/sts v1.21.5/go.mod h1:VC7JDqsqiwXukYEDjoHh9U0fOJtNWh04FPQz4ct4GGU= +github.com/aws/smithy-go v1.14.2 h1:MJU9hqBGbvWZdApzpvoF2WAIJDbtjK2NDJSiJP7HblQ= +github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -262,6 +288,7 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -340,6 +367,10 @@ github.com/itchyny/gojq v0.12.6/go.mod h1:ZHrkfu7A+RbZLy5J1/JKpS4poEqrzItSTGDItq github.com/itchyny/timefmt-go v0.1.3 h1:7M3LGVDsqcd0VZH2U+x393obrzZisp7C0uEe921iRkU= github.com/itchyny/timefmt-go v0.1.3/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= diff --git a/pkg/authenticator/ecrsecret.go b/pkg/authenticator/ecrsecret.go index 563a045e..81a623f3 100644 --- a/pkg/authenticator/ecrsecret.go +++ b/pkg/authenticator/ecrsecret.go @@ -21,7 +21,7 @@ const ( ecrTokenName = "ecr-token" cronJobName = "cron-ecr-renew" jobExecName = "eksa-auth-refresher-" - mirrorCredName = "registry-mirror-cred" + MirrorCredName = "registry-mirror-cred" ) type ecrSecret struct { @@ -136,7 +136,7 @@ func (s *ecrSecret) GetSecretValues(ctx context.Context, namespace string) (map[ imagePullSecret[0] = make(map[string]string) imagePullSecret[0]["name"] = ecrTokenName imagePullSecret[1] = make(map[string]string) - imagePullSecret[1]["name"] = mirrorCredName + imagePullSecret[1]["name"] = MirrorCredName values["imagePullSecrets"] = imagePullSecret return values, nil @@ -147,7 +147,6 @@ func (s *ecrSecret) cleanupPrevRuns(ctx context.Context) error { deletePropagation := metav1.DeletePropagationBackground jobs, err := s.clientset.BatchV1().Jobs(api.PackageNamespace). List(ctx, metav1.ListOptions{LabelSelector: labels.Set(labelSelector.MatchLabels).String()}) - if err != nil { return err } diff --git a/pkg/registry/ecr_cred_injector.go b/pkg/registry/ecr_cred_injector.go new file mode 100644 index 00000000..b20497f4 --- /dev/null +++ b/pkg/registry/ecr_cred_injector.go @@ -0,0 +1,255 @@ +package registry + +import ( + "bytes" + "context" + "encoding/base64" + "fmt" + "os" + "strings" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + awsConfig "github.com/aws/aws-sdk-go-v2/config" + awsCredentials "github.com/aws/aws-sdk-go-v2/credentials" + "github.com/aws/aws-sdk-go-v2/service/ecr" + "github.com/docker/cli/cli/config" + dockerTypes "github.com/docker/cli/cli/config/types" + "github.com/go-logr/logr" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "oras.land/oras-go/v2/registry/remote/auth" + "sigs.k8s.io/controller-runtime/pkg/client" + + api "github.com/aws/eks-anywhere-packages/api/v1alpha1" + "github.com/aws/eks-anywhere-packages/pkg/authenticator" +) + +const ( + awsSecretPath = "/tmp/config/aws-secret" //#nosec G101 +) + +// ECRCredInjector is an adapter to convert ECR credential to Docker credential. Since the converted docker credential is only valid for 12 hours, this adapter is constantly running. It's responsibility is to make sure docker config in the filesystem contains ECR credential to pull bundle yaml and charts. +type ECRCredInjector struct { + k8sClient client.Client + ecrClient *ecr.Client + log logr.Logger +} + +func NewECRCredInjector(ctx context.Context, k8sClient client.Client, log logr.Logger) (*ECRCredInjector, error) { + l := log.WithName("ECRCredInjector") + ecrClient, err := GetECRClient(ctx, l) + if err != nil { + return nil, err + } + + return &ECRCredInjector{ + k8sClient: k8sClient, + ecrClient: ecrClient, + log: l, + }, nil +} + +func (a *ECRCredInjector) Run(ctx context.Context) { + err := a.Refresh(ctx) + if err != nil { + a.log.Error(err, "Failed to inject ECR credential to docker config") + } else { + a.log.Info("ECR credential is injected to the docker config file") + } + + for range time.Tick(time.Hour) { + err := a.Refresh(ctx) + if err != nil { + a.log.Error(err, "Failed to refresh ECR credential in dockerconfig file") + } else { + a.log.Info("injected ECR credential has be refreshed") + } + } +} + +func (a *ECRCredInjector) Refresh(ctx context.Context) error { + a.log.Info("Refreshing ECR credential") + cred, err := GetCredential(a.ecrClient) + if err != nil { + return err + } + + dockerSecret, err := a.GetRegistryMirrorSecret(ctx) + if err != nil { + return err + } + + registry, err := a.GetECR(ctx) + if err != nil { + return err + } + + if !IsECRRegistry(registry) { + a.log.Info("defaultRegistry is not ECR registry, skip injecting credential to docker config") + } + // update "config.json" in dockerSecret + return a.InjectCredential(ctx, *dockerSecret, registry, cred) +} + +// GetECR get the defaultRegistry config from package bundle controller. +func (a *ECRCredInjector) GetECR(ctx context.Context) (string, error) { + pbc := &api.PackageBundleController{} + err := a.k8sClient.Get(ctx, types.NamespacedName{ + Namespace: api.PackageNamespace, + Name: os.Getenv("CLUSTER_NAME"), + }, pbc) + if err != nil { + return "", err + } + + // defaultRegistry could be followed by path + ss := strings.Split(pbc.Spec.DefaultRegistry, "/") + return ss[0], nil +} + +// GetRegistryMirrorSecret gets registry mirror secret from eksa-packages namespace +func (a *ECRCredInjector) GetRegistryMirrorSecret(ctx context.Context) (*v1.Secret, error) { + var secret v1.Secret + + err := a.k8sClient.Get(ctx, types.NamespacedName{ + Namespace: api.PackageNamespace, + // this secret is populated by token refresher + Name: authenticator.MirrorCredName, + }, &secret) + if err != nil { + return nil, err + } + + return &secret, nil +} + +// InjectCredential update field "config.json" in the secret, which is used by packages controller's oras and helm +func (a *ECRCredInjector) InjectCredential(ctx context.Context, secret v1.Secret, registry string, cred auth.Credential) error { + d := secret.Data["config.json"] + var configJson []byte + if _, err := base64.StdEncoding.Decode(d, configJson); err != nil { + return err + } + + dockerConfig, err := config.LoadFromReader(strings.NewReader(string(configJson))) + if err != nil { + return err + } + dockerConfig.AuthConfigs[registry] = dockerTypes.AuthConfig{ + Username: cred.Username, + Password: cred.Password, + } + + buf := new(bytes.Buffer) + err = dockerConfig.SaveToWriter(buf) + if err != nil { + return err + } + + secret.Data["config.json"] = buf.Bytes() + return a.k8sClient.Update(ctx, &secret, &client.UpdateOptions{}) +} + +func GetECRClient(ctx context.Context, log logr.Logger) (*ecr.Client, error) { + var c *ecr.Client + var err error + c = getECRClientFromConfig(ctx, log) + if c == nil { + c, err = getECRClientFromVariables(ctx, log) + if err != nil { + return nil, fmt.Errorf("Unable to load AWS config, " + err.Error()) + } + } + return c, nil +} + +// getECRClientFromConfig tries to get ecrClient from aws config file, if failed, return nil +func getECRClientFromConfig(ctx context.Context, log logr.Logger) *ecr.Client { + configPath := awsSecretPath + "/config" + // check if configPath exist + if _, err := os.Stat(configPath); os.IsNotExist(err) { + log.Info("aws config file does not exist, skip loading config") + return nil + } + + cfg, err := awsConfig.LoadDefaultConfig(ctx, + awsConfig.WithSharedConfigFiles([]string{configPath}), + ) + if err != nil { + fmt.Println("Unable to load AWS config from file, " + err.Error()) + return nil + } + + return ecr.NewFromConfig(cfg) +} + +// getECRClientFromVariables tries to get ecrClient from access_key and region +func getECRClientFromVariables(ctx context.Context, log logr.Logger) (*ecr.Client, error) { + // similar to https://github.com/aws/eks-anywhere-packages/blob/eca65837c277f7769f721f2251b3e92f0d8edb68/credentialproviderpackage/pkg/awscred/awscred.go#L11 + accessKeyPath := awsSecretPath + "/AWS_ACCESS_KEY_ID" + secretAccessKeyPath := awsSecretPath + "/AWS_SECRET_ACCESS_KEY" + regionPath := awsSecretPath + "/REGION" + + accessKeyByte, err := os.ReadFile(accessKeyPath) + if err != nil { + log.Error(err, "Cannot get access key from file") + } + accessKey := strings.Trim(string(accessKeyByte), "'") + secretAccessKeyByte, err := os.ReadFile(secretAccessKeyPath) + if err != nil { + log.Error(err, "Cannot get secret access key from file") + } + secretAccessKey := strings.Trim(string(secretAccessKeyByte), "'") + regionByte, err := os.ReadFile(regionPath) + if err != nil { + log.Error(err, "Cannot get region from file, %v") + } + region := strings.Trim(string(regionByte), "'") + + cfg, err := awsConfig.LoadDefaultConfig(ctx, + awsConfig.WithCredentialsProvider(awsCredentials.NewStaticCredentialsProvider(accessKey, secretAccessKey, "")), + awsConfig.WithRegion(region), + ) + if err != nil { + return nil, err + } + + return ecr.NewFromConfig(cfg), nil +} + +func GetCredential(ecrClient *ecr.Client) (auth.Credential, error) { + out, err := ecrClient.GetAuthorizationToken(context.Background(), &ecr.GetAuthorizationTokenInput{}) + if err != nil { + return auth.EmptyCredential, err + } + token := out.AuthorizationData[0].AuthorizationToken + + cred, err := ExtractECRToken(aws.ToString(token)) + if err != nil { + return auth.EmptyCredential, err + } + + return *cred, nil +} + +func ExtractECRToken(token string) (*auth.Credential, error) { + decodedToken, err := base64.StdEncoding.DecodeString(token) + if err != nil { + return nil, fmt.Errorf("invalid token: %w", err) + } + + parts := strings.SplitN(string(decodedToken), ":", 2) + if len(parts) < 2 { + return nil, fmt.Errorf("invalid token: expected two parts, got %d", len(parts)) + } + + return &auth.Credential{ + Username: parts[0], + Password: parts[1], + }, nil +} + +func IsECRRegistry(registry string) bool { + return strings.HasSuffix(registry, "amazonaws.com") +} diff --git a/pkg/registry/ecr_cred_injector_test.go b/pkg/registry/ecr_cred_injector_test.go new file mode 100644 index 00000000..d42897fd --- /dev/null +++ b/pkg/registry/ecr_cred_injector_test.go @@ -0,0 +1,50 @@ +package registry_test + +import ( + "context" + "encoding/base64" + "testing" + + "github.com/go-logr/logr" + "github.com/golang/mock/gomock" + + ctrlmocks "github.com/aws/eks-anywhere-packages/controllers/mocks" + "github.com/aws/eks-anywhere-packages/pkg/registry" +) + +func TestECRCredInjector(t *testing.T) { + gomockController := gomock.NewController(t) + k8sClient := ctrlmocks.NewMockClient(gomockController) + injector, err := registry.NewECRCredInjector(context.Background(), k8sClient, logr.Discard()) + if err != nil { + t.Errorf("should not have failed to create injector: %v", err) + } + err = injector.Refresh(context.Background()) + if err == nil { + t.Error("refresh should have failed because no AWS credential has been set") + } +} + +func TestExtractECRToken(t *testing.T) { + auth, err := registry.ExtractECRToken(base64.StdEncoding.EncodeToString([]byte("username:password"))) + if err != nil { + t.Errorf("encode should not fail: %v", err) + } + if auth.Username != "username" { + t.Errorf("username is not expected") + } + if auth.Password != "password" { + t.Errorf("password is not expected") + } +} + +func TestIsECRRegistry(t *testing.T) { + res := registry.IsECRRegistry("5551212.dkr.ecr.us-west-2.amazonaws.com") + if res != true { + t.Errorf("registry is expected to be ECR") + } + res = registry.IsECRRegistry("localhost") + if res != false { + t.Errorf("registry is not ECR") + } +}