CKS — Restrict API Access

6 minute read

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
# /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 to 0 (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

Updated: