Skip to main content

regulator-portal-service — Security Model

Version: 1.0 Status: Draft Owner: Regulator-facing + Legal Last Updated: 2026-04-21 Companion: API_CONTRACTS · DATA_MODEL · EVENT_SCHEMAS · 13-security-compliance-tenancy.md · ADR-0004 §11 HSM, §14 multi-region


1. Authentication

1.1 Regulator plane (:3082) — mTLS + national PKI

  • Envoy ingress with require_client_certificate: true.
  • Trust anchors (regulator_ca_trust_store) loaded from Vault PKI; refreshed every 15 min.
  • OCSP enforcement per RFC 6066 — stapled response preferred. If absent and OCSP_HARD_FAIL=true (default), the handshake is terminated. Stapled response max-age 24 h.
  • CRL fallback — full CRL refreshed every 15 min cached in Redis under regulator:crl:{issuerDn}; entry-found → revoked → hard reject.
  • Cert-extension pinningkeyUsage ⊇ {digitalSignature, keyAgreement}, extendedKeyUsage ⊇ {clientAuth}, subjectAltName URI or DNS matching *.atra.af or configured accredited bodies.
  • TLS 1.3 only; cipher-suite allowlist: TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256, TLS_AES_128_GCM_SHA256. No session resumption across pods (no fast-forward on revoked certs).
  • Session issued server-side (opaque Redis token) after subject-DN lookup in regulator.users. Client cert is re-presented on every request; session cookie binds to cert fingerprint (rotating client cert mid-session triggers re-auth).

1.2 Auditor plane (:3083) — mTLS + auditor PKI

  • Separate trust anchor (auditor_ca_trust_store); no overlap with national PKI.
  • Same OCSP / CRL / cipher constraints.
  • Additional gate: DB row in regulator.auditor_access with state = GRANTED and access_expires_at > now(). Expired → 403 AUDITOR_ACCESS_EXPIRED before any data read.
  • Session TTL min(30 min, time remaining on access grant).

1.3 Internal admin plane (:3084) — Kong JWT

  • Kong validates platform JWT (RS256, JWKS from auth-service).
  • Role platform.regulator.admin required for most endpoints; platform.compliance.admin for bundle-generation and LI escalation.
  • Service-to-service internal calls (cron pods, BFF) use short-lived HS256 tokens signed by a dedicated regulator-internal HMAC key.

1.4 IdP-agnostic

Per ADR-0002, the platform IdP is irrelevant to regulator and auditor authentication — they use mTLS exclusively. The idp claim (when present on internal JWTs) is captured in audit rows for forensic purposes only.


2. Authorization (RBAC)

Regulator-facing roles:

RoleCapabilities
regulator-readList own org's LI + complaints + reports; read attestation catalog; generate ad-hoc reports
regulator-liAbove + submit LI requests; read LI delivery manifest
regulator-auditorRead attestation evidence + request bundle generation
external-auditorRead evidence within grantedFrameworks; download bundles

Internal roles (via platform JWT):

RoleCapabilities
platform.regulator.adminSIEM destination CRUD, auditor grant CRUD, evidence collection trigger
platform.compliance.adminLI escalation, bundle generation
platform.legalLI transition initiator
platform.securityLI transition approver; cert-revocation cascade override
platform.auditorRead-only audit-log query

Enforcement:

  1. NestJS RoleGuard (or equivalent) at handler boundary — 403 INSUFFICIENT_SCOPE before handler entry.
  2. Region scopingallowedRegions (ISO 3166-2) on the user controls which rows are returned; handler sets SET LOCAL app.current_org_name, app.caller_role, app.allowed_regions. Postgres RLS enforces row filtering as the last line of defence.
  3. Dual-control on LI transitions — Ed25519 detached signature from a distinct user; same-user signature rejected by service.
  4. Time-box on auditor reads — every handler first validates access_expires_at > now(); Redis session TTL mirrors backend TTL.

3. Data Protection

3.1 PII inventory & classification

