<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en_US"><generator uri="https://jekyllrb.com/" version="3.9.2">Jekyll</generator><link href="https://dille.name/feed.xml" rel="self" type="application/atom+xml" /><link href="https://dille.name/" rel="alternate" type="text/html" hreflang="en_US" /><updated>2026-04-21T08:54:26-05:00</updated><id>https://dille.name/feed.xml</id><title type="html">Nicholas Dille</title><subtitle>Automation, DevOps and Containerization</subtitle><author><name>Nicholas Dille</name></author><entry><title type="html">Talk about mastering #kubectl #Kubernetes @ #DevOps #Meetup #Freiburg</title><link href="https://dille.name/blog/2025/12/15/talk-about-kubectl-at-devops-meetup/" rel="alternate" type="text/html" title="Talk about mastering #kubectl #Kubernetes @ #DevOps #Meetup #Freiburg" /><published>2025-12-15T14:00:00-06:00</published><updated>2025-12-15T14:00:00-06:00</updated><id>https://dille.name/blog/2025/12/15/talk-about-kubectl-at-devops-meetup</id><content type="html" xml:base="https://dille.name/blog/2025/12/15/talk-about-kubectl-at-devops-meetup/">&lt;p&gt;I gave a talk at &lt;a href=&quot;https://www.meetup.com/de-de/devops-freiburg/events/311972440/&quot;&gt;DevOps Meetup Freiburg&lt;/a&gt; about mastering kubectl with a focus on little known facts and tricks.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../../media/2025/12/jade-lee-h_Uxi-NeNSA-unsplash.jpg&quot; style=&quot;object-fit: cover; object-position: center 30%; width: 100%; height: 250px;&quot; /&gt;&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Find my slides &lt;a href=&quot;/slides/2025-12-15/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The slides are a result of my slide and demo build system. Take a look at the &lt;a href=&quot;https://github.com/nicholasdille/container-slides/releases/tag/20251215.0&quot;&gt;release for this talk&lt;/a&gt;.&lt;/p&gt;</content><author><name>Nicholas Dille</name></author><category term="Haufe-Lexware" /><category term="Slides" /><category term="Slide Deck" /><category term="Event" /><category term="Meetup" /><category term="Talk" /><category term="Kubernetes" /><summary type="html">I gave a talk at DevOps Meetup Freiburg about mastering kubectl with a focus on little known facts and tricks.</summary></entry><entry><title type="html">Two day workshop about #GitLab CI (German)</title><link href="https://dille.name/blog/2025/11/27/Two-day-workshop-about-gitlab-ci/" rel="alternate" type="text/html" title="Two day workshop about #GitLab CI (German)" /><published>2025-11-27T14:00:00-06:00</published><updated>2025-11-27T14:00:00-06:00</updated><id>https://dille.name/blog/2025/11/27/workshop-gitlab-ci</id><content type="html" xml:base="https://dille.name/blog/2025/11/27/Two-day-workshop-about-gitlab-ci/">&lt;p&gt;In addition to operating GitLab for our development teams, we are also using GitLab ourselves to automate the deployment and update of our services. Based on this experience I held a two day workshop for &lt;a href=&quot;https://heise-academy.de/&quot;&gt;heise Academy&lt;/a&gt; to demonstrate the wide range of features provided by GitLab CI.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2022/03/izabel-ouwdw--XNzo-unsplash.jpg&quot; style=&quot;object-fit: cover; object-position: center 60%; width: 100%; height: 150px;&quot; /&gt;&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;I covered the following topics:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Fundamentals&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;Introduction to jobs and stages&lt;/li&gt;
      &lt;li&gt;Using variables&lt;/li&gt;
      &lt;li&gt;Adding before and after scripts&lt;/li&gt;
      &lt;li&gt;Using images&lt;/li&gt;
      &lt;li&gt;Adding defaults&lt;/li&gt;
      &lt;li&gt;Storing and retrieving artifacts&lt;/li&gt;
      &lt;li&gt;Job dependencies&lt;/li&gt;
      &lt;li&gt;Adding schedules&lt;/li&gt;
      &lt;li&gt;CI/CD configuration&lt;/li&gt;
      &lt;li&gt;Adding unit tests&lt;/li&gt;
      &lt;li&gt;Manually starting pipelines with forms&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Advanced topics&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;Using environment for deployments&lt;/li&gt;
      &lt;li&gt;Triggering pipelines&lt;/li&gt;
      &lt;li&gt;Child pipelines&lt;/li&gt;
      &lt;li&gt;Job templates&lt;/li&gt;
      &lt;li&gt;(Workflow) Rules&lt;/li&gt;
      &lt;li&gt;Merge requests&lt;/li&gt;
      &lt;li&gt;Matrix jobs&lt;/li&gt;
      &lt;li&gt;Variable precedence&lt;/li&gt;
      &lt;li&gt;GitLab feature deprecations&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Expert level&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;Roles and permissions in GitLab&lt;/li&gt;
      &lt;li&gt;Understanding job tokens&lt;/li&gt;
      &lt;li&gt;Working with Git submodules&lt;/li&gt;
      &lt;li&gt;Adding services&lt;/li&gt;
      &lt;li&gt;Build container images&lt;/li&gt;
      &lt;li&gt;Using the GitLab container registry&lt;/li&gt;
      &lt;li&gt;Creating releases&lt;/li&gt;
      &lt;li&gt;Using branch protection&lt;/li&gt;
      &lt;li&gt;Troubleshooting pipelines&lt;/li&gt;
      &lt;li&gt;Using runners&lt;/li&gt;
      &lt;li&gt;Caching intermediate results&lt;/li&gt;
      &lt;li&gt;Renovating dependencies&lt;/li&gt;
      &lt;li&gt;Exploring security features in GitLab community edition&lt;/li&gt;
      &lt;li&gt;CI/CD components&lt;/li&gt;
      &lt;li&gt;CI/CD steps&lt;/li&gt;
      &lt;li&gt;Using pipeline inputs&lt;/li&gt;
      &lt;li&gt;Using secure files&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Find my slides &lt;a href=&quot;/slides/2025-11-27/&quot;&gt;here&lt;/a&gt; as well as the &lt;a href=&quot;/hands-on/2025-11-27/&quot;&gt;exercises&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The slides and exercises are a result of my slide and demo build system. Take a look at the &lt;a href=&quot;https://github.com/nicholasdille/container-slides/releases/tag/20251127.0&quot;&gt;release for this event&lt;/a&gt;.&lt;/p&gt;</content><author><name>Nicholas Dille</name></author><category term="Haufe-Lexware" /><category term="Docker" /><category term="Container" /><category term="Slides" /><category term="Slide Deck" /><category term="GitLab" /><category term="Event" /><category term="Workshop" /><summary type="html">In addition to operating GitLab for our development teams, we are also using GitLab ourselves to automate the deployment and update of our services. Based on this experience I held a two day workshop for heise Academy to demonstrate the wide range of features provided by GitLab CI.</summary></entry><entry><title type="html">Talk about Shell Code @ CLC Conference #CLC_Conf</title><link href="https://dille.name/blog/2025/11/20/talk-about-shell-code-at-clc/" rel="alternate" type="text/html" title="Talk about Shell Code @ CLC Conference #CLC_Conf" /><published>2025-11-20T14:00:00-06:00</published><updated>2025-11-20T14:00:00-06:00</updated><id>https://dille.name/blog/2025/11/20/talk-at-clc</id><content type="html" xml:base="https://dille.name/blog/2025/11/20/talk-about-shell-code-at-clc/">&lt;p&gt;I just attended this year’s &lt;a href=&quot;https://www.containerconf.de&quot;&gt;ContainerConf&lt;/a&gt; in Mannheim. It was awesome meeting old and new friends and talking all things containers. I had the pleasure to contribute a full-day &lt;a href=&quot;/blog/2025/11/18/workshop-about-gitlab-ci-at-clc/&quot;&gt;workshop about CI/CD with GitLab&lt;/a&gt; as well as a talk about shell code.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2022/11/joao-cruz-IkEpl3JkVqU-unsplash.jpg&quot; style=&quot;object-fit: cover; object-position: center 70%; width: 100%; height: 150px;&quot; /&gt;&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Find my slides and demos for the talk &lt;a href=&quot;https://github.com/nicholasdille/container-slides/releases/tag/20251120.0&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;</content><author><name>Nicholas Dille</name></author><category term="Haufe-Lexware" /><category term="Docker" /><category term="Container" /><category term="Slides" /><category term="Slide Deck" /><category term="Kubernetes" /><category term="Security" /><category term="Continuous Integration" /><category term="Event" /><category term="Conference" /><category term="Talk" /><summary type="html">I just attended this year’s ContainerConf in Mannheim. It was awesome meeting old and new friends and talking all things containers. I had the pleasure to contribute a full-day workshop about CI/CD with GitLab as well as a talk about shell code.</summary></entry><entry><title type="html">Workshop about #GitLab CI @ CLC Conference #CLC_Conf</title><link href="https://dille.name/blog/2025/11/18/workshop-about-gitlab-ci-at-clc/" rel="alternate" type="text/html" title="Workshop about #GitLab CI @ CLC Conference #CLC_Conf" /><published>2025-11-18T14:00:00-06:00</published><updated>2025-11-18T14:00:00-06:00</updated><id>https://dille.name/blog/2025/11/18/workshop-at-clc</id><content type="html" xml:base="https://dille.name/blog/2025/11/18/workshop-about-gitlab-ci-at-clc/">&lt;p&gt;I just attended this year’s &lt;a href=&quot;https://www.clc-conference.eu&quot;&gt;CLC Conference&lt;/a&gt; in Mannheim. It was awesome meeting old and new friends and talking all things containers and automation. I had the pleasure to contribute a full-day workshop about CI/CD with GitLab as well as a &lt;a href=&quot;/blog/2025/11/20/talk-about-shell-code-at-clc/&quot;&gt;talk about shell code&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2022/11/joao-cruz-IkEpl3JkVqU-unsplash.jpg&quot; style=&quot;object-fit: cover; object-position: center 70%; width: 100%; height: 150px;&quot; /&gt;&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Find my slides and demos for the workshop &lt;a href=&quot;https://github.com/nicholasdille/container-slides/releases/tag/20251118.0&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;</content><author><name>Nicholas Dille</name></author><category term="Haufe-Lexware" /><category term="Docker" /><category term="Container" /><category term="Slides" /><category term="Slide Deck" /><category term="Kubernetes" /><category term="Security" /><category term="Continuous Integration" /><category term="Event" /><category term="Conference" /><category term="Workshop" /><summary type="html">I just attended this year’s CLC Conference in Mannheim. It was awesome meeting old and new friends and talking all things containers and automation. I had the pleasure to contribute a full-day workshop about CI/CD with GitLab as well as a talk about shell code.</summary></entry><entry><title type="html">Workshop about operating #GitLab (German)</title><link href="https://dille.name/blog/2025/11/13/workshop-about-operating-gitlab/" rel="alternate" type="text/html" title="Workshop about operating #GitLab (German)" /><published>2025-11-13T14:00:00-06:00</published><updated>2025-11-13T14:00:00-06:00</updated><id>https://dille.name/blog/2025/11/13/workshop-gitlab-ops</id><content type="html" xml:base="https://dille.name/blog/2025/11/13/workshop-about-operating-gitlab/">&lt;p&gt;Part of my daily work is operating a GitLab instance for our development teams. Based on this experience I am offering a workshop for &lt;a href=&quot;https://heise-academy.de/&quot;&gt;heise Academy&lt;/a&gt; to share my knowledge.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2022/03/abby-ar-1uwzsExrKzY-unsplash.jpg&quot; style=&quot;object-fit: cover; object-position: center 60%; width: 100%; height: 150px;&quot; /&gt;&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;I covered the following topics:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Handling the web UI&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;Projects&lt;/li&gt;
      &lt;li&gt;Users&lt;/li&gt;
      &lt;li&gt;Authorization&lt;/li&gt;
      &lt;li&gt;User Profiles&lt;/li&gt;
      &lt;li&gt;Server settings&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Customizing the deployment&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;Using a reverse proxy for routing requests&lt;/li&gt;
      &lt;li&gt;Understanding the directory layout&lt;/li&gt;
      &lt;li&gt;Authenicating against LDAP&lt;/li&gt;
      &lt;li&gt;Outgoing mail through SMTP&lt;/li&gt;
      &lt;li&gt;Using the GitLab container registry&lt;/li&gt;
      &lt;li&gt;Adding integrations&lt;/li&gt;
      &lt;li&gt;Troubleshooting&lt;/li&gt;
      &lt;li&gt;Updating GitLab&lt;/li&gt;
      &lt;li&gt;Attaching runners for GitLab CI&lt;/li&gt;
      &lt;li&gt;Using GitLab Pages&lt;/li&gt;
      &lt;li&gt;Monitoring GitLab&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Find my slides &lt;a href=&quot;/slides/2025-11-13/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The slides are a result of my slide and demo build system. Take a look at the &lt;a href=&quot;https://github.com/nicholasdille/container-slides/releases/tag/20251113.0&quot;&gt;release for this event&lt;/a&gt;.&lt;/p&gt;</content><author><name>Nicholas Dille</name></author><category term="Haufe-Lexware" /><category term="Docker" /><category term="Container" /><category term="Slides" /><category term="Slide Deck" /><category term="GitLab" /><category term="Event" /><category term="Workshop" /><summary type="html">Part of my daily work is operating a GitLab instance for our development teams. Based on this experience I am offering a workshop for heise Academy to share my knowledge.</summary></entry><entry><title type="html">Fun with envsubst</title><link href="https://dille.name/blog/2025/05/25/fun-with-envsubst/" rel="alternate" type="text/html" title="Fun with envsubst" /><published>2025-05-25T15:02:00-05:00</published><updated>2025-05-25T15:02:00-05:00</updated><id>https://dille.name/blog/2025/05/25/fun-with-envsubst</id><content type="html" xml:base="https://dille.name/blog/2025/05/25/fun-with-envsubst/">&lt;p&gt;When using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enbsubst&lt;/code&gt; to substitute environment variables, empty variables will be replaced with an empty string. This may not be the desired result. Even though, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;envsubst&lt;/code&gt; supports explcitly naming the variables to substitute, it is uncomfortable to use. This post demonstrated additional options to preserve environment variables that have no value set.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2025/05/woliul-hasan-bUbk6ge6Ze0-unsplash.jpg&quot; style=&quot;object-fit: cover; object-position: center 45%; width: 100%; height: 300px;&quot; /&gt;&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;/h2&gt;

