Безопасность и Комплаенс
Архитектура безопасности, СОРМ, защита данных, сетевая безопасность и соответствие регуляторным требованиям.
Безопасность и Комплаенс
Для оператора связи безопасность — это не опция, а лицензионное требование. Этот раздел описывает архитектуру безопасности платформы, соответствие российским регуляторным нормам и практики защиты данных.
ISP-специфика: В отличие от типичных SaaS-приложений, ISP-оператор обязан соблюдать строгие регуляторные требования (СОРМ, ФЗ-152, ФЗ-374), имеет доступ к sensitive данным абонентов (паспортные данные, адреса, история сессий, трафик) и управляет критической инфраструктурой (сетевое оборудование). Нарушение безопасности может привести к отзыву лицензии на связь, штрафам от РКН и потере доверия абонентов. Монолитные ISP-системы (WHMCS, BillMax) часто хранят все данные в одной БД — наш подход с database-per-service минимизирует blast radius при компрометации отдельного сервиса.
Регуляторные требования (Россия)
СОРМ (Система оперативно-розыскных мероприятий)
Каждый оператор связи обязан обеспечить техническую возможность для проведения ОРМ (Федеральный закон №126-ФЗ "О связи", ст. 64).
Компоненты СОРМ в архитектуре:
Уровни СОРМ:
| Уровень | Назначение | Данные |
|---|---|---|
| СОРМ-1 | Перехват трафика | Зеркалирование на уровне BRAS/коммутатора |
| СОРМ-2 | Информация о соединениях | CDR/UDR: кто, куда, когда, сколько |
| СОРМ-3 | Хранение данных (закон Яровой) | Хранение трафика до 6 мес., метаданных — 3 года |
Требования к реализации:
- Mediation Service формирует и передаёт CDR/UDR в формате, согласованном с ПУ ФСБ.
- Данные передаются по выделенному защищённому каналу (VPN/VLAN).
- Логирование запросов от СОРМ ПУ для аудита (с ограниченным доступом).
- Хранение данных в соответствии со сроками закона Яровой (ФЗ-374).
Роскомнадзор (РКН)
- Реестр запрещённых сайтов: Интеграция с выгрузкой РКН для фильтрации на DPI/BRAS.
- Персональные данные (ФЗ-152): Хранение и обработка ПДн только на территории РФ.
- Отчётность: Регулярная подача статистических форм (номерная ёмкость, количество абонентов).
Архитектура безопасности
Модель Zero Trust
Мы следуем принципу "никому не доверяем по умолчанию":
Принципы:
- mTLS между сервисами: Все внутренние коммуникации шифруются. Сервисы аутентифицируют друг друга через сертификаты.
- NGINX Ingress Controller: Единая точка входа для внешних клиентов. TLS termination + rate limiting + routing. JWT валидация — на уровне ConnectRPC interceptor.
- Network Policies: Микросервисы имеют доступ только к тем ресурсам, которые им необходимы (least privilege).
- Secrets Management: Все секреты (пароли БД, API-ключи, RADIUS shared secrets) хранятся в Vault / Kubernetes Secrets (encrypted).
Secrets Management: HashiCorp Vault + ESO
Все секреты хранятся в HashiCorp Vault и синхронизируются в Kubernetes через External Secrets Operator (ESO).
Типы секретов:
| Секрет | Vault Engine | Ротация | Примечание |
|---|---|---|---|
| DB password | Database (dynamic) | Автоматическая (TTL 1h) | Vault генерирует уникальные creds для каждого pod |
| RabbitMQ password | KV v2 | Ежемесячно | Через ESO → K8s Secret |
| RADIUS shared secret | KV v2 | Каждые 90 дней | Уникальный для каждого NAS |
| mTLS сертификаты | PKI | Автоматическая (TTL 24h) | Vault CA → cert-manager → pod |
| API-ключи партнёров | KV v2 | По запросу | Revoke немедленно при компрометации |
| JWT signing key | KV 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 UI | OIDC (Keycloak) | SSO для операторов, ролевая модель (RBAC) |
| Service-to-Service | mTLS + Service Account | Сертификаты, без пользовательских токенов |
| External Partners | API 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 | Права |
|---|---|---|
viewer | isp-viewer | Чтение данных клиентов, просмотр заказов |
operator | isp-operator | viewer + создание/изменение заказов, работа с тикетами |
installer | isp-installer | Просмотр назначенных заказов, обновление статуса FSM |
finance | isp-finance | operator + ручные корректировки баланса, возвраты |
admin | isp-admin | finance + управление продуктами, тарифами, конфигурацией |
super_admin | isp-superadmin | admin + управление пользователями, ролями, аудит |
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: UDPContainer 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-viewer→isp-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)
Ссылки по теме
- Мониторинг безопасности: Алерты, логирование ПДн, runbooks — Observability.
- RADIUS: Shared secrets, CoA, vendor attributes — Интеграция с RADIUS.
- Инфраструктура: Kustomize, Pod Security, Network Policies — Технологический стек.
- Принципы: Clean Architecture, DDD, слои зависимостей — Принципы архитектуры.
- API: ConnectRPC interceptors, mTLS, RabbitMQ ACL — API-контракты.
- Термины: Zero Trust, RBAC, ESO, Vault, СОРМ — Глоссарий.