<?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>Detection | FirewallSync</title><link>https://firewallsync.com/tags/detection/</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>Sun, 25 Oct 2026 04:45:00 +0000</lastBuildDate><atom:link href="https://firewallsync.com/tags/detection/index.xml" rel="self" type="application/rss+xml"/><item><title>Kubernetes Audit Policy: What to Log, at What Level</title><link>https://firewallsync.com/posts/kubernetes-audit-policy-what-to-log-and-at-what-level/</link><guid>https://firewallsync.com/posts/kubernetes-audit-policy-what-to-log-and-at-what-level/</guid><pubDate>Sun, 25 Oct 2026 04:45:00 +0000</pubDate><dc:creator>Craig Leslie</dc:creator><category>Cloud Security</category><description>A Kubernetes audit policy decides which API requests kube-apiserver records and how much of each it keeps. The first matching rule wins, so one misplaced rule can write Secret values into your logs. Here is a six-rule policy we ran against kube-apiserver v1.34.1, what each rule captured, and the jq queries that turn the log into answers.</description><enclosure url="https://firewallsync.com/posts/kubernetes-audit-policy-what-to-log-and-at-what-level/cover_hu8208247904918711124.jpg" type="image/jpeg" length="0"/><media:content url="https://firewallsync.com/posts/kubernetes-audit-policy-what-to-log-and-at-what-level/cover_hu8208247904918711124.jpg" medium="image" type="image/jpeg" width="1200" height="630"/><content:encoded><![CDATA[<p><img src="https://firewallsync.com/posts/kubernetes-audit-policy-what-to-log-and-at-what-level/cover_hu8208247904918711124.jpg" alt="A worn, yellowed ledger lying open on green baize, its ruled pages covered in faded handwriting, with a white quill pen resting across them" width="1200" height="630"></p><p>A Kubernetes audit policy is the file that tells kube-apiserver which API requests to record and how much of each to keep. Without one, upstream kube-apiserver records nothing. A good policy logs everything at <code>Metadata</code> at minimum, keeps request bodies for changes that alter access, and never logs the contents of Secrets. Rule order is what makes that work: the first matching rule wins, and a broad rule placed too high will quietly write Secret values into your logs. We demonstrate that below.</p>
<p>This guide gives a six-rule policy we ran against kube-apiserver v1.34.1, shows what each rule captured, and ends with queries you can run against the log during an incident.</p>
<h2 id="how-kube-apiserver-evaluates-a-policy">How kube-apiserver evaluates a policy</h2>
<p>Every request produces audit events at one or more <strong>stages</strong>:</p>
<table>
  <thead>
      <tr>
          <th>Stage</th>
          <th>When</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>RequestReceived</code></td>
          <td>As soon as the audit handler receives the request</td>
      </tr>
      <tr>
          <td><code>ResponseStarted</code></td>
          <td>After response headers are sent, for long-running requests such as watch</td>
      </tr>
      <tr>
          <td><code>ResponseComplete</code></td>
          <td>When the response body is finished</td>
      </tr>
      <tr>
          <td><code>Panic</code></td>
          <td>When a panic occurred</td>
      </tr>
  </tbody>
</table>
<p>The policy is a list of rules. kube-apiserver compares each event with the rules <strong>in order</strong>, and the first rule that matches sets the event&rsquo;s <strong>level</strong>:</p>
<table>
  <thead>
      <tr>
          <th>Level</th>
          <th>What gets recorded</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>None</code></td>
          <td>Nothing</td>
      </tr>
      <tr>
          <td><code>Metadata</code></td>
          <td>User, groups, source IPs, user agent, verb, resource, namespace, name, response status, timestamps, annotations</td>
      </tr>
      <tr>
          <td><code>Request</code></td>
          <td>Metadata plus the request body</td>
      </tr>
      <tr>
          <td><code>RequestResponse</code></td>
          <td>Metadata plus request and response bodies</td>
      </tr>
  </tbody>