&lt;p&gt;Let’s consider the following file:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;This line contains a variable that exists: &lt;span class=&quot;nv&quot;&gt;$varexists&lt;/span&gt;
This line contains a variable that is not &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt;: &lt;span class=&quot;nv&quot;&gt;$varnotset&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;well-known-solution&quot;&gt;Well-known solution&lt;/h2&gt;

&lt;p&gt;The well-known solution is to explicitly name the variables that should be substituted. This will only substitute the variable that exists and leave the other variable untouched:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cat &lt;/span&gt;file | envsubst &lt;span class=&quot;s1&quot;&gt;&apos;${varexists}&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;obfuscating-the-dollar-sign&quot;&gt;Obfuscating the dollar sign&lt;/h2&gt;

&lt;p&gt;The first solution is to obfuscate the dollar sign in front of the variable that is not set. This will prevent &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;envsubst&lt;/code&gt; from substituting the variable:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;This line contains a variable that exists: &lt;span class=&quot;nv&quot;&gt;$varexists&lt;/span&gt;
This line contains a variable that is not &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt;: §varnotset
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The following command will process the above example:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cat &lt;/span&gt;file | envsubst | &lt;span class=&quot;nb&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;s/§/$/g&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;inserting-the-dollar-sign&quot;&gt;Inserting the dollar sign&lt;/h2&gt;

