Skip to content

RKE2 Security Hardening Guide

This guide provides comprehensive security hardening practices for production RKE2 deployments, focusing on CIS benchmark compliance, FIPS 140-2 compliance, and defense-in-depth strategies.

CIS Benchmark Compliance

RKE2 includes built-in support for CIS Kubernetes Benchmark v1.9 compliance through the CIS profile.

Enabling CIS Profile

Configure RKE2 with the CIS profile to automatically apply security hardening:

yaml
# /etc/rancher/rke2/config.yaml
profile: "cis"
secrets-encryption: true
write-kubeconfig-mode: "0640"

CIS Profile Features

The CIS profile automatically configures:

  • API Server Hardening: Secure API server flags and authentication
  • etcd Security: Encrypted communication and proper permissions
  • Kubelet Hardening: Secure kubelet configuration
  • Network Policies: Default deny-all network policies
  • Pod Security Standards: Restricted pod security policies
  • RBAC: Least-privilege access controls

Required System Preparation

Create etcd User and Group

CIS compliance requires etcd to run as a non-root user:

bash
# Create etcd group if it doesn't exist
getent group etcd >/dev/null || sudo groupadd --system etcd

# Create etcd user if it doesn't exist
id -u etcd >/dev/null 2>&1 || sudo useradd --system --no-create-home --shell /sbin/nologin --gid etcd etcd

SELinux Configuration (RHEL/CentOS/Rocky)

For RHEL-based systems, ensure SELinux is properly configured:

bash
# Check SELinux status
sestatus

# Ensure SELinux is enforcing
sudo setenforce 1

# Install RKE2 SELinux policies
sudo dnf install -y rke2-selinux

# Verify policies are loaded
sudo semodule -l | grep rke2

AppArmor Configuration (Ubuntu/Debian)

For Ubuntu-based systems, configure AppArmor:

bash
# Check AppArmor status
sudo aa-status

# Ensure AppArmor is enabled
sudo systemctl enable apparmor
sudo systemctl start apparmor

