A prototype analog of a Kubernetes admission controller for Amazon Elastic Container Service (ECS) which:
NOTE: This is Proof of Concept code and not yet production-ready. In the event of a bug or misconfiguration, it can prevent any ECS tasks from running. This system intercepts tasks as they start (not before) and may not fully prevent unsigned containers.
If you'd like to learn more or give feedback, please file an issue or send an email to interest@chainguard.dev.
This system comprises a Lambda function (4) which listens to EventBridge events (3) triggered on every ECS task run (1). The function checks that the task's container image has a valid cosign signature in ECR (2) with a specific public key, provided directly or stored in KMS (5). If the check fails, the function terminates the task and sends a notification to an SNS topic (7) which you can subscribe to via email.
In order:
You will need the following tools installed:
make
(e.g., GNU make)cosign
: for generating keysdocker
: if you need to make imagesYou should configure the AWS CLI for your project and account.
To deploy, run:
make sam_deploy
It will check against the key in cosign.pub
(see detailed instructions for how
to change this).
To test, we need an ECS cluster on which to run our signed/unsigned tasks.
The terraform
subdirectory contains a Terraform template for such a cluster,
with corresponding task definitions. First, initialize (to download the AWS
provider for Terraform), then deploy:
make tf_init
make tf_apply # run `make tf_plan` to see the plan first
We can then run our tasks (these will run two public "hello world" Alpine images with and without a signature):
make run_unsigned_task
make run_signed_task
Check that it worked. You should see the unsigned task in the STOPPED
tasks
and the signed task in the RUNNING
tasks:
make task_status
To clean up, run:
make stop_tasks
make tf_destroy
make sam_delete
cosign-ecs-verify
uses a SAM template (template.yml
) to create:
cosign-ecs-function/
) which:
To configure, run sam deploy
with either the KeyArn
set to a KMS key to use, or
KeyPem
set to a full public key in PEM format. In the provided Makefile
, we
hardcode the key in cosign.pub
.
We provide a test ECS cluster configuration (in terraform/
), containing;
signed
and unsigned
, which (by default) run images.If you would like to use your own images (for example, from a previous post on
the Chainguard blog), export $IMAGE_URL_SIGNED
and
$IMAGE_URL_UNSIGNED
before running make tf_apply
. We give instructions below
for making your own to test.
cosign
We need a key against which to verify image signatures. If you have an existing keypair for cosign in AWS KMS, set it:
export KEY_ALIAS=my-key
Otherwise, we can make one:
export KEY_ALIAS=my-key
export AWS_SDK_LOAD_CONFIG=true
make key_gen
To see cosign-ecs-verify
in action, you need an example of a signed and
unsigned image. Here, we'll build two simple images, push them to Amazon ECR,
and sign only one.
First, login to ECR with Docker. We recommend using a credential helper for
docker, but we also provide a make target make ecr_auth
that will authenticate
you to the default registry.
Then, we can create a repository for the signed/unsigned images.
REPO_URL=$(aws ecr create-repository \
--repository-name $REPO_NAME \
--query repository.repositoryUri \
--output text)
Finally, we can build and push two simple images (see Dockerfile
):
# Export these so we can make ECR task definitions for running them.
export IMAGE_URL_SIGNED=$REPO_URL:signed
export IMAGE_URL_UNSIGNED=$REPO_URL:unsigned
# Make 2 example images and push both.
# The --build-arg is to make sure the images have different digests.
docker build . --build-arg signed=true --tag $IMAGE_URL_SIGNED
docker build . --build-arg signed=false --tag $IMAGE_URL_UNSIGNED
docker push $IMAGE_URL_SIGNED
docker push $IMAGE_URL_UNSIGNED
And sign only one of them (make sure you have a key pair):
cosign sign --key awskms:///alias/$KEY_ALIAS $IMAGE_URL_SIGNED
Now, you can proceed with the Terraform instructions.
We require Go 1.17 for development.
You can also use the SAM local feature to run in a simulated Lambda environment:
make sam_local
make sam_local_debug