<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Security on Kinoko's TIL Log</title><link>https://kinoko-tech-blog-theta.vercel.app/tags/security/</link><description>Recent content in Security on Kinoko's TIL Log</description><generator>Hugo -- gohugo.io</generator><language>en</language><managingEditor>pippimotta@gmail.com (Kinoko)</managingEditor><webMaster>pippimotta@gmail.com (Kinoko)</webMaster><copyright>&amp;copy; Brewed by Kinoko</copyright><lastBuildDate>Tue, 17 Mar 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://kinoko-tech-blog-theta.vercel.app/tags/security/index.xml" rel="self" type="application/rss+xml"/><item><title>RBAC Model</title><link>https://kinoko-tech-blog-theta.vercel.app/posts/rbac-model/</link><pubDate>Tue, 17 Mar 2026 00:00:00 +0000</pubDate><author>pippimotta@gmail.com (Kinoko)</author><guid>https://kinoko-tech-blog-theta.vercel.app/posts/rbac-model/</guid><description>&lt;h2 id="the-point"&gt;The Point&lt;/h2&gt;
&lt;p&gt;RBAC (Role-Based Access Control) is a permissions model where you first define what roles can do, then assign roles to people. K8s uses RBAC to control who can perform which operations on which resources. CD tools use RBAC to control who can deploy to which environment.&lt;/p&gt;
&lt;h2 id="explanation"&gt;Explanation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Three core elements of RBAC&lt;/strong&gt;&lt;/p&gt;





&lt;pre tabindex="0"&gt;&lt;code&gt;Subject (who) → RoleBinding (assignment) → Role (what they can do)&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Subject&lt;/strong&gt;: a user, group, or service account (programmatic identity)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Role&lt;/strong&gt;: defines a set of permissions (which operations on which resources)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RoleBinding&lt;/strong&gt;: assigns a Role to a Subject&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Intuitive analogy: a Role is a job title (engineer, manager), a RoleBinding is the &amp;ldquo;appointment letter,&amp;rdquo; and the Subject is the person being appointed.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="the-point">The Point</h2>
<p>RBAC (Role-Based Access Control) is a permissions model where you first define what roles can do, then assign roles to people. K8s uses RBAC to control who can perform which operations on which resources. CD tools use RBAC to control who can deploy to which environment.</p>
<h2 id="explanation">Explanation</h2>
<p><strong>Three core elements of RBAC</strong></p>





<pre tabindex="0"><code>Subject (who)  →  RoleBinding (assignment)  →  Role (what they can do)</code></pre><ul>
<li><strong>Subject</strong>: a user, group, or service account (programmatic identity)</li>
<li><strong>Role</strong>: defines a set of permissions (which operations on which resources)</li>
<li><strong>RoleBinding</strong>: assigns a Role to a Subject</li>
</ul>
<p>Intuitive analogy: a Role is a job title (engineer, manager), a RoleBinding is the &ldquo;appointment letter,&rdquo; and the Subject is the person being appointed.</p>
<hr>
<p><strong>RBAC in Kubernetes</strong></p>
<p>K8s RBAC controls &ldquo;who can do what against the K8s API.&rdquo;</p>
<p><strong>Resources &amp; Verbs</strong></p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="nt">rules</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl">- <span class="nt">apiGroups</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">  </span><span class="nt">resources</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;pods&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;services&#34;</span><span class="p">]</span><span class="w">   </span><span class="c"># resource types</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">  </span><span class="nt">verbs</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;get&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;list&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;watch&#34;</span><span class="p">]</span><span class="w">   </span><span class="c"># allowed operations</span></span></span></code></pre></div><p>Common verbs: <code>get</code> <code>list</code> <code>watch</code> <code>create</code> <code>update</code> <code>patch</code> <code>delete</code></p>
<p><strong>Role vs ClusterRole</strong></p>
<table>
	<thead>
			<tr>
					<th></th>
					<th>Role</th>
					<th>ClusterRole</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Scope</td>
					<td>Single namespace</td>
					<td>Entire cluster</td>
			</tr>
			<tr>
					<td>Best for</td>
					<td>Restrict a team to their own namespace</td>
					<td>Cross-namespace or cluster-level resources (nodes, PVs)</td>
			</tr>
	</tbody>
