Skip to main content

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 (restricted profile 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 IDItemAction
1.2.6Audit policyConfigure manually (see above)
1.2.16Admission control: PodSecurityEnabled by default in openCenter
3.2.1Network segmentationApply sysctl hardening above
4.2.4Read-only port 10255Disable per above
4.2.6Protect kernel defaultsSet in kubelet config
5.1.6Service account tokensSet automountServiceAccountToken: false on unused SAs
5.2.1Pod Security StandardsEnforced by default via Kyverno + PSA
5.7.1Namespace isolationApplied 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.