Secret Injection for Github Actions using AWS Secret Manager

Rishabh Nama's avatar

Rishabh Nama

AWS Secrets manager is a widely used managed service to store confidential secrets/environment variables such as API keys, database credentials, etc. In this blog we will see how to leverage the secrets from Secret Manager into Github actions pipeline and use them securely as environment variables for deployments.

This can be achieved with minimal exposure to sensitive data into the pipeline. Sensitive credentials like aws access key and aws secret access key are often stored into a an external .env file or github secrets and then pulled into the CI which maybe exposed with unauthorized access if not handled properly.

We can eliminate this risk by running a self hosted Github actions runner and IAM role which will allow the authentication of your AWS account inside the CI without the use of AWS credentials. Let us now see how we can achieve this.

  1. A self hosted github runner on an AWS EC2
  2. Create and store confidential key/values as a secret in the AWS secrets manager
  3. Create an IAM policy which provides read-only access to AWS Secret Manager and apply this policy to an IAM Role
  4. Attach the IAM role to the EC2 server to enable read access to the secrets created
  5. Setup an actions.yaml file in github and pull these secrets into the CI and print out the values on the console as proof of concept for the solution

Create A Self-Hosted Github Runner On An Aws EC2

Follow the steps and setup the runner on your EC2 server

Create A Secret In Aws Secret Manager

  1. Go to Secrets Manager Console, and click on Store a new secret
  2. Select Other as secret type
  3. Enter the confidential values in form of key/value pairs (which can later be converted to .env file format if needed)
  4. Give your secret a name, for eg: apicreds and store it

For this example I have created a secret apicreds with the following key:value respectively.

url: api.explample.com
key: 123456
secret: mycomplexsecret

Note: For this example we are relying on your secrets that are encrypted using the default AWS managed encryption key. If your secrets are encrypted using a customer managed AWS Key Management Service (KMS) key, then the IAM policy (below) will be different.

Create An IAM Role And Attach A Policy To It

For this example, we will create role SecretManagerReadOnlyEC2Role and a policy secrets-manager-read-only-policy with the following configuration with least privilege best practice which gives read-only access to aws secrets

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "secretsmanager:GetSecretValue",
                "sts:AssumeRole",
                "sts:TagSession"

            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}

Note: If you get an error The requested DurationSeconds exceeds the MaxSessionDuration set for this role while running the CI, try setting the session duration to max 43200 seconds using the instructions here

Attach This Role To The EC2 instance to allow access to secrets from the Secrets Manager into the EC2 instance

In the EC2 instances console, go to the instance security configuration, under security details attach the role created in the previous step.

Setting Up Github Actions

Setup github actions.yaml to fetch secrets from AWS Secret Manager and display them on console as proof of concept for the solution.


name: Self-hosted runner test for secrets injection

on:

  push:

  workflow_dispatch:

jobs:

  build:

    runs-on: self-hosted

    steps:

      - uses: actions/checkout@v2

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          role-to-assume: arn:aws:iam::xxxxxxxxxxxx:role/SecretManagerReadOnlyEC2Role
          aws-region: ap-south-1
          role-session-name: github-runner-session-1


      - name: Read secrets from AWS Secrets Manager into environment variables
        uses: abhilash1in/aws-secrets-manager-action@v2.1.0
        with:
          secrets: apicreds
          parse-json: true

        #Optional Steps

      - name: Check if env variable is set after fetching secrets
        run: |
          if [ -z ${APICREDS_URL+x} ]; then echo "APICREDS_URL is unset"; else echo "APICREDS_URL is set to '$APICREDS_URL'"; fi
          if [ -z ${APICREDS_KEY+x} ]; then echo "APICREDS_KEY is unset"; else echo "APICREDS_KEY is set to '$APICREDS_KEY'"; fi
          if [ -z ${APICREDS_SECRET+x} ]; then echo "APICREDS_SECRET is unset"; else echo "APICREDS_SECRET is set to '$APICREDS_SECRET'"; fi

If your secret name contains any characters other than upper case letters, digits and underscores, it will not be used directly as the environment variable name. Rather, it will be transformed into a string that only contains upper case letters, digits and underscores.

For example:

  • If your secret name is dev.foo, the injected environment variable name will be DEV_FOO.
  • If your secret name is 1/dev/foo, the injected environment variable name will be _1_DEV_FOO.
  • If your secret name is dev/foo, value is { "bar": "baz" } and parse-json is set to true, the injected environment variable name will be DEV_FOO_BAR (and value will be baz).

Optional step: To convert the env vars into project specific env configuration, create an env.txt file and source the file into the CI.

export URL = $APICREDS_URL
export KEY = $APICREDS_KEY
export SECRET = $APICREDS_SECRET

      - name: Fetch project specific env config from env.txt
        run: |
          source env.txt
          echo $URL
          echo $KEY
          echo $SECRET

For more info on how env vars are generated inside the CI

Conclusion

We have now injected secrets from AWS secrets manager into the self-hosted github CI without using aws access key or aws secret access key instead by using only IAM role. We can now use these environment variables in subsequent steps of your workflow securely. By using the IAM role and policy mechanism we have eliminated the potential risk factor of storing access keys for AWS authentication in the pipeline.

Additional dubugging links for common errors encountered:

  1. If you're using IAM role chaining and encounter session duration error
  2. IAM troubleshooting
  3. Using IAM roles with sessions
  4. AWS authentication action in Github Actions CI
  5. IAM policy examples for secrets in AWS Secrets Manager