How to use Bitbucket pipelines without AWS credentials
Juli already showed you how to use GitHub Actions to access AWS resources without using AWS credentials as secrets. Now let’s do the same for Bitbucket pipelines.
Introduction
Sometimes we need to use Bitbucket pipelines to access resources in our AWS accounts, for example, to push a Docker image to private ECR repositories, put and get objects from S3 buckets, or deploy applications to EC2, Beanstalk, EKS, ECS, and so on. All these scenarios require the Bitbucket pipeline to have permission to perform actions over resources in AWS. In this teratip I show you how to grant these permissions using an IAM Role, which is the best choice because IAM Roles provide temporary access using tokens, instead of fixed credentials.
Let's get started!
Identity Provider
The first step is to add an OpenID Connect IAM Identity provider for Bitbucket in your AWS account.
Get the Provider URL and Audience from Bitbucket > Repository settings > OpenID Connect.
IAM Role
Now let’s create an IAM Role choosing Web identity in Trusted entity type and selecting the Identity provider and Audience.
After clicking on Next choose the permissions that your pipeline needs to perform actions over the AWS services.
Trust policy
During the creation of the IAM Role AWS will provide us the Trust policy that defines who can assume the role. It’s possible to edit the Trust policy to restrict the usage only to specific repositories in the Bitbucket workspace using the Repository UUID.
Get this information from Bitbucket > Repository settings > OpenID Connect.
This example shows the original Trust policy provided by AWS that grant access to all repositories in the workspace:
And this example defines only one repository that can assume the role:
The main difference is in the Condition block. The first policy the audience defined by:
While the second one defines only one repository using the Repository UUID with:
That’s all! This change in AWS access from Bitbucket pipelines improves the security of your account replacing fixed credentials that can be used by anyone with access to them, for temporary access role that can only be assumed by the entities defined in the Trust policy.
Configure the pipeline to use the IAM Role
The last step is to update the bitbucket-pipeline.yml with the following block of code:
image: amazon/aws-cli
pipelines:
default:
- step:
oidc: true
script:
- export AWS_REGION=us-east-1
- export AWS_ROLE_ARN=arn:aws:iam::XXXXXXXXXXXX:role/bitbucketRole
- export AWS_WEB_IDENTITY_TOKEN_FILE=$(pwd)/web-identity-token
- echo $BITBUCKET_STEP_OIDC_TOKEN > $(pwd)/web-identity-token
- aws sts get-caller-identity
AWS_REGION will be the region where the AWS resources exist.
AWS_ROLE_ARN is the ARN of your role.
The command aws sts get-caller-identity isn’t really needed, it’s just a confirmation the role is assumed properly.
Using terraform
You can apply all this configuration using Terraform with the following code:
Identity provider
resource "aws_iam_openid_connect_provider" "bitbucket" {
url = "https://api.bitbucket.org/2.0/workspaces/rubioignacio/pipelines-config/identity/oidc" client_id_list = ["ari:cloud:bitbucket::workspace/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"] thumbprint_list = ["a031c46782e6e6c662c2c87c76da9aa62ccabd8e"]
}
The Thumbprint it's generated based on the certificate's SSL key of the OpenID from Bitbucket. This value is based on the URL, so it’s static and can be copied and pasted easily.
IAM Role
resource "aws_iam_role" "bitbucket_role" {
name = "BitbucketRole"
assume_role_policy = data.aws_iam_policy_document.bitbucket_role_trust_policy.json
}
data "aws_iam_policy_document" "bitbucket_role_trust_policy" {
statement {
sid = ""
effect = "Allow"
actions = ["sts:AssumeRoleWithWebIdentity"]
condition {
test = "StringLike"
values = ["{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}:*"]
}
principals {
type = "Federated"
identifiers = ["arn:aws:iam::xxxxxxxxxxxx:oidc-provider/api.bitbucket.org/2.0/workspaces/rubioignacio/pipelines-config/identity/oidc"]
}
}
}
resource "aws_iam_role_policy_attachment" "ecr_policy_attachment" {
role = aws_iam_role.bitbucket_role.arn
policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess"
}
resource "aws_iam_role_policy_attachment" "beanstalk_policy_attachment" {
role = aws_iam_role.bitbucket_role.arn
policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess-AWSElasticBeanstalk"
}
In this example, I’m attaching AWS-managed policies to the IAM Role to access Amazon ECR and AWS Elastic Beanstalk. Finally, update the bitbucket-pipeline.yml in the same way we did it before using Terraform.
Ignacio Rubio
Cloud Engineer
Teracloud