nginx — Public Ingress¶
nginx — публичный шлюз OneWallet: терминирует TLS, маршрутизирует трафик из Flutter-приложения, делегирует аутентификацию (auth_request) Auth Center и проксирует read-only запросы напрямую в Payment Manager (баланс, история).
Назначение¶
nginx — единственная публично доступная точка входа в систему OneWallet. Все внешние клиенты (Flutter App, веб-кошелёк, мини-аппы) обращаются исключительно к нему; внутренние сервисы (Auth Center, Payment Manager, TigerBeetle, Redis) находятся в приватной кластерной сети и недоступны напрямую снаружи.
Ключевые функции:
- TLS-терминация и HSTS на публичном домене.
- Аутентификация запросов через директиву
auth_requestк Auth Center (валидация JWT). - Маршрутизация: read-only PM-эндпоинты (
/api/pm/*) — напрямую в PM; всё остальное (/api/*) — в Auth Center. - Inject HMAC-заголовков (
X-Service-Id,X-Timestamp,X-Signature) для запросов в PM от имениnginx-gateway. - Inject
X-User-Idиз ответаauth_requestдля пробрасывания идентификатора пользователя в downstream.
Общая диаграмма с местом nginx в архитектуре — см. корневой /CLAUDE.md.
Routing¶
/api/pm/* → PM direct (с auth_request)¶
Маршруты read-only, ориентированные на быстрый ответ без round-trip через Auth Center как бэкенд:
| Путь | Цель | Назначение |
|---|---|---|
GET /api/pm/accounts/balance |
PM | Текущий баланс пользователя |
GET /api/pm/intents/history |
PM | История платёжных операций |
Поток обработки запроса:
- nginx принимает запрос от Flutter App с заголовком
Authorization: Bearer <JWT>. - Внутренний sub-request
auth_requestк Auth Center валидирует JWT. - При успехе Auth Center возвращает
X-User-Idв response-заголовкеauth_request. - nginx подписывает запрос HMAC-ключом сервиса
nginx-gatewayи проксирует его в PM, передаваяX-User-Id. - PM проверяет HMAC, авторизует операцию по
permissionsключаnginx-gateway(read-only) и отвечает.
Остальные /api/* → Auth Center¶
Все прочие запросы (login, KYC, профили, mini-app каталог, создание интентов и т. д.) проксируются в Auth Center как в самостоятельный backend. Дальнейшие вызовы в PM (например, createIntent → POST /intents) Auth Center делает уже сам — со своим service key auth-center и собственной HMAC-подписью.
Trust Boundary¶
nginx живёт в той же приватной кластерной сети, что и PM, но это не даёт ему привилегий:
- NO-GO: «доверенные» вызовы без подписи. PM обязан проверять HMAC на каждом входящем запросе независимо от источника. Network-level доверие (cluster-internal IP, mTLS между подами) — дополнительный слой защиты, а не замена HMAC.
- Service key
nginx-gatewayимеет минимальные права (allowedOperationTypes: []) — он не может создавать интенты, только дёргать read-only эндпоинты. - HMAC-секрет для
nginx-gatewayхранится вSERVICE_SECRETS(env), не в БД. PM сверяет подпись runtime черезsrc/auth/hmacPlugin.ts. - Любой компромис nginx (RCE, утечка конфига) → атакующий получает только read-only-доступ к балансам/истории; write-операции по-прежнему требуют ключа
auth-center.
Это прямо соответствует кросс-сервисному правилу из корневого CLAUDE.md:
Единый HMAC auth для всех вызовов в PM — никаких «доверенных» вызовов без подписи (
X-Service-Id,X-Timestamp,X-Signature).
Service Key: nginx-gateway¶
В drizzle/seed.ts ключ зарегистрирован как:
{
serviceId: 'nginx-gateway',
permissions: {
allowedOperationTypes: [], // read-only: balance + history only
},
active: true,
}
Поведенческие последствия:
- Пустой
allowedOperationTypes→ любойPOST /intentsот имениnginx-gatewayотклоняется на этапе авторизации в PM. nginx физически не может породить интент даже при ошибке конфигурации location-блока. - Нет
fromAccountOverride/toAccountOverride— переопределять счета этому ключу запрещено. forceResolveне выставлен —X-User-Id, переданный nginx, используется как есть; PM сам резолвит дефолтный кошелёк пользователя для read-операций.
Секрет ключа (SERVICE_SECRETS['nginx-gateway']) обязателен на старте PM — seed-скрипт делает fail-fast, если он отсутствует:
if (!secrets['auth-center'] || !secrets['nginx-gateway'] || ...) {
console.error('SERVICE_SECRETS must include auth-center, nginx-gateway, ...')
process.exit(1)
}
Заголовки между слоями¶
| Заголовок | Откуда | Куда | Назначение |
|---|---|---|---|
Authorization: Bearer <JWT> |
Flutter App | nginx → Auth Center (sub-request) | Валидация пользователя |
X-User-Id |
Auth Center response | nginx → PM | Идентификатор пользователя для read-операций |
X-Service-Id: nginx-gateway |
nginx | PM | Идентификация сервиса |
X-Timestamp |
nginx | PM | Защита от replay (PM требует ±N сек) |
X-Signature |
nginx | PM | HMAC-SHA256 над канонической строкой запроса |
Пример конфигурации (упрощённо)¶
Схема location-блоков (без TLS, rate-limit и логирования — только маршрутизация):
# Внутренний sub-request к Auth Center для валидации JWT
location = /_auth_verify {
internal;
proxy_pass http://auth-center:8080/_verify;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header Authorization $http_authorization;
}
# Read-only PM endpoints
location /api/pm/ {
auth_request /_auth_verify;
auth_request_set $user_id $upstream_http_x_user_id;
# HMAC-заголовки рассчитывает Lua/sidecar и кладёт в переменные
proxy_set_header X-User-Id $user_id;
proxy_set_header X-Service-Id nginx-gateway;
proxy_set_header X-Timestamp $hmac_ts;
proxy_set_header X-Signature $hmac_sig;
proxy_pass http://payment-manager:3000/;
}
# Всё остальное — в Auth Center
location /api/ {
proxy_pass http://auth-center:8080/;
}
Расчёт HMAC внутри nginx делается через ngx_http_lua_module (или sidecar-процесс); ключ nginx-gateway инжектится в окружение nginx-пода так же, как SERVICE_SECRETS в PM.
FAQ¶
Почему PM не доверяет cluster-internal IP nginx и всё равно проверяет HMAC? Defence-in-depth: компромис любого пода в кластере (sidecar, неправильно настроенный NetworkPolicy, утечка через service mesh) не должен автоматически давать доступ к API PM. HMAC привязывает запрос к конкретному ключу + времени, не к сетевому пути.
Может ли nginx создавать интенты от имени пользователя?
Нет. allowedOperationTypes: [] в seed → PM отклонит любой POST /intents с X-Service-Id: nginx-gateway. Создание интентов идёт строго через Auth Center (X-Service-Id: auth-center).
Что произойдёт, если HMAC-подпись неверна?
PM возвращает 401 Unauthorized ещё до маршрутизации в бизнес-логику — это поведение src/auth/hmacPlugin.ts и оно одинаково для всех сервисных ключей, включая nginx-gateway.
Связанные документы¶
- /CLAUDE.md — общая диаграмма OneWallet и кросс-сервисные правила.
- docs/AUTH-POLICIES.md — политики step-up auth (если read-операция требует повышенного уровня).
- docs/dev/integrations/auth-center.md — как Auth Center сам ходит в PM (через тот же HMAC, но со своим ключом).