Learning Platform
Глоссарий Troubleshooting
Урок 15.05 · 22 мин
Продвинутый
best practicesimage securitysecretsnetwork securityauditleast privilege

Production security best practices

Когда первичная защита (SecurityContext + RBAC + PSA) на месте, начинается работа над глубиной обороны: что делать с images, как хранить secrets вне etcd, как изолировать трафик NetworkPolicies, как настраивать audit log. На CKAD это уровень знания концепций — экзамен не требует настроить Falco или Vault, но ожидает что вы понимаете, в каком слое работает каждая практика и почему. В production это превращается в чеклист security review.


Trivy и Docker Scout: сканеры CVE для образов

Image security

containers:
- name: app
  # 1. Pin digest — не tag
  image: registry.example.com/app@sha256:e8c4ab3fb1e2...
  # ИЛИ хотя бы immutable tag
  # image: registry.example.com/app:1.4.2-2026-04-15
Image security layers
minimal baseDistroless (gcr.io/distroless/...), scratch (для Go binaries), alpine. Меньше layers — меньше CVEs. Distroless вообще не имеет shell — attacker не может exec в bash для разведки.
pin digestimage@sha256:... — immutable reference. Tag latest или 1.0 можно перезаписать в registry; digest — криптографически уникален. В production только digests гарантируют идентичность того что протестировано и того что задеплоено.
scan в CITrivy, Snyk, Clair, Anchore. Сканируют image на known CVEs из NVD, Red Hat security advisories. Должны бежать на каждом build, блокировать CI при критических CVEs.
signcosign (sigstore), Notary v2. Подписывают image хешем, верификация при pull. Защищает от supply chain attack (compromised registry, MitM). Verification через admission controller (kyverno, cosigned).
private registryRegistry с authentication. imagePullSecrets type kubernetes.io/dockerconfigjson или OIDC-based pull. Запрещает анонимный pull, аудит логирует кто что скачал.
SBOMSoftware Bill of Materials. SPDX или CycloneDX формат — список всех packages и versions в image. Trivy и syft генерируют автоматически. Для compliance (NIST, SLSA).
WARNING

image: nginx без tag — это nginx:latest, который завтра может быть чем-то совсем другим. Никогда не используйте latest в production — это блокирует rollback (нет конкретной версии для отката) и делает поведение не reproducible.


Pod hardening: must-have checklist

spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    fsGroup: 2000
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: app
    image: registry.example.com/app@sha256:...
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop: [ALL]
        add: [NET_BIND_SERVICE]
    resources:
      requests:
        memory: 64Mi
        cpu: 100m
      limits:
        memory: 256Mi
        cpu: 500m
    volumeMounts:
    - name: tmp
      mountPath: /tmp
  volumes:
  - name: tmp
    emptyDir: {}

Минимальный hardening:

  • runAsNonRoot + runAsUser явный — нельзя бежать под root.
  • allowPrivilegeEscalation: false — no_new_privs.
  • readOnlyRootFilesystem: true + emptyDir для tmp — нельзя писать в /.
  • capabilities drop ALL + явный add для нужных.
  • seccompProfile RuntimeDefault — стандартный seccomp filter блокирует опасные syscalls.
  • resources requests + limits — защита от resource exhaustion.

Secrets management

K8s Secret — это base64-encoded payload в etcd. Это не encryption. Кто получил доступ к etcd backup, получил все Secrets кластера.

