> ## 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.

# API Server Should Set An Audit Log Path

### More Info:

Verifies that the API server --audit-log-path argument is set so API activity is recorded. Without audit logging, security incidents cannot be investigated.

### Risk Level

High

### Address

Security

### Compliance Standards

* CIS Kubernetes

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. On every control plane node, back up the existing manifest so you can roll back if needed:
           ```bash theme={null}
           sudo cp -a /etc/kubernetes/manifests/kube-apiserver.yaml /etc/kubernetes/manifests/kube-apiserver.yaml.backup
           ```

        2. On every control plane node, create the audit log directory and set safe permissions:
           ```bash theme={null}
           sudo mkdir -p /var/log/apiserver
           sudo chmod 700 /var/log/apiserver
           sudo chown root:root /var/log/apiserver
           ```

        3. On every control plane node, edit the API server static pod manifest:
           ```bash theme={null}
           sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
           ```
           In the `command:` list for `kube-apiserver`, add (or modify) this flag so it appears as a separate list item:
           ```yaml theme={null}
           - --audit-log-path=/var/log/apiserver/audit.log
           ```
           Save and exit. Editing this file will cause the kubelet to restart the kube-apiserver pod automatically.

        4. On every control plane node, wait for the API server pod to restart and become Running:
           ```bash theme={null}
           sudo crictl ps | grep kube-apiserver
           ```
           (If you use Docker instead of containerd, use `sudo docker ps | grep kube-apiserver`.)

        5. On any machine with `kubectl` access, confirm the API server is healthy:
           ```bash theme={null}
           kubectl get --raw=/healthz
           ```
           Ensure the output is `ok`.

        6. On every control plane node, verify the process now includes the `--audit-log-path` flag:
           ```bash theme={null}
           /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -- '--audit-log-path='
           ```
           The command should return a line showing `--audit-log-path=/var/log/apiserver/audit.log`.
      </Accordion>

      <Accordion title="Using kubectl">
        kubectl cannot configure API server process flags or edit the static pod manifest at `/etc/kubernetes/manifests/kube-apiserver.yaml` on control plane nodes. To set `--audit-log-path` as required, follow the host-level instructions in the **Manual Steps** section on every control plane node.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Automate CIS Kubernetes 1.2.16:
        # Ensure that the --audit-log-path argument is set on the kube-apiserver
        #
        # Run on: every control plane node (with root privileges)
        # Safe to re-run: yes (idempotent)

        set -euo pipefail

        APISERVER_MANIFEST="/etc/kubernetes/manifests/kube-apiserver.yaml"
        AUDIT_LOG_PATH="/var/log/apiserver/audit.log"
        BACKUP_DIR="/etc/kubernetes/manifests/backup-apiserver-$(date +%Y%m%d%H%M%S)"

        echo "==> Verifying kube-apiserver manifest exists at ${APISERVER_MANIFEST}"
        if [[ ! -f "${APISERVER_MANIFEST}" ]]; then
          echo "ERROR: ${APISERVER_MANIFEST} not found on this node. Are you on a control plane node?"
          exit 1
        fi

        echo "==> Creating backup in ${BACKUP_DIR}"
        mkdir -p "${BACKUP_DIR}"
        cp -p "${APISERVER_MANIFEST}" "${BACKUP_DIR}/"

        echo "==> Ensuring audit log directory exists: $(dirname "${AUDIT_LOG_PATH}")"
        mkdir -p "$(dirname "${AUDIT_LOG_PATH}")"
        chmod 750 "$(dirname "${AUDIT_LOG_PATH}")" || true

        # Function to check if --audit-log-path is already present
        has_audit_log_path() {
          grep -E -- '--audit-log-path(=| )' "${APISERVER_MANIFEST}" >/dev/null 2>&1
        }

        # Function to update or insert --audit-log-path in the container args
        add_or_update_audit_log_path() {
          # If flag is already there, replace its value to enforce desired path (idempotent)
          if has_audit_log_path; then
            echo "==> Updating existing --audit-log-path value to ${AUDIT_LOG_PATH}"
            # Handle both "--audit-log-path=/path" and "--audit-log-path", "/path" styles
            python3 - <<'PY' "${APISERVER_MANIFEST}" "${AUDIT_LOG_PATH}"
        import sys, ruamel.yaml
        manifest_path = sys.argv[1]
        audit_path = sys.argv[2]

        yaml = ruamel.yaml.YAML()
        yaml.preserve_quotes = True

        with open(manifest_path, 'r') as f:
            data = yaml.load(f)

        containers = data.get('spec', {}).get('containers', [])
        for c in containers:
            if c.get('name') == 'kube-apiserver':
                args = c.get('args', [])
                new_args = []
                skip_next = False
                for i, a in enumerate(args):
                    if skip_next:
                        skip_next = False
                        continue
                    if a.startswith('--audit-log-path='):
                        new_args.append(f'--audit-log-path={audit_path}')
                    elif a == '--audit-log-path':
                        # Skip this and next (old value), replace with new single arg
                        skip_next = True
                        new_args.append(f'--audit-log-path={audit_path}')
                    else:
                        new_args.append(a)
                c['args'] = new_args

        with open(manifest_path, 'w') as f:
            yaml.dump(data, f)
        PY
            return
          fi

          echo "==> Inserting --audit-log-path=${AUDIT_LOG_PATH} into kube-apiserver args"
          python3 - <<'PY' "${APISERVER_MANIFEST}" "${AUDIT_LOG_PATH}"
        import sys, ruamel.yaml
        manifest_path = sys.argv[1]
        audit_path = sys.argv[2]

        yaml = ruamel.yaml.YAML()
        yaml.preserve_quotes = True

        with open(manifest_path, 'r') as f:
            data = yaml.load(f)

        spec = data.setdefault('spec', {})
        containers = spec.setdefault('containers', [])
        for c in containers:
            if c.get('name') == 'kube-apiserver':
                args = c.setdefault('args', [])
                # Add flag only if truly absent (extra safety)
                if not any(
                    a == '--audit-log-path' or a.startswith('--audit-log-path=')
                    for a in args
                ):
                    args.append(f'--audit-log-path={audit_path}')

        with open(manifest_path, 'w') as f:
            yaml.dump(data, f)
        PY
        }

        # Ensure python3 and ruamel.yaml are present
        if ! command -v python3 >/dev/null 2>&1; then
          echo "ERROR: python3 is required for YAML-safe editing. Install python3 and re-run."
          exit 1
        fi

        if ! python3 -c "import ruamel.yaml" >/dev/null 2>&1; then
          echo "==> Installing ruamel.yaml via pip (requires network/pip)"
          if ! command -v pip3 >/dev/null 2>&1; then
            echo "ERROR: pip3 not found and ruamel.yaml is required. Install pip3/ruamel.yaml and re-run."
            exit 1
          fi
          pip3 install --quiet 'ruamel.yaml>=0.17'
        fi

        add_or_update_audit_log_path

        echo "==> Configuration updated. kubelet will automatically restart the kube-apiserver static pod."
        echo "    Note: This will cause a brief kube-apiserver restart on this control plane node."

        # Wait for kube-apiserver process to come back with the correct flag
        echo "==> Waiting for kube-apiserver to be running with --audit-log-path ..."
        RETRY=30
        SLEEP=5
        OK=0
        for i in $(seq 1 "${RETRY}"); do
          if /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -E -- '--audit-log-path(=| )' >/dev/null 2>&1; then
            OK=1
            break
          fi
          sleep "${SLEEP}"
        done

        echo "==> Verification:"
        if [[ "${OK}" -eq 1 ]]; then
          /bin/ps -ef | grep kube-apiserver | grep -v grep | sed 's/^/  /'
          echo "==> PASS: kube-apiserver is running with --audit-log-path set."
          exit 0
        else
          echo "ERROR: kube-apiserver did not show --audit-log-path in process args within timeout."
          echo "Current kube-apiserver processes:"
          /bin/ps -ef | grep kube-apiserver | grep -v grep || true
          exit 1
        fi
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
