<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Encryption | FirewallSync</title><link>https://firewallsync.com/tags/encryption/</link><description>Practical cybersecurity writing for engineering teams that ship fast.</description><language>en-us</language><image><url>https://firewallsync.com/icon-512.png</url><title>FirewallSync</title><link>https://firewallsync.com/</link></image><lastBuildDate>Fri, 23 Oct 2026 04:45:00 +0000</lastBuildDate><atom:link href="https://firewallsync.com/tags/encryption/index.xml" rel="self" type="application/rss+xml"/><item><title>Kubernetes Secrets Encryption at Rest: What It Protects</title><link>https://firewallsync.com/posts/kubernetes-secrets-encryption-at-rest-what-it-protects/</link><guid>https://firewallsync.com/posts/kubernetes-secrets-encryption-at-rest-what-it-protects/</guid><pubDate>Fri, 23 Oct 2026 04:45:00 +0000</pubDate><dc:creator>Craig Leslie</dc:creator><category>Cloud Security</category><description>Kubernetes stores Secrets unencrypted in etcd unless you configure an EncryptionConfiguration. Turning it on protects etcd data, but not against anyone who can read Secrets through the API, create Pods, or, with local keys, read the control plane host. This guide maps each threat to the control that actually stops it, with a tested config checker.</description><enclosure url="https://firewallsync.com/posts/kubernetes-secrets-encryption-at-rest-what-it-protects/cover_hu14419892505578868447.jpg" type="image/jpeg" length="0"/><media:content url="https://firewallsync.com/posts/kubernetes-secrets-encryption-at-rest-what-it-protects/cover_hu14419892505578868447.jpg" medium="image" type="image/jpeg" width="1200" height="630"/><content:encoded><![CDATA[<p><img src="https://firewallsync.com/posts/kubernetes-secrets-encryption-at-rest-what-it-protects/cover_hu14419892505578868447.jpg" alt="Rows of ornate brass deposit boxes with keyholes and numbers 564 to 624, viewed at an angle" width="1200" height="630"></p><p>Kubernetes encryption at rest protects one thing: the Secret data stored in etcd. It stops someone who gets etcd&rsquo;s data, or a copy of it, from reading Secrets in plain text. It does not stop anyone the API server is willing to serve, and that group is larger than most teams assume: everyone with <code>get</code>, <code>list</code> or <code>watch</code> on Secrets, and everyone who can create a Pod in the namespace. Turn encryption on, but don&rsquo;t count it as your Secrets access control.</p>
<h2 id="what-kubernetes-does-by-default">What Kubernetes does by default</h2>
<p>The Kubernetes documentation is blunt: &ldquo;Kubernetes Secrets are, by default, stored unencrypted in the API server&rsquo;s underlying data store (etcd).&rdquo; The default provider is <code>identity</code>, which, in the docs&rsquo; words, &ldquo;does not encrypt stored data and provides <em>no</em> additional confidentiality protection.&rdquo;</p>
<p>The base64 you see in a Secret manifest doesn&rsquo;t change that. The good-practices page says it plainly: &ldquo;Base64 encoding is <em>not</em> an encryption method, it provides no additional confidentiality over plain text.&rdquo; A Secret manifest committed to Git is a plain-text credential in Git.</p>
<p>Managed Kubernetes services run the control plane for you, and their defaults may differ from upstream. This article describes upstream behavior; check your provider&rsquo;s documentation for what it encrypts by default and whether it supports envelope encryption with your own key.</p>
<h2 id="who-can-read-a-secret">Who can read a Secret</h2>
<p>Before choosing an encryption provider, list who can read Secrets at all. Encryption at rest changes only the last two rows of this table.</p>
<table>
  <thead>
      <tr>
          <th>Who</th>
          <th>How they read Secrets</th>
          <th>Does encryption at rest stop it?</th>
          <th>Control that does</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>A user or service account with <code>get</code> on Secrets</td>
          <td>Asks the API server</td>
          <td>No; the API server decrypts for them</td>
          <td>Least-privilege RBAC</td>
      </tr>
      <tr>
          <td>A subject with <code>list</code> or <code>watch</code> on Secrets</td>
          <td>A list response includes the contents of every Secret returned</td>
          <td>No</td>
          <td>Restrict <code>list</code>/<code>watch</code> to system components</td>
      </tr>
      <tr>
          <td>Anyone who can create Pods, Deployments or other workloads in the namespace</td>
          <td>Mounts any Secret in that namespace into a Pod they control</td>
          <td>No</td>
          <td>Separate namespaces by trust; RBAC on workload creation</td>
      </tr>
      <tr>
          <td>Someone with access to etcd, its disks or a copy of its data</td>
          <td>Reads stored bytes directly</td>
          <td><strong>Yes</strong></td>
          <td>Encryption at rest; restrict etcd access</td>
      </tr>
      <tr>
          <td>Someone with root on a control plane host</td>
          <td>Reads etcd data <em>and</em> the local key file</td>
          <td>Only with KMS</td>
          <td>KMS provider; host hardening</td>
      </tr>
  </tbody>
