<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Valentin Despa</title>
    <description>The latest articles on DEV Community by Valentin Despa (@vdespa).</description>
    <link>https://dev.to/vdespa</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3265365%2F0ac77e0b-5820-40d6-a575-7d84f4586302.png</url>
      <title>DEV Community: Valentin Despa</title>
      <link>https://dev.to/vdespa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vdespa"/>
    <language>en</language>
    <item>
      <title>Configuring OIDC Authentication between GitLab CI/CD and AWS</title>
      <dc:creator>Valentin Despa</dc:creator>
      <pubDate>Sat, 14 Jun 2025 16:23:45 +0000</pubDate>
      <link>https://dev.to/vdespa/configuring-oidc-authentication-between-gitlab-cicd-and-aws-4a9j</link>
      <guid>https://dev.to/vdespa/configuring-oidc-authentication-between-gitlab-cicd-and-aws-4a9j</guid>
      <description>&lt;p&gt;In this tutorial, we will configure AWS and create a GitLab CI/CD job that lists all buckets using AWS CLI - all this without configuring authentication credentials in GitLab.&lt;/p&gt;

&lt;p&gt;In enterprise CI/CD, exchanging a short-lived OIDC token for AWS STS credentials is far safer and more maintainable than defining &lt;code&gt;AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY&lt;/code&gt; as secret environment variables.&lt;/p&gt;

&lt;p&gt;Using OIDC instead of static AWS keys means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No long-lived secrets to leak or rotate&lt;/li&gt;
&lt;li&gt;Context-scoped access (project/branch/environment)&lt;/li&gt;
&lt;li&gt;Auto-expiring STS tokens for a tiny attack window&lt;/li&gt;
&lt;li&gt;Precise audit trails tied to each pipeline/job&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To get this to work, we need to complete 4 steps:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Add GitLab as an identity provider in AWS IAM
&lt;/h2&gt;

&lt;p&gt;For this step, we will use the AWS console.  Inside IAM, identify the menu item called "Identity providers".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsje4nvfcyy3jbbk42lop.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsje4nvfcyy3jbbk42lop.png" alt="Image description" width="590" height="1014"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From there, click on "Add provider", select OpenAI Connect, and fill in your &lt;strong&gt;public&lt;/strong&gt; GitLab instance URL. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1zvzctoh20belks63e90.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1zvzctoh20belks63e90.png" alt="Image description" width="800" height="507"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Configure an IAM role
&lt;/h2&gt;

&lt;p&gt;An IAM role is a reusable set of AWS permissions that we "borrow" temporarily to perform actions without using permanent access keys.&lt;/p&gt;

&lt;p&gt;From the menu, select Roles &amp;gt; Create role. The type needs to be "Web identity".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5mus6o82dna2kibe13va.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5mus6o82dna2kibe13va.png" alt="Image description" width="800" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select gitlab.com as an identity provider.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Favsqq1df9a8vaocxrtli.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Favsqq1df9a8vaocxrtli.png" alt="Image description" width="800" height="580"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This role needs to be restricted to a specific GitLab group, but it can be narrowed down to a project as well.&lt;/p&gt;

&lt;p&gt;Initially, I recommend you skip adding permissions. &lt;/p&gt;

&lt;p&gt;I will name this role GitLabWebIdentityRole_MyGitLabProjects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foq3h7iiorc2m930488pe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foq3h7iiorc2m930488pe.png" alt="Image description" width="800" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The policy generated will look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F44e1m73ao2d9r1zyxsjo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F44e1m73ao2d9r1zyxsjo.png" alt="Image description" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Find the newly created policy in the list and open it. The ARN will be needed for the next step:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy3edc6cenaoqsscihjva.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy3edc6cenaoqsscihjva.png" alt="Image description" width="800" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Create the .gitlab-ci.yml file
&lt;/h2&gt;

&lt;p&gt;First of all, define the variable &lt;code&gt;ROLE_ARN&lt;/code&gt; and set it to the value of the ARN for the IAM role.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;sts_test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;amazon/aws-cli:2.27.35&lt;/span&gt;
    &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;id_tokens&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;GITLAB_OIDC_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;aud&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://gitlab.com&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# this is split out for correct exit code handling&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="s"&gt;aws_sts_output=$(aws sts assume-role-with-web-identity&lt;/span&gt;
      &lt;span class="s"&gt;--role-arn "${ROLE_ARN}"&lt;/span&gt;
      &lt;span class="s"&gt;--role-session-name "GitLabRunner-${CI_PROJECT_ID}-${CI_PIPELINE_ID}"&lt;/span&gt;
      &lt;span class="s"&gt;--web-identity-token ${GITLAB_OIDC_TOKEN}&lt;/span&gt;
      &lt;span class="s"&gt;--duration-seconds 3600&lt;/span&gt;
      &lt;span class="s"&gt;--query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]'&lt;/span&gt;
      &lt;span class="s"&gt;--output text)&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s" $aws_sts_output)&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;aws sts get-caller-identity&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even if you haven't set any permission policies to the role, the job should still succeed. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffne3otskgkdh1029nn03.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffne3otskgkdh1029nn03.png" alt="Image description" width="800" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the configuration is quite verbose, but it offers full control over every STS parameter (duration, session name, JSON parsing, exit‐code handling).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add permission policies&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Open the role and add the policies that are needed for the job/project/group.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2yy3suar08z0657rrc2q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2yy3suar08z0657rrc2q.png" alt="Image description" width="800" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.gitlab.com/ci/cloud_services/aws/" rel="noopener noreferrer"&gt;https://docs.gitlab.com/ci/cloud_services/aws/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-idp_oidc.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-idp_oidc.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>gitlab</category>
      <category>aws</category>
      <category>sts</category>
    </item>
  </channel>
</rss>
