https://github.com/awslabs/ecs-secrets

Runtime secrets management solution for ECS using Task IAM Roles

https://github.com/awslabs/ecs-secrets

Science Score: 13.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
  • codemeta.json file
    Found codemeta.json file
  • .zenodo.json file
  • DOI references
  • Academic publication links
  • Academic email domains
  • Institutional organization owner
  • JOSS paper metadata
  • Scientific vocabulary similarity
    Low similarity (11.4%) to scientific vocabulary

Keywords

docker ecs secret-management
Last synced: 6 months ago · JSON representation

Repository

Runtime secrets management solution for ECS using Task IAM Roles

Basic Info
  • Host: GitHub
  • Owner: awslabs
  • License: apache-2.0
  • Language: Go
  • Default Branch: master
  • Homepage:
  • Size: 488 KB
Statistics
  • Stars: 177
  • Watchers: 38
  • Forks: 21
  • Open Issues: 4
  • Releases: 1
Topics
docker ecs secret-management
Created over 9 years ago · Last pushed almost 9 years ago
Metadata Files
Readme License

README.md

ECS Secrets - Managing Runtime Secrets for Containers in ECS

Containerized applications frequently need access to sensitive information at runtime such as API keys, passwords, certificates etc (aka secrets). Handling such secrets is a challenging and recurring problem for Docker containers. ECS customers also come up against this issue and there's a need to provide a mechanism for delivering secrets securely to such containerized applications.

A write-up of various approaches to this problem is summarized in the "Secrets: write-up best practices, do's and don'ts, roadmap" docker issue. This issue also outlines the risks with the following commonly used work-arounds for secrets management:

  • Environment Variables: Secrets are easily leaked and unencrypted as secrets are visible in docker inspect
  • Volumes: Secrets are easily leaked via volumes-from and when creating images from existing containers
  • Building into the container image: Secrets are unencrypted and can be leaked easily via build cache or image sharing

The How to Manage Secrets for Amazon EC2 Container Service–Based Applications by Using Amazon S3 and Docker blog documents how you could store secrets in an Amazon S3 bucket and use AWS Identity and Management (IAM) roles to grant access to those stored secrets. The Managing Secrets for Amazon ECS Applications Using Parameter Store and IAM Roles for Tasks blog illustrates how the EC2 SSM Parameter Store can be used to do the same. The ecs-secrets tool takes an alternative approach of using the AWS Key Management Service (KMS) to encrypt and decrypt secrets stored in Amazon DynamoDB service and use IAM roles for ECS Tasks to control access to these secrets.

What is ecs-secrets?

ecs-secrets provides an out-of-the-box solution for managing and accessing runtime secrets for containers on ECS. It provides a simple command line interface and RESTful APIs to create, rotate, fetch and revoke runtime secrets. It uses DynamoDB to store and retrieve application secret key-value pairs. The secret payload is encrypted and decrypted using KMS Data Keys. Permissions and policies to access these secrets can then be set using IAM Users and Roles.

This design helps in addressing the following security goals:

  • Privilege Separation: Different entities can be authorized to perform secret management and secret query. This ensures that applications that read secrets can be authorized to just fetch secrets while a separate IAM user/role is used to create, rotate and revoke secrets
  • Encryption: Secrets are encrypted at rest in DynamoDB using KMS data keys and also in transit using AWS SDK (AWS SDK uses HTTPS by default to ensure secrets are encrypted in transit). Even if access is gained to view entries in the DynamoDB table, IAM role/user permissions to decrypt using KMS Customer Master Key are still needed to view secrets
  • Access Control: Access to secrets from containers and tasks can be controlled using IAM roles
  • Version Control: Secrets are version controlled. New versions of secrets can be registered and existing versions can be revoked