</table>
<p>The middle row surprises people. The Secret documentation says &ldquo;anyone who is authorized to create a Pod in a namespace can use that access to read any Secret in that namespace; this includes indirect access such as the ability to create a Deployment.&rdquo; Our <a href="/posts/kubernetes-rbac-audit-11-permissions-that-escalate-access/">Kubernetes RBAC audit</a> covers how to find these grants in your bindings; this article stays on the storage side.</p>
<h2 id="how-encryption-at-rest-is-configured">How encryption at rest is configured</h2>
<p>You pass an <code>EncryptionConfiguration</code> file to <code>kube-apiserver</code> with <code>--encryption-provider-config</code>. A minimal example for Secrets with a local AES-CBC key, shaped like the one in the Kubernetes task page:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">apiserver.config.k8s.io/v1</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">EncryptionConfiguration</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">resources</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">resources</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#ae81ff">secrets</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">providers</span>:
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">aescbc</span>:
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">keys</span>:
</span></span><span style="display:flex;"><span>            - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">key1</span>
</span></span><span style="display:flex;"><span>              <span style="color:#f92672">secret</span>: <span style="color:#ae81ff">&lt;BASE64-ENCODED 32-BYTE KEY&gt;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">identity</span>: {} <span style="color:#75715e"># lets the API server read Secrets written before encryption</span>
</span></span></code></pre></div><p>Three rules from the documentation decide what this file does:</p>
<ul>
<li><strong>The first provider encrypts.</strong> &ldquo;The first provider in the list is used to encrypt resources written into the storage.&rdquo; If <code>identity</code> is first, new writes are plain text even if other providers are listed.</li>
<li><strong>Later providers only decrypt.</strong> On reads, each matching provider is tried in order. The trailing <code>identity</code> is what lets the API server keep reading old plain-text Secrets during a migration. Once everything is rewritten, you can remove it.</li>
<li><strong>Order across rules matters too.</strong> The <code>resources</code> list is processed in order, so an earlier rule that names <code>secrets</code> with <code>identity</code> will win over a later <code>*.*</code> wildcard that encrypts. Wildcards are <code>*.</code> for the core group (which includes Secrets) and <code>*.*</code> for everything.</li>
</ul>
<h2 id="which-provider-stops-which-attacker">Which provider stops which attacker</h2>
<table>
  <thead>
      <tr>
          <th>Provider</th>
          <th>Key location</th>
          <th>Kubernetes docs say</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>identity</code></td>
          <td>None</td>
          <td>No encryption; the default</td>
      </tr>
      <tr>
          <td><code>aescbc</code></td>
          <td>Local file on each control plane host</td>
          <td>Not recommended: CBC is vulnerable to padding oracle attacks</td>
      </tr>
      <tr>
          <td><code>aesgcm</code></td>
          <td>Local file</td>
          <td>Not recommended unless you automate key rotation</td>
      </tr>
      <tr>
          <td><code>secretbox</code></td>
          <td>Local file</td>
          <td>Newer cryptography that may not pass high-review environments</td>
      </tr>
      <tr>
          <td><code>kms</code> v1</td>
          <td>External KMS</td>
          <td>Deprecated since Kubernetes v1.28</td>
      </tr>
      <tr>
          <td><code>kms</code> v2</td>
          <td>External KMS</td>
          <td>Strongest; envelope encryption with AES-GCM data keys; stable from v1.29</td>
      </tr>
  </tbody>
