security-audit: креды Kafka и PAT Zitadel — из статических Secret'ов вместо Vault Agent

В контуре uralkal роли Vault (security-audit, security-audit-bootstrap) не заведены.
Коллекторы и Job idp-actions-bootstrap монтируют Secret'ы namespace (security-audit-kafka,
kafka-ca) projected volume'ом в /vault/secrets под теми же именами файлов — конфиг
коллекторов (${file:...}, ca_file) не меняется. Из NetworkPolicy убран egress в Vault,
из оверлея uralkal — dependsOn на HelmRelease vault.
This commit is contained in:
emelinda 2026-11-06 17:22:20 +03:00
parent f0014c5d00
commit 3162679524
7 changed files with 59 additions and 93 deletions

View File

@ -104,16 +104,13 @@ DaemonSet `audit-collector-agent` на узлах `node-role.kubernetes.io/contr
rm /tmp/admin.properties
```
3. **Vault** (`secrets/` — KV v2):
- секрет `secrets/data/security-audit/kafka` с ключами `username`, `password`, `ca_crt`;
`ca_crt` — содержимое `ca.crt` из Secret `kafka/kafka-kafka-contour-tls` (CA генерирует чарт `kafka-contour`,
срок 365 дней; при перевыпуске обновить и здесь);
- политика `read` на `secrets/data/security-audit/kafka`;
- роль `security-audit` в `auth/kubernetes`: bound SA `audit-collector` **и** `access-collector`, namespace
`security-audit`, политика выше.
Заводится в `infra/terraform`, ветка `contour`, модуль `vault-platform-secrets` (роли `vault_kubernetes_auth_backend_role`,
KV-секреты).
3. **Креды Kafka — статические Secret'ы** в namespace `security-audit` (роли в Vault не заведены, агент-инжектор
не используется):
- Secret `security-audit-kafka` с ключами `username`, `password` (SCRAM-пользователь `security-audit`);
- Secret `kafka-ca` с ключом `ca.crt` — содержимое `ca.crt` из Secret `kafka/kafka-kafka-contour-tls`
(CA генерирует чарт `kafka-contour`, срок 365 дней; при перевыпуске обновить и здесь).
Оба монтируются projected volume'ом в `/vault/secrets` под именами `kafka-username`, `kafka-password`,
`kafka-ca.crt` — конфиг коллекторов (`${file:...}`, `ca_file`) не меняется.
4. **Образ** `cr.yandex/crp3ccidau046kdj8g9q/opentelemetry-collector/opentelemetry-collector-contrib:0.121.0-amd64`
доступен в registry контура (тот же, что дефолт чарта `opentelemetry-collector-prod` 0.117.1), `regcred` есть в
namespace `security-audit`.
@ -126,7 +123,7 @@ DaemonSet `audit-collector-agent` на узлах `node-role.kubernetes.io/contr
- ../../infrastructure/security-audit/uralkal
```
`dependsOn` в оверлее ждёт HelmRelease `kafka/kafka` и `vault/vault`.
`dependsOn` в оверлее ждёт HelmRelease `kafka/kafka`.
Порядок: сначала должен раскататься провайдер `audit-access` в istiod (`infrastructure/istio-pilot/uralkal/istio-pilot.yaml`,
применяется сразу, как только попадёт в master: кластер его уже подключает). Telemetry из `mesh/` ссылается на этот провайдер по имени. Проверка:
@ -260,9 +257,8 @@ spec:
Метка `istio-injection: disabled` отключила бы инъекцию совсем, даже по метке пода.
- `PeerAuthentication` в репозитории нет, поэтому режим PERMISSIVE. Шлюз шлёт в коллектор по auto-mTLS (оба в mesh),
коллектор в Kafka — тоже через mTLS (`kafka` в mesh), внутри остаётся `SASL_SSL`.
- Vault Agent (`agent-init-first`, pre-populate) + istio-proxy: по паттерну контура (`apps/bi`, `apps/bim`, `apps/cde`…)
`traffic.sidecar.istio.io/excludeOutboundPorts: "8200"`. Init-агент ходит в Vault мимо iptables-перехвата,
даже если `istio-init` отработал раньше.
- Креды Kafka — статические Secret'ы (projected volume в `/vault/secrets`), Vault Agent не используется:
роли `security-audit` в Vault контура нет.
- `holdApplicationUntilProxyStarts`: kafka exporter подключается к брокерам на старте и без готового proxy падает.
- `istio-init` требует `NET_ADMIN`/`NET_RAW`: enforce `privileged` это разрешает, профиль `warn: baseline` даст
предупреждения при создании подов — ожидаемо.
@ -382,8 +378,8 @@ HTTP-probe на pilot-agent `:15020`, а тот ходит в приложени
- **Mesh.** Job в mesh (`sidecar.istio.io/inject=true`, `holdApplicationUntilProxyStarts`). В конце скрипт вызывает
`POST 127.0.0.1:15020/quitquitquit`, istio-proxy завершается, под — `Completed`. STRICT mTLS у Zitadel сейчас нет, но
Job к нему готов.
- **Vault:** роль `security-audit-bootstrap`, `secrets/data/security-audit/zitadel-bootstrap` → `pat` →
`/vault/secrets/zitadel-pat`; `excludeOutboundPorts: 8200` — паттерн контура.
- **PAT:** статический Secret в namespace `security-audit` (роли в Vault нет), монтируется в
`/vault/secrets/zitadel-pat`; имя Secret'а и ключа — в `job.yaml`, сверить с созданным в контуре.
- **Скрипт** (`files/idp-actions-bootstrap.sh`, POSIX sh) идемпотентен:
1. ждёт `/debug/ready` (10 попыток × 15 с);
2. печатает все типы событий инстанса (`known: …`, `POST /admin/v1/events/types/_search`);
@ -405,10 +401,8 @@ HTTP-probe на pilot-agent `:15020`, а тот ходит в приложени
- Топик `security.idp-events`: 3 партиции, `retention.ms=259200000`, RF по контуру, тем же путём, что остальные
топики. Пользователь `security-audit`, ACL не нужны.
- Сервисный пользователь Zitadel `security-audit-bootstrap` (machine user) с ролью `IAM_OWNER` на инстансе.
Нужны `action.target.read/write/delete`, `action.execution.read/write`, `events.read`. Его PAT — во Vault
`secrets/data/security-audit/zitadel-bootstrap`, ключ `pat`.
- Роль Vault `security-audit-bootstrap` в `auth/kubernetes`: bound SA `idp-actions-bootstrap`, namespace `security-audit`,
политика `read` на `secrets/data/security-audit/zitadel-bootstrap`.
Нужны `action.target.read/write/delete`, `action.execution.read/write`, `events.read`. Его PAT — ключ
в статическом Secret'е namespace `security-audit` (см. `base/idp-actions-bootstrap/job.yaml`).
- Feature flag не нужен: в v4.12.2 `actions` в `/v2/features/instance` удалён (reserved), Actions v2 включены всегда.
Проверка API изнутри кластера (ожидается `"https://login.sarex.local.uralkali.com"`):

