Múlt héten hajnali 2-kor kaptam riasztást egy payment service-re, ami eldobálta a kéréseket. Az első ösztönöm ugyanaz volt, mint mindig: elő a cluster-admin kubeconfig-gal a csapat wiki oldaláról, aztán nézzük, mi van. Tíz perc alatt megtaláltam a hibát, de másnap reggel a security csapat jelezte, hogy az audit logokban ott virít a session-öm. Jogosan. Az a “temporary” cluster-admin kubeconfig nagyjából nyolc hónapja volt használatban.

Szóval végre nekiültem és összeraktam egy rendes debugging workflow-t. Olyat, ami pontosan annyi hozzáférést ad az ügyeletesnek, amennyire szüksége van, pontosan annyi időre, amennyire kell.

Mi a baj a “Használd a cluster-admint” megközelítéssel?

Minden csapatnál, ahol dolgoztam, ugyanaz a sztori. Valaki létrehoz egy magas jogosultságú kubeconfig-ot “csak vészhelyzetekre.” Aztán bekerül a password managerbe, megosztják az egész csapattal, sosem rotálják. Az audit logokban egy generikus service account csinál dolgokat, és senki nem tudja, ki futtatott kubectl exec-et hajnali 3-kor.

A valódi költség nem csak a biztonsági kockázat. Hanem az, hogy ha valami félremegy, nem tudod rekonstruálni, mi történt. A megosztott hitelesítő adatok megölik az audit trail-t.

1. lépés: Namespace-szintű Debug Role

A cluster-admin helyett csináltam egy Role-t, ami lefedi, amire az ügyeletnek tényleg szüksége van:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: oncall-debug
  namespace: payments