You can interact with ecs-secrets using either the CLI or the RESTful API endpoint. To ensure easy access to IAM role credentials for different entities managing secrets, and to ensure Separation of Privilege between the same, it's recommended that you incorporate the packaged amazon/amazon-ecs-secrets container into an existing Task Definition as a sidecar and make use of the RESTful APIs by running it in the daemon mode.

Getting Started

To use ecs-secrets, you can download the amazon/amazon-ecs-secrets container from dockerhub and execute the setup command. You can run this from either your local host or an EC2 Instance as long as you have access to credentials that let you create a CloudFormation stack with a KMS Master Key and a Dynamo DB table.

In this example, credentials for an IAM user with administrative privileges for an account have been saved in a profile named default to setup ecs-secrets: bash $ cat << EOF > setup-env.txt AWS_REGION=us-west-2 AWS_PROFILE=default AWS_SHARED_CREDENTIALS_FILE=/root/.aws/credentials EOF The following command deploys up a Cloudformation stack named ECS-Secrets-cryptex, which creates:

  • A DynamoDB table named ECS-Secrets-cryptex-Secrets
  • A KMS Master Key with alias ECSSecretsMaskerKey-cryptex with policies to ensure that:
    • Only the SecretsAdmin IAM role is allowed to create, rotate and revoke secrets
    • Only the MyApplicationRole IAM role can be used to fetch secrets

Note that you can use Task IAM roles to grant access permissions to Tasks. You can read more about creating an IAM Role for your Task here.

```bash $ docker run --env-file setup-env.txt -v ~/.aws:/root/.aws \ amazon/amazon-ecs-secrets setup \ --application-name cryptex \ --create-principal arn:aws:iam::123456789012:role/SecretsAdmin \ --fetch-role arn:aws:iam::123456789012:role/MyApplicationRole

2016-10-29T15:35:06Z [INFO] Unable to describe stack: ECS-Secrets-cryptex, creating a new one 2016-10-29T15:36:23Z [INFO] Secrets are stored in the table: arn:aws:dynamodb:us-west-2:123456789012:table/ECS-Secrets-cryptex-Secrets 2016-10-29T15:36:23Z [INFO] Update 'arn:aws:iam::123456789012:role/MyApplicationRole' to provide read access for this table by updating the policy statement with: { "Effect": "Allow", "Action": [ "dynamodb:Query", "dynamodb:GetItem" ], "Resource": [ "arn:aws:dynamodb:us-west-2:123456789012:table/ECS-Secrets-cryptex-Secrets" ] } 2016-10-29T15:36:23Z [INFO] Update 'arn:aws:iam::123456789012:role/SecretsAdmin' to provide write access for this table by updating the policy statement with: { "Effect": "Allow", "Action": [ "dynamodb:PutItem", "dynamodb:Query", "dynamodb:UpdateItem" ], "Resource": [ "arn:aws:dynamodb:us-west-2:123456789012:table/ECS-Secrets-cryptex-Secrets" ] } 2016-10-29T15:36:28Z [INFO] Setup complete ```

Update the write and read policies as per the output of the command. This is critical to provide the necessary permissions to the admin and the application roles to write and read secrets from the DynamoDB table.

An example of doing the same from the CLI is provided next:

Example: Updating the admin role: ```bash $ cat << EOF > write-policy.json { "Version": "2012-10-17", "Statement" : [{ "Effect": "Allow", "Action": [ "dynamodb:PutItem", "dynamodb:Query", "dynamodb:UpdateItem" ], "Resource": [ "arn:aws:dynamodb:us-west-2:123456789012:table/ECS-Secrets-cryptex-Secrets" ] }] } EOF

$ aws --region us-west-2 iam create-policy --policy-name ECS-Secrets-cryptex-Secrets-write --policy-document file://write-policy.json { "Policy": { "PolicyName": "ECS-Secrets-cryptex-Secrets-write", "CreateDate": "...", ... "Path": "/", "Arn": "arn:aws:iam::123456789012:policy/ECS-Secrets-cryptex-Secrets-write", "UpdateDate": "..." } }

$ aws --region us-west-2 iam attach-role-policy --role-name SecretsAdmin --policy-arn arn:aws:iam::123456789012:policy/ECS-Secrets-cryptex-Secrets-write ```

