Securing CI/CD Workflows with GitLab CI and OIDC

Securing CI/CD Workflows with GitLab CI and OIDC

Disclaimer: This post was originally written for my company’s blog . I’ve translated it and added a personal touch for this space.

CI/CD pipelines are one of those things a lot of people tend to set up once and then mostly forget about - well, at least until something breaks. 😇 And while pipelines help us avoid the classic “works on my machine” problem plus bring many more advantages in terms of reliability and speed of our deployments, they often introduce different challenges. In this post, I’ll focus on one of them: How to handle credentials securely.

In many setups out in the wild, secrets end up as long-lived environment variables in GitLab CI. That works, but it’s not ideal. Way better: Using OpenID Connect (OIDC) with GitLab CI to authenticate pipelines without storing static credentials. So, let’s get started.

What is OIDC?

OpenID Connect (OIDC) is an authentication layer built on top of OAuth 2.0., using JSON Web Tokens (JWTs) to authenticate users and workloads. Unlike long-term credentials that must be stored as GitLab CI environment variables, OIDC uses short-lived tokens, issued on demand.

Why use OIDC with GitLab

Using OIDC in GitLab CI has a few practical advantages:

  • Better security (and fewer secrets lying around): OIDC helps us avoid static credentials in our pipelines. Instead of storing long-lived access keys as CI variables, the pipeline receives a short-lived token that’s valid only for that run. If something leaks, the blast radius is small - and the token expires on its own.

  • Simpler credential management: With OIDC, access control lives in one place instead of being scattered across multiple CI variables and scripts. Permissions can be defined centrally, audited properly, and adjusted without touching every pipeline configuration.

  • Seamless Integration: GitLab CI integrates nicely with cloud providers and services that support OIDC (which are also quite some). This makes it much easier to use the same authentication approach across different environments - especially when more than one platform is involved.

  • Scalability and Flexibility: Because OIDC is a standardized protocol, it adapts well to different setups. Whether it’s a small project with a single pipeline or a larger setup with multiple teams and environments, the same basic approach still applies.

What does it look like in practice?

The exact setup depends on the cloud provider / target platform, but the general steps are always similar:

1. Configure GitLab as an OIDC Provider: GitLab is registered as a trusted identity provider in the target system.

2. Create a role that accepts OIDC tokens: This role can be assumed using tokens issued by GitLab. A reference to the role is usually stored as a CI/CD variable.

3. Assign Permissions: The role gets only the permissions the pipeline actually needs. As always, following the “least privilege” principle saves trouble later. Access can also be scoped to GitLab groups, projects, branches, or tags.

4. Configure GitLab CI/CD Pipelines: GitLab CI can request an OIDC token directly in the pipeline configuration. A minimal example looks like this:

    .before-script:
        id_tokens:
            GITLAB_OIDC_TOKEN;
                 aud: https://gitlab.example.com
        before-script:
            - echo "${GITLAB_OIDC_TOKEN}" > /tmp/web_identity_token

How the token is then used depends on the target platform. Authentication details vary, so it’s worth checking the official documentation .

A quick note on pitfalls

OIDC removes a lot of credential-related problems, but it’s not magic. Misconfigured roles or overly broad permissions can still cause issues. Also, debugging authentication failures can be a bit annoying at first —-especially when tokens expire faster than expected. 🙃

To sum it up

Using OIDC with GitLab CI gives us a much cleaner way to authenticate pipelines. Short-lived tokens replace long-lived credentials, which improves security and reduces maintenance work. 💪