Přeskočit na obsah

API Manager

API Manager (Fastify 5) se stará o generování, šifrování, ukládání a rotaci API tokenů pro komunikaci mezi microservices.

Microservices potřebují bezpečné tokeny pro vzájemnou autentizaci. API Manager poskytuje:

  • Centralizované úložiště tokenů — jediný zdroj pravdy
  • Šifrování at-rest — AES-256-GCM s per-record náhodnou solí
  • Auto-rotaci — automatický refresh cyklus každých 7 dní
  • Rate limiting — 100 požadavků/15 min na IP
  • Admin endpointy — status, manuální rotate/revoke, vynucená rotace
  • Port: 3200
  • Health: GET /health
  • Interní URL: http://api-manager:3200
  • Framework: Fastify ^5.9.0 (migrace z Fastify 4 kvůli remediaci CVE — viz CHANGELOG)
  • Runtime: Node.js >=20.0.0
Terminál
GET /v1/token/:service
X-Service-Secret: <SERVICE_SECRET>

API Manager postupuje takto:

  1. Vyhledá :service v šifrovaném /data volume
  2. Pokud token existuje a je mladší než 7 dní → vrátí ho (z cache)
  3. Pokud chybí nebo je expirovaný → zavolá Strapi admin API pro vygenerování nového tokenu
  4. Zašifruje token pomocí AES-256-GCM
  5. Uloží do /data/tokens/:service.enc
  6. Vrátí volajícímu plaintext token
/data/
├── tokens/
│ ├── git-connector.enc
│ ├── cloud-connector.enc
│ └── webhook-publisher.enc

Každý zašifrovaný soubor tokenu je jeden řetězec se 4 hex segmenty oddělenými dvojtečkou — viz Detaily šifrování níže.

Každých 7 dní (konfigurovatelné):

Cron job (běží každých 6 hodin, kontroluje expiraci)
→ Pro každý token: pokud stáří > 7 dní
→ Vyžádá nový token ze Strapi
→ Zašifruje + uloží
→ Pokračuje v obsluze nového tokenu
Terminál
curl -X POST http://api-manager:3200/v1/admin/rotate/git-connector \
-H "X-Admin-Key: <ADMIN_KEY>"
# Odpověď: { "success": true, "message": "Token rotated for git-connector (local)", "data": { ... } }

.env proměnné — dvě vrstvy:

API Manager samotný čte pouze proměnnou ENCRYPTION_KEY (viz api-manager/.env.example) — jeho kód nezná žádný název s prefixem API_MANAGER_. Orchestrační vrstva definuje stejný secret jako API_MANAGER_ENCRYPTION_KEY v orchestration/.env.example a docker-compose.core.yml ho do kontejneru mapuje jako obyčejné ENCRYPTION_KEY:

orchestration/docker-compose.core.yml
environment:
- ENCRYPTION_KEY=${API_MANAGER_ENCRYPTION_KEY}

Při nastavování klíče tedy editujte API_MANAGER_ENCRYPTION_KEY v orchestration/.env (orchestrační název); pokud spouštíte api-manager samostatně (mimo orchestraci, přes vlastní .env), nastavte přímo ENCRYPTION_KEY.

Terminál
# Orchestrační název (orchestration/.env)
API_MANAGER_ENCRYPTION_KEY=<base64-32-bytes>
# In-process název, který čte kód api-manageru (api-manager/.env, standalone dev)
ENCRYPTION_KEY=<base64-32-bytes>
# Admin API klíč pro admin/* endpointy
ADMIN_API_KEY=<random-secret>
# Service secrets (jeden na konzumenta)
SERVICE_SECRET_GIT_CONNECTOR=<secret>
SERVICE_SECRET_CLOUD_CONNECTOR=<secret>
SERVICE_SECRET_WEBHOOK_PUBLISHER=<secret>
# Strapi backend (per konfigurovaný region, např. "local")
STRAPI_BACKEND_local_URL=<strapi-url>
STRAPI_BACKEND_local_ADMIN_EMAIL=admin@sencai.space
STRAPI_BACKEND_local_ADMIN_PASSWORD=<password>

