> ## Documentation Index
> Fetch the complete documentation index at: https://cloudanix.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Ensure Minimal Audit Policy Is Created

### More Info:

Kubernetes can audit the details of requests made to the API server. The --auditpolicy-file flag must be set for this logging to be enabled.

### Risk Level

Low

### Address

Security

### Compliance Standards

* CIS Kubernetes

### Triage and Remediation

<Tabs>
  <Tab title="Remediation">
    ### Remediation

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. **Confirm current API server static pod manifest**
           * On every control plane node:
             ```bash theme={null}
             sudo ls -l /etc/kubernetes/manifests/kube-apiserver.yaml
             ```

        2. **Create a minimal audit policy file**
           * On every control plane node (same path on each):
             ```bash theme={null}
             sudo mkdir -p /etc/kubernetes/audit
             sudo tee /etc/kubernetes/audit/audit-policy.yaml >/dev/null <<'EOF'
             apiVersion: audit.k8s.io/v1
             kind: Policy
             rules:
               # Log all requests at the Metadata level.
               - level: Metadata
             EOF
             ```

        3. **Configure the API server to use the audit policy file and log file**
           * On every control plane node, edit the static pod manifest:
             ```bash theme={null}
             sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
             ```
           * Under `spec.containers[0].command:` add (or adjust) these flags (one per line) making sure paths match what you created:
             ```yaml theme={null}
               - --audit-policy-file=/etc/kubernetes/audit/audit-policy.yaml
               - --audit-log-path=/var/log/kubernetes/audit.log
               - --audit-log-maxage=30
               - --audit-log-maxbackup=10
               - --audit-log-maxsize=100
             ```
           * Under `spec.containers[0].volumeMounts:` ensure:
             ```yaml theme={null}
               - mountPath: /etc/kubernetes/audit
                 name: audit-policy
                 readOnly: true
               - mountPath: /var/log/kubernetes
                 name: audit-log
             ```
           * Under `spec.volumes:` ensure:
             ```yaml theme={null}
               - name: audit-policy
                 hostPath:
                   path: /etc/kubernetes/audit
                   type: DirectoryOrCreate
               - name: audit-log
                 hostPath:
                   path: /var/log/kubernetes
                   type: DirectoryOrCreate
             ```
           * Saving this file will cause the kubelet to restart the kube-apiserver static pod on that node.

        4. **Ensure log directory exists and has correct ownership**
           * On every control plane node:
             ```bash theme={null}
             sudo mkdir -p /var/log/kubernetes
             sudo chown root:root /var/log/kubernetes
             sudo chmod 750 /var/log/kubernetes
             ```

        5. **Wait for kube-apiserver to restart and stabilize**
           * On every control plane node:
             ```bash theme={null}
             sudo crictl ps | grep kube-apiserver || sudo docker ps | grep kube-apiserver
             ```

        6. **Verification (audit command–based)**
           * On every control plane node, confirm the audit policy flag is present:
             ```bash theme={null}
             /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -- '--audit-policy-file=/etc/kubernetes/audit/audit-policy.yaml'
             ```
           * Optionally confirm audit log file is being written:
             ```bash theme={null}
             sudo ls -l /var/log/kubernetes/audit.log
             ```
      </Accordion>

      <Accordion title="Using kubectl">
        kubectl cannot be used to configure the API server’s `--audit-policy-file` or to edit `/etc/kubernetes/manifests/kube-apiserver.yaml`; those are host-level files on every control plane node. To address this finding, follow the guidance in the Manual Steps section on each control plane node.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Automation: Ensure minimal Kubernetes API server audit policy is created
        # and configured via the kube-apiserver static pod manifest.
        #
        # Run on: every control plane node (with root privileges)
        #
        # Behavior:
        # - Creates a minimal audit policy file if missing (idempotent)
        # - Ensures kube-apiserver manifest has:
        #     --audit-policy-file=/etc/kubernetes/audit-policy.yaml
        #     --audit-log-path=/var/log/kubernetes/audit.log
        # - Ensures /var/log/kubernetes exists
        # - kubelet will automatically restart the kube-apiserver static pod
        #   after the manifest is modified (expect brief control-plane disruption)
        # - Verifies that the kube-apiserver process has the correct flags

        set -euo pipefail

        APISERVER_MANIFEST="/etc/kubernetes/manifests/kube-apiserver.yaml"
        AUDIT_POLICY_FILE="/etc/kubernetes/audit-policy.yaml"
        AUDIT_LOG_DIR="/var/log/kubernetes"
        AUDIT_LOG_FILE="${AUDIT_LOG_DIR}/audit.log"

        # Minimal, broadly compatible audit policy
        read -r -d '' MINIMAL_POLICY <<"EOF" || true
        apiVersion: audit.k8s.io/v1
        kind: Policy
        # Log only metadata for all requests; adjust as needed for your org.
        rules:
          - level: Metadata
        EOF

        require_root() {
          if [[ "$(id -u)" -ne 0 ]]; then
            echo "ERROR: This script must be run as root on each control plane node." >&2
            exit 1
          fi
        }

        check_manifest_exists() {
          if [[ ! -f "${APISERVER_MANIFEST}" ]]; then
            echo "ERROR: kube-apiserver manifest not found at ${APISERVER_MANIFEST}." >&2
            echo "       This script assumes a static pod manifest; adjust for your environment." >&2
            exit 1
          fi
        }

        ensure_audit_policy_file() {
          if [[ -f "${AUDIT_POLICY_FILE}" ]]; then
            echo "Audit policy file already exists at ${AUDIT_POLICY_FILE}; leaving as-is."
          else
            echo "Creating minimal audit policy at ${AUDIT_POLICY_FILE}."
            umask 027
            echo "${MINIMAL_POLICY}" > "${AUDIT_POLICY_FILE}"
            chmod 640 "${AUDIT_POLICY_FILE}"
          fi
        }

        ensure_audit_log_dir() {
          if [[ ! -d "${AUDIT_LOG_DIR}" ]]; then
            echo "Creating audit log directory ${AUDIT_LOG_DIR}."
            mkdir -p "${AUDIT_LOG_DIR}"
            chmod 750 "${AUDIT_LOG_DIR}"
          else
            echo "Audit log directory ${AUDIT_LOG_DIR} already exists."
          fi
        }

        # Idempotently ensure a command-line flag in the kube-apiserver manifest
        ensure_apiserver_flag() {
          local flag_name="$1"      # e.g. --audit-policy-file
          local flag_value="$2"     # e.g. /etc/kubernetes/audit-policy.yaml
          local manifest="${APISERVER_MANIFEST}"

          if grep -qE "[[:space:]]${flag_name}=" "${manifest}"; then
            # Update in place
            echo "Ensuring ${flag_name} is set to ${flag_value} in ${manifest}."
            # Use sed to replace the value only; keep surrounding formatting
            sed -i.bak -E "s#(${flag_name}=)[^[:space:]]*#\1${flag_value}#g" "${manifest}"
          else
            # Add a new flag line under the kube-apiserver container args
            echo "Adding ${flag_name}=${flag_value} to ${manifest}."
            # Insert before the first line that starts with '    - --' under args:
            # This is heuristic but safe to re-run (we use grep above to avoid duplicates).
            python3 - "$manifest" "$flag_name" "$flag_value" << 'PYEOF'
        import sys, yaml

        manifest_path, flag_name, flag_value = sys.argv[1:]
        with open(manifest_path) as f:
            doc = yaml.safe_load(f)

        updated = False
        for c in doc.get('spec', {}).get('containers', []):
            if c.get('name') == 'kube-apiserver':
                args = c.setdefault('command', []) or c.setdefault('args', [])
                # If "command" is used explicitly, it's usually ["kube-apiserver", ...]
                # If "args" is used, they are direct flags. We normalize to list of strings.
                if not isinstance(args, list):
                    continue
                # Safeguard: avoid duplicating the flag
                if not any(a.startswith(flag_name + "=") for a in args):
                    args.append(f"{flag_name}={flag_value}")
                updated = True
                break

        if not updated:
            print("ERROR: Could not locate kube-apiserver container in manifest to add flag.", file=sys.stderr)
            sys.exit(1)

        with open(manifest_path, "w") as f:
            yaml.safe_dump(doc, f, default_flow_style=False)
        PYEOF
          fi
        }

        main() {
          require_root
          check_manifest_exists

          # Ensure policy file and log directory
          ensure_audit_policy_file
          ensure_audit_log_dir

          # Ensure kube-apiserver flags
          ensure_apiserver_flag "--audit-policy-file" "${AUDIT_POLICY_FILE}"
          ensure_apiserver_flag "--audit-log-path"    "${AUDIT_LOG_FILE}"

          echo "Waiting for kube-apiserver to restart with new configuration..."
          sleep 15

          echo "Verification: checking kube-apiserver process flags."
          /bin/ps -ef | grep kube-apiserver | grep -v grep || {
            echo "ERROR: kube-apiserver process not found; investigate pod status." >&2
            exit 1
          }

          if /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -q -- "--audit-policy-file=${AUDIT_POLICY_FILE}"; then
            echo "Verified: kube-apiserver is running with --audit-policy-file=${AUDIT_POLICY_FILE}."
          else
            echo "ERROR: kube-apiserver is not running with the expected --audit-policy-file flag." >&2
            exit 1
          fi

          if /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -q -- "--audit-log-path=${AUDIT_LOG_FILE}"; then
            echo "Verified: kube-apiserver is running with --audit-log-path=${AUDIT_LOG_FILE}."
          else
            echo "ERROR: kube-apiserver is not running with the expected --audit-log-path flag." >&2
            exit 1
          fi

          echo "Automation complete on this control plane node."
        }

        main "$@"
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>

### Additional Reading:

* [https://kubernetes.io/docs/tasks/debug-application-cluster/audit/](https://kubernetes.io/docs/tasks/debug-application-cluster/audit/)
