Skip to content
This repository has been archived by the owner on Dec 9, 2021. It is now read-only.

Commit

Permalink
Initial import
Browse files Browse the repository at this point in the history
  • Loading branch information
toricls committed Jan 14, 2021
0 parents commit f4c6753
Show file tree
Hide file tree
Showing 12 changed files with 384 additions and 0 deletions.
7 changes: 7 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
cronjob.yaml
LICENSE
NOTICE
README.md
serviceaccount.yaml
examples
iam-permission.json
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.DS_Store
.vscode/*
57 changes: 57 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Contributing Guidelines

Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
documentation, we greatly value feedback and contributions from our community.

Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
information to effectively respond to your bug report or contribution.

## Reporting Bugs/Feature Requests

We welcome you to use the GitHub issue tracker to report bugs or suggest features.

When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already
reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:

* A reproducible test case or series of steps
* The version of our code being used
* Any modifications you've made relevant to the bug
* Anything unusual about your environment or deployment

## Contributing via Pull Requests

Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:

1. You are working against the latest source on the *main* branch.
2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
3. You open an issue to discuss any significant work - we would hate for your time to be wasted.

To send us a pull request, please:

1. Fork the repository.
2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change.
3. Ensure local tests pass.
4. Commit to your fork using clear commit messages.
5. Send us a pull request, answering any default questions in the pull request interface.
6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.

GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
[creating a pull request](https://help.github.com/articles/creating-a-pull-request/).

## Finding contributions to work on

Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start.

## Code of Conduct

This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
[email protected] with any additional questions or comments.

## Security issue notifications

If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue.

## Licensing

See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
24 changes: 24 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0

ARG AWS_CLI_VERSION=2.1.12

FROM amazon/aws-cli:${AWS_CLI_VERSION}

RUN yum update -y \
&& yum install -y openssl \
&& yum clean all

# See https://docs.aws.amazon.com/eks/latest/userguide/install-kubectl.html for other kubectl versions or AWS China regions
ARG KUBECTL_DOWNLOAD_URL=https://amazon-eks.s3.us-west-2.amazonaws.com/1.18.9/2020-11-02/bin/linux/amd64/kubectl
ARG KUBECTL_SHA256_URL=https://amazon-eks.s3.us-west-2.amazonaws.com/1.18.9/2020-11-02/bin/linux/amd64/kubectl.sha256

RUN curl -o kubectl ${KUBECTL_DOWNLOAD_URL} \
&& curl -o kubectl.sha256 ${KUBECTL_SHA256_URL} \
&& openssl sha1 -sha256 kubectl \
&& chmod +x ./kubectl \
&& mv ./kubectl /usr/local/bin/kubectl

COPY entrypoint.sh /

ENTRYPOINT ["/entrypoint.sh"]
14 changes: 14 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
171 changes: 171 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# Amazon ECR "Public" credentials helper script for Kubernetes

Amazon ECR "Public" credentials helper script for Kubernetes (`ecr-public-creds-helper-for-k8s` for short) allows your Kubernetes clusters pull public container images from [Amazon ECR Public](https://aws.amazon.com/blogs/aws/amazon-ecr-public-a-new-public-container-registry/) registries **as authenticated users** to get the limit upgraded to `10` pulls per second which is `1` for unauthenticated users as described [here](https://docs.aws.amazon.com/AmazonECR/latest/public/public-service-quotas.html), and unlimited data bandwidth as described [here](https://aws.amazon.com/ecr/pricing/).

`ecr-public-creds-helper-for-k8s` is one of the workarounds to access ECR Public as authenticated users from your Kubernetes clusters until 1) Amazon ECR Public get supported by the upstream Kubernetes project and/or 2) Official Amazon ECR Public support for AWS Fargate by Amazon EKS.

`ecr-public-creds-helper-for-k8s` runs in your cluster as a Kubernetes CronJob every 8 hours by default. It authenticates against ECR Public and stores the auth token as Kubernetes Secrets within namespaces you specified.

Each pod (even on AWS Fargate) will reference that Kubernetes Secret in its namespace by specifying the `imagePullSecrets` field in the PodSpec. You may also want to patch the `default` service account in each namespace to avoid writing `imagePullSecrets` in all PodSpecs, see the comments at the [entrypoint.sh#L21](entrypoint.sh#L21) for further details.

See the "[Create a Secret by providing credentials on the command line](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#create-a-secret-by-providing-credentials-on-the-command-line)" section and the "[Create a Pod that uses your Secret](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/#create-a-pod-that-uses-your-secret)" in the Kubernetes documentation to understand how it works.

## Motivations

This project aims to fill the gap between Amazon EKS (Kubernetes, both EC2 and Fargate) and Amazon ECR Public. There are two pain points for users at the time of release of this repository as follows:

1. Kubernetes supports Amazon ECR (the private one) to pull private ECR images with automatic auth via IAM roles, but still the latest version (1.20, at this moment) of Kubernetes doesn't support ECR Public yet. Thus there is no straightforward way to pull ECR Public container images from EKS/Kubernetes clusters as authenticated users. This means users are forced to access ECR Public as unauthenticated users from their Kubernetes clusters, resulting that pulls from ECR Public could be throttled easily and frequently.
1. [A PR in the "awslabs/amazon-ecr-credential-helper" GitHub repository](https://github.com/awslabs/amazon-ecr-credential-helper/pull/253) could solve the pain point #1 someday, but the "awslabs/amazon-ecr-credential-helper" itself cannot be used for EKS/Fargate workloads by its design. EKS/Fargate users, not only EKS/EC2 users, obviously need a way to use ECR Public as authenticated users, so this is also the pain point that this project addresses.

## Installation

### Step 0 - Clone repo

```shell
$ git clone https://github.com/aws-containers/amazon-ecr-public-creds-helper-for-k8s.git
$ cd amazon-ecr-public-creds-helper-for-k8s
```

### Step 1 - Build and Push creds-helper container image

```shell
$ export CREDS_HELPER_CONTAINER_IMAGE=<your-creds-helper-container-image-name-here>

$ docker build -t ${CREDS_HELPER_CONTAINER_IMAGE} .

$ docker push ${CREDS_HELPER_CONTAINER_IMAGE}
```

### Step 2 - Create namespace

Create a namespace for `ecr-public-creds-helper-for-k8s` to run as a CronJob in your Kubernetes cluster.

```shell
$ kubectl apply -f namespace.yaml
namespace/ecr-public-creds-helper created
```

### Step 3 - Create service account

Create a service account to allow `ecr-public-creds-helper-for-k8s` to edit Kubernetes secrets.

```shell
$ kubectl apply -f serviceaccount.yaml
serviceaccount/sa-secrets-editor created
clusterrole.rbac.authorization.k8s.io/secrets-editor created
clusterrolebinding.rbac.authorization.k8s.io/edit-secrets created
```

### Step 4 - Create IAM role

Create an AWS IAM role to allow `ecr-public-creds-helper-for-k8s` to authenticate against Amazon ECR Public. We use the mechanism called [IAM Roles for Service Accounts (IRSA)](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) to map it to the service account which you created in the previous step.

If you have not enabled IRSA in your Kubernetes cluster yet, please follow the [IRSA documentation](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html) and/or the [blog post](https://aws.amazon.com/blogs/opensource/introducing-fine-grained-iam-roles-service-accounts/) for enabling IRSA for your Kubernetes cluster.

#### Steps with eksctl

We're going to use `eksctl` here to show the steps to create and map the IAM role in an EKS cluster, but you can also use the AWS CLI, the AWS management console, CloudFormation, Terraform or whatever you want to use.

```shell
$ export POLICY_ARN=$(aws iam create-policy --policy-name AmazonECRPublicAuthOnlyPolicy --policy-document file://iam-permission.json --query Policy.Arn --output text)
## Check you've created the policy successfully
$ echo ${POLICY_ARN}
arn:aws:iam::YOUR_AWS_ACCOUNT_ID:policy/AmazonECRPublicAuthOnlyPolicy

$ export EKS_CLUSTER_NAME=<your-eks-cluster-name-here>

$ eksctl create iamserviceaccount --cluster=${EKS_CLUSTER_NAME} \
--name=sa-secrets-editor \
--namespace=ecr-public-creds-helper \
--attach-policy-arn=${POLICY_ARN} \
--override-existing-serviceaccounts \
--approve
```

### Step 5 - Configure and Apply

#### Configure cronjob.yaml

Edit the [cronjob.yaml](cronjob.yaml) before running `ecr-public-creds-helper-for-k8s` in your Kubernetes cluster.

```shell
$ vim cronjob.yaml
```

There are two **required** fields to change.

##### 1. `image` field

Replace `${CREDS_HELPER_CONTAINER_IMAGE}` in [line.22 in the cronjob.yaml](cronjob.yaml#L22) with your creds helper container image name which you built and pushed in the Step 1.

##### 2. `env.value` field

Replace the value (`default foo bar`) of `TARGET_NAMESPACES` environment variable in [line.26 in the cronjob.yaml](cronjob.yaml#L26) with a space-delimited list which includes one or multiple Kuberentes namespaces where your pods need the auth token for ECR Public.

Let's say you want to pull ECR Public container images in three namespaces (`default`, `prometheus`, `my-app`) with auth token, then the `env.value` field will look like: `value: "default prometheus my-app"`.

`ecr-public-creds-helper-for-k8s` will store the auth token as Kubernetes Secrets in these namespaces.

#### Apply cronjob.yaml

Run `ecr-public-creds-helper-for-k8s` in your Kubernetes cluster.

```shell
$ kubectl apply -f cronjob.yaml
cronjob.batch/ecr-public-creds-helper created
```

### Step 6 - (Optional but recommended) Run Job manually

Create an initial auth token manually to let your pods use it without waiting the initial cronjob to be started. Note that `ecs-public-creds-helper-for-k8s` [refreshes the auth token in every 8 hours by default](cronjob.yaml#L8).

```shell
$ kubectl create job initial-creds-job \
-n ecr-public-creds-helper \
--from=cronjob/ecr-public-creds-helper
job.batch/initial-creds-job created

## Check the pod log to make sure it works as expected
$ export POD_NAME=$(kubectl get pods --selector=job-name=initial-creds-job -n ecr-public-creds-helper -o jsonpath='{.items[0].metadata.name}')

$ echo ${POD_NAME}
initial-creds-job-r4fbp # you'll see something like this

$ kubectl logs ${POD_NAME} -n ecr-public-creds-helper
### You'll see the same number of lines as the namespaces you specified in the "TARGET_NAMESPACES" in the cronjob.yaml
secret/ecr-public-token created
secret/ecr-public-token created
secret/ecr-public-token created

$ kubectl delete job initial-creds-job -n ecr-public-creds-helper
job.batch "initial-creds-job" deleted
```

## Use auth tokens in Pods

Now your pod can use the auth token (Kubernetes secret) created by `ecr-public-creds-helper-for-k8s` to pull public container images as an authenticated user from Amazon ECR Public registries.

You can reference the auth token from your pods like:

```yaml
apiVersion: v1
kind: Pod
# ~ snip ~
spec:
# ~ snip ~
imagePullSecrets:
- name: ecr-public-token
# ~ snip ~
```

See also [examples/pod.yaml](examples/pod.yaml) for full example.

If you don't want to add `imagePullSecrets` in each PodSpec, you may want to see the comments in the [entrypoint.sh](entrypoint.sh#L18).

## Security

See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information.

## License

Licensed under the MIT-0 License. See the [LICENSE](LICENSE) file.
26 changes: 26 additions & 0 deletions cronjob.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: ecr-public-creds-helper
namespace: ecr-public-creds-helper
spec:
schedule: "0 */8 * * *" # Every 8 hours, Note: An auth token for ECR Public is valid for 12 hours
successfulJobsHistoryLimit: 2
failedJobsHistoryLimit: 2
jobTemplate:
spec:
backoffLimit: 3
template:
spec:
serviceAccountName: sa-secrets-editor
terminationGracePeriodSeconds: 0
restartPolicy: Never
containers:
- name: kubectl
imagePullPolicy: Always
image: ${CREDS_HELPER_CONTAINER_IMAGE} ### <- 1. EDIT HERE !!!
env:
# Specify "existing" namespaces as a space-delimited list where you want to create Kubernetes secrets with auth token for ECR Public
- name: TARGET_NAMESPACES
value: "default foo bar" ### <- 2. EDIT HERE !!!
25 changes: 25 additions & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0

