Files
K3S/docs/security.md
Sergey Antropoff 38aaadbfb1 docs: sync addon docs with explicit external/internal service modes
Обновлена документация под новые аддоны (gitlab, redis, mongodb, kafka, kafka-ui, rabbitmq) и новую модель явного выбора зависимостей. Добавлены и унифицированы описания переключателей *_database_mode и *_redis_mode, обновлена таблица зависимостей аддонов, примеры конфигурации и список vault-секретов.
2026-04-29 23:21:04 +03:00

9.3 KiB
Raw Blame History

Безопасность

Стек безопасности: HashiCorp Vault, External Secrets Operator, CrowdSec, Vaultwarden.

HashiCorp Vault

Централизованное управление секретами, PKI, динамические credentials. Подробный README: addons/vault/README.md.

Быстрый старт

# group_vars/all/addons.yml
addon_vault: true

vault_mode: "standalone"            # standalone | ha
vault_auto_unseal_type: "none"      # none | k8s | aws | gcp | azure | transit
vault_injector_enabled: true        # Vault Agent Injector
vault_ingress_host: "vault-hc.example.com"
vault_ingress_tls: true
make addon-vault

Режимы auto-unseal

Тип Описание Где хранятся ключи
none Ручной unseal при каждом рестарте Нигде — только у оператора
k8s Ключи в Kubernetes Secret + unsealer Pod Secret vault-unseal-keys
aws AWS KMS AWS KMS ключ
gcp Google Cloud KMS GCP KMS ключ
azure Azure Key Vault Azure Key Vault
transit Другой Vault (transit engine) Другой Vault

Для homelab рекомендуется k8s:

vault_auto_unseal_type: "k8s"
vault_auto_unseal_k8s_shares: 5
vault_auto_unseal_k8s_threshold: 3

HA режим (3 ноды, Raft)

vault_mode: "ha"
vault_ha_replicas: 3
vault_auto_unseal_type: "k8s"   # обязателен для HA

Инициализация (первый запуск)

После установки Ansible автоматически:

  1. Инициализирует Vault (vault operator init)
  2. Сохраняет ключи unseal в Secret (при vault_auto_unseal_type: k8s)
  3. Unseals Vault
  4. Выводит root token

Vault Agent Injector — инжекция секретов в поды

# Аннотации на Pod/Deployment:
annotations:
  vault.hashicorp.com/agent-inject: "true"
  vault.hashicorp.com/role: "my-app"
  vault.hashicorp.com/agent-inject-secret-config.env: "secret/myapp/config"
  vault.hashicorp.com/agent-inject-template-config.env: |
    {{- with secret "secret/myapp/config" -}}
    DB_PASSWORD={{ .Data.data.db_password }}
    API_KEY={{ .Data.data.api_key }}
    {{- end }}

Секрет будет доступен в поде как /vault/secrets/config.env.

Примеры: как подключать env в манифесты из HashiCorp Vault