rules:
  - apiGroups: [""]
    resources: ["pods", "events"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["pods/log"]
    verbs: ["get"]
  - apiGroups: [""]
    resources: ["pods/exec", "pods/portforward"]
    verbs: ["create"]
  - apiGroups: ["apps"]
    resources: ["deployments", "replicasets"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["pods/ephemeralcontainers"]
    verbs: ["update"]

Az utolsó szabály a kubectl debug-hoz kell, erről mindjárt lesz szó. A lényeg, hogy ezt egy csoporthoz kötjük, nem egyéni felhasználókhoz:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: oncall-debug
  namespace: payments
subjects:
  - kind: Group
    name: oncall-payments
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: oncall-debug
  apiGroup: rbac.authorization.k8s.io

Az identity provider kezeli, ki van az oncall-payments csoportban. Amikor változik az ügyeleti beosztás, senki nem nyúl a Kubernetes RBAC-hoz. A csoport tagság automatikusan frissül.

2. lépés: Rövid életű hitelesítő adatok

A legnagyobb nyereség az volt, hogy olyan credential-ökre váltottunk, amik lejárnak. OIDC-t használunk az identity providerrel, szóval a kubeconfig egyszerűen meghív egy credential helper-t:

users:
- name: oncall
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1
      command: cred-helper
      args: ["--cluster=prod", "--ttl=30m"]

30 percenként lejár a token. Nincs több elavult kubeconfig a wiki oldalakon. Ha nincs OIDC beállítva, rövid életű kliens tanúsítványokat is használhatsz:

# Kulcs generálás lokálisan
openssl genpkey -algorithm Ed25519 -out oncall.key

# CSR létrehozása az identitásoddal és a csapat csoporttal
openssl req -new -key oncall.key -out oncall.csr \
  -subj "/CN=robert/O=oncall-payments"

Aztán beküldünk egy CertificateSigningRequest-et 30 perces TTL-lel:

apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: oncall-robert-20260319
spec:
  request: <base64-encoded oncall.csr>
  signerName: kubernetes.io/kube-apiserver-client
  expirationSeconds: 1800
  usages:
    - client auth

Jóváhagyás után kapsz egy tanúsítványt, ami pontosan 30 percig érvényes. A /O=oncall-payments a subject-ben a Kubernetes csoportra képeződik le, szóval az RBAC automatikusan életbe lép.

3. lépés: Ephemeral Containerek SSH Helyett

A régi módszer egy rosszul viselkedő pod debuggolására az volt, hogy kubectl exec-kel beléptünk és menet közben telepítettünk eszközöket. Ez működik, amíg rá nem jössz, hogy a distroless image-eidben nincs curl, tcpdump, de még shell sem.

Az ephemeral containerek ezt rendesen megoldják:

kubectl debug -it payment-api-7d4f8b-x2k9n \
  --image=nicolaka/netshoot \
  --target=payment-api \
  -n payments

Ez egy debug containert csatol a futó podhoz anélkül, hogy újraindítaná. A netshoot image-ben minden hálózati eszköz megvan, amire szükséged lehet. A --target flag megosztja a process namespace-t az app containerrel, szóval látod a folyamatait és hálózati kapcsolatait.

Pár dolog, amit a saját bőrömön tanultam:

  • Az ephemeral container ott marad lecsatlakozás után. Nem takarítja el magát. Én egy labelt teszek rá és egy CronJob-bal gyűjtöm össze az elavult debug containereket.
  • A resource limitek számítanak. Ha a podod már közel van a memória limitjéhez, egy ephemeral container tcpdump-pal átlökheti. Állíts be resource request-eket a debug containernek.
  • Nem minden runtime támogatja a process namespace megosztást. Ellenőrizd, hogy a shareProcessNamespace nincs explicit kikapcsolva a pod spec-ben.

4. lépés: Mindent naplózz

OIDC-vel vagy kliens tanúsítványokkal minden API hívás valódi identitáshoz kötődik. De azt is tudni akarod, milyen parancsokat futtattak az exec session-ökben. A Kubernetes audit logging rögzíti az API hívásokat:

apiVersion: audit.k8s.io/v1
kind: Policy
rules:
  - level: RequestResponse
    resources:
      - group: ""
        resources: ["pods/exec", "pods/portforward"]
    verbs: ["create"]
  - level: Metadata
    resources:
      - group: ""
        resources: ["pods/ephemeralcontainers"]
    verbs: ["update"]

Ez naplózza a teljes kérést és választ exec-nél és port-forward-nál, és metaadatokat az ephemeral container létrehozásnál. Küldd el a SIEM-edbe és megvan a teljes nyomvonal arról, ki mit debuggolt és mikor.

Mi változott

Miután ezt bevezettük, az incidenskezelésünk tényleg gyorsabb lett. Ellentmondásosan hangzik, de van oka: a mérnökök nem agyalnak azon, hogy “szabad-e” debuggolniuk valamit. A korlátok egyértelműek, a hozzáférés automatikus az ügyeletesnek, és senkinek nem kell hajnali 2-kor megosztott kubeconfig-ot vadásznia.

A beállítás nagyjából egy napot vett igénybe. Ennek nagy részét az OIDC provider csoportos claim-jeinek helyes konfigurálása tette ki (minden identity providernek megvannak a maga szeszélyei). Az RBAC manifestek namespace-enként talán 30 sor YAML.

Ha még mindig megosztott cluster-admin credential-öket használsz production debugging-hoz, ez egy jó hét, hogy abbahagyd. A Kubernetes blog épp tegnap publikált egy részletes útmutatót pontosan erről a témáról, ami még több mintát mutat be, például access broker-eket és hardveres kulcsokat.

Kezdd a namespace-szintű Role-lal és egy RoleBinding-gal egy csoporthoz. Ez önmagában kiküszöböli a kockázat nagy részét. Add hozzá a rövid életű credential-öket, amikor készen állsz, és az ephemeral containereket, amikor a csapat már kényelmesen használja a workflow-t. Nem kell mindent egyszerre megcsinálni.