Why Use OIDC for Secure GitLab CI/CD Deployments?

Letā€™s say you are working on a GitLab CI/CD pipeline that deploys infrastructure to AWS. To authenticate the job that interacts with AWS within the pipeline, you store your access keys in GitLabā€™s environment variables, thinking itā€™s secure because the credentials arenā€™t hardcoded within the repository. But hereā€™s the problem - anyone with access to the pipeline can extract those credentials in plain text. A simple echo $AWS_SECRET_ACCESS_KEY in the .gitlab-ci.yml file prints the key right in the job logs. If an engineer adds this line for debugging or if an attacker gains access to GitLab, they can easily retrieve and misuse these credentials outside the pipeline.

Even if logs are hidden, thereā€™s another issue. Anyone with access to the project can trigger deployments to AWS from any branch. If the pipeline runs on every push without restrictions, a developer working on a feature branch could accidentally deploy changes to production. Worse, if someone with GitLab access modifies .gitlab-ci.yml, they could steal AWS keys or make infrastructure changes without approval. Nothing stops them from deploying EC2 instances, changing security groups, or even deleting resources.

This is where OIDC authentication removes the risk of static credentials. Instead of storing long-lived access keys, GitLab dynamically generates a short-lived authentication token when the pipeline runs. AWS verifies this token in real time and grants temporary access only if the request matches the configured IAM role. There are no persistent credentials to steal, no long-lived secrets in GitLab, and no way to assume AWS permissions without an active, verified pipeline job.

The difference is that authentication is tied to the pipeline execution itself, not a stored key. OIDC allows you to enforce strict access controls at the IAM role level, limiting deployments to specific branches, repositories, or commit references. If a developer tries to deploy from an unauthorized branch, AWSā€™s Identity provider will reject the request. If someone from your team who is not authorized gets access to GitLab, they wonā€™t be able to assume AWS permissions without a valid, short-lived token.

By removing static credentials, OIDC makes authentication more secure and manageable. Instead of manually rotating access keys and worrying about leaks, teams define IAM roles that GitLab assumes when needed. The entire process is automated, reducing operational overhead while enforcing least-privilege access.

OIDC vs. Static Credentials: When to Use What?

Storing AWS credentials in GitLabā€™s environment variables works, but it comes with security risks and operational complexity. OIDC eliminates the need for long-lived credentials by allowing GitLab to authenticate dynamically when a pipeline runs. But does this mean static credentials should never be used? Not necessarily. The right approach depends on the use case, infrastructure setup, and security requirements.

To understand when each method makes sense, letā€™s compare OIDC and static credentials based on security, management overhead, compatibility with GitLab CI/CD, and specific deployment scenarios.

Security Considerations

Once an AWS access key is created and stored in GitLab, it remains valid until revoked or rotated. This means that if someone extracts the key - whether from logs, environment variables, or a misconfigured .gitlab-ci.yml file - they can use it outside GitLab to access AWS indefinitely. Even if permissions are scoped, a compromised key gives unauthorized access, and without proper monitoring, you may not even realize it has been leaked until itā€™s too late.

AWS OIDC solves this problem by entirely removing long-lived secrets from GitLab. Instead of storing credentials, GitLab pipelines authenticate by requesting a short-lived, one-time-use OIDC token. AWS verifies this token against its identity provider configuration, and if everything checks out, it issues temporary credentials for the job. These credentials expire automatically once the job is done, so even if an attacker somehow gets hold of them, they are useless outside the execution window.

Thereā€™s also the question of who can use the credentials. With static keys, anyone with access to the GitLab project can trigger a pipeline and deploy to AWS, even from unauthorized branches. OIDC allows fine-grained control at the IAM role level, enforcing conditions such as:

  • Only allowing deployments from specific branches
  • Restricting role assumptions based on GitLab project metadata
  • Preventing access from personal forks or untrusted runners

With OIDC, authentication is tied directly to a pipeline execution rather than a stored key that anyone with project access can misuse.

Ease of Management

Managing AWS access keys across multiple GitLab projects quickly becomes a headache. Each key needs to be generated, securely stored, and rotated periodically to maintain security. If multiple teams use the same credentials, updates need to be manually propagated, increasing the risk of errors and outages.