Interval rotace a další per-service nastavení (např. rotationDays) se konfigurují per service v src/config/services/*.json, nikoliv jako jedna globální .env hodnota.

Všechny interní požadavky používají hlavičku X-Service-Secret:

Terminál
curl -H "X-Service-Secret: SERVICE_SECRET_GIT_CONNECTOR" \
http://api-manager:3200/v1/token/git-connector

Routy jsou verzované pod /v1/ (F3.API.01). Legacy neverzované cesty /api/ stále fungují jako deprecated aliasy — každá odpověď na těchto cestách nese hlavičku Sunset (RFC 8594) a hlavičku Link: <.../v1/...>; rel="successor-version" odkazující na /v1 nástupce. Každá odpověď pod /v1/* i /api/* navíc nese X-API-Version: 1.

GET /v1/token/:service
Headers: X-Service-Secret: <service-secret>
Response:
{
"success": true,
"data": {
"token": "eyJ...",
"expiresAt": "2026-04-26T12:00:00Z",
"region": "local",
"strapiBackendUrl": "http://backend:1337"
}
}
GET /health
Response:
{
"status": "up",
"timestamp": "2024-01-01T12:00:00Z"
}
POST /v1/admin/rotate/:service
Headers: X-Admin-Key: <admin-key>
Response:
{
"success": true,
"message": "Token rotated for git-connector (local)",
"data": {
"serviceName": "git-connector",
"region": "local",
"rotatedAt": "2026-04-26T12:00:00Z",
"expiresAt": "2026-05-03T12:00:00Z"
}
}
POST /v1/admin/revoke/:service
Headers: X-Admin-Key: <admin-key>
Response:
{
"success": true,
"message": "Token revoked for git-connector (local)"
}
GET /v1/admin/status
Headers: X-Admin-Key: <admin-key>
Response:
{
"success": true,
"data": {
"tokens": [ { "serviceName": "git-connector", "region": "local", "createdAt": "...", "expiresAt": "...", "rotatedAt": "..." } ],
"stats": { ... },
"rotator": { ... }
}
}
POST /v1/admin/force-rotation
Headers: X-Admin-Key: <admin-key>
Response:
{
"success": true,
"message": "Rotation check completed"
}

Příklad přes curl:

Terminál
curl -X POST http://api-manager:3200/v1/admin/force-rotation \
-H "X-Admin-Key: <ADMIN_KEY>"

Algoritmus: AES-256-GCM, klíč odvozený přes crypto.scryptSync(masterKey, salt, 32)

Velikost klíče: 256 bitů (32 bajtů)

Velikost IV: 128 bitů (16 bajtů)

Velikost auth tagu: 128 bitů (16 bajtů)

Aktuální formát (v2) — per-record náhodná sůl:

iv(hex):authTag(hex):ciphertext(hex):salt(hex)

Každé volání šifrování vygeneruje čerstvou náhodnou 16bajtovou sůl, která se uloží spolu se šifrovaným textem (4 hex segmenty oddělené dvojtečkou) a použije se pro odvození per-record klíče přes scrypt. Tím se nahradil starší fixní formát, který odvozoval klíč z jediné statické soli sdílené všemi tokeny — bezpečnostní oprava zdokumentovaná v CHANGELOG.md služby.

Legacy formát (v1) — zpětná kompatibilita jen pro dešifrování:

iv(hex):authTag(hex):ciphertext(hex)

Starší tokeny se 3 segmenty (bez uložené soli) se stále korektně dešifrují pádem zpět na legacy statickou sůl. Nové šifrování vždy zapisuje 4segmentový formát; 3segmentový formát se od teď již nikdy nezapisuje.

API Manager vynucuje rate limity:

  • Limit: 100 požadavků
  • Okno: 15 minut
  • Podle: IP adresy

Odpověď při překročení limitu:

{
"statusCode": 429,
"error": "Too Many Requests",
"message": "Rate limit exceeded. Try again in 15 minutes."
}

Q: „401 Unauthorized” při získávání tokenu

A: Hlavička X-Service-Secret chybí nebo neodpovídá SERVICE_SECRET_* v .env.

Q: „Token generation failed”

A: Strapi admin API může být nedostupné. Zkontrolujte:

  • Že Strapi běží (docker logs backend)
  • Že STRAPI_BACKEND_local_ADMIN_EMAIL a STRAPI_BACKEND_local_ADMIN_PASSWORD jsou správně
  • Síťovou konektivitu ke Strapi

Q: Nelze dešifrovat tokeny

A: Nesoulad ENCRYPTION_KEY (in-process) / API_MANAGER_ENCRYPTION_KEY (orchestrace). Ujistěte se, že klíč zůstává konzistentní napříč restarty i napříč oběma vrstvami.

Q: Rotace nikdy neproběhne

A: Zkontrolujte logy (docker logs api-manager). Ověřte, že běží rotační cron a že je Strapi dostupné.