Skip to main content

API Server Audit Log Maxage Should Be 30 Or More

More Info:

Verifies that --audit-log-maxage is set to 30 days or an appropriate value so audit records are retained long enough for investigations.

Risk Level

Medium

Address

Security

Compliance Standards

  • CIS Kubernetes

Triage and Remediation

Remediation

Manual Steps
  1. On every control plane node, open the API server static pod manifest for editing:

    sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
  2. In the command: (or args:) list for kube-apiserver, add or modify the audit log max age flag so it is present exactly once and set to at least 30. For example, ensure there is a line like:

    - --audit-log-maxage=30
  3. Save and exit the file. The kubelet will automatically detect the change and restart the kube-apiserver static pod. Be aware this briefly restarts the API server on that node.

  4. Wait for the kube-apiserver pod to restart and become Running on that node:

    sudo crictl ps | grep kube-apiserver

    (Use docker ps instead of crictl if the node uses Docker.)

  5. Verify the kube-apiserver process now includes the correct --audit-log-maxage parameter on each control plane node:

    /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -- '--audit-log-maxage=30'
Using kubectl

kubectl cannot modify the kube-apiserver static pod manifest or its process flags, so it cannot be used to set --audit-log-maxage. This must be fixed directly on each control plane node by editing /etc/kubernetes/manifests/kube-apiserver.yaml; see the Manual Steps section for the exact procedure.

Automation
#!/usr/bin/env bash
#
# Remediation: Ensure kube-apiserver --audit-log-maxage is set to 30 (or higher)
# Scope: every control plane node
#
# Usage: run as root on each control plane node:
# chmod +x fix-audit-log-maxage.sh
# ./fix-audit-log-maxage.sh
#
# This script is idempotent and safe to re-run.

set -euo pipefail

APISERVER_MANIFEST="/etc/kubernetes/manifests/kube-apiserver.yaml"
DESIRED_MAXAGE="30"

echo "=== Ensuring --audit-log-maxage is set to ${DESIRED_MAXAGE} (or higher) in ${APISERVER_MANIFEST}"

if [[ $EUID -ne 0 ]]; then
echo "ERROR: This script must be run as root." >&2
exit 1
fi

if [[ ! -f "${APISERVER_MANIFEST}" ]]; then
echo "ERROR: Manifest not found: ${APISERVER_MANIFEST}" >&2
exit 1
fi

# Backup once (idempotent: do not overwrite an existing backup)
BACKUP="${APISERVER_MANIFEST}.pre-audit-log-maxage.bak"
if [[ ! -f "${BACKUP}" ]]; then
cp -p "${APISERVER_MANIFEST}" "${BACKUP}"
echo "Backup created at ${BACKUP}"
else
echo "Backup already exists at ${BACKUP}"
fi

TMP_FILE="$(mktemp)"
trap 'rm -f "${TMP_FILE}"' EXIT

# Determine if the flag is already present
if grep -qE -- '--audit-log-maxage(=|\s)' "${APISERVER_MANIFEST}"; then
echo "Existing --audit-log-maxage flag found; updating value if needed."

# Replace any existing value with the desired value
# Handles forms:
# - --audit-log-maxage=10
# - --audit-log-maxage 10
sed -E \
-e "s/(--audit-log-maxage=)[0-9]+/\1${DESIRED_MAXAGE}/" \
-e "s/(--audit-log-maxage[[:space:]]+)[0-9]+/\1${DESIRED_MAXAGE}/" \
"${APISERVER_MANIFEST}" > "${TMP_FILE}"

else
echo "No --audit-log-maxage flag found; adding it."

# Insert a new line with the flag under the 'command:' or after '--audit-log-path'
# Preference: place it immediately after an existing --audit-log-path line if present.
if grep -q -- '--audit-log-path' "${APISERVER_MANIFEST}"; then
# Insert after first occurrence of --audit-log-path
awk -v flag=" - --audit-log-maxage=${DESIRED_MAXAGE}" '
{
print $0
if (!inserted && $0 ~ /--audit-log-path/) {
print flag
inserted=1
}
}
END {
if (!inserted) {
# Fallback: append at end of file (should not normally happen here)
print flag
}
}
' "${APISERVER_MANIFEST}" > "${TMP_FILE}"
else
# Fallback: append to the command list after detecting "command:" block
awk -v flag=" - --audit-log-maxage=${DESIRED_MAXAGE}" '
/command:[[:space:]]*$/ {
print $0
getline
# Print the first list element then insert our flag
print $0
print flag
printed=1
next
}
{ print $0 }
END {
if (!printed) {
# Absolute fallback: append at end of file
print flag
}
}
' "${APISERVER_MANIFEST}" > "${TMP_FILE}"
fi
fi

# Only replace the original if there is an actual change
if cmp -s "${APISERVER_MANIFEST}" "${TMP_FILE}"; then
echo "No changes required; manifest already configured with --audit-log-maxage=${DESIRED_MAXAGE}."
else
mv "${TMP_FILE}" "${APISERVER_MANIFEST}"
chmod 600 "${APISERVER_MANIFEST}" || true
echo "Updated ${APISERVER_MANIFEST} with --audit-log-maxage=${DESIRED_MAXAGE}."

echo
echo "NOTE: Because this is a static pod manifest under /etc/kubernetes/manifests,"
echo "the kube-apiserver pod on this node will be restarted automatically by kubelet."
fi

# Verification: check running kube-apiserver process on this node
# (there may be a short delay while the pod restarts)
echo
echo "=== Verifying running kube-apiserver process has --audit-log-maxage=${DESIRED_MAXAGE}"

# Wait up to 60 seconds for kube-apiserver to be running with the new flag
TRIES=12
SLEEP_SEC=5
SUCCESS=0

for i in $(seq 1 "${TRIES}"); do
if /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -q -- "--audit-log-maxage=${DESIRED_MAXAGE}"; then
SUCCESS=1
break
fi
echo " Attempt ${i}/${TRIES}: kube-apiserver with --audit-log-maxage=${DESIRED_MAXAGE} not detected yet; waiting ${SLEEP_SEC}s..."
sleep "${SLEEP_SEC}"
done

if [[ "${SUCCESS}" -eq 1 ]]; then
echo "Verification succeeded: kube-apiserver is running with --audit-log-maxage=${DESIRED_MAXAGE}."
echo
echo "Command used for verification (for manual re-check):"
echo "/bin/ps -ef | grep kube-apiserver | grep -v grep"
exit 0
else
echo "WARNING: After waiting, kube-apiserver with --audit-log-maxage=${DESIRED_MAXAGE} was not detected." >&2
echo "Please check the kube-apiserver pod status and logs." >&2
echo
echo "Manual verification command:"
echo "/bin/ps -ef | grep kube-apiserver | grep -v grep"
exit 1
fi