api-gateway (Kong) — Security Model
Status: populated Owner: TBD (Platform / SRE) Last updated: 2026-04-17 Companion: SERVICE_OVERVIEW · APPLICATION_LOGIC · 13 Security, Compliance & Tenancy · Service Template
1. Purpose
Kong is the platform's perimeter enforcement point. This document defines TLS posture, authentication, rate-limiting, IP controls, logging discipline, secrets, and the threat model at the edge.
2. TLS
| Control | Setting |
|---|---|
| Inbound TLS | 1.2 minimum, 1.3 preferred |
| Cipher suites | Modern per Mozilla "intermediate"; no RC4/3DES |
| Certificate source | Cloudflare origin certs for CF → Kong; Let's Encrypt (cert-manager) for direct origin; HSM-backed root for enterprise customers |
| HSTS | Issued by Cloudflare (edge); Kong also sends Strict-Transport-Security: max-age=31536000; includeSubDomains; preload |
| Upstream TLS (Kong → service) | mTLS on the cluster network preferred; plaintext acceptable only inside a cluster-private mesh |
| OCSP stapling | Enabled |
| Cert rotation | Automated via cert-manager or Cloudflare; alert on <14 days to expiry |
3. JWT authentication
- Plugin:
jwt. - Algorithms:
RS256only (asymmetric);HS256explicitly disabled. - JWKS source:
auth-serviceathttps://internal.auth-service.svc/.well-known/jwks.json. - Cache: 5 min in-process; refresh on
kidmiss. - Claims validated at Kong:
| Claim | Check |
|---|---|
iss | https://auth.ghasi.io (or env-equivalent) |
aud | ghasi-platform |
exp | Must be > now |
nbf | If present, must be ≤ now |
iat | Must be within 24 h |
kid | Must resolve in JWKS |
tid | Must be a non-empty string (Kong does not verify tenancy access — upstream does) |
Kong enforces presence and signature; business authorization (scope, role, resource ACL) is enforced by upstream services.
4. API key authentication
- Plugin:
key-auth— validates presence and uniqueness ofX-Api-Key. - Key storage: Hashed (SHA-256) in either Kong DB (DB mode) or, preferred, resolved at request-time by the custom
ghasi-api-key-lookupplugin againstauth-service. - Key format: opaque string, entropy ≥ 256 bits; prefixed
gk_live_/gk_test_byauth-serviceconvention. - Header stripping:
X-Api-Keyis not forwarded upstream. - Rotation:
auth-servicesupports two active keys per account (old + new) during rotation; no Kong restart required when using the custom plugin (cache TTL 60 s). - Revocation: flagged in
auth-service; plugin cache expires within 60 s; optional push-invalidation via aDELETE /cache/ghasi:apikey:<hash>admin call when immediate revocation is required.
5. IP allow/deny
- Plugin:
ip-restriction. - Admin routes (
/admin/*): allow-list of SRE/ops CIDRs + corporate VPN egress. - Partner integration routes: allow-list of partner source IPs (configured per tenant in
auth-service, surfaced to Kong via decK generation). - Global deny-list: known-bad sources (shared with Cloudflare; Cloudflare is the primary layer).
6. Header scrubbing and request transformation
- Plugin:
request-transformer(ingress) and response transformer for egress. - Strip inbound:
X-Forwarded-Host(can be spoofed), server fingerprint headers. - Inject upstream:
X-Gateway-Source: kong(so upstream can distinguish edge vs direct). - Strip outbound:
Server,X-Powered-By,Via.
7. Bot detection
- Plugin:
bot-detection. - Blocks obvious bot user-agents on public routes (
/v1/auth/login,/v1/webhooks/test). - Allows documented crawlers only where relevant (none, for the API).
8. Body logging policy
Body logging is globally disabled on all log sinks (http-log, datadog, file log). Rationale:
- SMS message content is PII and telecom-regulated.
- API keys might appear in body errors if upstream echoes them (defence in depth).
- GDPR / regional privacy regulations forbid unnecessary retention of message content.
Only headers and metadata are logged (and even then Authorization / X-Api-Key values are redacted — only a presence flag is captured). See EVENT_SCHEMAS §3.
9. Rate limiting as a security control
Per-key and per-account limits (see APPLICATION_LOGIC §4) serve DoS resistance, credential stuffing resistance, and brute-force resistance:
| Control | Limit |
|---|---|
/v1/auth/login per-IP | 5/min (anti-credential-stuffing) |
/v1/auth/login per-account | 10/15min (anti-lockout-bypass) |
/v1/sms/send per-key | 10/s default (tier-dependent) |
| Global burst | 5 000 req/s (guardrail) |
Failed-auth responses are rate-limited (Kong returns 429 after N failed attempts from same IP) to resist enumeration.
10. Secrets
| Secret | Where | Rotation |
|---|---|---|
| JWT signing key (private) | auth-service only; Kong never holds the private key | Handled by auth-service |
| JWKS (public) | Pulled by Kong at runtime | Automatic on cache expiry |
| TLS certificates (origin) | cert-manager / Cloudflare | 90 d auto |
| Redis password | Kong vault (K8s Secret / Vault) | Per SRE rotation policy |
| Custom plugin service token (Kong → auth-service) | Kong vault, short-lived mTLS cert preferred | 30 d |
| Admin API token | Vault; CI-only | Per CI policy |
Secrets appear in decK YAML only as vault references ({vault://env/<name>}).
11. Audit events
Kong itself does not emit audit events (no domain concept). Audit-relevant edge events are derived from:
| Event | Source |
|---|---|
| Auth success / failure | Access logs (status + X-Api-Key-Id / JWT sub) |
| Admin API change | Kong admin log (when DB mode) + decK sync PR in Git |
| Route/plugin change | Git commit history in ops/kong/ |
| Rate-limit rejection storm | Prometheus alert → on-call |
12. Threat model
| Threat | Likelihood | Mitigation |
|---|---|---|
Credential stuffing on /v1/auth/login | High | Per-IP rate limit, account lockout, Cloudflare Turnstile (optional at CF layer), bot-detection |
| API key leakage in logs | Medium | Body logging disabled; X-Api-Key stripped pre-log; only key ID in access logs |
| Expired / forged JWT | Medium | RS256 only; JWKS validated; aud/iss/exp checked |
Account enumeration on /v1/accounts/* | Medium | Upstream normalises errors; Kong rate-limits per IP |
| DoS / flood | High (public API) | Global + per-key limits; Cloudflare WAF; Kong pod HPA |
Header smuggling (spoofed X-Account-Id) | Low | Upstream only trusts when X-Gateway-Source: kong AND request arrived on internal net (mTLS / NetworkPolicy) |
| Admin API exposure | Low (by design) | NetworkPolicy + Kong listen on non-public interface; mTLS |
| Plugin supply-chain | Medium | Pinned Kong versions; SBOM in CI; custom plugin code-reviewed |
| Route drift vs OpenAPI | Medium | CI lint (see TESTING_STRATEGY) |
13. Tenant isolation
Kong does not enforce tenancy. It injects X-Account-Id / X-Tier from JWT or API-key resolution. Tenant isolation is enforced in upstream services via account_id scoping and PostgreSQL RLS (see 13 Security, Compliance & Tenancy).
14. GDPR / data privacy
Kong holds no personal data at rest. Access logs contain IP + UA, retained 14 d hot / 90 d archive, subject to the platform retention policy. GDPR erasure sagas do not touch Kong.
15. Open questions
- mTLS between Kong and upstream services — required in production?
- Cloudflare Turnstile (or equivalent bot challenge) on
/v1/auth/login— at CF edge or pushed to Kong via a plugin? - Admin API authentication strategy — mTLS, short-lived OIDC tokens, both?