Перейти к содержанию

Security и service mesh

Эта страница описывает текущую целевую модель безопасности stage-окружения Univex: секреты, service mesh, Tailscale-сегментацию, сетевые политики, hardening контейнеров и обязательные проверки перед релизом.


Базовая модель

Контур Требование
Внешний трафик Только HTTPS/WSS через ingress с cert-manager TLS
Внутренний трафик Istio sidecar + STRICT mTLS для stage workloads
Межсервисный доступ AuthorizationPolicy allowlist по service account и порту
Секреты Только Kubernetes Secrets / SealedSecrets; plaintext secrets не коммитятся
Контейнеры Non-root UID/GID 65532, allowPrivilegeEscalation: false, RuntimeDefault seccomp, drop all capabilities
ServiceAccount token automountServiceAccountToken: false для приложений, которые не вызывают Kubernetes API
Админ-доступ JWT + Casbin RBAC; для high-risk операций требуется step-up 2FA
Клиентская пагинация offset валидируется, limit нормализуется до максимума 200

Istio service mesh

Stage namespace stage должен быть включен в mesh:

metadata:
  labels:
    istio-injection: enabled

Целевая политика находится в исходных репозиториях:

  • univex/stage-deployments/istio-mtls.yaml
  • exchange/deployment/istio-mtls.yaml

Ключевые правила:

  • namespace-level PeerAuthentication использует STRICT;
  • у каждого stage Service есть selector-scoped PeerAuthentication в STRICT;
  • общий DestinationRule включает ISTIO_MUTUAL для *.stage.svc.cluster.local;
  • чувствительные сервисы закрываются AuthorizationPolicy allowlist-ами.

Минимальная проверка live-кластера:

KUBECONFIG=../config kubectl -n stage get peerauthentication,destinationrule,authorizationpolicy

Ожидаемо:

  • PeerAuthentication есть для namespace default и для workload selector-ов;
  • DestinationRule stage-mesh-mtls есть;
  • AuthorizationPolicy есть для привилегированных destinations: exchange-admin, exchange-engine, ledger, p2p, id-service, exchange-integration, sumsub-integration, trades-aggregator, redis.

Drift check

Наличие PeerAuthentication STRICT не заменяет AuthorizationPolicy. mTLS подтверждает identity и шифрует трафик, но без destination allowlist любой workload внутри mesh может попытаться обратиться к внутреннему сервису.


Destination allowlist

Для privileged services используется модель allow-by-source-principal. Пример для exchange-admin:

apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: exchange-admin-trusted-callers
  namespace: stage
spec:
  selector:
    matchLabels:
      app: exchange-admin
  action: ALLOW
  rules:
    - from:
        - source:
            principals:
              - cluster.local/ns/stage/sa/exchange-integration
      to:
        - operation:
            ports:
              - "1337"

Для сервисов с метриками отдельное правило может разрешать только metrics-port без source-principal, если этот endpoint не содержит секретов и не выполняет state-changing действий.

Публичные web-frontends (univex-frontend, univex-admin-frontend) тоже живут внутри namespace stage, но не должны быть частью внутреннего trust boundary. Для них действует отдельная модель:

  • входящий HTTP разрешён только от istio-ingressgateway;
  • automountServiceAccountToken: false, non-root UID/GID, dropped capabilities и seccompProfile: RuntimeDefault обязательны;
  • прямые вызовы этих service accounts к workload'ам namespace stage запрещены DENY-политикой frontend-workloads-deny-stage-egress;
  • backend endpoints должны вызываться браузером через публичные домены api.univex.nostress.dev, api.exchange.nostress.dev и admapi.univex.nostress.dev, а не через cluster-local services.

Tailscale segmentation

Univex stage infrastructure uses dedicated Tailscale tags:

  • k8s nodes: tag:univex-k8s;
  • DB host: tag:univex-db;
  • operator group: group:univex-admin.

tag:univex-k8s is allowed to talk to other Univex k8s nodes only on k3s, etcd, kubelet and flannel ports. It can talk to tag:univex-db only on explicit DB ports: PgBouncer 6100-6110 and direct Temporal PostgreSQL 6120. Runtime Temporal traffic stays inside k8s through temporal-frontend.temporal.svc.cluster.local:7233. Cross-project Tailscale grants should not be added to these tags.

