Hardening Guides
Purpose: For security officers and platform engineers, documents hardening steps beyond openCenter defaults that address CIS Kubernetes Benchmark items not covered by the default deployment.
Prerequisites
- Running openCenter cluster with Kyverno policies active
- SSH access to cluster nodes (for OS-level hardening)
- Cluster admin access via kubectl
What openCenter Covers by Default
openCenter ships with:
- Pod Security Admission (
restrictedprofile enforced on tenant namespaces) - Kyverno policies: disallow-privileged, require-non-root, restrict-host-namespaces
- SOPS-encrypted secrets in Git
- RBAC via rbac-manager with Keycloak OIDC
- Network policies for platform namespaces
- TLS on all service-to-service communication
This guide covers what remains after those defaults.
Kubelet Hardening
Anonymous Auth and Authorization
Verify kubelet configuration on each node (/var/lib/kubelet/config.yaml):
authentication:
anonymous:
enabled: false
webhook:
enabled: true
authorization:
mode: Webhook
CIS 4.2.1, 4.2.2 — openCenter's Kubespray deployment sets these by default. Verify with:
# On each node
curl -sk https://localhost:10250/healthz
# Should return 401 Unauthorized (not 200)
Read-Only Port
Disable the kubelet read-only port (CIS 4.2.4):
# /var/lib/kubelet/config.yaml
readOnlyPort: 0
Verify:
curl -s http://localhost:10255/healthz
# Should refuse connection
Protect Kernel Defaults
# /var/lib/kubelet/config.yaml
protectKernelDefaults: true
Event Rate Limiting
# /var/lib/kubelet/config.yaml
eventRecordQPS: 5
eventBurst: 10
Kernel Parameters (sysctl)
Apply these on all cluster nodes via /etc/sysctl.d/99-opencenter-hardening.conf:
# Disable IP forwarding for non-router nodes (CIS 3.2.1)
# NOTE: Keep enabled on nodes running Calico/Cilium CNI
# net.ipv4.ip_forward = 1 # Required for CNI
# Disable ICMP redirects (CIS 3.2.2)
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
# Disable source routing (CIS 3.2.3)
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
# Enable TCP SYN cookies (CIS 3.2.8)
net.ipv4.tcp_syncookies = 1
# Log martian packets
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
# Restrict core dumps
fs.suid_dumpable = 0
# Randomize virtual address space
kernel.randomize_va_space = 2
Apply without reboot:
sysctl --system
Audit Logging
API Server Audit Policy
Create /etc/kubernetes/audit-policy.yaml:
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# Don't log read-only requests to certain endpoints
- level: None
resources:
- group: ""
resources: ["endpoints", "services", "services/status"]
verbs: ["get", "watch", "list"]
# Log metadata for events
- level: None
resources:
- group: ""
resources: ["events"]
# Log request bodies for secrets, configmaps
- level: Request
resources:
- group: ""
resources: ["secrets", "configmaps"]
# Log request+response for RBAC changes
- level: RequestResponse
resources:
- group: "rbac.authorization.k8s.io"
# Log metadata for everything else
- level: Metadata
omitStages:
- RequestReceived
API Server Flags
Add to the kube-apiserver manifest:
- --audit-policy-file=/etc/kubernetes/audit-policy.yaml
- --audit-log-path=/var/log/kubernetes/audit.log
- --audit-log-maxage=30
- --audit-log-maxbackup=10
- --audit-log-maxsize=100
Audit Log Forwarding
Forward audit logs to the monitoring stack via Loki:
# Promtail configuration for audit logs
- job_name: kubernetes-audit
static_configs:
- targets: [localhost]
labels:
job: kubernetes-audit
__path__: /var/log/kubernetes/audit.log
Disable Unnecessary Services
On all cluster nodes:
# Disable unused services
systemctl disable --now avahi-daemon 2>/dev/null || true
systemctl disable --now cups 2>/dev/null || true
systemctl disable --now rpcbind 2>/dev/null || true
# Verify only required ports are listening
ss -tlnp | grep -v -E '(kubelet|kube-proxy|containerd|etcd|apiserver|calico)'
File Permissions
etcd Data Directory (CIS 1.1.11, 1.1.12)
chmod 700 /var/lib/etcd
chown etcd:etcd /var/lib/etcd
Kubernetes PKI (CIS 1.1.19–1.1.21)
chmod 600 /etc/kubernetes/pki/*.key
chmod 644 /etc/kubernetes/pki/*.crt
chown root:root /etc/kubernetes/pki/*
Kubelet Config (CIS 4.1.1–4.1.4)
chmod 644 /var/lib/kubelet/config.yaml
chown root:root /var/lib/kubelet/config.yaml
chmod 600 /etc/kubernetes/kubelet.conf
CNI Configuration
chmod 644 /etc/cni/net.d/*
chown root:root /etc/cni/net.d/*
CIS Items Not Covered by Default
| CIS ID | Item | Action |
|---|---|---|
| 1.2.6 | Audit policy | Configure manually (see above) |
| 1.2.16 | Admission control: PodSecurity | Enabled by default in openCenter |
| 3.2.1 | Network segmentation | Apply sysctl hardening above |
| 4.2.4 | Read-only port 10255 | Disable per above |
| 4.2.6 | Protect kernel defaults | Set in kubelet config |
| 5.1.6 | Service account tokens | Set automountServiceAccountToken: false on unused SAs |
| 5.2.1 | Pod Security Standards | Enforced by default via Kyverno + PSA |
| 5.7.1 | Namespace isolation | Applied via tenant onboarding NetworkPolicies |
Verification
Run a CIS benchmark scan with kube-bench:
# Deploy kube-bench as a Job
kubectl apply -f https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yaml
# View results
kubectl logs job/kube-bench
Expected: all PASS for items above. WARN items indicate optional hardening beyond this guide.