Example: Updating the application role: ```bash $ cat << EOF > read-policy.json { "Version": "2012-10-17", "Statement" : [{ "Effect": "Allow", "Action": [ "dynamodb:Query", "dynamodb:GetItem" ], "Resource": [ "arn:aws:dynamodb:us-west-2:123456789012:table/ECS-Secrets-cryptex-Secrets" ] }] } EOF

$ aws --region us-west-2 iam create-policy --policy-name ECS-Secrets-cryptex-Secrets-read --policy-document file://read-policy.json { "Policy": { "PolicyName": "ECS-Secrets-cryptex-Secrets-read", "CreateDate": "...", ... "Path": "/", "Arn": "arn:aws:iam::123456789012:policy/ECS-Secrets-cryptex-Secrets-read", "UpdateDate": "..." } }

$ aws --region us-west-2 iam attach-role-policy --role-name MyApplicationRole --policy-arn arn:aws:iam::123456789012:policy/ECS-Secrets-cryptex-Secrets-read ```

Creating Secrets

The following diagram illustrates the workflow for creating a secret. The first step is sending a HTTP POST, containing the secret in the body of the request to the RESTful endpoint exposed by the ecs-secrets container. The ecs-secrets container then invokes the KMS generate-data-key API using IAM Role credentials for the Task [2]. This causes the AWS IAM service to validate if the IAM Role associated with the task has the relevant permission to use the KMS generate-data-key API [3]. Next, a KMS Data Encryption Key is returned to the ecs-secrets container [4]. This key is used to encrypt the secret. The encrypted secret and the encrypted data key are then saved in a DynamoDB table [5].

The following task defintion provides an example of creating a secret using the ecs-secrets container running in daemon mode and an application container posting a request to create the secret. The task is registered with the the arn:aws:iam::123456789012:role/SecretsAdmin IAM role. This ensures that the ecs-secrets container is authorized to create KMS data encryption keys. The create-secrets container posts the request to create a secret named dbpassword with contents of the file named password.txt: { "taskRoleArn": "arn:aws:iam::123456789012:role/SecretsAdmin", "containerDefinitions": [ { "essential": true, "name": "ecs-secrets-daemon", "environment": [ { "name": "AWS_REGION", "value": "us-west-2" } ], "image": "amazon/amazon-ecs-secrets:latest", "command": [ "daemon", "--application-name", "cryptex", "--debug" ], "cpu": 25, "memoryReservation": 25 }, { "essential": true, "mountPoints": [ { "containerPath": "/secrets", "sourceVolume": "secrets", "readOnly": true } ], "name": "curl", "links": [ "ecs-secrets-daemon:ecs-secrets" ], "image": "tutum/curl", "command": [ "curl", "-X", "POST", "-d", "@/secrets/password.txt", "ecs-secrets:8080/latest/secrets/password" ], "cpu": 25, "memoryReservation": 25 } ], "volumes": [ { "host": { "sourcePath": "/tmp/secrets" }, "name": "secrets" } ], "family": "ecs-secrets-create" }

On the instance, the password file has the following contents: bash $ cat /tmp/secrets/password.txt {"payload":"123456"}

If you specified an IAM user as argument to --create-principal instead of an IAM role (Example: --create-principal arn:aws:iam::123456789012:user/cryptex-admin), you can also run the create command using the CLI. In the example listed next, the profile credentials from cryptex-admin profile are used to create secrets. ```bash $ cat << EOF > setup-env.txt AWSREGION=us-west-2 AWSDEFAULTPROFILE=cryptex-admin AWSSHAREDCREDENTIALSFILE=/root/.aws/credentials EOF

$ echo "mydbpassword" > secrets.txt

$ docker run --env-file setup-env.txt -v ~/.aws:/root/.aws \ amazon/amazon-ecs-secrets create \ --application-name cryptex \ --name dbpassword \ --payload cat secrets.txt ```

