Skip to content
Sedat Özdemir
Writing

cloud-native

The Day You Exposed Kube-apiserver to the Internet, You Handed Over the Keys to Your Cluster

If you aren't seeing 401 or 403 errors in your logs, you're either not being targeted or you're already compromised. Let's dive into the dirty realities of Kubernetes security and how to harden your cluster.

Sedat Özdemir
· 4 dk read

If you aren't seeing 401 or 403 errors in your kube-apiserver logs, either nobody is looking for you, or they're already inside and you're just not watching. Welcome to the world of Kubernetes (K8s); it's the peak of orchestration, but also a minefield where a single wrong indentation can hand over your entire infrastructure.

If you don't want to wake up one morning, run kubectl get pods -A, and see a bunch of 'xmrig' pods you don't recognize, we need to stop talking about the textbooks and start talking about the dirty realities of the field. Based on scenarios we’ve encountered during my time at Payten, let's dissect the vulnerabilities that penetrate the very core of the cluster and discuss how to patch them.

RBAC: Distributing Authority or Distributing the Cluster?

RBAC (Role-Based Access Control) is the heart of K8s security, but it's usually the most neglected part. It typically goes like this: A developer wants to install a tool, hits permission errors, someone says 'let's not deal with this now,' and suddenly that famous cluster-admin role is slapped onto a ServiceAccount (SA).

Look mate, as an attacker, the first thing I check when I pivot into a pod is the permissions of the token located at /var/run/secrets/kubernetes.io/serviceaccount/token. If I can list secrets with that token, the cluster belongs to me now.

Here is a defanged example of an RBAC disaster:

# THIS IS A RECIPE FOR DISASTER
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: dev-user-binding
subjects:
- kind: User
  name: developer-x
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: cluster-admin # Why should a single developer be the boss of the whole cluster?
  apiGroup: rbac.authorization.k8s.io

What should you do? The principle of 'Least Privilege' saves lives here. Use Role and RoleBinding specifically; keep ClusterRole only for global cluster-wide tasks. Prevent ServiceAccounts from mounting tokens by default using: automountServiceAccountToken: false.

Secrets: Base64 is NOT Encryption!

I’m still saying this today, and it honestly baffles me. By default, K8s Secret objects are stored unencrypted in etcd. They are merely Base64 encoded. This means if someone grabs an etcd backup or gains unauthorized access to the API server, your database passwords and API keys are effectively plaintext.

# 'Cracking' a secret is this simple:
echo "Z3V2ZW5saWRlZ2lsc2luCg==" | base64 --decode

The Solution:

  1. Encryption at Rest: Enable encryption for data in etcd by adding an EncryptionConfiguration to your API Server config.
  2. External Secrets / Vault: Use operators that store secrets outside the cluster (e.g., HashiCorp Vault, AWS Secrets Manager).

Pod Escape: Jumping from Container to Host

I'm inside a pod, I'm restricted, but I want to be free. If a pod is running with privileged: true or has sensitive host paths mounted, jumping from that pod to the host operating system is child's play.

Here is a pod definition that makes an attacker's mouth water:

apiVersion: v1
kind: Pod
metadata:
  name: host-confused-pod
spec:
  containers:
  - name: exploit-container
    image: example.com/innocent-image:latest
    securityContext:
      privileged: true # This is essentially turning off the container's safety walls
    volumeMounts:
    - mountPath: /host
      name: host-root
  volumes:
  - name: host-root
    hostPath:
      path: /

Related posts