Have you rotated your access keys today? If not, it is time to make your life easier and your GitLab pipelines more secure with GitLab’s built-in OpenID Connect (OIDC) provider.
Click here if you are using GitHub instead of GitLab.
Access keys are risky - they are long-lived secrets that can easily fall into the wrong hands (especially in public repositories). Creating, rotating, storing and revoking access keys can be time-consuming and error-prone. With OIDC, your pipelines request short-lived credentials from AWS at runtime using GitLab’s built-in OIDC provider.
sequenceDiagram participant Pipeline as GitLab
Pipeline participant IdP as GitLab
OIDC Provider participant IAM as AWS IAM IdP --> IAM: OIDC trust relationship Pipeline ->>+ IdP: Token request IdP -->>- Pipeline: Token Pipeline ->>+ IAM: Token,
IAM role ARN IAM -->>- Pipeline: Temporary credentials
Set up AWS IAM resources
You need two resources:
AWS::IAM::OIDCProvider
prepares AWS to use GitLab’s built-in OIDC provider.AWS::IAM::Role
creates a trust relationship with the OIDC provider and grants access to your cloud resources using policies.
While you can create these resources manually using the AWS Console, many engineers prefer an Infrastructure-as-Code tool like AWS CDK or Terraform.
Both AWS CDK and Terraform provide excellent ways to define your cloud resources in code, making the code reusable and maintainable.
Terraform
This is how it can look like with Terraform:
resource "aws_iam_openid_connect_provider" "gitlab" {
url = "https://gitlab.com"
client_id_list = ["https://gitlab.com"]
}
resource "aws_iam_role" "gitlab_oidc_role" {
name = "GitLabOidcRole"
assume_role_policy = data.aws_iam_policy_document.gitlab_oidc_role_trust_relationship.json
}
data "aws_iam_policy_document" "gitlab_oidc_role_trust_relationship" {
statement {
actions = ["sts:AssumeRoleWithWebIdentity"]
effect = "Allow"
principals {
type = "Federated"
identifiers = [aws_iam_openid_connect_provider.gitlab.arn]
}
condition {
test = "StringEq"
variable = "https://gitlab.com:aud"
values = "https://gitlab.com"
}
condition {
test = "StringLike"
variable = "https://gitlab.com:sub"
// Replace with your GitLab project path:
values = "project_path:<YOUR_GITLAB_GROUP>/<YOUR_PROJECT>:ref_type:*:ref:*"
}
}
}
resource "aws_iam_role_policy_attachment" "gitlab_oidc_s3_full_access" {
role = aws_iam_role.gitlab_oidc_role.name
// Replace with the policies your workflow needs:
policy_arn = "arn:aws:iam::aws:policy/AmazonS3FullAccess"
}
AWS CDK for TypeScript
This is how it can look like with AWS CDK for TypeScript:
import { type App, Stack, type StackProps } from 'aws-cdk-lib';
import {
OpenIdConnectProvider,
Role,
WebIdentityPrincipal,
PolicyDocument,
PolicyStatement
} from 'aws-cdk-lib/aws-iam';
class OidcIamRoleStack extends Stack {
constructor(scope: App, id: string, props?: StackProps) {
super(scope, id, props);
const oidcProvider = new OpenIdConnectProvider(this, 'GitLabOidcProvider', {
url: 'https://gitlab.com',
clientIds: ['https://gitlab.com']
});
new Role(this, 'GitLabOidcRole', {
assumedBy: new WebIdentityPrincipal(
oidcProvider.openIdConnectProviderArn,
{
StringEquals: {
'gitlab.com:aud': 'https://gitlab.com'
},
StringLike: {
// Replace with your GitLab project path:
'gitlab.com:sub': 'project_path:<YOUR_GITLAB_GROUP>/<YOUR_PROJECT>:ref_type:*:ref:*'
}
}
),
roleName: 'GitLabOidcRole',
inlinePolicies: {
GitLabCIPolicy: new PolicyDocument({
statements: [
// Replace with the policy statements your pipeline needs:
new PolicyStatement({
actions: ['s3:*'],
resources: ['arn:aws:s3:::<YOUR_BUCKET_NAME>/*']
})
]
})
}
});
}
}
Self-hosted GitLab instances
If you are using a self-hosted GitLab instance, replace gitlab.com
with the hostname of your instance.
Note that the following paths must be accessible from the public Internet:
/.well-known/openid-configuration
- The
jwks_uri
as described by/.well-known/openid-configuration
Apply further access restrictions
Following the principle of least privilege, you can further restrict the token subject (gitlab.com:sub
).
For example, you can restrict access to the main
branch: project_path:<YOUR_GITLAB_GROUP>/<YOUR_PROJECT>:ref_type:branch:ref:main
.
Deploy both IAM resources and note the IAM role ARN for the next step.
Update your GitLab CI/CD configuration
Now that you have the IAM role, update your GitLab CI/CD configuration (.gitlab-ci.yml
) to use it with GitLab’s built-in AWS integration.
deploy:
image: registry.gitlab.com/gitlab-org/cloud-deploy/aws-base:latest
id_tokens:
GITLAB_OIDC_TOKEN:
aud: https://gitlab.com
environment:
AWS_REGION: <YOUR_REGION> # Replace with your AWS region
before_script:
- >
aws_sts_output=$(aws sts assume-role-with-web-identity
--role-arn "<YOUR_ROLE_ARN>" # Replace with the IAM role ARN
--role-session-name "GitLabRunner-${CI_PROJECT_ID}-${CI_PIPELINE_ID}"
--web-identity-token "${GITLAB_OIDC_TOKEN}"
--duration-seconds "${CI_JOB_TIMEOUT}"
--query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]'
--output text)
- export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s" $aws_sts_output)
script:
- aws s3 sync ./public s3://<YOUR_BUCKET_NAME>
With OIDC, you are embracing a secure and flexible way to manage AWS access in your GitLab CI/CD pipelines. Now you can stop worrying about rotating your pipeline’s access keys and start focusing on what matters: Shipping great features.
Common problems
Error: Not authorized to perform sts:AssumeRoleWithWebIdentity
- Is your assume role policy correct?
- Does your token subject (
sub
) condition allow specific branches or tags? - Are you using wildcards and your condition function is
StringEq
instead ofStrinkLike
?
- Does your token subject (
Error: Couldn't retrieve verification key from your identity provider
- Is your self-hosted GitLab server accessible from the public Internet?
- Does your GitLab server respond within 5 seconds?
- Do you have an ingress controller or a web application firewall, which limits the number of requests to your GitLab server?
- Is your GitLab server using either a self-signed TLS certificate or a certificate from an uncommon certificate authority?
- Consider adding the thumbprint of the certificate issuer to the OIDC provider.