# Load default profiles
sudo apparmor_parser -r /etc/apparmor.d/*

Security Validation

CIS Benchmark Scanning

Using kube-bench

Install and run kube-bench for CIS compliance validation:

bash
# Download kube-bench
curl -Lo kube-bench https://github.com/aquasecurity/kube-bench/releases/latest/download/kube-bench-linux-amd64
chmod +x kube-bench && sudo mv kube-bench /usr/local/bin/

# Run CIS 1.9 benchmark
kube-bench --benchmark cis-1.9

# Generate report
kube-bench --benchmark cis-1.9 --json > cis-report.json

Automated CIS Scanning with Rancher

If using Rancher, enable automated CIS scanning:

yaml
apiVersion: cis.cattle.io/v1
kind: ClusterScan
metadata:
  name: cis-scan-scheduled
spec:
  scanProfileName: "cis-1.9"
  scoreWarning: 80
  runType: "scheduled"
  cronSchedule: "0 2 * * 0"  # Weekly on Sunday at 2 AM

Network Security

Cilium Network Policies

Implement defense-in-depth with Cilium network policies:

yaml
# Default deny-all policy
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  endpointSelector: {}
  ingress: []
  egress: []
---
# Allow necessary system communication
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: allow-system-dns
  namespace: production
spec:
  endpointSelector: {}
  egress:
  - toEndpoints:
    - matchLabels:
        k8s:io.kubernetes.pod.namespace: kube-system
        k8s-app: kube-dns
    toPorts:
    - ports:
      - port: "53"
        protocol: UDP
      - port: "53"
        protocol: TCP

WireGuard Encryption

Enable transparent WireGuard encryption for all pod-to-pod communication:

yaml
# Cilium configuration with WireGuard
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
  name: rke2-cilium
  namespace: kube-system
spec:
  valuesContent: |-
    encryption:
      enabled: true
      type: "wireguard"
    
    # Additional security configurations
    l7Proxy: false  # Disable if not needed for performance
    
    # Enable security monitoring
    hubble:
      enabled: true
      metrics:
        enabled:
          - dns
          - drop
          - tcp
          - flow
          - policy-verdict

Pod Security Standards

Pod Security Policies (Deprecated) vs Pod Security Standards

RKE2 with CIS profile uses Pod Security Standards instead of deprecated Pod Security Policies:

yaml
# Namespace with restricted pod security
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted

Security Context Requirements

Ensure all workloads use secure contexts:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: secure-app
  namespace: production
spec:
  template:
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        runAsGroup: 1000
        fsGroup: 1000
        seccompProfile:
          type: RuntimeDefault
      containers:
      - name: app
        image: nginx:alpine
        securityContext:
          allowPrivilegeEscalation: false
          runAsNonRoot: true
          runAsUser: 1000
          readOnlyRootFilesystem: true
          capabilities:
            drop:
            - ALL
        resources:
          limits:
            cpu: 100m
            memory: 128Mi
          requests:
            cpu: 50m
            memory: 64Mi
        volumeMounts:
        - name: tmp
          mountPath: /tmp
        - name: cache
          mountPath: /var/cache/nginx
      volumes:
      - name: tmp
        emptyDir: {}
      - name: cache
        emptyDir: {}

RBAC Configuration

Least Privilege Access

Implement strict RBAC policies:

yaml
# Service account for application
apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-service-account
  namespace: production
---
# Minimal role for application
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: app-role
  namespace: production
rules:
- apiGroups: [""]
  resources: ["configmaps", "secrets"]
  verbs: ["get", "list"]
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list", "watch"]
---
# Role binding
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: app-role-binding
  namespace: production
subjects:
- kind: ServiceAccount
  name: app-service-account
  namespace: production
roleRef:
  kind: Role
  name: app-role
  apiGroup: rbac.authorization.k8s.io

Cluster-Level Security

yaml
# Deny all cluster admin access by default
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: no-cluster-admin
rules:
- apiGroups: [""]
  resources: ["*"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["apps", "extensions"]
  resources: ["*"]
  verbs: ["get", "list", "watch"]

Certificate Security

Certificate Management

RKE2 automatically manages certificates with proper security:

bash
# View certificate expiration
for cert in /var/lib/rancher/rke2/server/tls/*.crt; do
    echo "=== $cert ==="
    openssl x509 -in "$cert" -noout -dates
done

# Check certificate SANs
openssl x509 -in /var/lib/rancher/rke2/server/tls/serving-kube-apiserver.crt -noout -text | grep -A1 "Subject Alternative Name"

Custom Certificate Authority

For enterprise environments, use a custom CA:

yaml
# /etc/rancher/rke2/config.yaml
cluster-ca-certificate: "/path/to/ca.crt"
cluster-ca-key: "/path/to/ca.key"

Secret Management

External Secrets Operator

Integrate with external secret management:

yaml
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: vault-backend
  namespace: production
spec:
  provider:
    vault:
      server: "https://vault.internal.example.com"
      path: "kv"
      version: "v2"
      auth:
        kubernetes:
          mountPath: "kubernetes"
          role: "rke2-production"
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: app-secrets
  namespace: production
spec:
  refreshInterval: 300s
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: app-secrets
    creationPolicy: Owner
  data:
  - secretKey: database-password
    remoteRef:
      key: app/database
      property: password

Audit Logging

Enable Comprehensive Auditing

yaml
# /etc/rancher/rke2/config.yaml
audit-log-path: "/var/lib/rancher/rke2/server/logs/audit.log"
audit-log-maxage: 30
audit-log-maxbackup: 10
audit-log-maxsize: 100
audit-policy-file: "/etc/rancher/rke2/audit-policy.yaml"

Audit Policy Configuration

yaml
# /etc/rancher/rke2/audit-policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# Log all security-related events
- level: RequestResponse
  resources:
  - group: ""
    resources: ["secrets", "configmaps"]
  - group: "rbac.authorization.k8s.io"
    resources: ["*"]

# Log administrative actions
- level: Request
  users: ["system:admin"]
  verbs: ["create", "update", "patch", "delete"]

# Log failed requests
- level: Metadata
  omitStages:
  - RequestReceived
  namespaces: ["kube-system", "kube-public"]

Runtime Security

Falco Integration

Deploy Falco for runtime threat detection:

yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: falco
  namespace: security
spec:
  selector:
    matchLabels:
      app: falco
  template:
    metadata:
      labels:
        app: falco
    spec:
      serviceAccount: falco
      hostNetwork: true
      hostPID: true
      containers:
      - name: falco
        image: falcosecurity/falco:latest
        securityContext:
          privileged: true
        resources:
          limits:
            cpu: 200m
            memory: 512Mi
        volumeMounts:
        - name: proc
          mountPath: /host/proc
          readOnly: true
        - name: boot
          mountPath: /host/boot
          readOnly: true
        - name: lib-modules
          mountPath: /host/lib/modules
          readOnly: true
        - name: usr
          mountPath: /host/usr
          readOnly: true
        - name: etc
          mountPath: /host/etc
          readOnly: true
      volumes:
      - name: proc
        hostPath:
          path: /proc
      - name: boot
        hostPath:
          path: /boot
      - name: lib-modules
        hostPath:
          path: /lib/modules
      - name: usr
        hostPath:
          path: /usr
      - name: etc
        hostPath:
          path: /etc

Security Monitoring

Security Metrics Collection

Monitor security events and compliance:

yaml
# Prometheus rules for security monitoring
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: security-monitoring
  namespace: monitoring
spec:
  groups:
  - name: security.rules
    rules:
    - alert: UnauthorizedAPIAccess
      expr: increase(apiserver_audit_total{verb="create",objectRef_resource="secrets"}[5m]) > 0
      for: 0m
      labels:
        severity: critical
      annotations:
        summary: "Unauthorized secret access detected"
        
    - alert: PodSecurityViolation
      expr: increase(pod_security_policy_violations_total[5m]) > 0
      for: 0m
      labels:
        severity: warning
      annotations:
        summary: "Pod security policy violation detected"

Compliance Automation

Automated Hardening Script

bash
#!/bin/bash
# RKE2 Security Hardening Automation

set -e

echo "Starting RKE2 security hardening..."

# Create etcd user
getent group etcd >/dev/null || sudo groupadd --system etcd
id -u etcd >/dev/null 2>&1 || sudo useradd --system --no-create-home --shell /sbin/nologin --gid etcd etcd

# Configure SELinux/AppArmor based on OS
if command -v getenforce >/dev/null 2>&1; then
    echo "Configuring SELinux..."
    sudo setenforce 1
    echo "SELINUX=enforcing" | sudo tee /etc/selinux/config
elif command -v aa-status >/dev/null 2>&1; then
    echo "Configuring AppArmor..."
    sudo systemctl enable apparmor
    sudo systemctl start apparmor
fi

# Set proper file permissions
sudo chmod 600 /etc/rancher/rke2/config.yaml
sudo chown root:root /etc/rancher/rke2/config.yaml

# Configure kernel parameters
cat <<EOF | sudo tee /etc/sysctl.d/99-rke2-cis.conf
# Network security
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
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
EOF

sudo sysctl --system

echo "RKE2 security hardening completed!"

This comprehensive security hardening guide ensures your RKE2 deployment meets enterprise security requirements and compliance standards.