Secrets: уровни защиты
encryption at restkube-apiserver конфигурируется EncryptionConfiguration с провайдером (aescbc, aesgcm, kms). Secrets шифруются при записи в etcd, расшифровываются при чтении. Защита от утечки backup etcd. KMS-provider (AWS KMS, Vault) — production-grade.
RBAC ограничениеДать get/list на secrets только нужным SA. Built-in view ClusterRole намеренно НЕ включает secrets — поэтому developers с view видят Pods, но не credentials.
External SecretsExternal Secrets Operator. CRD ExternalSecret декларирует mapping из Vault/AWS Secrets Manager/GCP Secret Manager в K8s Secret. Один canonical source, секреты в etcd только как cache. При компрометации кластера — секреты в SecretsManager защищены отдельным IAM.
CSI Secret StoreSecrets Store CSI Driver монтирует secrets из external source прямо в Pod как файлы, минуя K8s Secret object. Самый чистый вариант — секреты вообще не в etcd.
не env varsSecrets как env vars видны в kubectl describe pod, в crash dumps, в exec в Pod (printenv). Mount как файлы — secret виден только процессу, читающему файл, и автоматически обновляется при ротации.
rotationСменили secret — Pod должен забрать новую версию. Sidecar pattern с inotify, или Reloader operator который рестартует Pods при изменении Secret, или приложение читает файл при каждом use.
DANGER

Default K8s Secret в etcd — это лишь base64, не шифрование. Любой с read доступом к etcd (или его backup) видит все Secrets кластера в plaintext. Encryption at rest на kube-apiserver — must-have для production. KMS-based — best-practice.


Network security

Default-deny NetworkPolicy в каждом namespace — фундамент zero-trust:

# 1. Default-deny всё (ingress + egress)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
  namespace: production
spec:
  podSelector: {}
  policyTypes: [Ingress, Egress]
---
# 2. Allow DNS (нужен почти всем)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns
  namespace: production
spec:
  podSelector: {}
  policyTypes: [Egress]
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: kube-system
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - port: 53
      protocol: UDP
    - port: 53
      protocol: TCP
---
# 3. Specific allow rules для каждого приложения

Для зашифрованного service-to-service:

  • mTLS через service mesh: Istio, Linkerd. Automatic certificate rotation, identity-based authorization, traffic encryption — без изменения кода приложений.
  • SPIFFE / SPIRE identities — стандарт для workload identity, leveraged by Istio/Linkerd.

Least privilege RBAC

Принцип: каждый Pod / SA получает только нужные permissions:

# Plохо: дать app-sa cluster-admin для удобства
# Хорошо: точечная Role
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: app-permissions
  namespace: production
rules:
- apiGroups: [""]
  resources: ["configmaps"]
  resourceNames: ["app-config"]   # ТОЛЬКО этот ConfigMap
  verbs: ["get", "watch"]
Least privilege RBAC layers
resourceNamesОграничить разрешения конкретными именами ресурсов. Pod может читать только свой config, а не все ConfigMaps namespace. Работает только для verbs, указывающих имя: get, update, patch, delete.
отдельный SAКаждое приложение имеет собственный SA. Не используйте default SA для apps. Compromise одного Pod — компрометирует только его permissions.
no impersonate / escalate / bindЭти три verbs — privilege escalation paths. impersonate позволяет выполнять как другой user. escalate — создавать Roles с правами выше своих. bind — давать другим permissions через RoleBinding.
audit + reviewkubectl auth can-i --list для каждого SA. Регулярный review: какие SA имеют доступ к secrets, cluster-admin, etc. RBAC tools: rakkess, rbac-tool.

Audit logging

kube-apiserver может логировать каждый API-запрос в audit log. Это критично для:

  • Incident response — кто что сделал и когда.
  • Compliance (SOC 2, ISO 27001, HIPAA) — proof что доступ контролируется.
  • Detection of malicious activity — например, неожиданные get secrets под compromised SA.
# Пример audit policy на kube-apiserver
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# Логировать всё что касается Secrets — RequestResponse (полный payload)
- level: RequestResponse
  resources:
  - group: ""
    resources: ["secrets"]
# Логировать RBAC изменения
- level: RequestResponse
  resources:
  - group: "rbac.authorization.k8s.io"
    resources: ["*"]
# Минимальный лог для всего остального — Metadata only
- level: Metadata

Apiserver запускается с флагами --audit-policy-file=... и --audit-log-path=.... Audit logs обычно отправляют в SIEM (Splunk, Elastic) для корреляции.

NOTE