</table>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># Role: only effective in the production namespace</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Role</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">production</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">pod-reader</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nt">rules</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl">- <span class="nt">apiGroups</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">  </span><span class="nt">resources</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;pods&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">  </span><span class="nt">verbs</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">&#34;get&#34;</span><span class="p">,</span><span class="w"> </span><span class="s2">&#34;list&#34;</span><span class="p">]</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="nn">---</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c"># RoleBinding: assign this Role to alice</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">RoleBinding</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">production</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="nt">subjects</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl">- <span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">User</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">alice</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="nt">roleRef</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">  </span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Role</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">pod-reader</span></span></span></code></pre></div><p><strong>Service Account: identity for programs</strong></p>
<p>Programs running in pods (like a CD tool agent) are not people. K8s uses Service Accounts to give them an identity, then uses RoleBinding to control what they can do:</p>





<pre tabindex="0"><code>CD tool agent (Pod)
  → uses ServiceAccount: octopus-agent
  → RoleBinding → ClusterRole: deploy-permissions
  → can apply manifests, update Deployments</code></pre><hr>
<p><strong>RBAC in CD Tools (Octopus)</strong></p>
<p>The CD tool has its own RBAC layer, controlling &ldquo;who can operate which project / environment&rdquo;:</p>





<pre tabindex="0"><code>Engineer alice
  → belongs to Team: backend-team
  → Team is assigned Role: deployer (can deploy, cannot change project settings)
  → restricted to Environment: dev, staging (cannot touch prod)</code></pre><p>Typical role hierarchy:</p>
<table>
	<thead>
			<tr>
					<th>Role</th>
					<th>Permissions</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Viewer</td>
					<td>Can only view deploy status</td>
			</tr>
			<tr>
					<td>Deployer</td>
					<td>Can trigger deploys</td>
			</tr>
			<tr>
					<td>Project Lead</td>
					<td>Can modify deploy settings</td>
			</tr>
			<tr>
					<td>Admin</td>
					<td>Full control</td>
			</tr>
	</tbody>
</table>
<h2 id="knowledge-sugar">Knowledge Sugar</h2>
<p><strong>Why does K8s need RBAC?</strong></p>
<p>The K8s API can do a lot &ndash; delete Pods, modify Secrets, scale Deployments. Without access control, anyone or any program that can connect to the cluster can do anything. RBAC lets you:</p>
<ul>
<li>Restrict devs to only operate within their own namespace</li>
<li>Give CI/CD agents only manifest apply permission, not Secret deletion</li>
<li>Give SRE cluster-level read access without prod write access</li>
</ul>
<p><strong>Principle of Least Privilege</strong></p>
<p>The design philosophy behind RBAC: give each subject only the minimum permissions needed to do its job. If a CD tool agent only needs <code>apply</code>, do not give it <code>delete</code>.</p>
<p>For CD tool background, see the WarpCD vs Octopus Deploy post.</p>
]]></content:encoded></item><item><title>OIDC Federation from GKE to AWS S3</title><link>https://kinoko-tech-blog-theta.vercel.app/posts/oidc-gke-to-aws-s3/</link><pubDate>Tue, 24 Feb 2026 00:00:00 +0000</pubDate><author>pippimotta@gmail.com (Kinoko)</author><guid>https://kinoko-tech-blog-theta.vercel.app/posts/oidc-gke-to-aws-s3/</guid><description>&lt;h2 id="the-point"&gt;The Point&lt;/h2&gt;
&lt;p&gt;An application deployed on GKE can use OIDC federation to exchange a Kubernetes-issued JWT token for temporary AWS credentials, eliminating the need to hardcode AWS Access Keys.&lt;/p&gt;
&lt;h2 id="explanation"&gt;Explanation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;What OIDC does in this scenario&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;OIDC (OpenID Connect) is essentially a protocol for &amp;ldquo;let me prove this identity is real.&amp;rdquo; In this use case, GKE acts as the &lt;strong&gt;OIDC Identity Provider (IdP)&lt;/strong&gt;, and AWS is the party that &lt;strong&gt;trusts what GKE says&lt;/strong&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<h2 id="the-point">The Point</h2>
<p>An application deployed on GKE can use OIDC federation to exchange a Kubernetes-issued JWT token for temporary AWS credentials, eliminating the need to hardcode AWS Access Keys.</p>
<h2 id="explanation">Explanation</h2>
<p><strong>What OIDC does in this scenario</strong></p>
<p>OIDC (OpenID Connect) is essentially a protocol for &ldquo;let me prove this identity is real.&rdquo; In this use case, GKE acts as the <strong>OIDC Identity Provider (IdP)</strong>, and AWS is the party that <strong>trusts what GKE says</strong>.</p>
<p>The overall flow looks like this:</p>