&lt;p&gt;This solution uses a dummy variable to insert the dollar sign in front of variables that are not set:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;This line contains a variable that exists: &lt;span class=&quot;nv&quot;&gt;$varexists&lt;/span&gt;
This line contains a variable that is not &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt;: &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;dollar&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;varnotset
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The following command will process the above example:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cat &lt;/span&gt;file | &lt;span class=&quot;nv&quot;&gt;dollar&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;$&apos;&lt;/span&gt; envsubst
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;restoring-the-dollar-sign&quot;&gt;Restoring the dollar sign&lt;/h2&gt;

&lt;p&gt;This solution will work on the original file without any modifications:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;This line contains a variable that exists: &lt;span class=&quot;nv&quot;&gt;$varexists&lt;/span&gt;
This line contains a variable that is not &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt;: &lt;span class=&quot;nv&quot;&gt;$varnotset&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The following command will process the above example:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cat &lt;/span&gt;file | &lt;span class=&quot;nv&quot;&gt;varnotset&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;$varnotset&apos;&lt;/span&gt; envsubst
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;collecting-existing-variables&quot;&gt;Collecting existing variables&lt;/h2&gt;

&lt;p&gt;The last solution is based on an imaginary tool called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;envsubst-helper&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Collect all variables used in the file&lt;/li&gt;
  &lt;li&gt;Check which variables are set&lt;/li&gt;
  &lt;li&gt;Output all variables that are set&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cat &lt;/span&gt;file | envsubst &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;envsubst-helper&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;alternative-tools&quot;&gt;Alternative tools&lt;/h2&gt;