View File

@ -118,31 +118,24 @@ spec:
sidecar.istio.io/inject: "true"
# Kafka exporter подключается к брокерам на старте: без готового istio-proxy коллектор упадёт.
proxy.istio.io/config: '{"holdApplicationUntilProxyStarts": true}'
# Паттерн контура для Vault + sidecar (apps/*/base): трафик агента к Vault мимо istio-proxy.
traffic.sidecar.istio.io/excludeOutboundPorts: "8200"
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-init-first: "true"
vault.hashicorp.com/agent-pre-populate-only: "true"
vault.hashicorp.com/agent-requests-cpu: "25m"
vault.hashicorp.com/agent-limits-cpu: "100m"
vault.hashicorp.com/auth-path: auth/kubernetes
vault.hashicorp.com/role: security-audit
# Многострочные шаблоны — см. helmrelease-collector.yaml (toYaml | tpl в чарте).
vault.hashicorp.com/agent-inject-secret-kafka-username: secrets/data/security-audit/kafka
vault.hashicorp.com/agent-inject-template-kafka-username: |-
{{ "{{- with secret \"secrets/data/security-audit/kafka\" -}}" }}
{{ "{{ .Data.data.username }}" }}
{{ "{{- end -}}" }}
vault.hashicorp.com/agent-inject-secret-kafka-password: secrets/data/security-audit/kafka
vault.hashicorp.com/agent-inject-template-kafka-password: |-
{{ "{{- with secret \"secrets/data/security-audit/kafka\" -}}" }}
{{ "{{ .Data.data.password }}" }}
{{ "{{- end -}}" }}
vault.hashicorp.com/agent-inject-secret-kafka-ca.crt: secrets/data/security-audit/kafka
vault.hashicorp.com/agent-inject-template-kafka-ca.crt: |-
{{ "{{- with secret \"secrets/data/security-audit/kafka\" -}}" }}
{{ "{{ .Data.data.ca_crt }}" }}
{{ "{{- end -}}" }}
# Креды Kafka — статические Secret'ы namespace (роли security-audit в Vault нет).
# Те же пути /vault/secrets/..., что читает конфиг: ${file:...} и ca_file.
extraVolumes:
- name: kafka-secrets
projected:
sources:
- secret:
name: security-audit-kafka
items:
- {key: username, path: kafka-username}
- {key: password, path: kafka-password}
- secret:
name: kafka-ca
items:
- {key: ca.crt, path: kafka-ca.crt}
extraVolumeMounts:
- {name: kafka-secrets, mountPath: /vault/secrets, readOnly: true}
# Контур-специфичное (CONTOUR, KAFKA_BROKERS) задаётся в оверлее через extraEnvs.
alternateConfig:

View File

@ -51,7 +51,7 @@ spec:
create: false
serviceAccount:
create: true
name: audit-collector # = роль Vault security-audit, bound SA
name: audit-collector # bound SA роли Vault security-audit — если вернёмся на Vault Agent
presets:
logsCollection: {enabled: false}
hostMetrics: {enabled: false}
@ -96,39 +96,31 @@ spec:
hostPath: {path: /var/log/audit, type: DirectoryOrCreate}
- name: otelcol-storage
hostPath: {path: /var/lib/otelcol-audit, type: DirectoryOrCreate}
# Креды Kafka — статические Secret'ы namespace (роли security-audit в Vault нет).
# Projected volume складывает оба Secret'а в /vault/secrets под теми же именами файлов,
# что читает конфиг: ${file:/vault/secrets/kafka-username|kafka-password}, ca_file=kafka-ca.crt.
- name: kafka-secrets
projected:
sources:
- secret:
name: security-audit-kafka
items:
- {key: username, path: kafka-username}
- {key: password, path: kafka-password}
- secret:
name: kafka-ca
items:
- {key: ca.crt, path: kafka-ca.crt}
extraVolumeMounts:
- {name: audit-log, mountPath: /var/log/audit, readOnly: true}
- {name: otelcol-storage, mountPath: /var/lib/otelcol-audit}
- {name: kafka-secrets, mountPath: /vault/secrets, readOnly: true}
# Метка исключает под из webhook'а инъекции Istio ещё до мутации (namespace без istio-injection).
podLabels:
sidecar.istio.io/inject: "false"
podAnnotations:
sidecar.istio.io/inject: "false"
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-init-first: "true"
vault.hashicorp.com/agent-pre-populate-only: "true"
vault.hashicorp.com/agent-requests-cpu: "25m"
vault.hashicorp.com/agent-limits-cpu: "100m"
vault.hashicorp.com/auth-path: auth/kubernetes
vault.hashicorp.com/role: security-audit
# Чарт рендерит podAnnotations через toYaml | tpl: однострочный шаблон toYaml переносит,
# и литерал {{ "..." }} рвётся. Поэтому по строке на действие (block scalar не переносится).
vault.hashicorp.com/agent-inject-secret-kafka-username: secrets/data/security-audit/kafka
vault.hashicorp.com/agent-inject-template-kafka-username: |-
{{ "{{- with secret \"secrets/data/security-audit/kafka\" -}}" }}
{{ "{{ .Data.data.username }}" }}
{{ "{{- end -}}" }}
vault.hashicorp.com/agent-inject-secret-kafka-password: secrets/data/security-audit/kafka
vault.hashicorp.com/agent-inject-template-kafka-password: |-
{{ "{{- with secret \"secrets/data/security-audit/kafka\" -}}" }}
{{ "{{ .Data.data.password }}" }}
{{ "{{- end -}}" }}
vault.hashicorp.com/agent-inject-secret-kafka-ca.crt: secrets/data/security-audit/kafka
vault.hashicorp.com/agent-inject-template-kafka-ca.crt: |-
{{ "{{- with secret \"secrets/data/security-audit/kafka\" -}}" }}
{{ "{{ .Data.data.ca_crt }}" }}
{{ "{{- end -}}" }}
# Контур-специфичное (CONTOUR, KAFKA_BROKERS) задаётся в оверлее через extraEnvs.
# Полный конфиг вместо мержа с дефолтами чарта.