FieldClassStorageTransit
regulator.users.cert_subject_dnINTERNALPlainTLS 1.3
regulator.li_requests.target_msisdn_ciphertextRESTRICTEDAES-256-GCM per-request DEK, KEK transit/regulator-li-dekTLS 1.3
regulator.li_requests.warrant_object_ref (PDF)RESTRICTEDSSE-KMS + object-lockHTTPS
LI delivery packages (iri.json, cc.csv, cover.pdf)RESTRICTED-LEGALPer-request DEK, KEK transit/regulator-li-dek; signed .p7sSFTP over SSH-2
regulator.complaints.citizen_msisdn_ciphertextRESTRICTEDPer-tenant KEK transit/regulator-complaints-{tenantId}TLS 1.3
regulator.complaints.summaryCONFIDENTIALPlain (summary is already narrative)TLS 1.3
Reports PDFCONFIDENTIALSSE-KMS + object-lock; PKCS#7 signedHTTPS
Attestation evidenceCONFIDENTIALSSE-KMS + object-lockHTTPS
SIEM destination credentialsSECRETVault KVVault API
Signing keysSECRETVault Transit / HSM (PKCS#11); never exportedHSM API

3.2 Encryption keys

KeyStoreRotation
mTLS server cert (regulator + auditor planes)Vault PKI90 days
Per-tenant complaint KEKVault TransitAnnual
LI delivery KEKVault TransitAnnual
regulator-reports-signingHSM (PKCS#11 slot)Annual; prior retained 10 years
regulator-li-signingHSM (PKCS#11 slot)Annual; prior retained 10 years
attestation-bundle-signingHSM (PKCS#11 slot)Annual; prior retained permanently
SIEM destination credentials (HEC tokens, syslog keys)Vault KVQuarterly or on ATRA request
Internal HS256 HMAC (BFF ↔ REST)Vault KVMonthly
JWT validation — JWKS from auth-serviceN/Aauth-service managed

3.3 Redaction rules

  • In events. Target MSISDN in LI events is always masked. Citizen MSISDN in complaint events is always masked. Message content bodies never appear.
  • In logs. Pino redactor masks target_msisdn, citizen_msisdn, warrant_hash (shown as prefix+suffix only). ESLint rule forbids logger.*(..., { body }) patterns; pre-commit check.
  • In REST responses. Regulator sees masked target MSISDN except inside authenticated LI-detail calls scoped to that LI request. Auditor never sees MSISDNs.

3.4 HSM posture (per ADR-0004 §11)

  • Production HSM is network-attached FIPS 140-3 Level 3 (Thales/Luna equivalent).
  • PKCS#11 sessions are per-pod; no cached handles across restart.
  • HSM PIN lives in Vault KV, rotated monthly.
  • Key-usage policy: sign-only on all three signing keys; extraction forbidden.
  • Secondary (cold-standby) HSM in DR region; activation requires CISO + CTO sign.

3.5 LLM / AI data residency

Per AI_INTEGRATION.md — no LLM or external AI provider is used. The only AI is an on-prem classifier on complaint summaries. Start-up guard enforces AI_EXTERNAL_PROVIDER_ALLOWED=false in production.


4. Audit

All regulator and auditor actions are audit-logged:

ActorAudit tableRetention
Regulator login / failureregulator.users_audit13 months
LI state transitionsregulator.li_audit (hash-chained, Postgres rule-protected)7 years
Complaint state changesregulator.complaint_triage + outbox7 years
Report downloadoutbox + analytics-service archive7 years
Auditor readsregulator.auditor_access_audit13 months
Admin config changesoutbox to regulator.audit.v1 stream13 months

Audit events are also forwarded to the ATRA SIEM via UC-08 StreamToSiem, providing a tamper-evident external copy.

Hash chain: li_audit.hash_self = SHA-256(hash_prev || fromState || toState || initiator || approver || rationale || occurredAt). Chain integrity verified nightly; break detected → CRITICAL alert.


5. Fail-Closed Posture

  • Cert-chain failure → hard reject. No soft fail for regulator plane; soft fail tolerated for auditor plane only with Legal sign-off per engagement.
  • HSM unavailable → reports un-downloadable. Consumers see 503 HSM_UNAVAILABLE; the unsigned PDF is never served.
  • Postgres unavailable → /ready returns 503. K8s removes pod from service; regulator sees 503 from Envoy.
  • SIEM destination unreachable → disk-WAL engages; no event loss. See FAILURE_MODES §FM-02.
  • Upstream unreachable during report generation → report marked READY with a WARNINGS section. Regulator trust > silent omission.

Availability attacks cannot bypass auditability — they can only delay or degrade read surfaces.


6. Tenant / Org Isolation

  • regulator.users.org_name + RLS restrict each regulator's view to its own org's data.
  • Complaint resolution is scoped to the assigned resolver_tenant_id; other tenants cannot read.
  • Auditor grantedFrameworks restricts which control catalog and bundles the auditor sees.
  • Per-tenant complaint KEKs ensure a key compromise is scoped to that tenant's complaints.

7. Secrets

SecretStoreInjection
Envoy server cert + keyVault PKI → K8s Secret via CSI driverFile mount
Postgres credentialsVault DB dynamic secretEnv var, 1-hour TTL
Redis credentialsVault KVEnv var
NATS credentialsVault KVEnv var
HSM PINVault KVEnv var, 15-min TTL, refreshed by sidecar
SIEM destination authVault KV per destinationLoaded per request (short-lived cache)
S3 accessIRSA / AWS IAM Role for ServiceAccountNo key material on disk
Internal HS256 HMACVault KVEnv var

No secret is ever written to logs, events, or config files. Pre-commit gitleaks scan + CI-time scan.


8. Threat Model

ThreatMitigation
Stolen regulator certOCSP/CRL enforcement on every handshake + cert-revocation cascade flips user to REVOKED within 15 min
Compromised auditor certTime-box limits damage window; meta-audit flags anomalous download bursts
Malicious regulator submits fake LI requestWarrant PDF SHA-256 verified; ATRA signs warrant with national PKI; cross-check possible via regulator-side CA
Dual-control bypass attemptBackend rejects same-user initiator+approver; detached Ed25519 sig over liRequestId binds to distinct key
Admin compromise to tamper auditli_audit + auditor_access_audit are Postgres-rule protected append-only; NATS+S3 mirror provides tamper-evidence; hash chain makes tampering observable
SFTP MITM on LI deliverySSH-2 host-key pin in known_hosts.pinned; no host-key trust-on-first-use
Compromised SIEM endpointEach destination has scoped auth; one compromise does not cross-contaminate other destinations; outbound egress via NetworkPolicy allowlist
ATRA cert CA compromiseGhasi maintains pinning to specific intermediate CA DNs; rotation only after Legal sign
HSM unavailabilityBlock downloads, surface 503; no unsigned artefact ever served
Cross-region data leak (LI to DR)S3 ghasi-regulator bucket policy forbids cross-region replication for LI and complaint prefixes; bundles only cross-replicated for DR with legal sign

9. GDPR / Regulatory

Afghanistan does not yet have a GDPR-equivalent statute, but the platform pre-emptively aligns:

  • Right-to-erasure (auth.user.erased.v1): consumed to redact regulator.complaints.citizen_msisdn_ciphertext and summary while preserving audit-required metadata (complaint_id, regulator_ref, resolution_summary). li_requests.target_msisdn_ciphertext is not erased — LI evidence retention takes precedence (legal obligation).
  • Data portability: regulators can export their own LI + complaint history as signed PDFs via report generation.
  • Sub-processor disclosure: reports disclose on-prem AI classifier usage; no other processors.
  • Audit retention: 7-year for LI + complaints + reports (regulator mandate); permanent for attestation bundles; 13 months for audit trails.

10. Security Testing

  • Contract tests per API_CONTRACTS §9.
  • mTLS handshake tests with mock CRL / OCSP revocation scenarios in integration suite.
  • Role-matrix test — every endpoint × every role — asserts 200 / 403 behaviour.
  • Dual-control bypass test — same user cannot satisfy initiator + approver.
  • Audit-chain tamper test — manual row alteration in test DB verifies nightly hash-verify catches it.
  • SFTP host-key pinning test — altered host key rejects.
  • HSM fault injection — confirmed downloads blocked, no unsigned fallback.
  • Quarterly penetration test scoped to regulator + auditor planes.
  • Secret scanning (gitleaks), dependency scanning (osv-scanner), container scanning (trivy).