The workspace kubeconfig points to https://univex-k8s.tailb72375.ts.net. Server SSH should use tailscale ssh ubuntu@nsc-kub-1, nsc-kub-2, nsc-kub-3 and nsc-db-1. Public SSH is temporary during cutover; final public exposure should be limited to k8s 80/tcp and 443/tcp.

PostgreSQL clients must use TLS over the tailnet. Service databases use TLS-required PgBouncer on 100.123.97.86:6100-6110. Temporal's dedicated PostgreSQL uses direct TLS on 100.123.97.86:6120, without PgBouncer.


Kubernetes hardening

Каждый stage workload должен задавать pod/container hardening явно:

spec:
  automountServiceAccountToken: false
  securityContext:
    runAsNonRoot: true
    runAsUser: 65532
    runAsGroup: 65532
  containers:
    - securityContext:
        runAsNonRoot: true
        runAsUser: 65532
        runAsGroup: 65532
        allowPrivilegeEscalation: false
        seccompProfile:
          type: RuntimeDefault
        capabilities:
          drop:
            - ALL

Если контейнеру нужно слушать порт ниже 1024, добавляется только NET_BIND_SERVICE; остальные capabilities остаются сброшенными.

Проверка live-кластера:

KUBECONFIG=../config kubectl -n stage get deploy \
  -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.template.spec.automountServiceAccountToken}{"\t"}{range .spec.template.spec.containers[*]}{.name}:{.securityContext.runAsNonRoot}:{.securityContext.allowPrivilegeEscalation}:{.securityContext.seccompProfile.type}:{.securityContext.capabilities.drop}{" "}{end}{"\n"}{end}'

Секреты и конфиги

Правила:

  • plaintext Secret, private keys, DB passwords, JWT secrets, admin keys и provider API keys не коммитятся;
  • секреты хранятся как SealedSecret manifests;
  • startup logging не должен сериализовать полный config со значениями секретов;
  • поля config struct с секретами должны иметь json:"-" или выводиться только через safe summary.

Обновление SealedSecret:

kubectl --kubeconfig ../config -n stage create secret generic nsc-secrets \
  --from-literal=KEY_NAME='value' \
  --dry-run=client -o yaml \
  | kubeseal --kubeconfig ../config \
      --controller-name sealed-secrets-controller \
      --controller-namespace kube-system \
      --format yaml \
      --merge-into stage-deployments/sealed-secrets/nsc-secrets.yaml

Admin step-up 2FA

Admin Gateway использует JWT + Casbin RBAC как базовый контроль доступа. Для high-risk действий должен быть дополнительный step-up factor:

  • hotwallet transfer;
  • manual topup / withdraw;
  • AML bypass, AML clean/refund, set refund address;
  • withdraw limits и withdraw availability;
  • fiat settlement actions;
  • forced auth reset;
  • admin create/delete, role change, session revoke;
  • API-key delete;
  • risk category changes.

Требование: middleware должен не только проверять TOTP, если 2FA включена, но и отклонять high-risk операцию, если у администратора 2FA не включена.


Pagination guard

Клиентские offset и limit валидируются на границе HTTP/gRPC:

  • offset < 0 отклоняется;
  • offset > 100000 отклоняется;
  • limit < 0 отклоняется;
  • limit > 200 нормализуется до 200.

Это снижает риск resource exhaustion через большие смещения и выборки. Для новых маршрутов правило простое: не обходить gateway/admin-gateway pagination guard и не добавлять ручные Limit/Offset без такого же clamp/validation.


External HTTP clients

Любой внешний HTTP-клиент должен иметь:

  • общий request timeout;
  • transport timeouts, если клиент long-lived;
  • io.LimitReader или эквивалентный response body cap;
  • отсутствие логирования provider response body с секретами или PII.

Это особенно важно для security-critical путей: SMS/2FA, KYC/KYB, AML и withdrawals.


Release checklist

Перед деплоем:

  1. mkdocs build --strict для документации.
  2. govulncheck ./... в Go-репозиториях с актуальным toolchain.
  3. Проверить live PeerAuthentication, DestinationRule, AuthorizationPolicy.
  4. Проверить pod hardening и automountServiceAccountToken.
  5. Проверить, что новые high-risk admin routes требуют mandatory 2FA.
  6. Проверить, что новые публичные list endpoints соблюдают pagination guard.
  7. Проверить, что новые внешние HTTP clients имеют timeout и response body limit.