DEV Community

Atsushi Suzuki
Atsushi Suzuki

Posted on

Enhancing Deployment Security through the Integration of IAM Roles and GitHub Actions

Until now, I faced challenges regarding the management of credentials (IAM user's access key and secret access key) when deploying the AWS SDK to Lambda.

Using the method of reading the access key of an IAM user with strong resource permissions from environment variables posed a significant security risk in case of key leakage. As a result, I shifted to using IAM roles.

In this article, I'll outline the steps I took for this transition, serving as a reference for future reference.

Procedure

In the GitHub Actions deployment workflow, I loaded the access key and secret access key of an IAM user with AssumeRole permissions from GitHub Secrets. This allowed me to assume the IAM role's permissions through AssumeRole.

Creation of IAM User

Using the console or IaC tools, create an IAM user and associate the following custom policy. For the Resource field, copy and paste the ARN after creating the IAM role.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "sts:AssumeRole",
                "sts:TagSession"
            ],
            "Resource": "arn:aws:iam::<Account ID>:role/<Role Name>"
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

Not just sts:AssumeRole, but sts:TagSession is also essential.

Next, navigate to 'Security Credentials > Access keys' to issue an access key (and secret access key).
This access key is necessary when assuming the IAM role's permissions in the GitHub Actions deployment workflow.

This IAM user does not have permissions to operate resources. Even if the key leaks to a third party, they won't be able to perform significant resource operations. Thus, the security risk is low. Nevertheless, precautions (like key rotation) are essential to prevent leakage.

Creating an IAM Role

Similar to creating an IAM user, you can create an IAM role using the console or IaC tools.

IAM Role for Deployment Workflow

This policy grants permissions necessary for deployment operations such as CloudFormation, API Gateway, Lambda, ECR, IAM, and others.

Next, navigate to the 'Trust relationships' tab of the IAM role and select 'Edit trust policy'. The trust policy defines which entities can assume the role.

For the Principal's AWS, paste the ARN of the IAM user you created earlier.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::<Account ID>:user/<User Name>"
            },
            "Action": [
                "sts:AssumeRole",
                "sts:TagSession"
            ],
            "Condition": {}
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

IAM Role for Lambda

This policy grants permissions necessary for application operations like S3, SES, and others.

From the Lambda details screen, under the 'Settings tab > Permissions', associate the execution role you created as the IAM role. This eliminates the need to load credentials in your application code.

Image description

Registering Environment Variables in GitHub Secrets

From the GitHub repository's Settings tab, select 'Secrets and variables > Actions' and add the environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_IAM_ROLE_TO_ASSUME) for the workflow using 'New repository secret'.

For AWS_IAM_ROLE_TO_ASSUME, enter the ARN of the IAM role.

Image description

Modifying the GitHub Actions Deployment Workflow

Below is a part of the deployment workflow. In the configure aws step, it loads the environment variables you set earlier.

name: Lambda Deploy
on:
  push:
    branches:
      - develop
    paths:
      - "**"
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '18'

      - name: install dependencies
        run: yarn install
        working-directory: ./

      - name: configure aws
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          role-to-assume: ${{ secrets.AWS_IAM_ROLE_TO_ASSUME }}
          role-duration-seconds: 3600
Enter fullscreen mode Exit fullscreen mode

Ensure the role-duration-seconds: 3600 value is set to less than or equal to the IAM role's maximum session duration.

Development in the Local Environment

After deploying to Lambda, you can access AWS resources using the Lambda execution role. However, in a local environment (like Docker), since there's no execution role, you'll need to load credentials from environment variables as before.

Thus, you'll need to modify your application code as follows:

  private createS3Client(): S3Client {
    const options: S3ClientConfig = {
      region: 'ap-northeast-1',
      ...(process.env.ENV === 'local' && {
        credentials: {
          accessKeyId: process.env.ACCESS_KEY_ID as string,
          secretAccessKey: process.env.SECRET_ACCESS_KEY as string
        }
      })
    }

    return new S3Client(options)
  }
Enter fullscreen mode Exit fullscreen mode

Conclusion

The process is largely similar when deploying to ECS. By specifying the task role in the ECS task definition, containers running on EC2 instances or Fargate executing the ECS task can access AWS resources. This task role can be customized by attaching an IAM policy that defines access permissions to specific AWS services or resources.

Top comments (0)