&lt;p&gt;There are alternative tools that can be used to substitute existing environment variables but &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;envsubst&lt;/code&gt; is readily available on most systems.&lt;/p&gt;</content><author><name>Nicholas Dille</name></author><category term="Haufe-Lexware" /><category term="Kubernetes" /><summary type="html">When using enbsubst to substitute environment variables, empty variables will be replaced with an empty string. This may not be the desired result. Even though, envsubst supports explcitly naming the variables to substitute, it is uncomfortable to use. This post demonstrated additional options to preserve environment variables that have no value set.</summary></entry><entry><title type="html">Two day workshop about #GitLab CI (German)</title><link href="https://dille.name/blog/2025/05/14/Two-day-workshop-about-gitlab-ci/" rel="alternate" type="text/html" title="Two day workshop about #GitLab CI (German)" /><published>2025-05-14T15:00:00-05:00</published><updated>2025-05-14T15:00:00-05:00</updated><id>https://dille.name/blog/2025/05/14/workshop-gitlab-ci</id><content type="html" xml:base="https://dille.name/blog/2025/05/14/Two-day-workshop-about-gitlab-ci/">&lt;p&gt;In addition to operating GitLab for our development teams, we are also using GitLab ourselves to automate the deployment and update of our services. Based on this experience I held a two day workshop for &lt;a href=&quot;https://heise-academy.de/&quot;&gt;heise Academy&lt;/a&gt; to demonstrate the wide range of features provided by GitLab CI.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2022/03/izabel-ouwdw--XNzo-unsplash.jpg&quot; style=&quot;object-fit: cover; object-position: center 60%; width: 100%; height: 150px;&quot; /&gt;&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;I covered the following topics:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Fundamentals&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;Introduction to jobs and stages&lt;/li&gt;
      &lt;li&gt;Using variables&lt;/li&gt;
      &lt;li&gt;Adding before and after scripts&lt;/li&gt;
      &lt;li&gt;Using images&lt;/li&gt;
      &lt;li&gt;Adding defaults&lt;/li&gt;
      &lt;li&gt;Storing and retrieving artifacts&lt;/li&gt;
      &lt;li&gt;Job dependencies&lt;/li&gt;
      &lt;li&gt;Adding schedules&lt;/li&gt;
      &lt;li&gt;CI/CD configuration&lt;/li&gt;
      &lt;li&gt;Adding unit tests&lt;/li&gt;
      &lt;li&gt;Manually starting pipelines with forms&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Advanced topics&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;Using environment for deployments&lt;/li&gt;
      &lt;li&gt;Triggering pipelines&lt;/li&gt;
      &lt;li&gt;Child pipelines&lt;/li&gt;
      &lt;li&gt;Job templates&lt;/li&gt;
      &lt;li&gt;(Workflow) Rules&lt;/li&gt;
      &lt;li&gt;Merge requests&lt;/li&gt;
      &lt;li&gt;Matrix jobs&lt;/li&gt;
      &lt;li&gt;Variable precedence&lt;/li&gt;
      &lt;li&gt;GitLab feature deprecations&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Expert level&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;Roles and permissions in GitLab&lt;/li&gt;
      &lt;li&gt;Understanding job tokens&lt;/li&gt;
      &lt;li&gt;Working with Git submodules&lt;/li&gt;
      &lt;li&gt;Adding services&lt;/li&gt;
      &lt;li&gt;Build container images&lt;/li&gt;
      &lt;li&gt;Using the GitLab container registry&lt;/li&gt;
      &lt;li&gt;Creating releases&lt;/li&gt;
      &lt;li&gt;Using branch protection&lt;/li&gt;
      &lt;li&gt;Troubleshooting pipelines&lt;/li&gt;
      &lt;li&gt;Using runners&lt;/li&gt;
      &lt;li&gt;Caching intermediate results&lt;/li&gt;
      &lt;li&gt;Renovating dependencies&lt;/li&gt;
      &lt;li&gt;Exploring security features in GitLab community edition&lt;/li&gt;
      &lt;li&gt;CI/CD components&lt;/li&gt;
      &lt;li&gt;CI/CD steps&lt;/li&gt;
      &lt;li&gt;Using pipeline inputs&lt;/li&gt;
      &lt;li&gt;Using secure files&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Find my slides &lt;a href=&quot;/slides/2025-05-14/&quot;&gt;here&lt;/a&gt; as well as the &lt;a href=&quot;/hands-on/2025-05-14/&quot;&gt;exercises&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The slides and exercises are a result of my slide and demo build system. Take a look at the &lt;a href=&quot;https://github.com/nicholasdille/container-slides/releases/tag/20250514.3&quot;&gt;release for this event&lt;/a&gt;.&lt;/p&gt;</content><author><name>Nicholas Dille</name></author><category term="Haufe-Lexware" /><category term="Docker" /><category term="Container" /><category term="Slides" /><category term="Slide Deck" /><category term="GitLab" /><category term="Event" /><category term="Workshop" /><summary type="html">In addition to operating GitLab for our development teams, we are also using GitLab ourselves to automate the deployment and update of our services. Based on this experience I held a two day workshop for heise Academy to demonstrate the wide range of features provided by GitLab CI.</summary></entry><entry><title type="html">Using #GitLab #OIDC to authenticate against #Kubernetes</title><link href="https://dille.name/blog/2025/02/24/using-gitlab-oidc-to-authenticate-against-kubernetes/" rel="alternate" type="text/html" title="Using #GitLab #OIDC to authenticate against #Kubernetes" /><published>2025-02-24T14:02:00-06:00</published><updated>2025-02-24T14:02:00-06:00</updated><id>https://dille.name/blog/2025/02/24/gitlab-ci-workfload-identity-against-kubernetes</id><content type="html" xml:base="https://dille.name/blog/2025/02/24/using-gitlab-oidc-to-authenticate-against-kubernetes/">&lt;p&gt;OpenID Connect (OIDC) and workload identity have been hot topics for a couple of years. This post demonstrates how to use GitLab as an OIDC provider to authenticate against a Kubernetes cluster - covering interactive access by users as well as automated access from pipeline jobs. The challenge is to combine both use cases in a single configuration.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2025/01/merry-christmas-5219496_1920.jpg&quot; style=&quot;object-fit: cover; object-position: center 25%; width: 100%; height: 150px;&quot; /&gt;&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Traditionally, communication between two services has been secured using shared secrets. This approach has been proven to be insecure because secrets can be leaked. OIDC is a modern approach to secure communication between services by using auto-generated short-lived tokens. The tokens are issued by an identity provider and can be verified by the service receiving the token. See the &lt;a href=&quot;https://openid.net/developers/how-connect-works/&quot;&gt;official site&lt;/a&gt; for an explanation of OIDC.&lt;/p&gt;

