Module 1.5: GUI Security (Kubernetes Dashboard)
Complexity:
[MEDIUM]- Common attack surfaceTime to Complete: 30-35 minutes
Prerequisites: RBAC knowledge from CKA, Module 1.1 (Network Policies)
What You’ll Be Able to Do
Section titled “What You’ll Be Able to Do”After completing this module, you will be able to:
- Configure Kubernetes Dashboard with authentication and least-privilege RBAC
- Audit dashboard deployments for exposed services and overly permissive ServiceAccounts
- Implement network-level restrictions to limit dashboard access to authorized users
- Evaluate whether to deploy, restrict, or remove GUI components in production clusters
Why This Module Matters
Section titled “Why This Module Matters”The Kubernetes Dashboard has been a notorious attack vector. In 2018, Tesla’s Kubernetes cluster was compromised through an exposed dashboard—attackers used it to mine cryptocurrency. A misconfigured dashboard gives attackers full cluster control with a nice GUI.
CKS tests your ability to secure or restrict web-based cluster access.
The Dashboard Risk
Section titled “The Dashboard Risk”┌─────────────────────────────────────────────────────────────┐│ DASHBOARD ATTACK SCENARIO │├─────────────────────────────────────────────────────────────┤│ ││ Common Misconfiguration: ││ ││ Internet ────► Dashboard (exposed) ────► Full cluster ││ access! ││ ││ What goes wrong: ││ ───────────────────────────────────────────────────────── ││ 1. Dashboard exposed without authentication ││ 2. Dashboard uses cluster-admin ServiceAccount ││ 3. Skip button allows anonymous access ││ 4. No NetworkPolicy restricting access ││ ││ Result: ││ ⚠️ Anyone can view secrets ││ ⚠️ Anyone can deploy pods (cryptominers!) ││ ⚠️ Anyone can delete resources ││ ⚠️ Full cluster compromise ││ ││ Real incident: Tesla (2018) ││ └── Attackers mined crypto using exposed dashboard ││ │└─────────────────────────────────────────────────────────────┘Dashboard Security Options
Section titled “Dashboard Security Options”┌─────────────────────────────────────────────────────────────┐│ DASHBOARD ACCESS MODES │├─────────────────────────────────────────────────────────────┤│ ││ Option 1: Don't Install It ││ ───────────────────────────────────────────────────────── ││ Most secure. Use kubectl instead. ││ CLI is more secure than GUI. ││ ││ Option 2: Read-Only Access ││ ───────────────────────────────────────────────────────── ││ Dashboard can view but not modify. ││ Use minimal RBAC permissions. ││ ││ Option 3: Authenticated Access Only ││ ───────────────────────────────────────────────────────── ││ Require token or kubeconfig login. ││ No skip button. ││ ││ Option 4: Internal Access Only ││ ───────────────────────────────────────────────────────── ││ kubectl proxy or port-forward required. ││ No external exposure. ││ │└─────────────────────────────────────────────────────────────┘Stop and think: The Tesla breach happened because their dashboard was exposed without authentication. But the dashboard needed a ServiceAccount with permissions to read secrets. Why would anyone give a dashboard cluster-admin? Think about the convenience-vs-security trade-off that leads to this misconfiguration.
Secure Dashboard Installation
Section titled “Secure Dashboard Installation”Step 1: Deploy Dashboard
Section titled “Step 1: Deploy Dashboard”# Official dashboard installationkubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml
# Verify deploymentkubectl get pods -n kubernetes-dashboardkubectl get svc -n kubernetes-dashboardStep 2: Create Minimal ServiceAccount
Section titled “Step 2: Create Minimal ServiceAccount”# Read-only dashboard service accountapiVersion: v1kind: ServiceAccountmetadata: name: dashboard-readonly namespace: kubernetes-dashboard---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: dashboard-readonlyrules:- apiGroups: [""] resources: ["pods", "services", "configmaps", "namespaces"] verbs: ["get", "list", "watch"]- apiGroups: ["apps"] resources: ["deployments", "daemonsets", "replicasets", "statefulsets"] verbs: ["get", "list", "watch"]---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: dashboard-readonlyroleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: dashboard-readonlysubjects:- kind: ServiceAccount name: dashboard-readonly namespace: kubernetes-dashboardStep 3: Get Access Token
Section titled “Step 3: Get Access Token”# Create token for the service accountkubectl create token dashboard-readonly -n kubernetes-dashboard
# Or create a long-lived secret (older method)cat <<EOF | kubectl apply -f -apiVersion: v1kind: Secretmetadata: name: dashboard-readonly-token namespace: kubernetes-dashboard annotations: kubernetes.io/service-account.name: dashboard-readonlytype: kubernetes.io/service-account-tokenEOF
# Get the tokenkubectl get secret dashboard-readonly-token -n kubernetes-dashboard -o jsonpath='{.data.token}' | base64 -dAccess Methods
Section titled “Access Methods”Method 1: kubectl proxy (Most Secure)
Section titled “Method 1: kubectl proxy (Most Secure)”# Start proxy (only accessible from localhost)kubectl proxy
# Access dashboard at:# http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/Method 2: Port Forward
Section titled “Method 2: Port Forward”# Forward dashboard portkubectl port-forward -n kubernetes-dashboard svc/kubernetes-dashboard 8443:443
# Access at https://localhost:8443# Use token to authenticateMethod 3: NodePort (Less Secure)
Section titled “Method 3: NodePort (Less Secure)”# Expose dashboard as NodePortapiVersion: v1kind: Servicemetadata: name: kubernetes-dashboard-nodeport namespace: kubernetes-dashboardspec: type: NodePort selector: k8s-app: kubernetes-dashboard ports: - port: 443 targetPort: 8443 nodePort: 30443Warning: NodePort exposes on all nodes. Use NetworkPolicy to restrict access!
What would happen if: You deploy the dashboard with a read-only ServiceAccount, but a user logs in with a token from a different ServiceAccount that has cluster-admin. Does the dashboard’s ServiceAccount RBAC protect you? (Hint: the dashboard acts on behalf of the logged-in user.)
Pause and predict: Your team exposes the dashboard via a LoadBalancer Service for convenience. What’s the attack surface compared to
kubectl proxy? List at least 3 additional risks.
Restricting Dashboard Access
Section titled “Restricting Dashboard Access”NetworkPolicy for Dashboard
Section titled “NetworkPolicy for Dashboard”# Only allow access from specific namespace/podsapiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: dashboard-access namespace: kubernetes-dashboardspec: podSelector: matchLabels: k8s-app: kubernetes-dashboard policyTypes: - Ingress ingress: # Only from admin namespace - from: - namespaceSelector: matchLabels: name: admin-access ports: - port: 8443Disable Skip Button
Section titled “Disable Skip Button”The dashboard has a “Skip” button that allows anonymous access. Disable it:
# In dashboard deployment, add argumentspec: containers: - name: kubernetes-dashboard args: - --auto-generate-certificates - --namespace=kubernetes-dashboard - --enable-skip-login=false # Disable skip buttonOr patch existing deployment:
kubectl patch deployment kubernetes-dashboard -n kubernetes-dashboard \ --type='json' \ -p='[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--enable-skip-login=false"}]'Ingress for Dashboard (Production)
Section titled “Ingress for Dashboard (Production)”If you must expose dashboard externally:
apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: kubernetes-dashboard namespace: kubernetes-dashboard annotations: nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" nginx.ingress.kubernetes.io/ssl-redirect: "true" # Client certificate authentication nginx.ingress.kubernetes.io/auth-tls-verify-client: "on" nginx.ingress.kubernetes.io/auth-tls-secret: "kubernetes-dashboard/ca-secret"spec: ingressClassName: nginx tls: - hosts: - dashboard.example.com secretName: dashboard-tls rules: - host: dashboard.example.com http: paths: - path: / pathType: Prefix backend: service: name: kubernetes-dashboard port: number: 443Security Checklist
Section titled “Security Checklist”┌─────────────────────────────────────────────────────────────┐│ DASHBOARD SECURITY CHECKLIST │├─────────────────────────────────────────────────────────────┤│ ││ □ Do you really need the dashboard? ││ └── Consider kubectl or Lens instead ││ ││ □ Minimal RBAC permissions ││ └── Never use cluster-admin ││ └── Read-only if possible ││ ││ □ Skip button disabled ││ └── --enable-skip-login=false ││ ││ □ Access restricted ││ └── kubectl proxy or port-forward ││ └── NetworkPolicy limiting source ││ ││ □ If exposed externally ││ └── TLS required ││ └── mTLS client certificates ││ └── VPN access only ││ ││ □ Token-based authentication only ││ └── Short-lived tokens preferred ││ └── No basic auth ││ │└─────────────────────────────────────────────────────────────┘Real Exam Scenarios
Section titled “Real Exam Scenarios”Scenario 1: Restrict Dashboard RBAC
Section titled “Scenario 1: Restrict Dashboard RBAC”# Check current dashboard permissionskubectl get clusterrolebinding | grep dashboardkubectl describe clusterrolebinding kubernetes-dashboard
# If using cluster-admin, create restricted role insteadcat <<EOF | kubectl apply -f -apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: dashboard-viewerrules:- apiGroups: [""] resources: ["pods", "services", "nodes"] verbs: ["get", "list"]EOF
# Update bindingkubectl delete clusterrolebinding kubernetes-dashboardkubectl create clusterrolebinding kubernetes-dashboard \ --clusterrole=dashboard-viewer \ --serviceaccount=kubernetes-dashboard:kubernetes-dashboardScenario 2: Disable Anonymous Access
Section titled “Scenario 2: Disable Anonymous Access”# Patch dashboard to disable skipkubectl patch deployment kubernetes-dashboard -n kubernetes-dashboard \ --type='json' \ -p='[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--enable-skip-login=false"}]'
# Verifykubectl get deployment kubernetes-dashboard -n kubernetes-dashboard -o yaml | grep skipScenario 3: Apply NetworkPolicy
Section titled “Scenario 3: Apply NetworkPolicy”# Create NetworkPolicy to restrict accesscat <<EOF | kubectl apply -f -apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: dashboard-restrict namespace: kubernetes-dashboardspec: podSelector: matchLabels: k8s-app: kubernetes-dashboard policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: dashboard-access: "true"EOFAlternatives to Dashboard
Section titled “Alternatives to Dashboard”┌─────────────────────────────────────────────────────────────┐│ DASHBOARD ALTERNATIVES │├─────────────────────────────────────────────────────────────┤│ ││ kubectl (CLI) ││ ───────────────────────────────────────────────────────── ││ • Most secure - uses kubeconfig ││ • Full functionality ││ • Scriptable ││ ││ Lens (Desktop App) ││ ───────────────────────────────────────────────────────── ││ • Local GUI application ││ • Uses your kubeconfig ││ • No cluster-side components ││ ││ K9s (Terminal UI) ││ ───────────────────────────────────────────────────────── ││ • Terminal-based GUI ││ • Uses your kubeconfig ││ • Very efficient for operations ││ ││ Rancher/OpenShift Console ││ ───────────────────────────────────────────────────────── ││ • Enterprise-grade ││ • Built-in authentication ││ • More secure by design ││ │└─────────────────────────────────────────────────────────────┘Did You Know?
Section titled “Did You Know?”-
The Tesla breach in 2018 happened because their Kubernetes dashboard was exposed without password protection. Attackers deployed crypto-mining containers.
-
Dashboard v2.0+ disabled the skip button by default. Older versions had it enabled, making anonymous access trivially easy.
-
The dashboard pods themselves need RBAC permissions to read cluster resources. Limiting the dashboard’s ServiceAccount limits what users can see.
-
kubectl proxy is secure because it only binds to localhost and uses your kubeconfig credentials. The dashboard sees your permissions, not elevated ones.
Common Mistakes
Section titled “Common Mistakes”| Mistake | Why It Hurts | Solution |
|---|---|---|
| Using cluster-admin for dashboard | Full cluster access for attackers | Create minimal RBAC |
| Exposing via LoadBalancer | Public internet access | Use kubectl proxy |
| Leaving skip button enabled | Anonymous access possible | —enable-skip-login=false |
| No NetworkPolicy | Any pod can reach dashboard | Restrict ingress sources |
| Not updating dashboard | Known vulnerabilities | Keep updated |
-
Your SOC team discovers an unknown IP address accessing the Kubernetes dashboard at 3 AM. The dashboard is exposed via a LoadBalancer Service, and the attacker is browsing secrets across all namespaces. When you check the dashboard’s ServiceAccount, it’s bound to
cluster-admin. What immediate steps do you take, and how should the dashboard have been deployed to prevent this?Answer
Immediate response: delete or scale down the dashboard deployment to stop the breach, then rotate any secrets the attacker viewed. Long-term fix: never bind the dashboard to `cluster-admin` -- create a read-only ClusterRole with minimal permissions (get/list on specific resources only). Access should be through `kubectl proxy` (binds to localhost only, uses your kubeconfig credentials), not a LoadBalancer. Add a NetworkPolicy to restrict ingress sources, and disable the skip button with `--enable-skip-login=false`. The dashboard should inherit the logged-in user's RBAC permissions, not have its own elevated access. -
A developer reports they can access the Kubernetes dashboard without entering a token — they just click “Skip” and get full visibility into the cluster. The security team is alarmed. What dashboard argument prevents this, and why is the skip button dangerous even if the dashboard’s ServiceAccount has read-only permissions?
Answer
Add `--enable-skip-login=false` to the dashboard container arguments to remove the skip button. Even with read-only permissions, the skip button is dangerous because it allows completely unauthenticated access -- anyone who can reach the dashboard URL can view pod logs, ConfigMaps, environment variables, and service configurations. This reconnaissance data helps attackers plan further attacks. Additionally, if someone later escalates the ServiceAccount permissions (intentionally or accidentally), all anonymous users inherit those elevated permissions. Authentication should always be required. -
During a penetration test, the tester discovers the Kubernetes dashboard is exposed via NodePort 30443. They can reach it from any machine on the corporate network. The dashboard requires a token, but the tester finds a ServiceAccount token in a ConfigMap in the
defaultnamespace. They use it to log in and see workloads. What chain of security failures led to this compromise?Answer
Multiple failures combined: (1) The dashboard was exposed via NodePort instead of using `kubectl proxy` or port-forward, making it accessible from the network. (2) No NetworkPolicy restricted which sources could reach the dashboard pods. (3) A ServiceAccount token was stored in a ConfigMap -- tokens should never be stored in ConfigMaps as they're not encrypted. (4) The token had sufficient permissions to view workloads. The fix requires defense in depth: switch to `kubectl proxy` access, add a NetworkPolicy limiting ingress to the dashboard, remove the token from the ConfigMap, use short-lived tokens via `kubectl create token`, and apply RBAC least privilege. -
Your organization is debating whether to install the Kubernetes dashboard in production. The ops team wants it for convenience; the security team wants to ban it. A compromise is proposed: install it but restrict access. Design a security configuration that makes the dashboard acceptable — cover access method, RBAC, authentication, and network controls.
Answer
A secure dashboard deployment requires four layers: (1) Access method: use `kubectl proxy` only -- this binds to localhost and requires kubeconfig authentication, eliminating network exposure entirely. Never use LoadBalancer, NodePort, or Ingress. (2) RBAC: create a custom ClusterRole with only `get` and `list` verbs on specific resources (pods, services, deployments) -- never use `cluster-admin`. Exclude secrets from viewable resources. (3) Authentication: disable the skip button with `--enable-skip-login=false` and require token-based login with short-lived tokens from `kubectl create token`. (4) Network: apply a NetworkPolicy with `ingress: []` (deny all ingress) so only `kubectl proxy` works. If the ops team needs more than this allows, consider Lens or K9s as alternatives that use local kubeconfig without cluster-side components.
Hands-On Exercise
Section titled “Hands-On Exercise”Task: Secure a Kubernetes dashboard installation.
# Step 1: Install dashboardkubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml
# Step 2: Wait for deploymentkubectl wait --for=condition=available deployment/kubernetes-dashboard -n kubernetes-dashboard --timeout=120s
# Step 3: Create restricted ServiceAccountcat <<EOF | kubectl apply -f -apiVersion: v1kind: ServiceAccountmetadata: name: dashboard-readonly namespace: kubernetes-dashboard---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: dashboard-readonlyrules:- apiGroups: [""] resources: ["pods", "services"] verbs: ["get", "list"]---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: dashboard-readonlyroleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: dashboard-readonlysubjects:- kind: ServiceAccount name: dashboard-readonly namespace: kubernetes-dashboardEOF
# Step 4: Disable skip buttonkubectl patch deployment kubernetes-dashboard -n kubernetes-dashboard \ --type='json' \ -p='[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--enable-skip-login=false"}]'
# Step 5: Create NetworkPolicycat <<EOF | kubectl apply -f -apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: dashboard-ingress namespace: kubernetes-dashboardspec: podSelector: matchLabels: k8s-app: kubernetes-dashboard policyTypes: - Ingress ingress: [] # Deny all ingress - only kubectl proxy worksEOF
# Step 6: Get token for readonly userkubectl create token dashboard-readonly -n kubernetes-dashboard
# Step 7: Access via proxykubectl proxy &echo "Access dashboard at: http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/"
# Cleanupkubectl delete namespace kubernetes-dashboardSuccess criteria: Dashboard requires token, skip is disabled, NetworkPolicy restricts access.
Summary
Section titled “Summary”Dashboard Risks:
- Full cluster access if misconfigured
- Skip button allows anonymous access
- Public exposure invites attacks
Security Measures:
- Minimal RBAC (never cluster-admin)
- Disable skip button
- Use kubectl proxy for access
- NetworkPolicy restrictions
Best Practices:
- Consider not installing dashboard
- Use kubectl, Lens, or K9s instead
- If needed, restrict access heavily
- Token authentication only
Exam Tips:
- Know how to create minimal ServiceAccount
- Know the skip button argument
- Understand kubectl proxy is most secure
Part 1 Complete!
Section titled “Part 1 Complete!”You’ve finished Cluster Setup (10% of CKS). You now understand:
- Network Policies for segmentation
- CIS Benchmarks with kube-bench
- Ingress TLS and security headers
- Metadata service protection
- Dashboard security hardening
Next Part: Part 2: Cluster Hardening - RBAC, ServiceAccounts, and API security.