Вариант 1 — Vault Agent Injector + source /vault/secrets/*.env

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-with-injector
  namespace: my-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: app-with-injector
  template:
    metadata:
      labels:
        app: app-with-injector
      annotations:
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/role: "my-app"
        vault.hashicorp.com/agent-inject-secret-app.env: "secret/data/myapp/config"
        vault.hashicorp.com/agent-inject-template-app.env: |
          {{- with secret "secret/data/myapp/config" -}}
          DB_PASSWORD={{ .Data.data.db_password }}
          API_KEY={{ .Data.data.api_key }}
          {{- end }}
    spec:
      serviceAccountName: my-app
      containers:
        - name: app
          image: ghcr.io/example/app:latest
          command: ["/bin/sh", "-c"]
          args:
            - |
              set -a
              . /vault/secrets/app.env
              set +a
              exec /app/start

Вариант 2 — Vault → ExternalSecret → envFrom.secretRef

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: app-env
  namespace: my-app
spec:
  secretStoreRef:
    name: vault-backend
    kind: ClusterSecretStore
  target:
    name: app-env
  data:
    - secretKey: DB_PASSWORD
      remoteRef:
        key: secret/myapp
        property: db_password
    - secretKey: API_KEY
      remoteRef:
        key: secret/myapp
        property: api_key
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-with-envfrom
  namespace: my-app
spec:
  template:
    spec:
      containers:
        - name: app
          image: ghcr.io/example/app:latest
          envFrom:
            - secretRef:
                name: app-env

Вариант 3 — отдельные env-переменные через secretKeyRef

env:
  - name: DB_PASSWORD
    valueFrom:
      secretKeyRef:
        name: app-env
        key: DB_PASSWORD
  - name: API_KEY
    valueFrom:
      secretKeyRef:
        name: app-env
        key: API_KEY

Kubernetes Auth Method

# Включить Kubernetes auth в Vault:
vault auth enable kubernetes

vault write auth/kubernetes/config \
  token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
  kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443" \
  kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt

# Создать роль:
vault write auth/kubernetes/role/my-app \
  bound_service_account_names=my-app \
  bound_service_account_namespaces=my-app \
  policies=my-app-policy \
  ttl=24h

Helm чарты с Vault секретами

# Chart values через External Secrets (рекомендуется)
# или через Vault Agent annotations на Pod

# Прямой pull из Vault (в init container):
initContainers:
  - name: vault-auth
    image: vault:latest
    command:
      - sh
      - -c
      - |
        vault login -method=kubernetes role=my-app
        vault kv get -field=password secret/myapp/db > /vault/secrets/db-password
    env:
      - name: VAULT_ADDR
        value: "http://vault.vault.svc.cluster.local:8200"
    volumeMounts:
      - name: vault-secrets
        mountPath: /vault/secrets

External Secrets Operator

Синхронизирует секреты из Vault в Kubernetes Secrets автоматически. Подробный README: addons/external-secrets/README.md.

Быстрый старт

addon_external_secrets: true
addon_vault: true   # рекомендуется — ESO настраивается на Vault автоматически

external_secrets_vault_url: "http://vault.vault.svc.cluster.local:8200"
external_secrets_vault_role_id: "xxx"   # после создания AppRole в Vault

Секрет в vault.yml:

vault_eso_approle_secret_id: "xxx"

Создать AppRole в Vault

vault auth enable approle

vault policy write eso-policy - <<EOF
path "secret/data/*" {
  capabilities = ["read"]
}
EOF

vault write auth/approle/role/eso-role \
  token_policies="eso-policy" \
  token_ttl=1h \
  token_max_ttl=4h

vault read auth/approle/role/eso-role/role-id
vault write -f auth/approle/role/eso-role/secret-id

ExternalSecret — синхронизировать секрет

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: my-app-secrets
  namespace: my-app
spec:
  refreshInterval: 1m
  secretStoreRef:
    name: vault-backend    # ClusterSecretStore
    kind: ClusterSecretStore
  target:
    name: my-app-secret    # имя Kubernetes Secret
    creationPolicy: Owner
  data:
    - secretKey: db-password
      remoteRef:
        key: secret/myapp
        property: db_password
    - secretKey: api-key
      remoteRef:
        key: secret/myapp
        property: api_key

Использование в Deployment:

env:
  - name: DB_PASSWORD
    valueFrom:
      secretKeyRef:
        name: my-app-secret
        key: db-password

CrowdSec

IDS/IPS система обнаружения вторжений. Анализирует логи ingress-nginx. Подробнее: addons/crowdsec/README.md.

addon_crowdsec: true
crowdsec_nginx_bouncer_enabled: true   # блокировать IP через nginx

Vaultwarden

Self-hosted менеджер паролей (Bitwarden-совместимый). Подробнее: addons/vaultwarden/README.md.

addon_vaultwarden: true
vaultwarden_domain: "https://vault.example.com"
vaultwarden_signups_allowed: false

Общие рекомендации

Network Policies (Calico/Cilium)

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all
  namespace: my-app
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-ingress-nginx
  namespace: my-app
spec:
  podSelector:
    matchLabels:
      app: my-app
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              name: ingress-nginx
      ports:
        - port: 8080

Pod Security Standards

# Namespace с restricted policy:
apiVersion: v1
kind: Namespace
metadata:
  name: secure-app
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/audit: restricted

Non-root контейнеры

spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    fsGroup: 1000
  containers:
    - securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        capabilities:
          drop: [ALL]