OIDC eliminates manual credential management. Instead of creating access keys for each pipeline, teams configure IAM roles that GitLab assumes when needed. There are no keys to rotate, no secrets to manage, and no risk of credential drift. Everything is controlled centrally in AWS IAM, making it easier to audit and enforce security policies.

For teams managing multi-account AWS environments, OIDC simplifies cross-account access. Without it, each account requires its own credentials, leading to duplicate keys spread across multiple GitLab projects. With OIDC, a single IAM role can be shared across multiple repositories, dynamically granting access when necessary without storing any credentials in GitLab.

Compatibility with GitLab CI/CD

OIDC is natively supported in GitLab CI/CD. When a pipeline runs, GitLab automatically generates an OIDC token (CI_JOB_JWT_V2), which AWS can validate without requiring additional setup. Since the token is dynamically issued for each job, thereā€™s no need to manually configure or distribute credentials.

Static credentials, on the other hand, require storing sensitive values in GitLab CI/CD variables. While GitLab provides options to mask these values, there is always a risk of accidental exposure through logs, environment misconfigurations, or unintended access.

From a usability perspective, OIDC is the better choice for most GitLab CI/CD workflows because it integrates seamlessly with AWS IAM. However, an initial setup is required to define IAM roles and trust relationships, which may not be feasible for every team.

When to Use OIDC?

OIDC should be used whenever security is a priority, and pipelines should not rely on long-lived credentials. It is particularly useful when:

  • GitLab runners need role-based access to AWS without storing credentials. Instead of distributing access keys across multiple projects, a single IAM role can be assumed dynamically.
  • Working in a multi-account AWS environment. Instead of managing separate keys for each account, GitLab pipelines assume IAM roles across accounts as needed.
  • Enforcing security policies that limit deployments to specific branches or repositories. OIDC allows IAM roles to be restricted to approved branches, ensuring that unauthorized pipelines cannot deploy to production.
  • Reducing the risk of credential leaks. Since there are no static credentials stored in GitLab, there is no risk of accidental exposure in logs or environment variables.

For organizations following zero-trust security models, OIDC provides an additional layer of protection by enforcing just-in-time access. If a pipeline does not request a token, it has no AWS access - unlike static credentials, which remain valid until revoked.

When Static Credentials Might Be Needed?

Despite its advantages, OIDC is not always the best option. There are cases where static credentials might be necessary:

  • For pipelines running on self-hosted GitLab runners that lack internet access. OIDC tokens require AWS IAM to validate the identity provider, which may not work in air-gapped environments.
  • When long-term automation requires persistent credentials, if a non-interactive service needs to run AWS operations independently of GitLab, static credentials might be the only option.

That said, even in these scenarios, security can be improved by closely scoping IAM permissions, enforcing key rotation, and using GitLabā€™s protected variables to restrict access.

OIDC authentication offers stronger security, reduced credential management, and better integration with GitLab CI/CD, making it the preferred choice for most use cases. By eliminating static credentials and enforcing role-based access control, teams can minimize the risk of credential leaks while maintaining a scalable, automated deployment process.

For teams managing infrastructure in AWS through GitLab CI/CD, OIDC provides a modern, secure, and scalable authentication method. Replacing static credentials with dynamic role assumption enables a just-in-time access model, ensuring that permissions are granted only when needed and automatically revoked when the pipeline completes.

Implementing OIDC Authentication in GitLab CI/CD for AWS Deployments

Now that we understand the security risks of static AWS credentials and the benefits of OIDC authentication, it's time to implement OIDC authentication in GitLab CI/CD. This section will guide you through setting up AWS IAM to trust GitLab as an OIDC provider, creating an IAM role for GitLab pipelines, and configuring GitLab to authenticate dynamically. Finally, we will deploy an AWS resource using Terraform and verify OIDC authentication.

By the end of this hands-on, GitLab CI/CD pipelines will authenticate dynamically with AWS, assuming a least-privilege IAM role only when needed, without storing static credentials.

Step 1: Setting Up OIDC Authentication in AWS and Retrieving the IAM Role ARN

Now, before configuring GitLab CI/CD, we need to set up AWS IAM to trust GitLab as an OIDC provider and create an IAM role that GitLab pipelines will assume.Ā Ā Ā 

1.1: Define OIDC Provider and IAM Role in Terraform

Create the OIDC provider and IAM role in AWS by adding the following Terraform code in modules/oidc/main.tf:

