<?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>Forem: Vincent von Büren</title>
    <description>The latest articles on Forem by Vincent von Büren (@vincent_vonbren_834e9c3).</description>
    <link>https://forem.com/vincent_vonbren_834e9c3</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%2F2545787%2Fd79c1610-6025-4b9e-8efc-2c638f11a4fa.png</url>
      <title>Forem: Vincent von Büren</title>
      <link>https://forem.com/vincent_vonbren_834e9c3</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/vincent_vonbren_834e9c3"/>
    <language>en</language>
    <item>
      <title>That Time We Found a Service Account Token in my Log Files</title>
      <dc:creator>Vincent von Büren</dc:creator>
      <pubDate>Thu, 04 Sep 2025 07:59:04 +0000</pubDate>
      <link>https://forem.com/ipt/that-time-i-found-a-service-account-token-in-my-log-files-4d00</link>
      <guid>https://forem.com/ipt/that-time-i-found-a-service-account-token-in-my-log-files-4d00</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Disclaimer&lt;/strong&gt;&lt;br&gt;
This article assumes you're already somewhat familiar with Kubernetes concepts (Pods, ServiceAccounts) and the basics of JSON Web Tokens (JWTs).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It was a &lt;strong&gt;Tuesday&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Nothing special - just your average day as a platform engineer. My team's notifications were mercifully quiet, and I thought, "Perfect, I can finally clean up that old Helm chart that's been bothering me."&lt;/p&gt;

&lt;p&gt;I opened the repo of the underlying image written in Go to double-check the config before merging.&lt;/p&gt;

&lt;p&gt;Before I even got far, a colleague — Martin Odermatt — pinged me:&lt;/p&gt;

&lt;p&gt;“You might want to take a look at this…”&lt;/p&gt;

&lt;p&gt;He had spotted something concerning in the code:&lt;/p&gt;

&lt;p&gt;log.Println("SA Token:", token)&lt;/p&gt;

&lt;p&gt;Wait. What?&lt;/p&gt;

&lt;p&gt;A debug statement. Still in production code. Logging an actual Kubernetes ServiceAccount token. Not cool...&lt;/p&gt;

&lt;p&gt;I paused. My heart rate didn’t. Curious but mostly horrified, I took the token Martin had found and decoded the payload in my shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"iss"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://kubernetes.default.svc.cluster.local"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"kubernetes.io/serviceaccount/namespace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"payments"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"kubernetes.io/serviceaccount/secret.name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"payments-token-6gh49"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"kubernetes.io/serviceaccount/service-account.name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"payments-sa"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"kubernetes.io/serviceaccount/service-account.uid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"f9a2c144-11b3-4eb0-9f30-3c2a5063e2e7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"aud"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://kubernetes.default.svc.cluster.local"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"system:serviceaccount:payments:payments-sa"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"exp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1788201600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Sat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Aug&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;GMT&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"iat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1756665600&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Fri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Aug&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;GMT&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Default audience claim. A 1-year expiry.&lt;/p&gt;

&lt;p&gt;As we dug deeper, Martin Odermatt pointed out the underlying issue: this was a legacy ServiceAccount token, and we should be using projected tokens instead.&lt;/p&gt;

&lt;p&gt;This "bad boy" wasn't just a dev leftover - it was a high-privilege token with zero constraints floating around in plaintext logs!&lt;/p&gt;




&lt;h3&gt;
  
  
  What This Article Covers
&lt;/h3&gt;

&lt;p&gt;In this post, I'll guide you through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The inner workings of Vault authentication with JWT and Kubernetes methods&lt;/li&gt;
&lt;li&gt;What Kubernetes ServiceAccounts and their tokens are, and how they’re (mis)used&lt;/li&gt;
&lt;li&gt;How projected ServiceAccount tokens fix many of the hidden dangers of older token behavior&lt;/li&gt;
&lt;li&gt;Why you should start adopting token projection and Vault integration today&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll cover real-world use cases, implementation tips, and common pitfalls - so you don't end up like I did, staring at a:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SA token:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and wondering how close you just came to a security incident.&lt;/p&gt;




&lt;h3&gt;
  
  
  Why This Matters
&lt;/h3&gt;

&lt;p&gt;To really understand why that log statement gave me chills, we need to unpack a few core concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is a JWT?&lt;/li&gt;
&lt;li&gt;How do Kubernetes ServiceAccounts and their tokens work?&lt;/li&gt;
&lt;li&gt;And what role do these tokens play in authenticating to systems like Vault?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's start with the fundamentals.&lt;/p&gt;




&lt;h3&gt;
  
  
  What Is a JWT?
&lt;/h3&gt;

