CKS — Restrict API Access
Restrict Access to the Kubernetes API
The API server is the single entry point to the entire Kubernetes cluster. Every kubectl command, controller reconciliation loop, and kubelet heartbeat flows through it. Hardening API access is the first — and most critical — step in cluster security.
API Request Flow
Every request to the API server passes through three gates in order:
Client (kubectl / Pod / controller)
│
▼
┌───────────────────────────────────────────────────┐
│ 1. AUTHENTICATION (AuthN) │
│ Who are you? │
│ • Client certificates (x509) │
│ • Bearer tokens (ServiceAccount, OIDC) │
│ • Anonymous (if enabled) │
├───────────────────────────────────────────────────┤
│ 2. AUTHORIZATION (AuthZ) │
│ Are you allowed to do this? │
│ • RBAC • Node • Webhook • ABAC (legacy) │
├───────────────────────────────────────────────────┤
│ 3. ADMISSION CONTROL │
│ Should this request be modified or rejected? │
│ • Mutating webhooks │
│ • Validating webhooks │
│ • Built-in admission plugins │
└───────────────────────────────────────────────────┘
│
▼
etcd (persisted)
A request must pass all three stages. Failure at any stage returns an error to the client.
Anonymous Authentication
By default, the API server allows anonymous requests — unauthenticated requests are assigned the username system:anonymous and the group system:unauthenticated.
Why This Matters
If RBAC bindings grant permissions to system:unauthenticated or system:anonymous, anyone who can reach the API server over the network can perform those actions — no credentials needed.
Disabling Anonymous Authentication
Edit the API server manifest:
# /etc/kubernetes/manifests/kube-apiserver.yaml
spec:
containers:
- command:
- kube-apiserver
- --anonymous-auth=false # ← Disable anonymous access
- --authorization-mode=Node,RBAC
# ... other flags
Exam Note: Some health check endpoints (
/healthz,/readyz,/livez) require anonymous access. If you disable it completely, ensure your liveness probes can still authenticate, or configure an exception via--authentication-token-webhook-config-file.
Verifying Anonymous Access
# Test anonymous access (should return 401 Forbidden when disabled)
curl -k https://<API_SERVER>:6443/api/v1/namespaces
# With --anonymous-auth=true (default), you get a 403 if RBAC blocks it
# With --anonymous-auth=false, you get a 401 (not even authenticated)
API Server Authorization Modes
The --authorization-mode flag defines which authorization modules the API server uses. Requests are evaluated by each module in order; the first module to allow or deny takes effect.
| Mode | Description | Use in CKS |
|---|---|---|
RBAC |
Role-Based Access Control. Standard and recommended. | Always enabled |
Node |
Restricts kubelet to only access objects related to Pods scheduled on its node. | Always enabled |
Webhook |
Delegates authorization to an external HTTP service. | Used with external policy engines (OPA, etc.) |
ABAC |
Attribute-Based Access Control. Legacy, requires file-based config. | Avoid — deprecated in favor of RBAC |
AlwaysAllow |
Allows all requests. Extremely dangerous. | Never use in production |
AlwaysDeny |
Denies all requests. Only useful for testing. | Not used in practice |
Recommended Configuration
# /etc/kubernetes/manifests/kube-apiserver.yaml
spec:
containers:
- command:
- kube-apiserver
- --authorization-mode=Node,RBAC # ← Order matters
Order matters: Node is listed first so kubelet requests are handled efficiently by the Node authorizer before falling through to RBAC.
Insecure Port (Deprecated)
Older Kubernetes versions supported --insecure-port which served the API over plain HTTP with no authentication or authorization.
# DANGEROUS — DO NOT USE
- --insecure-port=8080 # Serves API without TLS, no auth
- --insecure-bind-address=0.0.0.0
Current Status
| Version | Status |
|---|---|
| ≤ 1.19 | --insecure-port available (default 8080) |
| 1.20+ | --insecure-port=0 is the only accepted value |
| 1.24+ | Flag removed entirely |
CKS Exam Tip: You may still be asked about
--insecure-port. The correct answer is: it should be set to0(disabled) or not present at all. If you see it set to anything else, it’s a security finding.
Admission Controllers
Admission controllers intercept requests after authentication and authorization but before persistence to etcd. They can mutate (modify) or validate (accept/reject) the request.
Security-Critical Admission Controllers
| Plugin | Type | Purpose |
|---|---|---|
NodeRestriction |
Validating | Limits kubelet to modifying only its own Node and Pod objects |
PodSecurity |
Validating | Enforces Pod Security Standards (replaces PodSecurityPolicy) |
AlwaysPullImages |
Mutating | Forces imagePullPolicy: Always — prevents using cached images |
DenyServiceExternalIPs |
Validating | Prevents abuse of externalIPs (CVE-2020-8554) |
ServiceAccount |
Mutating | Injects ServiceAccount tokens into Pods |
LimitRanger |
Mutating | Enforces resource limits if no explicit limits set |
Enabling Admission Plugins
# /etc/kubernetes/manifests/kube-apiserver.yaml
spec:
containers:
- command:
- kube-apiserver
- --enable-admission-plugins=NodeRestriction,PodSecurity,AlwaysPullImages
- --disable-admission-plugins=AlwaysAdmit
Verifying Enabled Admission Plugins
# Check which plugins are compiled in and enabled
kubectl -n kube-system exec kube-apiserver-<node> -- \
kube-apiserver --help 2>&1 | grep enable-admission-plugins
# Or inspect the running manifest
cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep admission
Securing the API Server with TLS
All communication with the API server must be encrypted with TLS. In a kubeadm cluster, this is configured by default — but you should know the relevant flags.
Key API Server TLS Flags
# /etc/kubernetes/manifests/kube-apiserver.yaml
spec:
containers:
- command:
- kube-apiserver
- --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
- --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
- --client-ca-file=/etc/kubernetes/pki/ca.crt
- --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
- --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key
- --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
- --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
- --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
| Flag | Purpose |
|---|---|
--tls-cert-file / --tls-private-key-file |
API server’s own TLS certificate and key (served to clients) |
--client-ca-file |
CA used to verify client certificates (mTLS) |
--kubelet-client-certificate / --kubelet-client-key |
Cert the API server uses when talking to kubelets |
--etcd-cafile / --etcd-certfile / --etcd-keyfile |
Certs for API server → etcd communication |
Checking Certificate Expiration
# Check API server certificate expiration
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -dates
# kubeadm can check all certs at once
kubeadm certs check-expiration
Restricting API Access by Source IP
You can limit which networks can reach the API server.
Using Firewall Rules (Host-Level)
# Allow only management network to reach API server port
iptables -A INPUT -p tcp --dport 6443 -s 10.0.0.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 6443 -j DROP
Using API Server Flags (Kubernetes 1.27+)
# /etc/kubernetes/manifests/kube-apiserver.yaml
spec:
containers:
- command:
- kube-apiserver
- --bind-address=10.0.0.10 # Bind to specific interface, not 0.0.0.0
Using NetworkPolicy for In-Cluster Access
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: restrict-api-access
namespace: kube-system
spec:
podSelector:
matchLabels:
component: kube-apiserver
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- port: 6443
protocol: TCP
Note: NetworkPolicy for API server Pods depends on CNI support and may not work in all environments. Host-level firewall rules are more reliable.
Kubernetes API Server Security Checklist
| Setting | Secure Value | Check Command |
|---|---|---|
| Anonymous auth | --anonymous-auth=false |
grep anonymous-auth /etc/kubernetes/manifests/kube-apiserver.yaml |
| Authorization mode | --authorization-mode=Node,RBAC |
grep authorization-mode /etc/kubernetes/manifests/kube-apiserver.yaml |
| Insecure port | --insecure-port=0 (or absent) |
grep insecure-port /etc/kubernetes/manifests/kube-apiserver.yaml |
| NodeRestriction | Present in --enable-admission-plugins |
grep enable-admission-plugins /etc/kubernetes/manifests/kube-apiserver.yaml |
| TLS enabled | --tls-cert-file and --tls-private-key-file set |
grep tls-cert-file /etc/kubernetes/manifests/kube-apiserver.yaml |
| Bind address | Not 0.0.0.0 in production |
grep bind-address /etc/kubernetes/manifests/kube-apiserver.yaml |
Quick Reference: API Server Security Flags
# One-liner to extract all security-relevant flags from running API server
ps aux | grep kube-apiserver | tr ' ' '\n' | grep -E \
'(anonymous|authorization|insecure|admission|tls-|client-ca|audit|bind-address)'
Practice Questions
Q1. You need to ensure that unauthenticated users cannot access any API endpoint. Which API server flag should you configure, and what value should it have?
Show Answer
Set --anonymous-auth=false in the API server manifest at /etc/kubernetes/manifests/kube-apiserver.yaml.
spec:
containers:
- command:
- kube-apiserver
- --anonymous-auth=false
This ensures all requests must present valid credentials. Without it, requests with no credentials are assigned the system:anonymous identity.
Q2. An API server has --authorization-mode=AlwaysAllow. What is the security impact, and how should you fix it?
Show Answer
AlwaysAllow means every authenticated request is authorized regardless of RBAC policies. Any user or ServiceAccount can perform any action — read Secrets, delete Pods, escalate privileges.
Fix by changing to:
- --authorization-mode=Node,RBAC
This enables the Node authorizer (for kubelet requests) and RBAC (for all other requests).
Q3. Which admission controller prevents kubelets from modifying objects that don’t belong to Pods scheduled on their node?
Show Answer
NodeRestriction. This admission plugin restricts kubelet API requests so that a kubelet can only modify:
- Its own Node object (limited fields)
- Pod objects bound to its node
- Pod status for Pods bound to its node
Enable it with:
- --enable-admission-plugins=NodeRestriction
Q4. You see --insecure-port=8080 in the API server manifest on a Kubernetes 1.19 cluster. What should you do?
Show Answer
Set --insecure-port=0 to disable it. The insecure port serves the API over plain HTTP with no authentication or authorization, which is a critical security vulnerability.
- --insecure-port=0
In Kubernetes 1.20+, this is the only accepted value. In 1.24+, the flag was removed entirely.
Q5. How can you verify which TLS certificate the API server is using and when it expires?
Show Answer
# Find the cert path from the API server manifest
grep tls-cert-file /etc/kubernetes/manifests/kube-apiserver.yaml
# Output: --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
# Check the certificate details and expiration
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -text | head -20
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -dates
# Or use kubeadm to check all certificates at once
kubeadm certs check-expiration
| Previous | Up | Next |
|---|---|---|
| Cluster Hardening | CKS Home | RBAC Best Practices |