You can register a new version of the secret key by running the create command again. Note that you can also specify the location of the file that containers secrets using --payload-location. Example: bash $ docker run --env-file setup-env.txt -v ~/.aws:/root/.aws \ amazon/amazon-ecs-secrets create \ --application-name cryptex \ --name dbpassword \ --payload-location secret.txt

Retrieving Secrets

The following diagram illustrates the workflow for retrieving secrets from the secret store. The application container sends a HTTP GET request with the name of the secret [1] to the ecs-secrets container's RESTful endpoint. The ecs-secrets container retrieves encrypted data keys and encrypted secrets from the DynamoDB table [2]. It then invokes the KMS decrypt API to decrypt the encrypted data key [3]. This causes the AWS IAM service to validate if the IAM Role associated with the task has the relevant permission to use the KMS decrypt API [4]. The decrypted data key is then used to decrypt the encrypted secret [6].

The following example illustrates a Task Definition for fetching secrets from the secret store: { "taskRoleArn": "arn:aws:iam::123456789012:role/MyApplicationRole", "containerDefinitions": [ { "name": "ecs-secrets-daemon", "environment": [ { "name": "AWS_REGION", "value": "us-west-2" } ], "image": "amazon/amazon-ecs-secrets:latest", "command": [ "daemon", "--application-name", "cryptex", "--debug" ], "cpu": 25, "memoryReservation": 25, "essential": true }, { "essential": true, "name": "curl", "links": [ "ecs-secrets-daemon:ecs-secrets" ], "image": "tutum/curl", "command": [ "curl", "ecs-secrets:8080/latest/secrets/password" ], "cpu": 25, "memoryReservation": 25 } ], "family": "ecs-secrets-fetch" }

On the instance, the output of the curl container shows the following: bash $ docker logs 9b8a8495ba99 % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 64 100 64 0 0 558 0 --:--:-- --:--:-- --:--:-- 566 {"name":"password","serial":1,"payload":"123456","active":true}

Revoking Secrets

ecs-secrets also supports versioning of secrets. You can use the revoke command to revoke specific versions of secrets. Example: ```bash $ docker run --env-file setup-env.txt -v ~/.aws:/root/.aws \ amazon/amazon-ecs-secrets revoke \ --application-name cryptex \ --name dbpassword \ --serial 1

$ docker run --env-file setup-env.txt -v ~/.aws:/root/.aws \ amazon/amazon-ecs-secrets fetch \ --application-name cryptex \ --name dbpassword \ --serial 1 {"name":"dbpassword","serial":1,"payload":"","active":false} In the above example, the version `1` of secret named `dbpassword` has been revoked, because of which, retrieving that version of the secret using the `fetch` command would not work. Where as, fetching the version `2` of the secret returns the appropriate value of the secret. bash $ docker run --env-file setup-env.txt -v ~/.aws:/root/.aws \ amazon/amazon-ecs-secrets fetch \ --application-name cryptex \ --name dbpassword {"name":"dbpassword","serial":2,"payload":"mydbpassword","active":true} ```

You can also run this within a Task Definition as with all other commands. A HTTP POST request to the /revoke endpoint revokes a secret. A sample task definition for the same is listed next:

{ "family": "ecs-secrets-demo-revoke", "taskRoleArn": "arn:aws:iam::123456789012:role/SecretsAdmin", "containerDefinitions": [ { "environment": [ { "name": "AWS_DEFAULT_REGION", "value": "us-west-2" }, ], "name": "ecs-secrets", "image": "amazon/amazon-ecs-secrets:latest", "cpu": 25, "memory": 25, "command": [ "ecs-secrets --application-name cryptex daemon --debug" ], "essential": true, }, { "environment": [ { "name": "AWS_DEFAULT_REGION", "value": "us-west-2" } ], "name": "fetch-secrets", "image": "tutum/curl", "cpu": 25, "entryPoint": [ "bash", "-c" ], "memory": 25, "command": [ "curl -X POST ecs-secrets:8080/latest/revoke/dbpassword/1" ], "essential": true, } ] }