</table>
<p><code>Request</code> and <code>RequestResponse</code> don&rsquo;t apply to non-resource URLs such as <code>/healthz</code>. A rule can match on <code>users</code>, <code>userGroups</code>, <code>verbs</code>, <code>resources</code> (group, resource, subresource and <code>resourceNames</code>), <code>namespaces</code> and <code>nonResourceURLs</code>. A rule with no match fields matches everything, which is what you want for the last rule and exactly what you don&rsquo;t want near the top.</p>
<p>The policy file is passed with <code>--audit-policy-file</code>. If the flag is omitted, no events are logged, and a policy with no rules is rejected.</p>
<h2 id="a-policy-that-answers-incident-questions">A policy that answers incident questions</h2>
<p>The policy below is built around the questions you get asked during an incident: who read this Secret, who exec&rsquo;d into that Pod, who granted this binding, and was it allowed. It is a starting point, not a complete production policy; see the limits at the end.</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">audit.k8s.io/v1</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Policy</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">omitStages</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#e6db74">&#34;RequestReceived&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">rules</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># 1. Drop health and version probes.</span>
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">level</span>: <span style="color:#ae81ff">None</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">nonResourceURLs</span>: [<span style="color:#e6db74">&#34;/healthz*&#34;</span>, <span style="color:#e6db74">&#34;/livez*&#34;</span>, <span style="color:#e6db74">&#34;/readyz*&#34;</span>, <span style="color:#e6db74">&#34;/version&#34;</span>]
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># 2. Sensitive payloads: who touched them, never the contents.</span>
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">level</span>: <span style="color:#ae81ff">Metadata</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">group</span>: <span style="color:#e6db74">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">resources</span>: [<span style="color:#e6db74">&#34;secrets&#34;</span>, <span style="color:#e6db74">&#34;configmaps&#34;</span>, <span style="color:#e6db74">&#34;serviceaccounts/token&#34;</span>]
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">group</span>: <span style="color:#e6db74">&#34;authentication.k8s.io&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">resources</span>: [<span style="color:#e6db74">&#34;tokenreviews&#34;</span>]
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># 3. Interactive access into running workloads.</span>
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">level</span>: <span style="color:#ae81ff">Metadata</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">group</span>: <span style="color:#e6db74">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">resources</span>: [<span style="color:#e6db74">&#34;pods/exec&#34;</span>, <span style="color:#e6db74">&#34;pods/attach&#34;</span>, <span style="color:#e6db74">&#34;pods/portforward&#34;</span>, <span style="color:#e6db74">&#34;pods/proxy&#34;</span>, <span style="color:#e6db74">&#34;nodes/proxy&#34;</span>]
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># 4. Changes to who can do what, and to admission control.</span>
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">level</span>: <span style="color:#ae81ff">RequestResponse</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">verbs</span>: [<span style="color:#e6db74">&#34;create&#34;</span>, <span style="color:#e6db74">&#34;update&#34;</span>, <span style="color:#e6db74">&#34;patch&#34;</span>, <span style="color:#e6db74">&#34;delete&#34;</span>, <span style="color:#e6db74">&#34;deletecollection&#34;</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">group</span>: <span style="color:#e6db74">&#34;rbac.authorization.k8s.io&#34;</span>
</span></span><span style="display:flex;"><span>      - <span style="color:#f92672">group</span>: <span style="color:#e6db74">&#34;admissionregistration.k8s.io&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># 5. Other writes: keep the submitted object.</span>
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">level</span>: <span style="color:#ae81ff">Request</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">verbs</span>: [<span style="color:#e6db74">&#34;create&#34;</span>, <span style="color:#e6db74">&#34;update&#34;</span>, <span style="color:#e6db74">&#34;patch&#34;</span>, <span style="color:#e6db74">&#34;delete&#34;</span>, <span style="color:#e6db74">&#34;deletecollection&#34;</span>]
</span></span><span style="display:flex;"><span>  <span style="color:#75715e"># 6. Everything else (reads, watches, non-resource URLs).</span>
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">level</span>: <span style="color:#ae81ff">Metadata</span>
</span></span></code></pre></div><p>Why each rule is where it is:</p>
<table>
  <thead>
      <tr>
          <th>Rule</th>
          <th>Level</th>
          <th>Reason</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>1. Probes</td>
          <td>None</td>
          <td>High volume, no investigative value. Leaving out <code>RequestReceived</code> globally also halves events for most requests, since <code>ResponseComplete</code> carries the same metadata plus the result.</td>
      </tr>
      <tr>
          <td>2. Secrets, ConfigMaps, TokenRequest, TokenReview</td>
          <td>Metadata</td>
          <td>Their bodies hold credentials and tokens. The upstream GCE policy treats them the same way, noting they &ldquo;can contain sensitive &amp; binary data&rdquo;. It must come before any rule that logs bodies.</td>
      </tr>
      <tr>
          <td>3. exec, attach, port-forward, proxy</td>
          <td>Metadata</td>
          <td>These are how people get inside workloads. The body adds nothing, but you always want the event.</td>
      </tr>
      <tr>
          <td>4. RBAC and admission configuration</td>
          <td>RequestResponse</td>
          <td>You need the exact subjects, <code>roleRef</code>, rules and webhook targets that were written, and the response confirms what was stored. Listing a group with no <code>resources</code> matches every resource in it.</td>
      </tr>
      <tr>
          <td>5. Other writes</td>
          <td>Request</td>
          <td>Keeps the object that was submitted, such as a Pod spec with a privileged container or a hostPath mount.</td>
      </tr>
      <tr>
          <td>6. Catch-all</td>
          <td>Metadata</td>
          <td>Every read and watch is still attributable to someone.</td>
      </tr>
  </tbody>