#!/usr/bin/env bash
set -euo pipefail

REGISTRY=https://public.ecr.aws
AUTH_USER=AWS
AUTH_TOKEN=$(aws ecr-public get-authorization-token --region us-east-1 --output=text --query 'authorizationData.authorizationToken' | base64 -d | cut -d: -f2)

for n in $(echo ${TARGET_NAMESPACES})
do
kubectl delete secret ecr-public-token \
-n "${n}" \
--ignore-not-found
kubectl create secret docker-registry ecr-public-token \
-n "${n}" \
--docker-server=${REGISTRY} \
--docker-username=${AUTH_USER} \
--docker-password=${AUTH_TOKEN}
# Uncomment the following lines if you want to make all pods use this auth token without specifying it in each PodSpec.
#kubectl patch serviceaccount default \
# -n "${n}"
# -p '{"imagePullSecrets":[{"name":"ecr-public-token"}]}'
done
12 changes: 12 additions & 0 deletions examples/pod.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: v1
kind: Pod
metadata:
name: ecr-public-authenticated-access-example
namespace: default
spec:
containers:
- name: my-container
image: public.ecr.aws/r6p5x8p7/everlasting-hey-yo:latest
# Use the Kubernetes secret created by ecr-public-creds-helper-for-k8s
imagePullSecrets:
- name: ecr-public-token
13 changes: 13 additions & 0 deletions iam-permission.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr-public:GetAuthorizationToken",
"sts:GetServiceBearerToken"
],
"Resource": "*"
}
]
}
4 changes: 4 additions & 0 deletions namespace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: ecr-public-creds-helper
29 changes: 29 additions & 0 deletions serviceaccount.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: sa-secrets-editor
namespace: ecr-public-creds-helper
---
# As you may realized, this is not the minimum permissions
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: secrets-editor
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["create", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: edit-secrets
subjects:
- kind: ServiceAccount
name: sa-secrets-editor
namespace: ecr-public-creds-helper
roleRef:
kind: ClusterRole
name: secrets-editor
apiGroup: rbac.authorization.k8s.io

0 comments on commit f4c6753

Please sign in to comment.