resource "aws_iam_openid_connect_provider" "gitlab" { url = var.gitlab_url client_id_list = [var.aud_value] thumbprint_list = ["a031c46782e6e6c662c2c87c76da9aa62ccabd8e"] # GitLab OIDC thumbprint } resource "aws_iam_role" "gitlab_ci_role" { name = var.name assume_role_policy = jsonencode({ Version = "2012-10-17" Statement = [{ Effect = "Allow" Principal = { Federated = aws_iam_openid_connect_provider.gitlab.arn } Action = "sts:AssumeRoleWithWebIdentity" Condition = { StringLike = { "${aws_iam_openid_connect_provider.gitlab.url}:sub" = var.match_value } } }] }) } resource "aws_iam_role_policy_attachment" "policy_attach" { for_each = toset(var.managed_policy_names) role = aws_iam_role.gitlab_ci_role.name policy_arn = "arn:aws:iam::aws:policy/${each.value}" } output "ROLE_ARN" { value = aws_iam_role.gitlab_ci_role.arn description = "IAM Role ARN for GitLab CI/CD OIDC" }

1.2: Define Required Variables in Terraform

Add the following variables to the modules/oidc/variables.tf:

variable "name" { description = "Name for the IAM Role" type = string } variable "gitlab_url" { description = "GitLab OIDC Provider URL" type = string } variable "aud_value" { description = "Audience Value for OIDC" type = string } variable "match_value" { description = "GitLab OIDC Match Value" type = list(string) } variable "managed_policy_names" { description = "List of AWS managed policies to attach" type = list(string) }

1.3: Reference the OIDC Module in the Management Environment

Now, reference this OIDC module in environments/management/main.tf:

module "oidc" { source = "../../modules/oidc" name = "GitLabOIDC" gitlab_url = "https://gitlab.com" aud_value = "https://gitlab.com" match_value = ["project_path:sakawasthi08/oidc-co:*"] managed_policy_names = ["ReadOnlyAccess"] # Restrict to ReadOnlyAccess policy } output "ROLE_ARN" { value = module.oidc.ROLE_ARN description = "IAM Role ARN for GitLab CI/CD OIDC" }

Now run terraform init and terraform apply commands to create the IAM Role. It will give an arn as an output after terraform apply exits.

Step 2: Configuring GitLab CI/CD to Use OIDC and Deploy Infrastructure

Now that we have the IAM Role ARN, we will configure GitLab pipelines to authenticate using OIDC and deploy an S3 bucket using Terraform.

2.1: Add IAM Role ARN to GitLab CI/CD Variables

  • Go to GitLab Project ā†’ Settings ā†’ CI/CD ā†’ Variables
  • Create a new variable named AWS_ROLE_ARN
  • Set its value to the IAM Role ARN retrieved from Terraform (arn:aws:iam::4849******42:role/GitLabOIDC)
  • Mark it as protected

2.2: Modify .gitlab-ci.yml for OIDC Authentication

Modify GitLabā€™s .gitlab-ci.yml to use OIDC authentication:

stages: - init - validate - plan - apply - destroy variables: GITLAB_OIDC_TOKEN_FILE: "/tmp/gitlab_oidc_token" default: image: name: "hashicorp/terraform:1.8.0" entrypoint: [""] before_script: - echo "${CI_JOB_JWT_V2}" > ${GITLAB_OIDC_TOKEN_FILE} - export AWS_ROLE_ARN="arn:aws:iam::484907513542:role/GitLabOIDC" - export AWS_WEB_IDENTITY_TOKEN_FILE=${GITLAB_OIDC_TOKEN_FILE} - export AWS_ROLE_SESSION_NAME="gitlab-ci-oidc" - terraform --version

2.3: Define S3 Bucket Deployment in Terraform

Create an S3 bucket using Terraform in environments/management/main.tf:

terraform { backend "s3" { bucket = "gitlab-terraform-state" key = "management/terraform.tfstate" region = "us-east-1" encrypt = true dynamodb_table = "terraform-lock" } } provider "aws" { region = "us-east-1" } resource "aws_s3_bucket" "gitlab_oidc_bucket" { bucket = "gitlab-oidc-test-bucket-${random_id.suffix.hex}" acl = "private" tags = { Name = "GitLab OIDC Bucket" } } resource "random_id" "suffix" { byte_length = 4 } output "bucket_name" { value = aws_s3_bucket.gitlab_oidc_bucket.id }

2.4: Configure GitLab Pipeline for Terraform Deployment

Modify .gitlab-ci.yml to add Terraform pipeline stages:

terraform:init: stage: init script: - terraform init terraform:validate: stage: validate script: - terraform validate terraform:plan: stage: plan script: - terraform plan -out=tfplan.json -input=false artifacts: paths: - tfplan.json expire_in: "1 day" terraform:apply: stage: apply script: - terraform apply -auto-approve -input=false tfplan.json dependencies: - terraform:plan when: manual

Now that GitLab CI/CD is configured to authenticate dynamically using OIDC, the pipeline needs to be tested and validated. The next step is running the GitLab pipeline to verify if Terraform can assume the IAM role and deploy the infrastructure. This ensures that GitLab successfully retrieves an OIDC token, AWS IAM recognizes it, and Terraform can apply the planned changes.

The pipeline consists of multiple stages, starting with initialization, validation, planning, and finally, applying the changes. Running terraform init will initialize the working directory and download required provider plugins.Ā 

After initialization, terraform validates and checks whether the configuration files are syntactically and structurally correct.Ā 

If validation passes, the plan phase executes, where Terraform determines what changes will be applied.Ā 

The final step is terraform apply, which executes the plan and provisions the resources in AWS.

When the pipeline is triggered, GitLab generates a short-lived OIDC token and injects it into the job environment as CI_JOB_JWT_V2. The Terraform job inside the pipeline uses this token to authenticate with AWS by assuming the IAM role that was created earlier. This role grants temporary access, allowing Terraform to execute operations like creating the S3 bucket.

At this point, the pipeline setup is complete, and GitLab is securely deploying AWS resources without storing static credentials. The entire process eliminates the risks associated with long-lived access keys and ensures that authentication is tied strictly to the GitLab job execution. Now, instead of managing and rotating credentials manually, authentication is handled dynamically, improving security and operational efficiency.

Best Practices for Secure OIDC Authentication in GitLab CI/CD

Now that the pipeline is set up and AWS resources have been successfully deployed using OIDC authentication, the next step is to ensure that this setup remains secure and scalable in a real-world environment. While OIDC eliminates the risks associated with static credentials, misconfigurations or overly permissive IAM roles can still expose security vulnerabilities. A well-structured OIDC implementation should follow best practices that enforce least-privilege access, control token usage, and provide continuous monitoring.

Design IAM Roles with Explicit and Granular Access

The first key aspect is designing IAM roles with explicit and granular access. Instead of assigning broad permissions to the IAM role assumed by GitLab, restrict it to the minimal actions required for the specific CI/CD workflows. For example, if the pipeline is responsible for provisioning data infrastructure, it should only have permission for relevant resources such as S3 buckets, RDS databases, Lambda functions, and DynamoDB tables - rather than full administrator access. Implementing policy conditions, such as allowing role assumption only from specific GitLab projects or branches, further strengthens security. AWS IAM allows conditions like StringEquals on sub-claims, ensuring that only authorized pipelines can assume the role.

Control OIDC Token Scope and Expiry

Another important aspect is controlling the OIDC token scope and expiry. The OIDC tokens issued by GitLab are short-lived, but it's important to define session durations appropriately. By default, AWS STS allows a session duration of one hour, but this can be adjusted based on the pipeline requirements. Shorter session durations reduce the risk of privilege escalation if an OIDC token is compromised. Additionally, limiting the aud (audience) claim makes sure that the token is only accepted by the intended repository or branch, preventing unauthorized access across different projects.

Monitor and Audit OIDC-Based Access in AWS

Monitoring and auditing OIDC-based access in AWS is essential to detect and prevent unauthorized actions. Enabling AWS CloudTrail logs for STS AssumeRole events helps track every instance where the IAM role is assumed via GitLab. Reviewing these logs can reveal any suspicious activity, such as attempts to assume roles from unauthorized pipelines. Using AWS Config rules to enforce compliance, such as ensuring that OIDC-based roles do not have overly permissive policies, provides an additional layer of security. If any unexpected role assumptions are detected, automated alerts can be triggered to notify security teams.

Implement Environment-Based Access Controls

Implementing environment-based access controls ensures that OIDC authentication is applied consistently across different environments. Instead of using the same IAM role for all deployments, define separate roles for development, staging, and production environments, each with its own permission boundaries. This prevents development pipelines from having access to production resources, reducing the blast radius in case of misconfigurations. Additionally, GitLab CI/CD can be configured to enforce role usage based on the environment, ensuring that sensitive deployments require explicit approval.

By following these best practices, OIDC authentication in GitLab CI/CD can be managed securely, preventing unauthorized access while maintaining the flexibility of cloud deployments.Ā 

However, securing authentication is only one part of the equation. As your infrastructure scales, managing visibility across all the Terraform deployments becomes a challenge. Pipelines may span multiple repositories, with configurations changing over time. Tracking changes, enforcing security policies, and estimating costs before deployment can quickly become difficult - especially when working across environments.

Firefly Workspaces: Enhancing Terraform Visibility

This is where Firefly Workspaces comes in. Firefly integrates easily with existing GitLab CI/CD pipelines to process the Terraform plan, providing a graphical view of resources, dependencies, and cost estimations before deployment. Instead of navigating through multiple configuration files manually, teams get a structured, visual representation of their Terraform infrastructure.

Visualizing Terraform Resources and Dependencies

After integrating Firefly with GitLab, every Terraform plan is analyzed, and a graphical view of the infrastructure is generated. This visualization shows all resources to be created, modified, or destroyed, along with their dependencies. Instead of relying solely on Terraform CLI outputs, teams can visually inspect relationships between AWS resources, ensuring that changes align with architectural standards.

In the above image, resources like AWS Route Table Association, Internet Gateway, and Subnet are interconnected, reflecting the infrastructure hierarchy. For example, the AWS Subnet depends on the VPC it resides in, while the Route Table Association links the subnet to a Route Table, ensuring proper network routing. The Internet Gateway is attached to the Route Table, allowing external internet access for resources within the subnet. These dependencies ensure that network components are provisioned in the correct order, preventing misconfigurations that could cause connectivity issues.

GitLab does not provide cost estimation for Terraform resources, meaning teams have to rely on external tools or manually analyze pricing details. Firefly, on the other hand, automatically calculates the estimated monthly cost of new and modified resources, giving teams full visibility into cloud expenses before deploying infrastructure. This prevents unexpected cost spikes and ensures that infrastructure changes fit within budget constraints.

Enforcing Security Guardrails in CI/CD Pipelines

Security is a major concern in infrastructure automation. While GitLab CI/CD handles authentication with OIDC, it does not prevent misconfigurations, such as overly permissive IAM roles or public S3 buckets. Firefly addresses this by adding Guardrails to the workspaces - policy enforcement mechanisms that detect violations and automatically block non-compliant changes.

If a terraform plan includes a security risk - such as a VPC subnet assigning a public IP - Firefly flags it and prevents the deployment. This makes sure that infrastructure changes align with organizational security policies.

Once infrastructure is deployed, ensuring ongoing compliance is important. Firefly continuously monitors for drift, policy violations, and cost inefficiencies, making sure that resources remain optimized and compliant over time. If an unexpected change occurs - such as an IAM role gaining excessive permissions - Firefly detects it and alerts the team before it leads to security or compliance issues.

By integrating Firefly Workspaces into GitLab CI/CD pipelines, teams gain full visibility into their Terraform infrastructure, automate security enforcement, and control costs - all without adding manual overhead.

Conclusion

Till now, you should have a clear understanding of how OIDC authentication secures GitLab CI/CD pipelines by eliminating static AWS credentials. It removes the risks of long-lived secrets, enforces least-privilege access, and automates authentication without manual credential management. With this setup, teams can deploy infrastructure securely while maintaining control over access and permissions.

FAQs

Does GitLab support OIDC?

Yes, GitLab supports OIDC and can generate OIDC tokens (CI_JOB_JWT_V2) for secure authentication with cloud providers like AWS.

How do I use OIDC authentication in GitLab CI/CD for AWS deployments?

To use OIDC authentication in GitLab CI/CD for AWS deployments, configure AWS IAM to trust GitLab as an OIDC provider, create an IAM role with the required permissions, and reference it in your pipeline. GitLab automatically generates a short-lived OIDC token (CI_JOB_JWT_V2), which AWS validates before granting temporary credentials to execute Terraform or other AWS operations.

Does OIDC use JWT?

Yes, OIDC uses JWT (JSON Web Token) as the format for ID tokens, which contain user identity claims and authentication information.