&lt;p&gt;When a token is issued by an OIDC provider, it contains a set of claims which can be used to identify the user or service. Often these claims are mapped to roles or groups. Claims can be used by the consumer to authorize access to resources.&lt;/p&gt;

&lt;p&gt;For example, a user authenticates against GitLab and receives a token containing claims representing the group memberships. The Kubernetes API server can be configured to accept tokens issued by GitLab and map the group memberships to Kubernetes roles using RBAC.&lt;/p&gt;

&lt;p&gt;Other use cases include using Kubernetes services accounts to authenticate and authorize against cloud services or signing container images and artifacts using &lt;a href=&quot;https://sigstore.dev/&quot;&gt;sigstore&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;GitLab ships with an &lt;a href=&quot;https://docs.gitlab.com/integration/openid_connect_provider/&quot;&gt;integrated OIDC provider&lt;/a&gt; which can be used to authenticate users and pipeline jobs. The issued tokens can be used to authenticate against Kubernetes and authorize access to resources using RBAC.&lt;/p&gt;

&lt;h2 id=&quot;part-1-authenticating-users&quot;&gt;Part 1: Authenticating users&lt;/h2&gt;

&lt;p&gt;Following &lt;a href=&quot;https://www.hoelzel.it/kubernetes/2023/04/17/k3s-gitlab-oidc-copy.html&quot;&gt;this guide&lt;/a&gt;, a GitLab application is configured to act as an OIDC provider. The application is configured to issue tokens containing claims representing the group memberships of the user.&lt;/p&gt;

&lt;p&gt;When authenticating user &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyUserName&lt;/code&gt; with ID 2 against GitLab at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://gitlab.example.com&lt;/code&gt;, the following represents a token issued by GitLab:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;iss&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://gitlab.example.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;sub&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;aud&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;REDACTED&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;exp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;iat&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;nonce&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;REDACTED&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;auth_time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;sub_legacy&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;REDACTED&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;MyUserName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;nickname&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;MyUserName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;preferred_username&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;MyUserName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;profile&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://gitlab.example.com/MyUserName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;picture&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://gitlab.example.com/uploads/-/system/user/avatar/2/avatar.png&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;groups_direct&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note that the claim &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;groups_direct&lt;/code&gt; contains the direct group memberships of the user. Based on the contents, the consumer is able to authorize access for the members of specific groups.&lt;/p&gt;

&lt;h2 id=&quot;part-2-authenticating-pipeline-jobs&quot;&gt;Part 2: Authenticating pipeline jobs&lt;/h2&gt;

&lt;p&gt;Since GitLab comes with the integrated OIDC provider, a pipeline job can easily &lt;a href=&quot;https://docs.gitlab.com/ci/secrets/id_token_authentication/&quot;&gt;request an ID token&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;my_job&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;id_tokens&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;FIRST_ID_TOKEN&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;aud&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;some_string&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;SECOND_ID_TOKEN&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;aud&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;another_string&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;printenv&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The typical use case for this feature is to &lt;a href=&quot;https://docs.gitlab.com/ci/cloud_services/&quot;&gt;authenticate against a cloud service&lt;/a&gt;, like AWS or Azure.&lt;/p&gt;

&lt;p&gt;When requesting an ID token a job with ID 234 in pipeline 123 triggered by user &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyUserName&lt;/code&gt; with ID 2 from project ` foo/bar` with ID 13, the following represents a token issued by GitLab:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;namespace_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;namespace_path&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;project_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;13&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;project_path&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;foo/bar&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;user_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;user_login&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;MyUserName&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;user_email&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;username@example.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;user_access_level&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;owner&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;pipeline_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;123&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;pipeline_source&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;push&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;job_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;234&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ref&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;main&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ref_type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;branch&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ref_path&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;refs/heads/main&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ref_protected&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;runner_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;runner_environment&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;self-hosted&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;sha&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0fffdd6505a3f5f573e34338cb9eac1211bace14&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;project_visibility&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;internal&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ci_config_ref_uri&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;gitlab.example.com/foo/bar//.gitlab-ci.yml@refs/heads/main&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ci_config_sha&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;0fffdd6505a3f5f573e34338cb9eac1211bace14&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;jti&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;979f6254-c7ce-4632-be39-1b65163792b8&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;iat&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;nbf&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;exp&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;iss&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://gitlab.example.com&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;sub&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;project_path:foo/bar:ref_type:branch:ref:main&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;aud&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;REDACTED&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice that the claim &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub&lt;/code&gt; contains a unique identifier for the project. The pipeline job is identified by the fields &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;namespace_path&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;project_path&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ref_type&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ref&lt;/code&gt; as well as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pipeline_id&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;job_id&lt;/code&gt;. Based on these fields, the consumer is able to identify the project, the pipeline or even the job.&lt;/p&gt;

&lt;h2 id=&quot;part-3-using-kubernetes-as-oidc-consumer&quot;&gt;Part 3: Using Kubernetes as OIDC consumer&lt;/h2&gt;