View File

@ -19,19 +19,6 @@ spec:
annotations:
sidecar.istio.io/inject: "true"
proxy.istio.io/config: '{"holdApplicationUntilProxyStarts": true}'
traffic.sidecar.istio.io/excludeOutboundPorts: "8200" # Vault мимо istio-proxy (паттерн контура)
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-init-first: "true"
vault.hashicorp.com/agent-pre-populate-only: "true"
vault.hashicorp.com/agent-requests-cpu: "25m"
vault.hashicorp.com/agent-limits-cpu: "100m"
vault.hashicorp.com/auth-path: auth/kubernetes
vault.hashicorp.com/role: security-audit-bootstrap
vault.hashicorp.com/agent-inject-secret-zitadel-pat: secrets/data/security-audit/zitadel-bootstrap
vault.hashicorp.com/agent-inject-template-zitadel-pat: |-
{{- with secret "secrets/data/security-audit/zitadel-bootstrap" -}}
{{ .Data.data.pat }}
{{- end -}}
spec:
serviceAccountName: idp-actions-bootstrap
restartPolicy: Never
@ -82,6 +69,7 @@ spec:
- {name: script, mountPath: /scripts, readOnly: true}
- {name: events, mountPath: /config, readOnly: true}
- {name: tmp, mountPath: /tmp}
- {name: zitadel-pat, mountPath: /vault/secrets, readOnly: true}
volumes:
- {name: tools, emptyDir: {sizeLimit: 16Mi}}
- {name: tmp, emptyDir: {medium: Memory, sizeLimit: 16Mi}} # заголовки с PAT — только в памяти
@ -89,3 +77,10 @@ spec:
configMap: {name: idp-actions-bootstrap-script}
- name: events
configMap: {name: idp-actions-bootstrap-events}
# PAT machine user'а security-audit-bootstrap — статический Secret (роли в Vault нет).
# TODO(verify): имя Secret'а и ключа сверить с созданным в контуре; скрипт читает PAT_FILE=/vault/secrets/zitadel-pat.
- name: zitadel-pat
secret:
secretName: security-audit-kafka
items:
- {key: zitadel_pat, path: zitadel-pat}

View File

@ -1,4 +1,5 @@
# Коллектору нужны только DNS, Kafka и Vault (агент-инжектор). Входящие — только health-check kubelet'а.
# Коллектору нужны только DNS и Kafka. Входящие — только health-check kubelet'а.
# (Раньше был egress в Vault для агента-инжектора; креды — статические Secret'ы.)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
@ -25,8 +26,3 @@ spec:
ports:
# client-листенер SASL_SSL: bootstrap kafka-kafka-contour и advertised-адреса подов controller-headless
- {port: 9092, protocol: TCP}
- to:
- namespaceSelector:
matchLabels: {kubernetes.io/metadata.name: vault}
ports:
- {port: 8200, protocol: TCP} # vault-vault-contour.vault.svc:8200 (AGENT_INJECT_VAULT_ADDR)

View File

@ -7,8 +7,6 @@ spec:
dependsOn:
- name: kafka
namespace: kafka
- name: vault
namespace: vault
values:
extraEnvs:
- name: CONTOUR

View File

@ -7,8 +7,6 @@ spec:
dependsOn:
- name: kafka
namespace: kafka
- name: vault
namespace: vault
values:
extraEnvs:
- name: CONTOUR