G-SERVICE Docs
Архитектура ISP (OSS/BSS)

Безопасность и Комплаенс

Архитектура безопасности, СОРМ, защита данных, сетевая безопасность и соответствие регуляторным требованиям.

Безопасность и Комплаенс

Для оператора связи безопасность — это не опция, а лицензионное требование. Этот раздел описывает архитектуру безопасности платформы, соответствие российским регуляторным нормам и практики защиты данных.

ISP-специфика: В отличие от типичных SaaS-приложений, ISP-оператор обязан соблюдать строгие регуляторные требования (СОРМ, ФЗ-152, ФЗ-374), имеет доступ к sensitive данным абонентов (паспортные данные, адреса, история сессий, трафик) и управляет критической инфраструктурой (сетевое оборудование). Нарушение безопасности может привести к отзыву лицензии на связь, штрафам от РКН и потере доверия абонентов. Монолитные ISP-системы (WHMCS, BillMax) часто хранят все данные в одной БД — наш подход с database-per-service минимизирует blast radius при компрометации отдельного сервиса.

Регуляторные требования (Россия)

СОРМ (Система оперативно-розыскных мероприятий)

Каждый оператор связи обязан обеспечить техническую возможность для проведения ОРМ (Федеральный закон №126-ФЗ "О связи", ст. 64).

Компоненты СОРМ в архитектуре:

Loading diagram...

Уровни СОРМ:

УровеньНазначениеДанные
СОРМ-1Перехват трафикаЗеркалирование на уровне BRAS/коммутатора
СОРМ-2Информация о соединенияхCDR/UDR: кто, куда, когда, сколько
СОРМ-3Хранение данных (закон Яровой)Хранение трафика до 6 мес., метаданных — 3 года

Требования к реализации:

  • Mediation Service формирует и передаёт CDR/UDR в формате, согласованном с ПУ ФСБ.
  • Данные передаются по выделенному защищённому каналу (VPN/VLAN).
  • Логирование запросов от СОРМ ПУ для аудита (с ограниченным доступом).
  • Хранение данных в соответствии со сроками закона Яровой (ФЗ-374).

Роскомнадзор (РКН)

  • Реестр запрещённых сайтов: Интеграция с выгрузкой РКН для фильтрации на DPI/BRAS.
  • Персональные данные (ФЗ-152): Хранение и обработка ПДн только на территории РФ.
  • Отчётность: Регулярная подача статистических форм (номерная ёмкость, количество абонентов).

Архитектура безопасности

Модель Zero Trust

Мы следуем принципу "никому не доверяем по умолчанию":

Loading diagram...

Принципы:

  1. mTLS между сервисами: Все внутренние коммуникации шифруются. Сервисы аутентифицируют друг друга через сертификаты.
  2. NGINX Ingress Controller: Единая точка входа для внешних клиентов. TLS termination + rate limiting + routing. JWT валидация — на уровне ConnectRPC interceptor.
  3. Network Policies: Микросервисы имеют доступ только к тем ресурсам, которые им необходимы (least privilege).
  4. Secrets Management: Все секреты (пароли БД, API-ключи, RADIUS shared secrets) хранятся в Vault / Kubernetes Secrets (encrypted).

Secrets Management: HashiCorp Vault + ESO

Все секреты хранятся в HashiCorp Vault и синхронизируются в Kubernetes через External Secrets Operator (ESO).

Loading diagram...

Типы секретов:

СекретVault EngineРотацияПримечание
DB passwordDatabase (dynamic)Автоматическая (TTL 1h)Vault генерирует уникальные creds для каждого pod
RabbitMQ passwordKV v2ЕжемесячноЧерез ESO → K8s Secret
RADIUS shared secretKV v2Каждые 90 днейУникальный для каждого NAS
mTLS сертификатыPKIАвтоматическая (TTL 24h)Vault CA → cert-manager → pod
API-ключи партнёровKV v2По запросуRevoke немедленно при компрометации
JWT signing keyKV v2ЕжегодноKeycloak RSA key pair

ExternalSecret пример:

# k8s/base/external-secrets/billing-db.yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: billing-db-creds
  namespace: billing
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: ClusterSecretStore
  target:
    name: billing-db-creds
    creationPolicy: Owner
  data:
    - secretKey: DB_DSN
      remoteRef:
        key: secret/data/billing/database
        property: dsn

Правило: Никогда не хардкодим секреты в коде, Dockerfile, ConfigMap или Git. Только Vault → ESO → K8s Secret → env/volume mount.

Аутентификация и Авторизация (платформа)