&lt;p&gt;In order for Kubernetes to accept tokens issued by GitLab, the API server must be configured to trust the OIDC provider. The following command line options configure the API server to trust GitLab as an OIDC provider. This is also explained in the &lt;a href=&quot;https://www.hoelzel.it/kubernetes/2023/04/17/k3s-gitlab-oidc-copy.html&quot;&gt;guide mentioned above&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kube-apiserver &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--oidc-client-id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$AUDIENCE&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--oidc-groups-claim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;groups_direct &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--oidc-groups-prefix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;gitlab:&apos;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--oidc-issuer-url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ISSUER_URL&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--oidc-username-claim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;preferred_username &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--oidc-username-prefix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;oidc:&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When populating the above options note the following:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Option&lt;/th&gt;
      &lt;th&gt;Description&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--oidc-client-id&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;The audience of the token issued by GitLab (matched the client or application ID)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--oidc-groups-claim&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Referenced a list in the ID token with group memberships&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--oidc-issuer-url&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;The URL of the OIDC provider (GitLab)&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--oidc-username-claim&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;References a field to identify the user or the pipeline job&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The options &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--oidc-groups-prefix&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--oidc-username-prefix&lt;/code&gt; are used to prefix the values of the claims to make them easier to read and understand in RBAC role bindings.&lt;/p&gt;

&lt;p&gt;Considering the two example tokens above, it becomes obvious that the API server cannot be configured to accept both token because of the difference in the fields: The primary use case for identifying a user is the list of group memberships. This field is missing from the ID token generated for a pipeline job. Pipelines are using fields that are not present in the ID token generated for a user.&lt;/p&gt;

&lt;p&gt;&lt;i class=&quot;fa-duotone fa-solid fa-triangle-exclamation&quot;&gt;&lt;/i&gt; Familiarize yourself with the command line options and thoroughly test them in a development cluster because syntax errors can cause the API server to fail during startup.&lt;/p&gt;

&lt;p&gt;&lt;i class=&quot;fa-duotone fa-solid fa-hand-holding-heart&quot;&gt;&lt;/i&gt; Fortunately, if authentication of ID token against the OIDC provider fails, the API server still recognizes and accepts all traditional ways of authentication including service account tokens.&lt;/p&gt;

&lt;h2 id=&quot;part-4-using-structured-authentication-configuration&quot;&gt;Part 4: Using structured authentication Configuration&lt;/h2&gt;

&lt;p&gt;In Kubernetes 1.30, &lt;a href=&quot;https://kubernetes.io/blog/2024/04/25/structured-authentication-moves-to-beta/&quot;&gt;structured authentication configuration&lt;/a&gt; moved to beta and provides an &lt;a href=&quot;https://kubernetes.io/docs/reference/access-authn-authz/authentication/#using-authentication-configuration&quot;&gt;alternative to the command line options&lt;/a&gt;. The structured configuration allows for more complex configurations and is easier to maintain. Unfortunately, it is mutually exclusive with the command line options presented above:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kube-apiserver &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--authentication-config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/etc/kubernetes/auth/auth-config.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;With the structured authentication configuration it is possible to cover both use cases - authenticating a user as well as a pipeline job. Note that it only supports one item per issuer. Considering that the two use cases produce so different ID tokens, the claim mappings must be configued using the Common Expression Language (CEL).&lt;/p&gt;

&lt;p&gt;The following structured authentication configuration uses conditionals to map the claims of the ID tokens to the fields used by Kubernetes:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apiserver.config.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AuthenticationConfiguration&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;jwt&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;issuer&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://gitlab.example.com&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;audiences&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;group_application_id&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;id_token_audience&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;audienceMatchPolicy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;MatchAny&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;claimMappings&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;has(claims.preferred_username)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;gitlab:&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;claims.preferred_username&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;gitlab-ci:&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;claims.sub&apos;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;groups&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;claim&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;grouops_direct&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;gitlab:&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;uid&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;has(claims.preferred_username)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;gitlab:&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;claims.preferred_username&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;gitlab-ci:&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;claims.sub&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;part-5-authorizing-access-using-rbac&quot;&gt;Part 5: Authorizing access using RBAC&lt;/h2&gt;

&lt;p&gt;Whenever an ID token was successfully authenticated against the OIDC provider by the API server, the claims are read from the token and Kubernetes objects are created to allow authorization:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Use Case&lt;/th&gt;
      &lt;th&gt;Claim&lt;/th&gt;
      &lt;th&gt;Resource type&lt;/th&gt;
      &lt;th&gt;Resource name&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;User authentication&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;preferred_username&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;User&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyUserName&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;User Authentication&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;groups_direct&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Group&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Job authentication&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;User&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;project_path:foo/bar:ref_type:branch:ref:main&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Job authentication&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;namespace_path&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;Group&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The following &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ClusterRoleBindings&lt;/code&gt; demonstrate how to authorize the group &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt; to become cluster administration and the pipeline job as well as the user &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MyUserName&lt;/code&gt; to become a cluster viewer:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ClusterRoleBinding&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rbac.authorization.k8s.io/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;oidc-viewer&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;roleRef&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;apiGroup&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rbac.authorization.k8s.io&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ClusterRole&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;view&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;subjects&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;User&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;gitlab-ci:project_path:foo/bar:ref_type:branch:ref:main&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;User&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;gitlab:MyUserName&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ClusterRoleBinding&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rbac.authorization.k8s.io/v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;oidc-admin&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;roleRef&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;apiGroup&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rbac.authorization.k8s.io&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ClusterRole&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;cluster-admin&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;subjects&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Group&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;gitlab:foo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Depending on your use case, you may have to adjust the claim mapping to produce &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Group&lt;/code&gt; resources with the correct names.&lt;/p&gt;

&lt;h2 id=&quot;part-6-authentication-using-kubectl&quot;&gt;Part 6: Authentication using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;When using OIDC to authenticate, ID tokens have a limited lifetime requiring to refresh the token. As a consequence, a process to obtain and refresh the ID token must be implemented. For &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl&lt;/code&gt; the &lt;a href=&quot;https://github.com/int128/kubelogin&quot;&gt;plugin called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubelogin&lt;/code&gt;&lt;/a&gt; is responsible for this. The binary must be installed into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl-oidc_login&lt;/code&gt; and placed in the path.&lt;/p&gt;