Owner

  • Name: Amazon Web Services - Labs
  • Login: awslabs
  • Kind: organization
  • Location: Seattle, WA

AWS Labs

GitHub Events

Total
Last Year

Issues and Pull Requests

Last synced: 6 months ago

All Time
  • Total issues: 4
  • Total pull requests: 0
  • Average time to close issues: N/A
  • Average time to close pull requests: N/A
  • Total issue authors: 4
  • Total pull request authors: 0
  • Average comments per issue: 1.75
  • Average comments per pull request: 0
  • Merged pull requests: 0
  • Bot issues: 0
  • Bot pull requests: 0
Past Year
  • Issues: 0
  • Pull requests: 0
  • Average time to close issues: N/A
  • Average time to close pull requests: N/A
  • Issue authors: 0
  • Pull request authors: 0
  • Average comments per issue: 0
  • Average comments per pull request: 0
  • Merged pull requests: 0
  • Bot issues: 0
  • Bot pull requests: 0
Top Authors
Issue Authors
  • vecchp (1)
  • andriy-f (1)
  • bwhaley (1)
  • jhaynes (1)
Pull Request Authors
Top Labels
Issue Labels
Pull Request Labels

Packages

  • Total packages: 1
  • Total downloads: unknown
  • Total dependent packages: 0
  • Total dependent repositories: 0
  • Total versions: 2
proxy.golang.org: github.com/awslabs/ecs-secrets
  • Versions: 2
  • Dependent Packages: 0
  • Dependent Repositories: 0
Rankings
Dependent packages count: 7.0%
Average: 8.2%
Dependent repos count: 9.3%
Last synced: 6 months ago

Dependencies

misc/certs/Dockerfile docker
  • debian latest build
Godeps/Godeps.json go
  • github.com/aws/aws-sdk-go/aws 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/aws/awserr 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/aws/awsutil 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/aws/client 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/aws/client/metadata 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/aws/corehandlers 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/aws/credentials 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/aws/credentials/endpointcreds 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/aws/defaults 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/aws/ec2metadata 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/aws/request 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/aws/session 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/aws/signer/v4 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/private/endpoints 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/private/protocol 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/private/protocol/json/jsonutil 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/private/protocol/jsonrpc 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/private/protocol/query 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/private/protocol/query/queryutil 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/private/protocol/rest 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/private/waiter 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/service/cloudformation 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/service/dynamodb 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/service/dynamodb/dynamodbattribute 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/aws/aws-sdk-go/service/kms 3c37d29820480639ff03fd66df00a0f27984f88d
  • github.com/cihub/seelog 752ef646ce438bc649b2bb565188471879185c33
  • github.com/go-ini/ini cf53f9204df4fbdd7ec4164b57fa6184ba168292
  • github.com/gorilla/context aed02d124ae4a0e94fea4541c8effd05bf0c8296
  • github.com/gorilla/mux 327d4b684c13f926b0984fff1087fb8305368948
  • github.com/gtank/cryptopasta e7e23673cac3f529f49e22f94e4af6d12bb49dba
  • github.com/jmespath/go-jmespath 0b12d6b521d83fc7f755e7cfc1b1fbdd35a01a74
  • github.com/urfave/cli d9021faab69f92295ef7061bd39e4a76dcbdef32
  • golang.org/x/crypto/bcrypt 9fbab14f903f89e23047b5971369b86380230e56
  • golang.org/x/crypto/blowfish 9fbab14f903f89e23047b5971369b86380230e56