#!/usr/bin/env bash
# Remediation: Ensure kube-apiserver --audit-log-maxbackup is set to >= 10
# Scope: Run on every control plane node (as root)
set -euo pipefail
APISERVER_MANIFEST="/etc/kubernetes/manifests/kube-apiserver.yaml"
MIN_VALUE=10
TMP_SUFFIX=".$$.tmp"
if [ ! -f "$APISERVER_MANIFEST" ]; then
echo "ERROR: $APISERVER_MANIFEST not found on this node. Are you on a control plane node?"
exit 1
fi
if ! command -v python3 >/dev/null 2>&1; then
echo "ERROR: python3 is required for safe YAML editing."
exit 1
fi
# 1) Backup once per node (idempotent if re-run)
BACKUP="${APISERVER_MANIFEST}.backup"
if [ ! -f "$BACKUP" ]; then
cp -p "$APISERVER_MANIFEST" "$BACKUP"
echo "Backup created at $BACKUP"
else
echo "Backup already exists at $BACKUP"
fi
# 2) Use Python to enforce --audit-log-maxbackup >= MIN_VALUE in the container args
python3 << 'EOF'
import sys, re, shutil, os
from pathlib import Path
manifest_path = Path("/etc/kubernetes/manifests/kube-apiserver.yaml")
min_value = 10
try:
import yaml # type: ignore
except Exception:
yaml = None
content = manifest_path.read_text()
if yaml is None:
# Fallback: regex-based edit on --audit-log-maxbackup=
pattern = re.compile(r'(--audit-log-maxbackup=)(\d+)\b')
m = pattern.search(content)
if m:
current = int(m.group(2))
if current >= min_value:
sys.exit(0)
new = f"{m.group(1)}{min_value}"
new_content = pattern.sub(new, content, count=1)
else:
# Try to inject into a line with kube-apiserver command
lines = content.splitlines()
inserted = False
for i, line in enumerate(lines):
if "kube-apiserver" in line and "command:" in content:
# For command-based spec, we won't try to auto-edit safely without YAML
break
if "kube-apiserver" in line and "--secure-port" in line:
# args-style single line; append new arg
indent = re.match(r'^(\s*)', line).group(1)
lines.insert(i + 1, f'{indent}- --audit-log-maxbackup={min_value}')
inserted = True
break
if not inserted:
# As a last resort, append under first 'args:' section
for i, line in enumerate(lines):
if re.match(r'^\s*args:\s*$', line):
indent = re.match(r'^(\s*)', lines[i+1]).group(1)
lines.insert(i + 1, f'{indent}- --audit-log-maxbackup={min_value}')
inserted = True
break
if not inserted:
print("ERROR: Could not safely inject --audit-log-maxbackup without PyYAML.", file=sys.stderr)
sys.exit(1)
new_content = "\n".join(lines)
tmp_path = str(manifest_path) + ".tmp"
with open(tmp_path, "w") as f:
f.write(new_content)
os.replace(tmp_path, manifest_path)
sys.exit(0)
# YAML-based edit (preferred)
with open(manifest_path) as f:
data = yaml.safe_load(f)
spec = data.get("spec", {})
containers = spec.get("containers", [])
if not containers:
print("ERROR: No containers found in kube-apiserver manifest.", file=sys.stderr)
sys.exit(1)
api = containers[0]
args = api.get("args", [])
# Normalize existing values, if any
new_args = []
found = False
for arg in args:
if isinstance(arg, str) and arg.startswith("--audit-log-maxbackup="):
try:
cur = int(arg.split("=", 1)[1])
except ValueError:
cur = min_value
if cur < min_value:
arg = f"--audit-log-maxbackup={min_value}"
found = True
new_args.append(arg)
if not found:
new_args.append(f"--audit-log-maxbackup={min_value}")
# If nothing changed and already >= min_value, exit without rewrite
if args == new_args:
sys.exit(0)
api["args"] = new_args
spec["containers"] = [api]
data["spec"] = spec
tmp_path = str(manifest_path) + ".tmp"
with open(tmp_path, "w") as f:
yaml.safe_dump(data, f, default_flow_style=False)
os.replace(tmp_path, manifest_path)
EOF
echo "Updated $APISERVER_MANIFEST; kubelet will restart kube-apiserver static pod automatically."
# 3) Wait for kube-apiserver to restart and stabilize
echo "Waiting up to 120s for kube-apiserver process to be running with new flag..."
end=$((SECONDS+120))
while [ $SECONDS -lt $end ]; do
if /bin/ps -ef | grep kube-apiserver | grep -v grep >/dev/null 2>&1; then
break
fi
sleep 3
done
# 4) Verification (same basis as audit command)
/bin/ps -ef | grep kube-apiserver | grep -v grep
# Additional verification of the flag value
echo
echo "Verifying --audit-log-maxbackup is set to at least ${MIN_VALUE}:"
if /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -- '--audit-log-maxbackup=' >/dev/null 2>&1; then
/bin/ps -ef | grep kube-apiserver | grep -v grep | tr -s ' ' | \
sed 's/ /\n/g' | grep -- '^--audit-log-maxbackup=' || true
else
echo "ERROR: kube-apiserver is running but --audit-log-maxbackup flag not found in process list."
exit 1
fi