&lt;p&gt;The plugin comes with a &lt;a href=&quot;https://github.com/int128/kubelogin/blob/master/docs/setup.md#2-authenticate-with-the-openid-connect-provider&quot;&gt;subcommand to setup&lt;/a&gt; the OIDC consumer. It will display the ID token in the terminal for your reference and display commands to update your kubeconfig:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl oidc-login setup &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--oidc-issuer-url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;ISSUER_URL &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--oidc-client-id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;YOUR_CLIENT_ID
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The following excerpt from the kubeconfig demonstrates how the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oidc&lt;/code&gt; user is configured to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubelogin&lt;/code&gt; plugin:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# REDACTED&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;users&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;oidc&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;client.authentication.k8s.io/v1&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kubectl&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;oidc-login&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;get-token&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;--oidc-issuer-url=ISSUER_URL&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;--oidc-client-id=YOUR_CLIENT_ID&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# REDACTED&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;By referencing the user &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;oidc&lt;/code&gt; in a context, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl&lt;/code&gt; will use the plugin to obtain an ID token from the OIDC provider, present it to the Kubernetes API server and execute the requested subcommand like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl get pods&lt;/code&gt;. It will also take care of refreshing the token when it expires.&lt;/p&gt;

&lt;h2 id=&quot;part-7-authenticating-using-id_tokens-from-gitlab-ci&quot;&gt;Part 7: Authenticating using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id_tokens&lt;/code&gt; from GitLab CI&lt;/h2&gt;

&lt;p&gt;Any pipeline job can easily request an ID token from the integrated OIDC provider using the &lt;a href=&quot;https://docs.gitlab.com/ci/yaml/#id_tokens&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;id_tokens&lt;/code&gt;&lt;/a&gt; keyword. The audience must be set using the sub-keyword &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aud&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When presenting such an ID token to Kubernetes (configured as dedscribed above), the pipeline job is effectively authenticated by the Kubernetes API server and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User&lt;/code&gt; as well as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Group&lt;/code&gt; resources are created based on the claims contained in the token.&lt;/p&gt;

&lt;p&gt;The following pipeline requests an ID token with the audience &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubernetes&lt;/code&gt; and creates a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeconfig&lt;/code&gt; to Kubernetes using the environment variables &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CONTROL_PLANE_ENDPOINT&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CONTROL_PLANE_CA&lt;/code&gt; containing the hostname and the CA certificate of the control plane, respectively.&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;demo&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;id_tokens&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;K8S_TOKEN&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;aud&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kubernetes&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;alpine&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;before_script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;apk add --update-cache \&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;kubectl&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;echo -n &quot;${CONTROL_PLANE_CA}&quot; | base64 -d &amp;gt;ca.pem&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;kubectl config set-cluster demo --server=${CONTROL_PLANE_ENDPOINT} --certificate-authority=./ca.pem&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;kubectl config set-credentials oidc-token --token=${K8S_TOKEN}&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;kubectl config set-context demo --cluster=demo --user=oidc-token --namespace=default&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;kubectl config use-context demo&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;kubectl get pods --all-namespaces&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Based on the authentication configuration presented above, Kubernetes creates a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User&lt;/code&gt; resource for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sub&lt;/code&gt; claim in the ID token which uniquely identifies the pipeline (project path and branch). The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User&lt;/code&gt; can be used for authorizing access to the cluster. The following command creates a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ClusterRoleBinding&lt;/code&gt; to grant viewer permissions to a specific pipeline:&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubectl create clusterrolebinding pipeline-foo-bar-main &lt;span class=&quot;nt&quot;&gt;--clusterrole&lt;/span&gt; view &lt;span class=&quot;nt&quot;&gt;--user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;gitlab-ci:project_path:foo/bar:ref_type:branch:ref:main
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Happy authenticating!&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;sidenote-rolling-out-the-authentication-configuration-file-using-cluster-api&quot;&gt;Sidenote: Rolling out the Authentication Configuration File using Cluster API&lt;/h2&gt;