На CKAD: знать что audit policy существует и конфигурируется на kube-apiserver. Самим написать policy не требуется — это работа cluster-admin. На задачах вас могут попросить найти audit log при наличии — обычно в /var/log/audit/audit.log на control plane node.


Supply chain: SLSA уровни

SLSA (Supply chain Levels for Software Artifacts, v1.0 release — 2023) — фреймворк для оценки security supply chain. С v1.0 уровни организованы по трекам (Build, Source) и привязаны к конкретному треку. Самый используемый — Build Track:

  • Build L1 — build process documented, есть provenance.
  • Build L2 — provenance подписан, build server tamper-resistant.
  • Build L3 — build platform изолирована, hosted/managed, provenance non-forgeable.

(SLSA 1.0 убрал «Build L4» из активных уровней — пункт hermetic/reproducible builds перенесён в advisory.)

В K8s контексте: подписанные images (cosign), provenance (in-toto attestations), build на hosted CI с trusted builders (GitHub Actions OIDC + sigstore). На CKAD концепции хватит.


Quick production checklist

Production security must-have
PodSecurityContext с runAsNonRoot, allowPrivilegeEscalation false, capabilities drop ALL, seccompProfile RuntimeDefault, readOnlyRootFilesystem.
ImageDigest pinned, scanned in CI, signed (cosign), private registry. Distroless или alpine base.
NamespacePSA enforce baseline minimum, restricted для security-conscious workloads. Default-deny NetworkPolicy + точечные allow.
RBACКаждое приложение — собственный SA. Минимальные permissions через resourceNames. Никаких cluster-admin для apps.
SecretsEncryption at rest на kube-apiserver (KMS). External Secrets для production secrets. Никогда не env vars — всегда file mounts.
Auditkube-apiserver audit policy с RequestResponse на Secrets и RBAC, Metadata на остальном. Centralized в SIEM. Регулярный review RBAC.

Проверка знанийKnowledge check
Production secret хранится как обычный K8s Secret в namespace default. Кластер скомпрометирован, attacker получил etcd backup. Какие secrets в опасности?
ОтветAnswer
Все. K8s Secret — это base64 (не шифрование). Кто читает etcd, видит все Secrets в plaintext. Защита: 1) EncryptionConfiguration на kube-apiserver с KMS (AWS KMS / Vault) — encryption at rest. 2) External Secrets Operator + Vault, чтобы Secrets вообще не лежали в etcd. На CKAD достаточно знать что просто Secret object не шифрован.
Проверка знанийKnowledge check
App нужно скачивать image из приватного registry. Где правильно конфигурировать imagePullSecrets и почему?
ОтветAnswer
На ServiceAccount, который использует Pod. imagePullSecrets на SA автоматически применяется ко всем Pods с этим SA — не нужно дублировать в каждом Pod. Альтернатива — на каждом Pod в spec.imagePullSecrets, но это дублирование, и Pod может быть создан без него — pull provoque ImagePullBackOff.
Проверка знанийKnowledge check
Image: nginx — почему это плохо в production?
ОтветAnswer
Дважды плохо: 1) implicit tag latest, который может перезаписаться в registry — невозможный rollback и non-reproducible deployments. 2) Public Docker Hub image без подписи и без digest pinning — supply chain attack risk. Production: image: registry.example.com/nginx@sha256:abc... — собственный registry, immutable digest.
Проверка знанийKnowledge check
App SA получает permissions list pods. После compromise приложения attacker делает list secrets — get неожиданный 403. Что произошло и хорошо ли это?
ОтветAnswer
RBAC сработал по принципу least privilege: SA имеет только list pods, нет permissions на secrets, apiserver вернул 403 Forbidden. Это правильное поведение — отдельный SA с точечными permissions ограничивает blast radius compromise. Если бы SA имел cluster-admin или view-all, attacker получил бы все secrets.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 5. Production secret хранится как обычный K8s Secret. Кластер скомпрометирован, attacker получил etcd backup. Какие данные раскрыты?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5