When developing an APP in ECS, We need to pass the environment variables to the tasks container.

Because we have sensitive and non-sensitive environment variables, so we need to take care in choosing the policy.

About the non-sensitive environment variables

About the non-sensitive variable is an environment variable for anyone who can access it.

We can pass env in the docker run by using the --env option to pass the environment variables to the container.

When using the ECS, we add env variables on creating ECS task definition Environment variables options: Add environment variable or Add environment file from S3.

First the environment variable in task definication like:

{
    "containerDefinitions": [
        { 
            ...,
            "environment": [
                { 
                    "name": "variable_name",
                    "value": "variable_value"
                }, {
                    ...
                }
            ]
        }
    ]
}

Seconds, Add environment file can import those environment files to tasks by environmentFiles parameter.

Here are something needs to consider when defining environment variables in S3:

  • file extension need to use the .env and UTF8 encoding
  • If duplicate environment variables are declared, duplicate variables will be ignored.
  • ECS tasks platform version must 1.4.0 or later
  • container agent require version 1.39.0 or later

Following is the S3 env file example:

VARIABLE_NAME=VARIABLE_VALUE
EX_HOST=https://url.pth
EX_HOST_PORT=80

In ECS tasks definication, can specify environment variables from S3 by the following setting:

{
    "containerDefinitions": [
        { 
            ...
            "environmentFiles": [
                { 
                    "value": "arn:aws:s3:::s3_bucket_name/envfile_object_name.env",
                    "type": "s3"
                }
            ]
        }
    ]
}

After setting the S3 environment variables, next should be setting the IAM permissions.

For example, using the inline policy to grant permissions:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject"
      ],
      "Resource": [
        "arn:aws:s3:::examplebucket/folder_name/env_file_name"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetBucketLocation"
      ],
      "Resource": [
        "arn:aws:s3:::examplebucket"
      ]
    }
  ]
}

How store the secret data?

If our environment variables are sensitive data, it is better to storing in AWS Secrets Manager secrets or AWS System Manager Parameter Store Parameter.

AWS Secret Manager

Secret Manager can manage credentials like database, and API keygen, and can rotate the secret by lambda functions. In applications, can use the secret to prevent hardcoded secrets.

Following are some benefits to used a secret manager:

  • Rotate secrets without application code change
  • Centralized management of secrets and monitorable by AWS and Cloudtrail
  • Protect access to secrets by AWS Identity and Management Policies

Goto AWS Secrets Manager > Secrets > Store a new secret

Here we choose the Other type of secret which can set key-value pairs for customerize secrets.

AWS_SECRETS_MANAGER_KEY=This is the secrets manager key-value pair

After creating Secret Manager, go back to Task Definitions, and Create new task definition revision

In the Environment variables, add Key name that we created on Secret manager AWS_SECRETS_MANAGER_KEY, Value type chooses ValueFrom, and the Value input the Secret Manager arn.

AWS_SECRETS_MANAGER_KEY=aarn:aws:secretsmanager:<region>:<account_id>:parameter/<parameter_key>

Following is an example of how to get the secret manager key:

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
)

type AwsSecretManager struct {
	AWS_SECRETS_MANAGER_KEY string
}

func handler(w http.ResponseWriter, r *http.Request) {

	var awsSecretManager AwsSecretManager

	aws_secrets_manger_key := []byte(os.Getenv("AWS_SECRETS_MANAGER_KEY"))

	err := json.Unmarshal(aws_secrets_manger_key, &awsSecretManager)
	if err != nil {
		fmt.Println(err)
	}

	fmt.Fprintf(w, ", AWS_SECRETS_MANAGER_KEY :"+awsSecretManager.AWS_SECRETS_MANAGER_KEY)
}
func main() {
	http.HandleFunc("/", handler)
	http.ListenAndServe(":80", nil)
}

If meet the following error, can confirm the secret manager Encryption key: here we using aws/secretsmanager:

ResourceInitializationError: unable to pull secrets or registry auth: execution resource retrieval failed: unable to retrieve secret from asm: service call has been retried 1 time(s): failed to fetch secret arn: from secrets manager: AccessDeniedException: Access to KMS is not allowed status code: 400, request id:

AWS Systems Manager

Goto AWS Systems Manager > Parameter Store and press Create parameter

Next, fully the detail like:

Name: /demo-app/AWS_SYSTEM_MANAGER Tirer: Standard Type: String Datatype: text Value: aws system manager value

Name: /demo-app/AWS_SYSTEM_MANAGER_SECRET Tirer: Standard Type: SecureString KMS key source: My current account KMS Key ID: alias/aws/ssm Datatype: text Value: aws system manager secret value

!note, here we use the default KMS key, this means that all users can access this secret value. (You can generate and use your own KMS Key ID)

You have selected the default AWS managed key. All users in the current AWS account and Region will have access to this parameter. To restrict access to the parameter, use a customer-managed key (CMK) instead.

Next, we need to add the System Manager inline policy to ecsTaskExecutionRole

Inline policy Service: System Manager Access level: GetParameter, GetParameters

Back to ECS Task Definitions, and add environment variables:

Key: AWS_SYSTEM_MANAGER
ValueFrom: arn:aws:ssm:<region>:<aws_account_id>:parameter/<parameter_name>

Following is an example:

package main

import (
	"fmt"
	"net/http"
	"os"
)

func handler(w http.ResponseWriter, r *http.Request) {

	aws_ssm_key := os.Getenv("AWS_SYSTEM_MANAGER")
	aws_ssm_key_secret := os.Getenv("AWS_SYSTEM_MANAGER_SECRET")

	fmt.Fprintf(w, "AWS_SSM:"+aws_ssm_key+", AWS_SSM_SECRET:"+aws_ssm_key_secret)
}
func main() {
	http.HandleFunc("/", handler)
	http.ListenAndServe(":80", nil)
}

References

Passing environment variables to a container AWS set environment variable for ECS tasks