Постановка задачи
Необходимо усилить защиту Kubernetes-кластера от угроз безопасности, внедрить корректные механизмы контроля доступа, шифровать чувствительные данные и защищать сетевые коммуникации между сервисами.
Уровни безопасности
┌──────────────────────────────────────────────────────────────────┐
│ External Security │
│ • WAF, DDoS Protection, SSL/TLS Termination │
└──────────────────────────────────────────────────────────────────┘
│
┌──────────────────────────────────────────────────────────────────┐
│ Cluster Security │
│ • RBAC, Network Policies, Pod Security │
└──────────────────────────────────────────────────────────────────┘
│
┌──────────────────────────────────────────────────────────────────┐
│ Application Security │
│ • Secret Management, Container Scanning, Runtime Security │
└──────────────────────────────────────────────────────────────────┘
│
┌──────────────────────────────────────────────────────────────────┐
│ Data Security │
│ • Encryption at Rest, Encryption in Transit, Backup Security │
└──────────────────────────────────────────────────────────────────┘
1. Role-Based Access Control (RBAC)
Принцип наименьших привилегий
Создавайте отдельные роли для разных команд и сценариев использования.
Роль разработчика (в пределах namespace)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: developer
namespace: development
rules:
# Доступ только на чтение к большинству ресурсов
- apiGroups: [""]
resources: ["pods", "services", "configmaps", "events"]
verbs: ["get", "list", "watch"]
# Может управлять deployments
- apiGroups: ["apps"]
resources: ["deployments", "replicasets"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
# Может просматривать логи и выполнять exec в pod
- apiGroups: [""]
resources: ["pods/log", "pods/exec"]
verbs: ["get", "create"]
# Не может получать доступ к secret
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: developer-binding
namespace: development
subjects:
- kind: Group
name: developers
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: developer
apiGroup: rbac.authorization.k8s.io
Роль для CI/CD pipeline
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: cicd-deployer
namespace: production
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "update", "patch"]
- apiGroups: [""]
resources: ["configmaps", "secrets"]
verbs: ["get", "list", "create", "update", "patch"]
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list", "watch"]
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: gitlab-deployer
namespace: production
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: cicd-deployer-binding
namespace: production
subjects:
- kind: ServiceAccount
name: gitlab-deployer
namespace: production
roleRef:
kind: Role
name: cicd-deployer
apiGroup: rbac.authorization.k8s.io
Аудит разрешений RBAC
# Проверьте, что может делать пользователь
kubectl auth can-i --list [email protected]
# Проверьте конкретное разрешение
kubectl auth can-i delete pods --namespace=production [email protected]
# Выведите все cluster role bindings
kubectl get clusterrolebindings -o wide
2. Pod Security Standards
Restricted Pod Security Policy
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
Безопасная конфигурация Pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: secure-app
spec:
template:
spec:
# Не используйте service account по умолчанию
serviceAccountName: app-service-account
automountServiceAccountToken: false
# Security context на уровне pod
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: myapp:v1.0
# Security context на уровне container
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
# Resource limits предотвращают DoS
resources:
limits:
cpu: "1"
memory: "512Mi"
requests:
cpu: "100m"
memory: "128Mi"
# Используйте tmpfs для каталогов, требующих записи
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /app/cache
volumes:
- name: tmp
emptyDir:
medium: Memory
sizeLimit: 100Mi
- name: cache
emptyDir: {}
3. Network Policies
Default Deny All
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
Разрешить конкретный трафик
# Разрешить трафик от frontend к backend
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-allow-frontend
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
---
# Разрешить трафик от backend к database
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: database-allow-backend
namespace: production
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: backend
ports:
- protocol: TCP
port: 5432
---
# Разрешить egress к внешним API
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-external-api
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
ports:
- protocol: TCP
port: 443
# Разрешить DNS
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
4. Управление Secret
Использование External Secrets Operator с HashiCorp Vault
# Сначала установите External Secrets Operator
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: vault-backend
spec:
provider:
vault:
server: "https://vault.example.com"
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "external-secrets"
serviceAccountRef:
name: external-secrets
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: app-secrets
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
target:
name: app-secrets
creationPolicy: Owner
data:
- secretKey: database-password
remoteRef:
key: production/database
property: password
- secretKey: api-key
remoteRef:
key: production/api
property: key
Шифрование Secret при хранении (at rest)
# /etc/kubernetes/encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <base64-encoded-32-byte-key>
- identity: {}
Примените к kube-apiserver:
# Добавьте в manifest kube-apiserver
spec:
containers:
- command:
- kube-apiserver
- --encryption-provider-config=/etc/kubernetes/encryption-config.yaml
volumeMounts:
- mountPath: /etc/kubernetes/encryption-config.yaml
name: encryption-config
readOnly: true
volumes:
- hostPath:
path: /etc/kubernetes/encryption-config.yaml
type: FileOrCreate
name: encryption-config
5. Безопасность container image
Сканирование image в CI/CD
# .gitlab-ci.yml
container-scan:
stage: security
image:
name: aquasec/trivy:latest
entrypoint: [""]
script:
- trivy image --exit-code 1 --severity CRITICAL $IMAGE_TAG
- trivy image --exit-code 0 --severity HIGH,MEDIUM --format json -o trivy-report.json $IMAGE_TAG
artifacts:
reports:
container_scanning: trivy-report.json
Политика для image с Kyverno
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-signed-images
spec:
validationFailureAction: enforce
rules:
- name: verify-signature
match:
resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "myregistry.com/*"
attestors:
- entries:
- keys:
publicKeys: |
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
---
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-latest-tag
spec:
validationFailureAction: enforce
rules:
- name: require-image-tag
match:
resources:
kinds:
- Pod
validate:
message: "Using 'latest' tag is not allowed"
pattern:
spec:
containers:
- image: "!*:latest"
6. Безопасность service mesh (Istio)
Включить mTLS
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: production
spec:
mtls:
mode: STRICT
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: backend-policy
namespace: production
spec:
selector:
matchLabels:
app: backend
rules:
- from:
- source:
principals:
- "cluster.local/ns/production/sa/frontend"
to:
- operation:
methods: ["GET", "POST"]
paths: ["/api/*"]
7. Audit Logging
Включить audit logs
# /etc/kubernetes/audit-policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# Логируйте ошибки аутентификации на уровне Metadata
- level: Metadata
users: ["system:anonymous"]
verbs: ["*"]
# Логируйте доступ к secret на уровне RequestResponse
- level: RequestResponse
resources:
- group: ""
resources: ["secrets"]
# Логируйте все изменения deployments
- level: RequestResponse
verbs: ["create", "update", "patch", "delete"]
resources:
- group: "apps"
resources: ["deployments"]
# По умолчанию: логирование на уровне Metadata
- level: Metadata
Отправка в SIEM
# ConfigMap Fluentd для audit logs
apiVersion: v1
kind: ConfigMap
metadata:
name: fluentd-config
data:
fluent.conf: |
<source>
@type tail
path /var/log/kubernetes/audit/*.log
pos_file /var/log/fluentd-audit.pos
tag kubernetes.audit
<parse>
@type json
</parse>
</source>
<filter kubernetes.audit>
@type record_transformer
<record>
cluster_name production
</record>
</filter>
<match kubernetes.audit>
@type elasticsearch
host elasticsearch.logging.svc
port 9200
index_name kubernetes-audit
</match>
8. Безопасность etcd
Шифрование коммуникаций etcd
# Проверьте текущее шифрование etcd
ETCDCTL_API=3 etcdctl endpoint health \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
Ограничение доступа к etcd
# Только API server должен иметь доступ к etcd
firewall-cmd --zone=trusted --add-source=<api-server-ip>
firewall-cmd --zone=public --remove-port=2379/tcp
firewall-cmd --zone=public --remove-port=2380/tcp
Регулярные резервные копии etcd с шифрованием
Зашифрованные резервные копии, хранящиеся вне площадки, защищают как от потери данных, так и от инцидентов безопасности:
# Создайте зашифрованную резервную копию
ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-$(date +%Y%m%d).db \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/healthcheck-client.crt \
--key=/etc/kubernetes/pki/etcd/healthcheck-client.key
# Зашифруйте резервную копию перед сохранением
gpg --encrypt --recipient [email protected] /backup/etcd-*.db
Мышление senior-инженера по безопасности
Senior-инженеры понимают, что утечки и взломы чаще происходят из‑за ошибок конфигурации, а не из‑за сложных атак. Один опубликованный secret, container с избыточными привилегиями или отсутствующая network policy могут скомпрометировать весь кластер.
Defense in Depth: несколько перекрывающихся мер контроля гарантируют, что единичный сбой не приведёт к компрометации системы:
- безопасность container image (минимальные base image, сканирование уязвимостей)
- pod security standards (non-root, read-only filesystem)
- network policies (default deny, явное разрешение)
- RBAC (наименьшие привилегии, регулярные ревью доступа)
- управление secret (шифрование at rest, внешние хранилища)
Распространённые векторы атак:
- Kubernetes Secrets кодируются в base64, но не шифруются — требуется дополнительная защита
- network policies по умолчанию позволяют всем pod взаимодействовать — начинайте с deny-all
- токены service account по умолчанию автоматически монтируются — отключайте, когда не нужно
- доступ cluster-admin выдаётся слишком широко — регулярно проводите аудит
Чек-лист аудита безопасности
Уровень кластера
- [ ] RBAC включён и корректно настроен
- [ ] Анонимная аутентификация отключена
- [ ] Audit logging на API server включён
- [ ] etcd зашифрован и доступ ограничен
- [ ] Сертификаты кластера регулярно ротируются
- [ ] Версия Kubernetes актуальна
Уровень сети
- [ ] Настроены network policies по умолчанию: deny
- [ ] Service mesh с включённым mTLS
- [ ] Ingress использует TLS/SSL
- [ ] Нет сервисов, опубликованных через NodePort без необходимости
- [ ] Внешний доступ только через ingress controller
Уровень Pod
- [ ] Pod запускаются не от root
- [ ] Read-only root filesystem
- [ ] Нет privileged containers
- [ ] Определены resource limits
- [ ] Настроены security contexts
- [ ] Токены service account не монтируются автоматически
Уровень image
- [ ] Image просканированы на уязвимости
- [ ] Base image из доверенных источников
- [ ] В production не используются теги latest
- [ ] Image pull policy установлена в Always
- [ ] Проверяется подпись image
Управление Secret
- [ ] Secret зашифрованы при хранении (at rest)
- [ ] Внешнее управление secret (Vault и т. п.)
- [ ] Secret не передаются через environment variables
- [ ] Регулярная ротация secret
- [ ] Нет secret в container image
Связанные статьи Wiki