Rancher is a multi-cluster Kubernetes management platform that provides centralized authentication, RBAC, policy enforcement, and observability. As a control plane managing multiple downstream clusters, Rancher requires comprehensive security hardening to protect cluster access, authentication, and management APIs. This guide covers securing Rancher server, authentication, RBAC, cluster registration, and downstream cluster security.
Integrate with enterprise identity providers:
# Configure OIDC provider (via Rancher UI or API)
# Administration → Users & Authentication → OIDC
# Example OIDC configuration
apiVersion: management.cattle.io/v3
kind: AuthConfig
metadata:
name: oidc
spec:
oidc:
issuer: https://idp.example.com/oauth2/default
authEndpoint: https://idp.example.com/oauth2/default/v1/authorize
tokenEndpoint: https://idp.example.com/oauth2/default/v1/token
userInfoEndpoint: https://idp.example.com/oauth2/default/v1/userinfo
jwksUrl: https://idp.example.com/oauth2/default/v1/keys
clientId: rancher-client-id
clientSecret: <secret>
groupsClaim: groups
usernameClaim: email
Disable local admin fallback:
# After configuring SSO, disable local authentication
# Administration → Users & Authentication → Auth Provider
# Select your provider and click "Disable Local Auth"
# Or via API
curl -u admin:password -X PUT \
-H "Content-Type: application/json" \
-d '{"enabled": false}' \
https://rancher.example.com/v3/authConfigs/local
Configure MFA at identity provider:
Use least-privilege roles:
# Global roles
apiVersion: management.cattle.io/v3
kind: GlobalRole
metadata:
name: cluster-viewer
rules:
- apiGroups:
- management.cattle.io
resources:
- clusters
verbs:
- get
- list
- watch
Create project-specific roles:
# Project role binding
apiVersion: management.cattle.io/v3
kind: ProjectRoleBinding
metadata:
name: prb-developer
namespace: project-id
spec:
projectName: project-id
userName: user-abc123
roleTemplateName: project-member
Avoid Cluster-Owner overuse:
# Check cluster-owner bindings
kubectl get clusterrolebindings -o json | \
jq '.items[] | select(.roleRef.name == "cluster-owner") | .subjects'
# Use Cluster-Member or custom roles instead
# Administration → Users & Authentication → Users
# Edit user → Global Permissions → Uncheck "Cluster Owner"
Audit RBAC permissions:
# List all global role bindings
kubectl get globalrolebindings -A
# List all cluster role bindings
kubectl get clusterrolebindings | grep -E "cluster-owner|cluster-member"
# Find users with excessive permissions
kubectl get users -o json | jq '.items[] | {name: .metadata.name, roles: .spec}'
Limit who can create/import clusters:
# Create custom global role for cluster creation
apiVersion: management.cattle.io/v3
kind: GlobalRole
metadata:
name: cluster-creator
rules:
- apiGroups:
- management.cattle.io
resources:
- clusters
verbs:
- create
- apiGroups:
- management.cattle.io
resources:
- clustertemplates
verbs:
- get
- list
- watch
Configure default user role:
# Set default user role via Rancher UI
# Administration → Settings → Default User Role
# Select "Standard User" instead of "User Base"
# Or via API
curl -u admin:password -X PUT \
-H "Content-Type: application/json" \
-d '{"value": "user"}' \
https://rancher.example.com/v3/settings/default-user-role
Rotate registration tokens after onboarding:
# Get current registration token
kubectl -n cattle-system get secret cattle-credentials -o json
# Rotate token
kubectl -n cattle-system delete secret cattle-credentials
# Rancher will regenerate automatically
# Download new registration command from UI
Restrict token usage:
# Cluster registration token with expiration
apiVersion: management.cattle.io/v3
kind: ClusterRegistrationToken
metadata:
name: token-limited
namespace: cluster-id
spec:
clusterName: cluster-id
ttl: 86400 # 24 hours
Remove stale tokens:
# List all registration tokens
kubectl get clusterregistrationtokens --all-namespaces
# Delete old/unused tokens
kubectl delete clusterregistrationtoken old-token -n cluster-id
Verify downstream cluster certificates:
# Check certificate expiration
kubectl get secret -n cattle-system cattle-ca -o json | \
jq '.data."ca.crt"' | tr -d '"' | base64 -d | \
openssl x509 -noout -dates
# Check agent connection
kubectl -n cattle-system logs -l app=cattle-cluster-agent
Configure certificate rotation:
# Rotate certificates via Rancher UI
# Cluster Management → Cluster → Rotate Certificates
# Or via kubectl
kubectl -n cattle-system create job cert-rotate \
--image=rancher/rancher-agent:latest \
-- /usr/bin/rotate-certificates
Identify disconnected clusters:
# Check cluster agent status
kubectl get clusters.management.cattle.io -o json | \
jq '.items[] | {name: .metadata.name, connected: .status.connected}'
# Find disconnected agents
kubectl get pods --all-namespaces | grep -E "cattle|agent" | grep -v "Running"
Clean up disconnected clusters:
# Remove disconnected cluster
kubectl delete clusters.management.cattle.io disconnected-cluster
# Clean up finalizers if stuck
kubectl patch clusters.management.cattle.io disconnected-cluster \
-p '{"metadata":{"finalizers":[]}}' --type=merge
Expose Rancher only via HTTPS:
# Ingress configuration with TLS
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: rancher
namespace: cattle-system
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
tls:
- hosts:
- rancher.example.com
secretName: tls-rancher
rules:
- host: rancher.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: rancher
port:
number: 80
Configure strong TLS settings:
# Nginx ingress TLS configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-configuration
namespace: ingress-nginx
data:
ssl-protocols: "TLSv1.2 TLSv1.3"
ssl-ciphers: "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384"
ssl-prefer-server-ciphers: "true"
hsts: "true"
hsts-include-subdomains: "true"
hsts-max-age: "31536000"
Use Let’s Encrypt certificates:
# cert-manager Certificate
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: tls-rancher
namespace: cattle-system
spec:
commonName: rancher.example.com
dnsNames:
- rancher.example.com
issuerRef:
kind: ClusterIssuer
name: letsencrypt-prod
secretName: tls-rancher
Restrict access by IP:
# Nginx ingress IP whitelist
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: rancher
namespace: cattle-system
annotations:
nginx.ingress.kubernetes.io/whitelist-source-range: "10.0.0.0/8,192.168.0.0/16"
spec:
rules:
- host: rancher.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: rancher
port:
number: 80
Use VPN for admin access:
Install Rancher Backup Operator:
# Install via Helm
helm repo add rancher-latest https://releases.rancher.com/server-charts/latest
helm install rancher-backup rancher-latest/rancher-backup \
-n cattle-resources-system --create-namespace
Configure encrypted backups:
# Backup resource with encryption
apiVersion: resources.cattle.io/v1
kind: Backup
metadata:
name: rancher-backup
namespace: cattle-resources-system
spec:
retentionCount: 10
storageLocation:
s3:
bucketName: rancher-backups
endpoint: s3.amazonaws.com
region: us-west-2
credentialSecretName: s3-credentials
encryptionConfigSecretName: encryption-key
Test restore procedure:
# Create Restore resource
apiVersion: resources.cattle.io/v1
kind: Restore
metadata:
name: rancher-restore
namespace: cattle-resources-system
spec:
backupFilename: rancher-backup
prune: true
Check current version:
# Check Rancher version
kubectl -n cattle-system get deploy rancher -o jsonpath='{.spec.template.spec.containers[0].image}'
# Check available versions
curl -s https://releases.rancher.com/server-charts/latest/index.yaml | \
yq '.releases | keys'
Upgrade Rancher:
# Upgrade via Helm
helm repo update rancher-latest
helm upgrade rancher rancher-latest/rancher \
--namespace cattle-system \
--set hostname=rancher.example.com \
--set replicas=3
# Verify upgrade
kubectl -n cattle-system rollout status deploy/rancher
Configure Pod Security Admission:
# Apply PSA to downstream cluster
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/audit-version: latest
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/warn-version: latest
Apply via Rancher UI:
Deploy network policy controller:
# Enable Calico network policies
apiVersion: management.cattle.io/v3
kind: Cluster
metadata:
name: downstream-cluster
spec:
clusterAgentDeploymentCustomization:
overrideAffinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: cattle-cluster-agent
topologyKey: kubernetes.io/hostname
Apply default deny policy:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
Run CIS benchmark scan:
# Deploy kube-bench via Rancher
apiVersion: v1
kind: Pod
metadata:
name: kube-bench
namespace: kube-system
spec:
containers:
- name: kube-bench
image: aquasec/kube-bench:latest
command: ["kube-bench"]
restartPolicy: Never
---
# Run scan
kubectl apply -f kube-bench.yaml
kubectl logs kube-bench
Remediate CIS findings:
Configure Rancher audit logging:
# ClusterFlow for audit logs
apiVersion: logging.banzaicloud.io/v1beta1
kind: ClusterFlow
metadata:
name: rancher-audit
namespace: cattle-system
spec:
filters:
- tag_normaliser: {}
match:
- select:
labels:
app: rancher
outputs:
- name: elasticsearch-output
Forward logs to SIEM:
# ClusterOutput for SIEM integration
apiVersion: logging.banzaicloud.io/v1beta1
kind: ClusterOutput
metadata:
name: siem-output
spec:
elasticsearch:
host: elasticsearch.example.com
port: 9200
scheme: https
ssl_verify: true
buffer:
timekey: 1m
timekey_wait: 30s
timekey_use_utc: true
Watch for security events:
# Watch for failed authentications
kubectl get events --all-namespaces --field-selector reason=FailedScheduling
# Watch for RBAC denials
kubectl get events --all-namespaces | grep -i "forbidden\|unauthorized"
# Watch for pod security violations
kubectl get events --all-namespaces | grep -i "podsecurity"
Configure alerts:
# PrometheusRule for security alerts
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: rancher-security
namespace: cattle-system
spec:
groups:
- name: security
rules:
- alert: RancherHighErrorRate
expr: rate(rancher_api_errors_total[5m]) > 0.1
for: 5m
labels:
severity: warning
annotations:
summary: "High API error rate detected"
- alert: RancherAgentDisconnected
expr: rancher_agent_connected == 0
for: 5m
labels:
severity: warning
annotations:
summary: "Cluster agent disconnected"
Install Rancher Security Scan:
# Install via Rancher UI
# Apps & Marketplace → Security Scan → Install
# Or via Helm
helm install security-scan rancher-latest/security-scan \
-n cattle-system --create-namespace
Run security scan:
# Create scan profile
apiVersion: security-scan.cattle.io/v1
kind: ScanProfile
metadata:
name: cis-profile
spec:
benchmarkVersion: cis-1.6
profile: permissive
skipTests: []
| Control | Status | Notes |
|---|---|---|
| SSO/IdP integrated | ☐ | OIDC, SAML, LDAP |
| Local auth disabled | ☐ | After SSO configured |
| MFA required | ☐ | At IdP level |
| RBAC least privilege | ☐ | No unnecessary cluster-owner |
| Cluster creation restricted | ☐ | Custom roles |
| Registration tokens rotated | ☐ | After onboarding |
| TLS configured | ☐ | Strong ciphers, TLS 1.2+ |
| API access restricted | ☐ | IP whitelist/VPN |
| Backups encrypted | ☐ | S3 with encryption |
| Rancher updated | ☐ | Latest stable version |
| Pod Security Standards | ☐ | Restricted policy |
| Network policies enabled | ☐ | Default deny |
| Audit logging enabled | ☐ | Forward to SIEM |
| Security scanning | ☐ | Regular CIS scans |
Check Rancher security status:
# Check Rancher pods
kubectl -n cattle-system get pods
# Check TLS configuration
kubectl -n cattle-system get secret tls-rancher -o json
# Check authentication providers
kubectl get authconfigs.management.cattle.io
# Check global role bindings
kubectl get globalrolebindings -A
# Check cluster registration tokens
kubectl get clusterregistrationtokens --all-namespaces
Run security audit:
# Check for stale clusters
kubectl get clusters.management.cattle.io -o json | \
jq '.items[] | {name: .metadata.name, connected: .status.connected}'
# Check for privileged role bindings
kubectl get clusterrolebindings -o json | \
jq '.items[] | select(.roleRef.name | contains("admin") or contains("owner"))'
# Check certificate expiration
kubectl -n cattle-system get secret cattle-ca -o json | \
jq -r '.data."ca.crt"' | base64 -d | openssl x509 -noout -dates