&lt;p&gt;The authentication configuration file can also be rolled out using &lt;a href=&quot;https://cluster-api.sigs.k8s.io/&quot;&gt;Cluster API&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Add a new item under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;files&lt;/code&gt; to create &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/kubernetes/auth/auth-config.yaml&lt;/code&gt; on the node&lt;/li&gt;
  &lt;li&gt;Add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;extraVolumes&lt;/code&gt; to mount the file into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kube-apiserver&lt;/code&gt; pods&lt;/li&gt;
  &lt;li&gt;Add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;authentication-config&lt;/code&gt; under &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;extraArgs&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;controlplane.cluster.x-k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;KubeadmControlPlane&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;my-cluster&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;kubeadmConfigSpec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;clusterConfiguration&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;apiServer&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;extraVolumes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;auth-config&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;mountPath&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/etc/kubernetes/auth&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;hostPath&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/etc/kubernetes/auth&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;pathType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Directory&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;readOnly&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;extraArgs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;cloud-provider&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;external&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;authentication-config&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/etc/kubernetes/auth/auth-config.yaml&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;controllerManager&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;extraArgs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;cloud-provider&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;external&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;files&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# REDACTED&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;apiVersion: apiserver.config.k8s.io/v1beta1&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;kind: AuthenticationConfiguration&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;jwt:&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;- issuer:&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;url: https://gitlab.example.com&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;audiences:&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;- group_application_id&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;- id_token_audience&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;audienceMatchPolicy: MatchAny&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;claimMappings:&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;username:&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;expression: &apos;has(claims.preferred_username) ? &quot;gitlab:&quot; + claims.preferred_username : &quot;gitlab-ci:&quot; + claims.sub&apos;&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;groups:&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;claims: groups_direct&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;prefix: &quot;gitlab:&quot;&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;uid:&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;expression: &apos;has(claims.preferred_username) ? &quot;gitlab:&quot; + claims.preferred_username : &quot;gitlab-ci:&quot; + claims.sub&apos;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;owner&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;root:root&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;/etc/kubernetes/auth/auth-config.yaml&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;permissions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;0600&quot;&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# REDACTED&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>Nicholas Dille</name></author><category term="Haufe-Lexware" /><category term="gitlab" /><category term="kubernetes" /><category term="oidc" /><category term="authentication" /><category term="workload" /><category term="identity" /><summary type="html">OpenID Connect (OIDC) and workload identity have been hot topics for a couple of years. This post demonstrates how to use GitLab as an OIDC provider to authenticate against a Kubernetes cluster - covering interactive access by users as well as automated access from pipeline jobs. The challenge is to combine both use cases in a single configuration.</summary></entry><entry><title type="html">My contributions @ ContainerConf 2024 #CLC_Conf #GitLab #kubernetes #RBAC</title><link href="https://dille.name/blog/2024/11/12/my-contributions-at-containerconf/" rel="alternate" type="text/html" title="My contributions @ ContainerConf 2024 #CLC_Conf #GitLab #kubernetes #RBAC" /><published>2024-11-12T16:08:00-06:00</published><updated>2024-11-12T16:08:00-06:00</updated><id>https://dille.name/blog/2024/11/12/containerconf-2024</id><content type="html" xml:base="https://dille.name/blog/2024/11/12/my-contributions-at-containerconf/">&lt;p&gt;I just attended this year’s &lt;a href=&quot;https://www.containerconf.de&quot;&gt;ContainerConf&lt;/a&gt; in Mannheim. It was awesome meeting old and new friends and talking all things containers. I had the pleasure to contribute a full-day workshop about CI/CD with GitLab as well as a talk about tricks and caveats when using Kubernetes Role Based Access Control (RBAC).&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2022/11/joao-cruz-IkEpl3JkVqU-unsplash.jpg&quot; style=&quot;object-fit: cover; object-position: center 70%; width: 100%; height: 150px;&quot; /&gt;&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Find my slides and demos for the workshop &lt;a href=&quot;https://github.com/nicholasdille/container-slides/releases/tag/20241112.0&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Find my slides and demos for the talk &lt;a href=&quot;https://github.com/nicholasdille/container-slides/releases/tag/20241113.0&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;</content><author><name>Nicholas Dille</name></author><category term="Haufe-Lexware" /><category term="Docker" /><category term="Container" /><category term="Slides" /><category term="Slide Deck" /><category term="Kubernetes" /><category term="Security" /><category term="Continuous Integration" /><category term="Event" /><category term="Conference" /><category term="Talk" /><category term="Workshop" /><summary type="html">I just attended this year’s ContainerConf in Mannheim. It was awesome meeting old and new friends and talking all things containers. I had the pleasure to contribute a full-day workshop about CI/CD with GitLab as well as a talk about tricks and caveats when using Kubernetes Role Based Access Control (RBAC).</summary></entry><entry><title type="html">Workshop about operating #GitLab (German)</title><link href="https://dille.name/blog/2024/11/07/workshop-about-operating-gitlab/" rel="alternate" type="text/html" title="Workshop about operating #GitLab (German)" /><published>2024-11-07T14:00:00-06:00</published><updated>2024-11-07T14:00:00-06:00</updated><id>https://dille.name/blog/2024/11/07/workshop-gitlab-ops</id><content type="html" xml:base="https://dille.name/blog/2024/11/07/workshop-about-operating-gitlab/">&lt;p&gt;Part of my daily work is operating a GitLab instance for our development teams. Based on this experience I held a workshop for &lt;a href=&quot;https://heise-academy.de/&quot;&gt;heise Academy&lt;/a&gt; to share my knowledge.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/media/2022/03/abby-ar-1uwzsExrKzY-unsplash.jpg&quot; style=&quot;object-fit: cover; object-position: center 60%; width: 100%; height: 150px;&quot; /&gt;&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;I covered the following topics:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;Handling the web UI&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;Projects&lt;/li&gt;
      &lt;li&gt;Users&lt;/li&gt;
      &lt;li&gt;Authorization&lt;/li&gt;
      &lt;li&gt;User Profiles&lt;/li&gt;
      &lt;li&gt;Server settings&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Customizing the deployment&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;Using a reverse proxy for routing requests&lt;/li&gt;
      &lt;li&gt;Understanding the directory layout&lt;/li&gt;
      &lt;li&gt;Authenicating against LDAP&lt;/li&gt;
      &lt;li&gt;Outgoing mail through SMTP&lt;/li&gt;
      &lt;li&gt;Using the GitLab container registry&lt;/li&gt;
      &lt;li&gt;Adding integrations&lt;/li&gt;
      &lt;li&gt;Troubleshooting&lt;/li&gt;
      &lt;li&gt;Updating GitLab&lt;/li&gt;
      &lt;li&gt;Attaching runners for GitLab CI&lt;/li&gt;
      &lt;li&gt;Using GitLab Pages&lt;/li&gt;
      &lt;li&gt;Monitoring GitLab&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Find my slides &lt;a href=&quot;/slides/2024-11-07/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The slides are a result of my slide and demo build system. Take a look at the &lt;a href=&quot;https://github.com/nicholasdille/container-slides/releases/tag/20241107.0&quot;&gt;release for this event&lt;/a&gt;.&lt;/p&gt;</content><author><name>Nicholas Dille</name></author><category term="Haufe-Lexware" /><category term="Docker" /><category term="Container" /><category term="Slides" /><category term="Slide Deck" /><category term="GitLab" /><category term="Event" /><category term="Workshop" /><summary type="html">Part of my daily work is operating a GitLab instance for our development teams. Based on this experience I held a workshop for heise Academy to share my knowledge.</summary></entry></feed>