</table>
<p>Pod Security Admission and our <a href="/posts/kubernetes-rbac-audit-11-permissions-that-escalate-access/">RBAC permission audit</a> tell you what <em>could</em> happen. The audit log is how you find out what did.</p>
<h2 id="what-it-recorded">What it recorded</h2>
<p>We ran the policy on kube-apiserver v1.34.1 (the envtest build from the kubernetes-sigs/controller-tools releases) with etcd and RBAC authorization, then made requests as a <code>system:masters</code> user and as an unprivileged user <code>alice</code>. There were no nodes, so no Pod actually ran. Results:</p>
<table>
  <thead>
      <tr>
          <th>Action</th>
          <th>Level logged</th>
          <th>What the event held</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>kubectl create secret generic db-creds --from-literal=password=...</code></td>
          <td>Metadata</td>
          <td>User, namespace, name, <code>201</code>. No request body; the value appeared nowhere in the log.</td>
      </tr>
      <tr>
          <td><code>kubectl create rolebinding alice-view --clusterrole=view --user=alice</code></td>
          <td>RequestResponse</td>
          <td>Full <code>requestObject</code> and <code>responseObject</code>, including the subject <code>alice</code> and <code>roleRef</code> <code>view</code>.</td>
      </tr>
      <tr>
          <td><code>alice</code> lists Secrets in <code>kube-system</code></td>
          <td>Metadata</td>
          <td><code>403</code>, annotation <code>authorization.k8s.io/decision: forbid</code></td>
      </tr>
      <tr>
          <td>Admin runs <code>kubectl --as alice get pods</code></td>
          <td>Metadata</td>
          <td><code>user.username: admin</code> and <code>impersonatedUser.username: alice</code> in the same event</td>
      </tr>
      <tr>
          <td><code>kubectl create deployment web --image=nginx:1.27</code></td>
          <td>Request</td>
          <td><code>requestObject</code> with the Deployment spec, no response body</td>
      </tr>
      <tr>
          <td><code>kubectl create token ci-bot</code></td>
          <td>Metadata</td>
          <td>Resource <code>serviceaccounts</code>, subresource <code>token</code>, name <code>ci-bot</code>, no token in the log</td>
      </tr>
      <tr>
          <td><code>kubectl exec shell -- cat /etc/shadow</code></td>
          <td>Metadata</td>
          <td><code>requestURI</code> of <code>/api/v1/namespaces/default/pods/shell/exec?command=cat&amp;command=%2Fetc%2Fshadow&amp;...</code></td>
      </tr>
  </tbody>