<pre tabindex="0"><code>GKE Pod
  │
  │ 1. Kubernetes automatically mounts a Service Account JWT token on the Pod
  │    (this token contains the workload&#39;s identity info, signed by GKE)
  │
  ▼
AWS STS (AssumeRoleWithWebIdentity)
  │
  │ 2. Submit the JWT token to AWS STS
  │    AWS validates the token against GKE&#39;s OIDC endpoint
  │
  ▼
Temporary AWS credentials (Access Key + Secret + Session Token)
  │
  │ 3. Use temporary credentials to operate on S3
  │
  ▼
S3 Bucket ✓</code></pre><pre class="mermaid">sequenceDiagram
    participant Pod as GKE Pod
    participant STS as AWS STS
    participant OIDC as GKE OIDC
    participant S3

    Note over Pod: K8s mounts JWT token
    Pod->>STS: AssumeRoleWithWebIdentity(JWT)
    STS->>OIDC: Verify signature
    OIDC-->>STS: OK
    STS-->>Pod: Temporary credentials (with TTL)
    Pod->>S3: Upload CSV
    S3-->>Pod: 200 OK
</pre>

<p><strong>What needs to be configured in Terraform</strong></p>
<ol>
<li><strong>AWS IAM OIDC Provider</strong> &ndash; tells AWS &ldquo;I trust tokens issued by this GKE cluster&rdquo;; requires the GKE OIDC issuer URL and thumbprint</li>
<li><strong>AWS IAM Role + Trust Policy</strong> &ndash; defines which GKE service account can assume this role; the condition typically requires the <code>sub</code> claim to match <code>system:serviceaccount:&lt;namespace&gt;:&lt;ksa-name&gt;</code></li>
<li><strong>S3 Permission</strong> &ndash; attach the S3 read/write policy to this IAM Role</li>
</ol>
<p><strong>Go STS package upgrade</strong></p>
<p>In the Go application, use <code>AssumeRoleWithWebIdentity</code>, passing in the JWT token path read from the Pod (typically <code>/var/run/secrets/kubernetes.io/serviceaccount/token</code>). After exchanging for temporary credentials, initialize the S3 client. The package upgrade was mainly to ensure compatibility with SDK v2&rsquo;s credential provider interface.</p>
<h2 id="knowledge-sugar">Knowledge Sugar</h2>
<p><strong>Why not just use AWS Access Keys?</strong>
Hardcoding keys has rotation issues, leakage risks, and makes it hard to audit which workload is accessing what. OIDC federation&rsquo;s temporary credentials have a TTL and expire automatically, making them significantly more secure.</p>
<p><strong>What&rsquo;s inside the JWT token?</strong>
The token issued by GKE is a standard JWT. The payload contains:</p>
<ul>
<li><code>iss</code>: OIDC issuer (the GKE cluster&rsquo;s URL)</li>
<li><code>sub</code>: <code>system:serviceaccount:&lt;namespace&gt;:&lt;name&gt;</code></li>
<li><code>exp</code>: expiration time</li>
</ul>
<p>When AWS STS receives the token, it fetches the public key from the <code>iss</code> URL&rsquo;s <code>/.well-known/openid-configuration</code> endpoint to verify the signature.</p>
<p><strong>How is the JWT token mounted into the Pod?</strong>
This is a built-in Kubernetes mechanism &ndash; every Pod automatically gets a Service Account token mounted at creation, no extra config needed. However, the default token doesn&rsquo;t specify an <code>audience</code>, which AWS STS won&rsquo;t accept. So you need to mount a dedicated one using a projected volume in the Pod spec:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">aws-token</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="nt">projected</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">      </span><span class="nt">sources</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">        </span>- <span class="nt">serviceAccountToken</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w">            </span><span class="nt">audience</span><span class="p">:</span><span class="w"> </span><span class="l">sts.amazonaws.com</span><span class="w"> </span><span class="c"># here!</span><span class="w">
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="w">            </span><span class="nt">expirationSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">3600</span><span class="w">
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="w">            </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">token</span></span></span></code></pre></div><p>Terraform only handles the AWS side (OIDC Provider, IAM Role); the token mounting itself is done by Kubernetes.</p>
<p><strong>The official name for this pattern: Workload Identity Federation</strong>
GCP&rsquo;s own Workload Identity uses the same principle, just with GCP resources. For cross-cloud scenarios (GKE -&gt; AWS), OIDC serves as the universal standard that bridges them.</p>
]]></content:encoded></item></channel></rss>