&lt;p&gt;If you've been around authentication systems long enough, you've probably seen one of these beasts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;eyJhbGciOiJSUzI&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="err"&gt;NiIsInR&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="err"&gt;cCI&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="err"&gt;IkpXVCJ&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;This is a JSON Web Token (short: JWT).&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's a compact, URL-safe format for representing claims between two parties. They're used everywhere: web apps, APIs, and yes — inside your Kubernetes cluster.&lt;/p&gt;

&lt;p&gt;A JWT consists of three parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Header&lt;/strong&gt; – declares the algorithm used to sign the token (e.g. RS256)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payload&lt;/strong&gt; – contains the claims (who you are, what you're allowed to do, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signature&lt;/strong&gt; – a cryptographic seal that verifies the payload hasn't been tampered with&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Claims are the heart of a JWT — key-value pairs that describe who the token refers to and what it can be used for. &lt;/p&gt;

&lt;p&gt;They can be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Standard claims defined by the spec (e.g., &lt;code&gt;iss&lt;/code&gt;, &lt;code&gt;sub&lt;/code&gt;, &lt;code&gt;exp&lt;/code&gt;, &lt;code&gt;aud&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Custom claims added by the issuer for domain-specific needs&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Closer Look at &lt;code&gt;aud&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;audience&lt;/strong&gt; (&lt;code&gt;aud&lt;/code&gt;) claim tells &lt;strong&gt;who the token is meant for&lt;/strong&gt;. Think of it as the intended recipient.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Imagine a Coldplay concert ticket. It says &lt;em&gt;valid for Stadium X on 01-09-2025&lt;/em&gt;. You can't take the same ticket and use it at Stadium Y — they'll reject it (...trust me, I tried).&lt;/p&gt;

&lt;p&gt;A JWT works the same way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the token has &lt;code&gt;"aud": "https://kubernetes.default.svc"&lt;/code&gt;, then only the Kubernetes API server should accept it.&lt;/li&gt;
&lt;li&gt;If some other service receives that token, the &lt;code&gt;aud&lt;/code&gt; won't match and the token must be rejected.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without this check, a token could be misused anywhere that trusts the signing key. With &lt;code&gt;aud&lt;/code&gt;, it's scoped to the right system.&lt;/p&gt;




&lt;h3&gt;
  
  
  Kubernetes and ServiceAccounts
&lt;/h3&gt;

&lt;p&gt;Kubernetes is an open-source platform that orchestrates containers at scale. At its heart is the &lt;strong&gt;Pod&lt;/strong&gt; — the smallest deployable unit.&lt;/p&gt;

&lt;p&gt;But every pod needs an identity. That's where &lt;strong&gt;ServiceAccounts&lt;/strong&gt; come in.&lt;/p&gt;

&lt;h4&gt;
  
  
  ServiceAccounts 101
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Every Pod references a ServiceAccount (default if none is set), but a token is only mounted if enabled&lt;/li&gt;
&lt;li&gt;Kubernetes mounts the identity at:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/var/run/secrets/kubernetes.io/serviceaccount/token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;That token is a &lt;strong&gt;JWT&lt;/strong&gt;, signed by the Kubernetes control plane&lt;/li&gt;
&lt;li&gt;It lets the pod authenticate with the API server — and sometimes even external systems like Vault&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  The Catch
&lt;/h4&gt;

&lt;p&gt;Until recently, these tokens came with dangerous defaults:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Long-lived (often valid for a year)&lt;/li&gt;
&lt;li&gt;Previous to Kubernetes v1.24, there was no default audience set (&lt;a href="https://kubernetes.default.svc" rel="noopener noreferrer"&gt;https://kubernetes.default.svc&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Automatically mounted into every pod, even if unused&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Enter Vault: The Gatekeeper of Secrets
&lt;/h3&gt;

&lt;p&gt;HashiCorp Vault is your cluster’s paranoid librarian:&lt;br&gt;
it stores API keys, certs, passwords — and only hands them out when it's sure you should have them.&lt;/p&gt;

&lt;p&gt;How? &lt;strong&gt;Authentication methods.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Vault Authentication Methods
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Username &amp;amp; password&lt;/li&gt;
&lt;li&gt;AppRole&lt;/li&gt;
&lt;li&gt;LDAP&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kubernetes&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JWT&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's zoom into the last two.&lt;/p&gt;


&lt;h4&gt;
  
  
  Kubernetes Auth Method
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Pod sends its mounted ServiceAccount token to Vault&lt;/li&gt;
&lt;li&gt;Vault validates it against the Kubernetes API&lt;/li&gt;
&lt;li&gt;If valid, Vault maps it to a policy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is simple and works well when Vault runs inside the cluster.&lt;/p&gt;


&lt;h4&gt;
  
  
  JWT Auth Method
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Vault verifies the JWT itself (signature, claims, expiration)&lt;/li&gt;
&lt;li&gt;No need for Kubernetes API access&lt;/li&gt;
&lt;li&gt;More portable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Rule of thumb:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;Kubernetes&lt;/strong&gt; if Vault runs inside your cluster and simplicity matters&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;JWT&lt;/strong&gt; if you want portability, stronger boundaries, and flexibility&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  Projected Tokens: Because It's 2025
&lt;/h3&gt;

&lt;p&gt;Old tokens were static and long-lived — exactly what we were looking at here. As Martin pointed out during the investigation, projected tokens are designed to fix this entire class of problems.&lt;/p&gt;

&lt;p&gt;Instead of mounting a one-year token into every pod, Kubernetes can now generate &lt;strong&gt;short-lived, audience-bound tokens on demand.&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  What You Get
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Short TTL (e.g. 10 minutes)&lt;/li&gt;
&lt;li&gt;Audience restrictions (&lt;code&gt;aud: vault&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Automatic rotation by &lt;code&gt;kubelet&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;No automatic mounting into pods&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Example Pod with Projected Token
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pod&lt;/span&gt;
&lt;span class="na"&gt;metadata&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;projected-token-test-pod&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;serviceAccountName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;projected-auth-sa&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&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;projected-auth-test&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo/vault-curl:latest&lt;/span&gt;
      &lt;span class="na"&gt;command&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;sleep"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3600"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&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;token&lt;/span&gt;
          &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/run/secrets/projected&lt;/span&gt;
          &lt;span class="na"&gt;readOnly&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&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;token&lt;/span&gt;
      &lt;span class="na"&gt;projected&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;serviceAccountToken&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;token&lt;/span&gt;
              &lt;span class="na"&gt;expirationSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600&lt;/span&gt;
              &lt;span class="na"&gt;audience&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vault&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why Vault Loves This
&lt;/h3&gt;

&lt;p&gt;Vault's JWT auth method is tailor-made for projected tokens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It parses and verifies the JWT signature (via a configured PEM key or JWKS endpoint)&lt;/li&gt;
&lt;li&gt;Validates all claims (&lt;code&gt;aud&lt;/code&gt;, &lt;code&gt;sub&lt;/code&gt;, &lt;code&gt;exp&lt;/code&gt;, &lt;code&gt;iss&lt;/code&gt;) locally&lt;/li&gt;
&lt;li&gt;Issues secrets only if every check passes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Minimal dependencies. Strong claim validation. Secure, verifiable checks.&lt;/p&gt;


&lt;h3&gt;
  
  
  Back to the Log
&lt;/h3&gt;

&lt;p&gt;Imagine you stumble upon this in a Go app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Auth Token:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Old world:&lt;/strong&gt; a one-year, cluster-wide token with no audience. A time bomb.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New world:&lt;/strong&gt; a 10-minute token, scoped to Vault, rotating automatically.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's still bad to log tokens — but at least it's not catastrophic.&lt;/p&gt;




&lt;h3&gt;
  
  
  Try It Yourself: Vault + K8s AuthN Lab
&lt;/h3&gt;

&lt;p&gt;I've built a hands-on demo repo where you can test this locally with KIND (Kubernetes in Docker) and Vault Helm charts:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/VincentvonBueren/erfa-projected-sa-token" rel="noopener noreferrer"&gt;GitHub: VincentvonBueren/erfa-projected-sa-token&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  What's Inside
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;KIND cluster with Vault&lt;/li&gt;
&lt;li&gt;Both Kubernetes and JWT auth methods enabled&lt;/li&gt;
&lt;li&gt;Vault policies and roles&lt;/li&gt;
&lt;li&gt;Four demo pods:

&lt;ul&gt;
&lt;li&gt;Kubernetes auth method&lt;/li&gt;
&lt;li&gt;JWT with static token&lt;/li&gt;
&lt;li&gt;JWT with projected token&lt;/li&gt;
&lt;li&gt;JWT with wrong audience (failure demo)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  Final Drop 🎤
&lt;/h3&gt;

&lt;p&gt;If your pods still run with default, long-lived tokens:&lt;br&gt;
you’re one debug log away from giving away the keys to your cluster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Projected tokens aren't optional. They're essential.&lt;/strong&gt;&lt;br&gt;
Adopt them today — and stop shipping security disasters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Acknowledgment
&lt;/h3&gt;

&lt;p&gt;The discovery of the exposed ServiceAccount token — and the push towards using projected tokens — came from my dear fellow engineer Martin Odermatt, whose input significantly shaped this investigation and motivated me to tell this story.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>serviceaccount</category>
      <category>security</category>
      <category>jwt</category>
    </item>
  </channel>
</rss>