КомпонентМетодОписание
Customer Portal (ЛК)OAuth 2.0 + PKCEАвторизация абонентов, JWT access/refresh
CRM / Operator UIOIDC (Keycloak)SSO для операторов, ролевая модель (RBAC)
Service-to-ServicemTLS + Service AccountСертификаты, без пользовательских токенов
External PartnersAPI Key + OAuth 2.0 Client CredentialsОграниченный доступ, rate limiting
RADIUS (абонент-сеть)PAP/CHAP/MS-CHAPv2Сетевая аутентификация через FreeRADIUS

RBAC (Role-Based Access Control)

Ролевая модель для операторов CRM (реализуется через Keycloak Realm Roles):

РольKeycloak RoleПрава
viewerisp-viewerЧтение данных клиентов, просмотр заказов
operatorisp-operatorviewer + создание/изменение заказов, работа с тикетами
installerisp-installerПросмотр назначенных заказов, обновление статуса FSM
financeisp-financeoperator + ручные корректировки баланса, возвраты
adminisp-adminfinance + управление продуктами, тарифами, конфигурацией
super_adminisp-superadminadmin + управление пользователями, ролями, аудит

ConnectRPC: JWT-авторизация (interceptor)

Каждый ConnectRPC-запрос проходит через auth interceptor, который валидирует JWT из Keycloak и проверяет роли:

// internal/pkg/auth/connect_interceptor.go
package auth

import (
    "context"
    "connectrpc.com/connect"
    "crypto/rsa"
)

func NewAuthInterceptor(publicKey *rsa.PublicKey, requiredRoles map[string][]string) connect.UnaryInterceptorFunc {
    return func(next connect.UnaryFunc) connect.UnaryFunc {
        return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
            // 1. Extract JWT from Authorization header
            token := extractBearerToken(req.Header().Get("Authorization"))
            if token == "" {
                return nil, connect.NewError(connect.CodeUnauthenticated, fmt.Errorf("missing token"))
            }

            // 2. Validate JWT signature (Keycloak public key, no network call)
            claims, err := validateJWT(token, publicKey)
            if err != nil {
                return nil, connect.NewError(connect.CodeUnauthenticated, err)
            }

            // 3. Check RBAC: does the user have the required role for this RPC?
            procedure := req.Spec().Procedure // e.g. "/billing.v1.BillingService/ManualAdjust"
            if roles, ok := requiredRoles[procedure]; ok {
                if !hasAnyRole(claims.RealmRoles, roles) {
                    return nil, connect.NewError(connect.CodePermissionDenied,
                        fmt.Errorf("requires one of roles: %v", roles))
                }
            }

            // 4. Inject claims into context
            ctx = WithClaims(ctx, claims)
            return next(ctx, req)
        }
    }
}

Конфигурация ролей для RPC-методов:

var rpcRoles = map[string][]string{
    "/billing.v1.BillingService/ManualAdjust":     {"isp-finance", "isp-admin"},
    "/billing.v1.BillingService/GetBalance":        {"isp-viewer", "isp-operator", "isp-finance"},
    "/customer.v1.CustomerService/DeleteCustomer":  {"isp-admin", "isp-superadmin"},
    "/customer.v1.CustomerService/GetCustomer":     {"isp-viewer", "isp-operator"},
    "/product.v1.ProductService/CreateProduct":     {"isp-admin"},
    "/oms.v1.OrderService/UpdateOrderStatus":       {"isp-operator", "isp-installer"},
}

Kubernetes Network Policies

Сервисы изолированы на сетевом уровне — каждый namespace имеет deny-all default + явные allow-правила:

# k8s/base/network-policies/billing-netpol.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: billing-service-netpol
  namespace: billing
spec:
  podSelector:
    matchLabels:
      app: billing-service
  policyTypes: [Ingress, Egress]
  ingress:
    # Разрешить входящие только от Ingress и OMS
    - from:
        - namespaceSelector:
            matchLabels: { name: gateway }
        - namespaceSelector:
            matchLabels: { name: oms }
      ports:
        - port: 8080 # ConnectRPC
        - port: 9090 # Prometheus metrics
  egress:
    # PostgreSQL
    - to:
        - namespaceSelector:
            matchLabels: { name: databases }
      ports:
        - port: 5432
    # RabbitMQ
    - to:
        - namespaceSelector:
            matchLabels: { name: rabbitmq }
      ports:
        - port: 5672
    # DNS
    - to: []
      ports:
        - port: 53
          protocol: UDP

Container Security (Pod Security Standards)

Все pod'ы запускаются в Restricted режиме:

# Фрагмент из Deployment — securityContext
securityContext:
  runAsNonRoot: true
  runAsUser: 65534 # nobody
  runAsGroup: 65534
  fsGroup: 65534
  seccompProfile:
    type: RuntimeDefault
  capabilities:
    drop: [ALL]
  readOnlyRootFilesystem: true
  allowPrivilegeEscalation: false

Правила:

  • Distroless base images — без shell, package manager, лишних утилит.
  • Non-root user — приложение запускается от пользователя nobody (UID 65534).
  • Read-only filesystem — запись только в /tmp (emptyDir volume).
  • No capabilities — все Linux capabilities отключены.
  • Seccomp — RuntimeDefault профиль.

Защита данных

Персональные данные (ПДн)

В соответствии с ФЗ-152:

  • Минимизация: Собираем только необходимые данные.
  • Шифрование at rest: AES-256 для БД с ПДн (Customer Core).
  • Шифрование in transit: TLS 1.3 для всех коммуникаций.
  • Маскирование: В логах и трейсах ПДн маскируются (passport: ***1234).
  • Право на удаление: Процедура анонимизации данных по запросу абонента (с учётом сроков хранения по закону).
  • Согласие: Явное согласие на обработку ПДн при заключении договора.

Аудит

Все критичные действия логируются в неизменяемый Audit Log:

{
  "timestamp": "2024-01-15T12:00:00Z",
  "actor": {
    "type": "operator",
    "id": "user-admin-01",
    "role": "finance",
    "ip": "10.0.1.50"
  },
  "action": "balance.manual_adjust",
  "target": {
    "type": "account",
    "id": "acc-123"
  },
  "details": {
    "amount": 500.0,
    "reason": "Компенсация за простой"
  },
  "correlation_id": "ticket-456"
}

Хранение: Audit log хранится минимум 3 года (требование ФЗ-126), в append-only хранилище.


Сетевая безопасность

Защита RADIUS

  • Shared Secret: Уникальный для каждого NAS, минимум 20 символов, ротация каждые 90 дней.
  • Сегментация: RADIUS-сервер в отдельном VLAN, доступ только от NAS (ACL на уровне сети).
  • Rate Limiting: Защита от brute-force на уровне FreeRADIUS (max_requests).
  • Failover: Два RADIUS-сервера (primary/secondary) для отказоустойчивости.

Защита RabbitMQ

  • TLS: Шифрование всего трафика между нодами кластера и клиентами.
  • SASL/PLAIN + TLS: Аутентификация клиентов (логин/пароль из Vault).
  • ACL: Каждый сервис имеет доступ только к своим топикам (read/write).
  • Encryption at rest: Шифрование данных на диске (зависит от инфраструктуры).

DDoS / Anti-Abuse

  • WAF (ModSecurity на NGINX Ingress) для защиты от injection, XSS, CSRF.
  • Rate Limiting на всех уровнях (см. API-контракты).
  • Anomaly Detection: Мониторинг аномальных паттернов (массовые авторизации, сканирование).
  • Captive Portal: Защита от несанкционированного доступа к сети (MAC spoofing → redirect).

Чек-лист безопасности для деплоя

Secrets & Crypto:

  • Все секреты в Vault → ESO → K8s Secrets, не в коде/Git
  • mTLS между всеми сервисами (Vault PKI → cert-manager)
  • TLS 1.3 на всех внешних точках (NGINX Ingress, ЛК)
  • DB credentials через Vault Database Engine (dynamic, TTL 1h)
  • RADIUS shared secrets уникальны для каждого NAS, ротация 90 дней

Auth & Access:

  • Keycloak RBAC: все роли созданы и назначены (isp-viewerisp-superadmin)
  • ConnectRPC auth interceptor: JWT-валидация + RBAC для каждого RPC-метода
  • Audit log включён для всех критичных операций (balance adjust, delete, role change)
  • Rate limiting на NGINX Ingress (annotations) + ConnectRPC interceptor

Container & Network:

  • Pod Security: runAsNonRoot, readOnlyRootFilesystem, drop ALL capabilities
  • Distroless base images (gcr.io/distroless/static-debian12)
  • Network Policies: deny-all default + explicit allow per namespace
  • RabbitMQ vhost/permissions: каждый сервис → свой vhost + least privilege

Compliance:

  • Маскирование ПДн в логах проверено (slog ReplaceAttr)
  • СОРМ интеграция протестирована с ПУ ФСБ
  • Бэкапы зашифрованы и хранятся в отдельной локации
  • Penetration testing проведён (минимум раз в год)
  • Данные хранятся на территории РФ (ФЗ-152)

Ссылки по теме

On this page