</table>
<p>Two details from the test are worth building detections around:</p>
<ul>
<li><strong>Exec shows up under more than one verb.</strong> That single <code>kubectl exec</code> produced events with verb <code>get</code> and verb <code>create</code> for the <code>exec</code> subresource. Filter on <code>objectRef.subresource == &quot;exec&quot;</code>, not on the verb.</li>
<li><strong>Impersonation keeps both identities.</strong> The authenticated user is in <code>user</code> and the identity they acted as is in <code>impersonatedUser</code>. If you only alert on <code>user.username</code>, impersonated actions look like they came from the admin&rsquo;s own account.</li>
</ul>
<p>Also keep in mind what the API reference warns about two fields: <code>userAgent</code> is reported by the client &ldquo;and must not be trusted&rdquo;, and in <code>sourceIPs</code> all entries except the last &ldquo;can be arbitrarily set by the client&rdquo;.</p>
<h2 id="the-rule-order-mistake-that-leaks-secrets">The rule-order mistake that leaks Secrets</h2>
<p>We then moved rule 5 (Request for all writes) above rule 2, restarted kube-apiserver with that policy, and created a Secret with the value <code>S3cretValue-456</code>. The audit log now contained:</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-text" data-lang="text"><span style="display:flex;"><span>&#34;data&#34;:{&#34;key&#34;:&#34;UzNjcmV0VmFsdWUtNDU2&#34;}
</span></span></code></pre></div><p>That is the base64 encoding of <code>S3cretValue-456</code>. The broad write rule matched first, logged the request body at <code>Request</code> level, and the Metadata rule for Secrets was never reached. With the correct order, the equivalent test (a Secret holding <code>S3cretValue-123</code>) left no trace of either the plain or the encoded value in the log.</p>
<p>This is the most common way an audit log becomes a credential store. Your audit pipeline usually has wider read access than the cluster&rsquo;s Secrets do, so treat a body-logging rule above your Secrets rule as a bug. Check it in code review with a simple test: create a dummy Secret in a test cluster and search the log for its encoded value.</p>
<p>If you already store <a href="/posts/kubernetes-secrets-encryption-at-rest-what-it-protects/">Secrets encrypted at rest</a>, remember that encryption protects etcd, not the audit log. And a <a href="/posts/kubernetes-service-account-tokens-bound-vs-legacy-secrets/">Secret-based service account token</a> leaked this way never expires.</p>
<h2 id="backends-and-volume">Backends and volume</h2>
<p>There are two backends: a log file and a webhook.</p>
<ul>
<li><strong>Log backend:</strong> <code>--audit-log-path</code> sets the file (<code>-</code> means stdout). Rotation is controlled by <code>--audit-log-maxage</code> (days), <code>--audit-log-maxbackup</code> (files) and <code>--audit-log-maxsize</code> (megabytes). Batching is off by default for the log backend.</li>
<li><strong>Webhook backend:</strong> <code>--audit-webhook-config-file</code> points at a kubeconfig-format file for the remote endpoint. Batching is on by default, with a maximum of 400 events per batch and a 30-second maximum wait.</li>
<li><strong>Truncation</strong> of oversized events is off by default for both backends and has to be enabled explicitly.</li>
</ul>
<p>The Kubernetes docs also note that auditing increases API server memory use, because some context is kept for each request, and that the increase depends on the policy. <code>RequestResponse</code> on a busy resource is where cost grows: the GCE reference policy, for instance, logs <code>get</code>, <code>list</code> and <code>watch</code> at <code>Request</code> level rather than <code>RequestResponse</code> because &ldquo;Get responses can be large&rdquo;.</p>
<p>Ship the log off the control plane. An attacker with control-plane access can edit a local file, and an <a href="/posts/logging-for-incidents-not-for-dashboards/">incident log is only useful</a> if it survives the incident.</p>
<h2 id="managed-kubernetes">Managed Kubernetes</h2>
<p>On EKS, AKS and similar services the provider runs kube-apiserver, so you generally choose whether audit events are exported, not what the policy contains. Check your provider&rsquo;s documentation for what its policy logs.</p>
<ul>
<li><strong>Amazon EKS:</strong> control plane logs are not sent to CloudWatch Logs by default. Enable the <code>audit</code> log type per cluster, for example with <code>aws eks update-cluster-config --logging</code>. The audit stream is named <code>kube-apiserver-audit-&lt;id&gt;</code> in the <code>/aws/eks/&lt;cluster&gt;/cluster</code> log group. AWS notes delivery is best effort and standard CloudWatch Logs charges apply.</li>
<li><strong>Azure AKS:</strong> resource logs are not collected until you create a diagnostic setting. Microsoft&rsquo;s guidance warns that <code>kube-audit</code> can be costly and suggests <code>kube-audit-admin</code>, which leaves out <code>get</code> and <code>list</code> events. That saves money but drops exactly the read events, such as Secret reads, that matter in a credential-theft investigation. Decide that trade-off deliberately.</li>
</ul>
<h2 id="queries-to-keep-ready">Queries to keep ready</h2>
<p>These run against a JSON-lines audit log with <code>jq</code>. We ran each one against the log from the test above.</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-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#75715e"># Who read or changed Secrets (excluding the API server itself)</span>
</span></span><span style="display:flex;"><span>jq -c <span style="color:#e6db74">&#39;select(.objectRef.resource==&#34;secrets&#34; and .user.username!=&#34;system:apiserver&#34;)
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">  | {ts:.requestReceivedTimestamp, user:.user.username, verb, ns:.objectRef.namespace,
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">     name:.objectRef.name, code:.responseStatus.code}&#39;</span> audit.log
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Every exec into a Pod, with the command line</span>
</span></span><span style="display:flex;"><span>jq -c <span style="color:#e6db74">&#39;select(.objectRef.subresource==&#34;exec&#34;)
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">  | {ts:.requestReceivedTimestamp, user:.user.username, ns:.objectRef.namespace,
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">     pod:.objectRef.name, uri:.requestURI}&#39;</span> audit.log
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Denied requests, including impersonated ones</span>
</span></span><span style="display:flex;"><span>jq -c <span style="color:#e6db74">&#39;select(.annotations[&#34;authorization.k8s.io/decision&#34;]==&#34;forbid&#34;)
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">  | {user:.user.username, as:.impersonatedUser.username, verb,
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">     res:.objectRef.resource, ns:.objectRef.namespace}&#39;</span> audit.log
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># RBAC writes by people rather than the control plane, with what they granted</span>
</span></span><span style="display:flex;"><span>jq -c <span style="color:#e6db74">&#39;select(.objectRef.apiGroup==&#34;rbac.authorization.k8s.io&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">       and (.verb|test(&#34;create|update|patch|delete&#34;))
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">       and (.user.username|startswith(&#34;system:&#34;)|not))
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">  | {user:.user.username, verb, res:.objectRef.resource, name:.objectRef.name,
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">     subjects:.requestObject.subjects, role:.requestObject.roleRef.name}&#39;</span> audit.log
</span></span></code></pre></div><p>A burst of <code>forbid</code> events from one identity across many resources can be a sign that someone is enumerating what a stolen credential can do.</p>
<h2 id="limits-of-this-article">Limits of this article</h2>
<p>We tested on a single kube-apiserver v1.34.1 with no nodes, no controller manager and static token authentication, so no workload actually ran and no kubelet or controller traffic was present. A real cluster produces far more events, and you will want extra <code>None</code> or <code>Metadata</code> rules for high-volume system traffic (the GCE policy is a good reference). The policy does not cover custom resources that hold credentials; add those to rule 2. <code>Request</code> level on Pod writes will log any credentials placed directly in environment variables. We did not test the webhook backend or any managed service.</p>
<h2 id="sources-and-verification">Sources and verification</h2>
<table>
  <thead>
      <tr>
          <th>Important claim</th>
          <th>Source</th>
          <th>Status</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Stages, levels, first-match evaluation, no logging without <code>--audit-policy-file</code>, zero-rule policy rejected, log and webhook flags, batching defaults, truncation off by default, memory note</td>
          <td><a href="https://kubernetes.io/docs/tasks/debug/debug-cluster/audit/">Kubernetes docs: Auditing</a> (<a href="https://github.com/kubernetes/website/blob/main/content/en/docs/tasks/debug/debug-cluster/audit.md">source</a>)</td>
          <td>Verified</td>
      </tr>
      <tr>
          <td>Policy fields, <code>sourceIPs</code> and <code>userAgent</code> trust warnings, empty <code>resources</code> matches whole group</td>
          <td><a href="https://github.com/kubernetes/apiserver/blob/master/pkg/apis/audit/v1/types.go">audit.k8s.io/v1 types</a></td>
          <td>Verified</td>
      </tr>
      <tr>
          <td>Secrets, ConfigMaps, TokenRequest and TokenReviews logged at Metadata; &ldquo;Get responses can be large&rdquo;</td>
          <td><a href="https://github.com/kubernetes/kubernetes/blob/master/cluster/gce/gci/configure-helper.sh">GCE configure-helper.sh audit policy</a></td>
          <td>Verified</td>
      </tr>
      <tr>
          <td><code>authorization.k8s.io/decision</code> annotation</td>
          <td><a href="https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apiserver/pkg/endpoints/filters/authorization.go">kube-apiserver authorization filter</a></td>
          <td>Verified</td>
      </tr>
      <tr>
          <td>Test results: Metadata Secret events without values, RequestResponse RBAC bodies, forbid annotation, impersonatedUser, exec URI and verbs, Secret value leaked with wrong rule order</td>
          <td>Our test on kube-apiserver v1.34.1 (envtest build)</td>
          <td>Verified (single test environment)</td>
      </tr>
      <tr>
          <td>EKS audit log off by default, stream naming, best-effort delivery, CloudWatch charges</td>
          <td><a href="https://docs.aws.amazon.com/eks/latest/userguide/control-plane-logs.html">Amazon EKS: Send control plane logs to CloudWatch Logs</a></td>
          <td>Verified</td>
      </tr>
      <tr>
          <td>AKS <code>kube-audit-admin</code> excludes get and list; logs need a diagnostic setting</td>
          <td><a href="https://docs.azure.cn/en-us/aks/monitor-apiserver">Microsoft: Monitor the AKS API server</a> (Azure China documentation mirror)</td>
          <td>Qualified</td>
      </tr>
      <tr>
          <td>Managed providers control the audit policy</td>
          <td>General provider architecture; not stated in one primary source</td>
          <td>Qualified</td>
      </tr>
  </tbody>
</table>
]]></content:encoded></item></channel></rss>