</table>
<p>The key-location column is the one that maps to threats. With a local key, the docs say encryption &ldquo;protects against an etcd compromise, but it fails to protect against a host compromise,&rdquo; because the key sits in the configuration file on the host. With KMS, Kubernetes encrypts data with a data encryption key and the KMS wraps that key, so an attacker &ldquo;would need to compromise etcd <strong>and</strong> the third-party KMS provider.&rdquo;</p>
<p>A local key is still worth having. It turns a leaked etcd snapshot or a mis-scoped etcd client certificate into ciphertext rather than credentials. Our view: use KMS v2 where you have a KMS to point at, and treat a local <code>aescbc</code> key as a stopgap you plan to replace.</p>
<h2 id="checking-a-configuration-file">Checking a configuration file</h2>
<p>We wrote a short checker that reads an <code>EncryptionConfiguration</code> and flags the weak settings above. It reads only the file and does not contact a cluster. It needs Python 3 and PyYAML; we ran it with Python 3 and PyYAML 6.0.3 against three sample files. It is illustrative: it does not validate the whole schema, and it reads <code>kms</code> without an <code>apiVersion</code> as v1.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#75715e">#!/usr/bin/env python3</span>
</span></span><span style="display:flex;"><span><span style="color:#e6db74">&#34;&#34;&#34;Flag weak settings in a Kubernetes EncryptionConfiguration file.
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">Usage: python3 check_encryption_config.py /path/to/encryption-config.yaml
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">Reads the file only; it does not contact a cluster.&#34;&#34;&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> sys
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> yaml
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>LOCAL_KEY <span style="color:#f92672">=</span> {<span style="color:#e6db74">&#34;aescbc&#34;</span>, <span style="color:#e6db74">&#34;aesgcm&#34;</span>, <span style="color:#e6db74">&#34;secretbox&#34;</span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">covers_secrets</span>(resources):
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> any(r <span style="color:#f92672">in</span> (<span style="color:#e6db74">&#34;secrets&#34;</span>, <span style="color:#e6db74">&#34;*.&#34;</span>, <span style="color:#e6db74">&#34;*.*&#34;</span>) <span style="color:#66d9ef">for</span> r <span style="color:#f92672">in</span> resources)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">def</span> <span style="color:#a6e22e">main</span>(path):
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">with</span> open(path) <span style="color:#66d9ef">as</span> f:
</span></span><span style="display:flex;"><span>        cfg <span style="color:#f92672">=</span> yaml<span style="color:#f92672">.</span>safe_load(f)
</span></span><span style="display:flex;"><span>    findings <span style="color:#f92672">=</span> []
</span></span><span style="display:flex;"><span>    secrets_rule_seen <span style="color:#f92672">=</span> <span style="color:#66d9ef">False</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> i, rule <span style="color:#f92672">in</span> enumerate(cfg<span style="color:#f92672">.</span>get(<span style="color:#e6db74">&#34;resources&#34;</span>, [])):
</span></span><span style="display:flex;"><span>        res <span style="color:#f92672">=</span> rule<span style="color:#f92672">.</span>get(<span style="color:#e6db74">&#34;resources&#34;</span>, [])
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> covers_secrets(res):
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">continue</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e"># The first matching rule wins, so only the first rule covering secrets counts.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> secrets_rule_seen:
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">continue</span>
</span></span><span style="display:flex;"><span>        secrets_rule_seen <span style="color:#f92672">=</span> <span style="color:#66d9ef">True</span>
</span></span><span style="display:flex;"><span>        providers <span style="color:#f92672">=</span> rule<span style="color:#f92672">.</span>get(<span style="color:#e6db74">&#34;providers&#34;</span>, [])
</span></span><span style="display:flex;"><span>        first <span style="color:#f92672">=</span> next(iter(providers[<span style="color:#ae81ff">0</span>])) <span style="color:#66d9ef">if</span> providers <span style="color:#66d9ef">else</span> <span style="color:#66d9ef">None</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> first <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;identity&#34;</span>:
</span></span><span style="display:flex;"><span>            findings<span style="color:#f92672">.</span>append(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;rule </span><span style="color:#e6db74">{</span>i<span style="color:#e6db74">}</span><span style="color:#e6db74">: first provider is identity, so new Secrets are written in plain text&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">elif</span> first <span style="color:#f92672">in</span> LOCAL_KEY:
</span></span><span style="display:flex;"><span>            findings<span style="color:#f92672">.</span>append(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;rule </span><span style="color:#e6db74">{</span>i<span style="color:#e6db74">}</span><span style="color:#e6db74">: first provider is </span><span style="color:#e6db74">{</span>first<span style="color:#e6db74">}</span><span style="color:#e6db74">; key sits in this file on every control plane host&#34;</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span> first <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;aescbc&#34;</span>:
</span></span><span style="display:flex;"><span>                findings<span style="color:#f92672">.</span>append(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;rule </span><span style="color:#e6db74">{</span>i<span style="color:#e6db74">}</span><span style="color:#e6db74">: aescbc is not recommended by the Kubernetes docs (padding oracle)&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">elif</span> first <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;kms&#34;</span>:
</span></span><span style="display:flex;"><span>            ver <span style="color:#f92672">=</span> providers[<span style="color:#ae81ff">0</span>][<span style="color:#e6db74">&#34;kms&#34;</span>]<span style="color:#f92672">.</span>get(<span style="color:#e6db74">&#34;apiVersion&#34;</span>, <span style="color:#e6db74">&#34;v1&#34;</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span> ver <span style="color:#f92672">!=</span> <span style="color:#e6db74">&#34;v2&#34;</span>:
</span></span><span style="display:flex;"><span>                findings<span style="color:#f92672">.</span>append(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;rule </span><span style="color:#e6db74">{</span>i<span style="color:#e6db74">}</span><span style="color:#e6db74">: KMS v1 is deprecated since Kubernetes v1.28; move to KMS v2&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> any(<span style="color:#e6db74">&#34;identity&#34;</span> <span style="color:#f92672">in</span> p <span style="color:#66d9ef">for</span> p <span style="color:#f92672">in</span> providers[<span style="color:#ae81ff">1</span>:]):
</span></span><span style="display:flex;"><span>            findings<span style="color:#f92672">.</span>append(<span style="color:#e6db74">f</span><span style="color:#e6db74">&#34;rule </span><span style="color:#e6db74">{</span>i<span style="color:#e6db74">}</span><span style="color:#e6db74">: identity fallback present; unencrypted Secrets can still be read (expected during migration)&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> secrets_rule_seen:
</span></span><span style="display:flex;"><span>        findings<span style="color:#f92672">.</span>append(<span style="color:#e6db74">&#34;no rule covers secrets: Secrets are stored unencrypted&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> f_ <span style="color:#f92672">in</span> findings:
</span></span><span style="display:flex;"><span>        print(<span style="color:#e6db74">&#34;WARN&#34;</span>, f_)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#f92672">not</span> findings:
</span></span><span style="display:flex;"><span>        print(<span style="color:#e6db74">&#34;OK no weak settings found for secrets&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#ae81ff">1</span> <span style="color:#66d9ef">if</span> findings <span style="color:#66d9ef">else</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> __name__ <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;__main__&#34;</span>:
</span></span><span style="display:flex;"><span>    sys<span style="color:#f92672">.</span>exit(main(sys<span style="color:#f92672">.</span>argv[<span style="color:#ae81ff">1</span>]))
</span></span></code></pre></div><p>Results on our samples: the <code>aescbc</code>-plus-<code>identity</code> file above produced three warnings and exit code 1; a file using <code>kms</code> with <code>apiVersion: v2</code> printed <code>OK</code> and exited 0; a file that only configured ConfigMaps reported that no rule covers Secrets. The non-zero exit code lets you run it in CI against the configuration you deploy to control plane nodes.</p>
<h2 id="rolling-it-out-without-locking-yourself-out">Rolling it out without locking yourself out</h2>
<p>The Kubernetes task page walks through the full procedure. The steps that most often go wrong:</p>
<ol>
<li>
<p><strong>Every control plane host needs the same file and key.</strong> The docs say to copy the key to every control plane host over a secure channel, and to restart each API server. An API server without the key can&rsquo;t read Secrets another one encrypted.</p>
</li>
<li>
<p><strong>Existing Secrets are not touched.</strong> Encryption applies on write. Rewrite everything once all API servers have the new configuration:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>kubectl get secrets --all-namespaces -o json | kubectl replace -f -
</span></span></code></pre></div></li>
<li>
<p><strong>Verify in etcd, not through the API.</strong> The API always returns decrypted values, so <code>kubectl get secret</code> proves nothing. Read a Secret with <code>etcdctl get /registry/secrets/&lt;namespace&gt;/&lt;name&gt;</code> and check that the stored value starts with <code>k8s:enc:&lt;provider&gt;:v1:&lt;key-name&gt;:</code>, for example <code>k8s:enc:aescbc:v1:key1:</code>.</p>
</li>
<li>
<p><strong>Rotate keys in two passes.</strong> The docs&rsquo; zero-downtime procedure: add the new key as the <em>second</em> entry on every control plane node and restart all API servers so each can decrypt with it; back the key up; then make it the first entry, restart again, rewrite all Secrets, and only then remove the old key. Losing every copy of a key means deleting the resources encrypted with it.</p>
</li>
</ol>
<h2 id="controls-that-matter-as-much-as-encryption">Controls that matter as much as encryption</h2>
<p>Encryption at rest is one line in the Kubernetes Secrets good-practices list. The others address the readers in the first table:</p>
<ul>
<li><strong>Restrict <code>get</code>, <code>list</code> and <code>watch</code>.</strong> The docs say to limit <code>watch</code> and <code>list</code> to &ldquo;the most privileged, system-level components&rdquo; and caution that <code>list</code> &ldquo;implicitly lets the subject fetch the contents of the Secrets.&rdquo;</li>
<li><strong>Separate namespaces by trust.</strong> Pod creation reaches every Secret in a namespace, so a namespace is the practical boundary. Enforce <a href="/posts/kubernetes-pod-security-admission-namespace-labels-modes-and-a-safe-rollout-from-warn-to-enforce/">Pod Security Admission</a> where workload creators are less trusted, and consider <a href="/posts/kubernetes-validatingadmissionpolicy-cel-rules-for-registry-allowlists-and-memory-limits-with-a-warn-first-rollout/">ValidatingAdmissionPolicy</a> for rules Pod Security doesn&rsquo;t cover.</li>
<li><strong>Audit Secret access at the Metadata level.</strong> The example audit policy in the Kubernetes docs logs Secrets and ConfigMaps at <code>Metadata</code>, which records who accessed what without writing values into your logs. The good-practices page suggests alerting on events such as one user reading many Secrets at once.</li>
<li><strong>Use short-lived credentials and external stores.</strong> The docs recommend short-lived Secrets and mention the Secrets Store CSI Driver for keeping values in an external store. The same reasoning drives <a href="/posts/aws-iam-least-privilege-a-practical-guide-to-finding-excess-permissions/">least privilege in cloud IAM</a>: a credential that expires in an hour is worth less to whoever reads it.</li>
</ul>
<h2 id="limits-of-this-article">Limits of this article</h2>
<p>This describes upstream Kubernetes as documented on kubernetes.io at the time of writing. We tested the checker against sample files only, not against a live cluster or a managed service. We have not tested the encryption rollout procedure ourselves; follow the Kubernetes task page and your distribution&rsquo;s documentation, and rehearse on a non-production cluster first.</p>
<h2 id="sources-and-verification">Sources and verification</h2>
<table>
  <thead>
      <tr>
          <th>Important claim</th>
          <th>Source</th>
          <th>Verification</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Secrets stored unencrypted in etcd by default; Pod creators can read any Secret in the namespace</td>
          <td><a href="https://kubernetes.io/docs/concepts/configuration/secret/">Kubernetes: Secrets</a></td>
          <td>Verified</td>
      </tr>
      <tr>
          <td>Base64 is not encryption; list access reveals contents; short-lived Secrets, audit alerting, Secrets Store CSI Driver</td>
          <td><a href="https://kubernetes.io/docs/concepts/security/secrets-good-practices/">Kubernetes: Good practices for Kubernetes Secrets</a></td>
          <td>Verified</td>
      </tr>
      <tr>
          <td>list and watch reveal contents; workload creation grants namespace Secret access</td>
          <td><a href="https://kubernetes.io/docs/concepts/security/rbac-good-practices/">Kubernetes: RBAC good practices</a></td>
          <td>Verified</td>
      </tr>
      <tr>
          <td>identity default; first provider encrypts; rule ordering; provider ratings; local vs KMS key threat model; KMS v1 deprecated v1.28; KMS v2 stable v1.29; rewrite command; k8s:enc: prefix; key rotation steps</td>
          <td><a href="https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/">Kubernetes: Encrypting confidential data at rest</a></td>
          <td>Verified</td>
      </tr>
      <tr>
          <td>Example audit policy logs Secrets and ConfigMaps at Metadata level</td>
          <td><a href="https://kubernetes.io/docs/tasks/debug/debug-cluster/audit/">Kubernetes: Auditing</a></td>
          <td>Verified</td>
      </tr>
      <tr>
          <td>A local key turns a leaked etcd copy into ciphertext</td>
          <td>Editorial inference from the encrypt-data page&rsquo;s statement that local keys protect against etcd compromise</td>
          <td>Qualified</td>
      </tr>
  </tbody>
</table>
]]></content:encoded></item></channel></rss>