Skip to main content

cbc-bridge-service — Security Model

Version: 1.0 Status: Draft Owner: Security + Government / Emergency Last Updated: 2026-04-21

Companion: API_CONTRACTS · DATA_MODEL · EVENT_SCHEMAS · FAILURE_MODES · 13-security-compliance-tenancy.md Related ADR: ADR-0004 §11 HSM key custody


1. Authentication

1.1 gRPC plane — government callers

  • mTLS required. Client cert MUST chain to the national-PKI root pinned in the HSM trust store.
  • OCSP stapling + CRL check on every handshake. CRL cached 4 h in Redis (cbc:crl:{issuerSha}); OCSP freshness ≤ 10 min. On cache miss + responder unreachable → fail-closed (CBC-US-008).
  • Cert-subject pinning. The client cert's Subject DN + SHA-256 fingerprint MUST match an active row in cbc.authorised_callers (CBC-US-009). Cert validity alone is insufficient.
  • Detached PKCS#7 signature in x-gov-signature over the RFC 8785 JCS canonical JSON of the request body. Verified via HSM PKCS#11 C_Verify against the presented cert's public key. In-process verification with a file-mounted key is prohibited and enforced by a start-up guard that refuses to boot when CBC_HSM_BYPASS=true outside NODE_ENV=development.
  • Anti-replay. (certFingerprint, nonce) must be unseen in the last 24 h (Redis SETNX + Postgres fallback). requestedAt must be within 120 s of server time.

1.2 gRPC plane — platform peers

regulator-portal-service calls on-behalf-of a government caller and therefore relays both its own SPIFFE SVID (service mesh mTLS) and the caller's national-PKI signature. The service enforces both identities. regulator-portal-service cannot impersonate callers because the signature is always against the caller's HSM-held private key — the portal never holds a signing key.

1.3 REST plane — admin + regulator read

  • Kong validates the platform JWT (issued by auth-service, RS256, JWKS-backed).
  • Kong injects X-User-Id, X-Roles, X-Caller-Role on the upstream request.
  • The service never parses JWTs directly — it trusts Kong's injection and enforces RLS via SET LOCAL app.caller_role.
  • /v1/cbc/public/drills* is anonymous (public drill feed, CBC-US-017); Kong rate-limits per source IP.

1.4 IdP-agnostic

Per ADR-0002, the IdP that authenticated the admin caller is irrelevant to cbc-bridge-service. All it ever sees is the platform JWT. The idp claim is captured in audit for forensic purposes.


2. Authorization (RBAC)

RoleCapabilities
platform.cbc.adminFull CRUD on authorised_callers, drills, cbe_credentials_ref, mno_bind_registry; trigger cell-DB refresh/rollback; read full signature_audit; view un-masked broadcast evidence
platform.auditorRead cbc.audit.*, signature_audit, hash-chain verifier status; read broadcasts with caller identity masked below cert-subject CN first 4 chars; no state changes
platform.regulator.readRead drill records, drill reports, broadcasts metadata (no PKI evidence); for the regulator workbench surface
platform.cbc.operatorCan initiate drill runs; cannot create/edit authorised_callers or cbe_credentials_ref
Government caller (mTLS)BroadcastEmergency, CancelBroadcast, GetBroadcastStatus on their own broadcasts (scoped by callerId)

Enforcement points:

  1. Kong (jwt + acl plugins) for REST.
  2. gRPC interceptor — requires mTLS + national-PKI signature verification before any application code runs.
  3. Application-level @RequireRoles(...) decorator for REST admin handlers.
  4. Postgres RLS on cbc.broadcasts, cbc.signature_audit as belt-and-braces.
  5. Body-redaction interceptor masks cert_subject / mou_ref outside platform.cbc.admin / platform.auditor.

3. Data protection

3.1 PII & sensitivity inventory

FieldClassStorageTransit
cbc.broadcasts.body_variantsPublic (will be broadcast)PlainTLS 1.3
cbc.authorised_callers.cert_subjectCONFIDENTIAL-INTERNALPlain; role-gated APITLS 1.3
cbc.authorised_callers.cert_fingerprint_sha256CONFIDENTIAL-INTERNALPlain; role-gated APITLS 1.3
cbc.authorised_callers.mou_refCONFIDENTIAL-INTERNAL (legal artifact)Plain; role-gated APITLS 1.3
cbc.signature_audit.*CONFIDENTIAL-INTERNALPlain; role-gated API + append-onlyTLS 1.3
cbc.mno_cell_database.lat/lngINTERNAL (national-infra map)Plain; role-gated APITLS 1.3
cbc.cbe_credentials_ref.vault_pathINTERNAL (no secret)Plain (pointer)TLS 1.3
cbc.nonce_audit.*INTERNALPlainTLS 1.3
HSM-sealed signing keys (national-PKI)RESTRICTEDHSM only; never exported
Per-MNO CBE credentialsRESTRICTEDVault Transit (secret/cbc/mno/{mno}/cbe-credentials)TLS 1.3

3.2 Encryption at rest

  • Postgres transparent data encryption (TDE) at the storage layer.
  • HSM-held keys never leave the HSM (PKCS#11 C_Verify operations happen in the HSM).
  • Per-MNO CBE credentials in Vault Transit; pods hold 15-min envelope leases.
  • No per-tenant KEK needed — there is no tenant-specific confidential content (bodies are public).

3.3 Encryption in transit

  • gRPC: mTLS 1.3 with ECDHE-ECDSA-AES256-GCM-SHA384 (platform default cipher suite).
  • REST: HTTPS via Kong (TLS 1.3, HSTS, X-Frame-Options: DENY).
  • MNO CBE: IPSec tunnel (where available) + TLS 1.3 at the adapter layer; failed tunnel = failed dispatch (CBC_DEPENDENCY_UNAVAILABLE).
  • NATS: TLS 1.3 + NATS ACCOUNT mTLS (platform-peer identity).

3.4 Redaction rules

  • In logs (Pino redactor): cert_subjectCN=****.gov.af; cert_fingerprint_sha256 → first 8 + last 4 hex; mou_ref**** in non-admin access logs. Body content is not sensitive and is logged verbatim for regulator evidence.
  • In events (EVENT_SCHEMAS §5): presentedCertSubjectMasked. Full value only on internal-only streams consumed by platform-internal subscribers.
  • In REST responses (body-redaction interceptor): caller identity masked unless platform.cbc.admin / platform.auditor.

4. Audit

  • Append-only. cbc.audit, cbc.signature_audit, cbc.authorised_callers_history, cbc.outbox, cbc.nonce_audit reject UPDATE/DELETE at the Postgres rule level.
  • Hash-chained. cbc.broadcasts and cbc.audit carry prevHash/rowHash. Verifier runs daily (UC-07) and alerts CbcAuditChainBroken CRITICAL on any break.
  • Tamper-evident cold archive. Partitions older than 13 months are promoted to an S3-compatible bucket with Object Lock (WORM retention 7 years) in dxb sovereign region.
  • Meta-audit. Auditor role reads of signature_audit are themselves audited (cbc.audit.meta.v1 — reserved; not yet implemented).

5. Fail-loud, fail-closed posture

  • HSM unavailable → every BroadcastEmergency rejected CBC_HSM_UNAVAILABLE; PagerDuty CbcHsmUnavailable CRITICAL; NDMA notified via side-channel (phone tree runbook).
  • Signature verify failsCBC_SIGNATURE_INVALID + audit row; if rate > 1/min over 5 min, CbcPkiVerifyFailureSpike HIGH alert (probing detection, CBC-US-007).
  • CRL / OCSP unreachable → fail-closed; no connection accepted until refresh.
  • Replay detectedCBC_REPLAY_DETECTED; alert on any repeat within 24 h per caller.
  • All MNO CBEs unreachable → broadcast FAILED; manual fallback runbook (runbooks/cbc-all-cbes-out.md).

Availability attacks cannot bypass policy — at worst they delay a broadcast, which is less harmful than a bypassed authorisation.


6. Caller isolation

  • Postgres RLS on cbc.broadcasts keyed on caller_id for government-caller self-view.
  • Evidence fetch (/v1/cbc/broadcasts/{id}/pdus) is platform.cbc.admin only; callers cannot retrieve their own PDU bytes via the customer surface (regulator evidence is a separate flow).

7. Secrets

SecretStoreInjected as
gRPC server cert (public-facing for gov callers)Vault PKI engine → K8s Secret via Vault AgentFile mount /etc/cbc-tls
HSM PKCS#11 unseal keyVault KV via Vault-Agent init containerEnv var (one-way, purged after HSM slot unseal)
National-PKI trust anchorK8s ConfigMap (+ HSM trust-store)File mount
Per-MNO CBE credentialsVault Transit (secret/cbc/mno/{mno}/cbe-credentials)Retrieved on pod start; cached 15 min
PostgreSQL credentialsVault DB dynamic secretEnv var; rotated hourly
Redis credentialsVault KVEnv var
NATS credentialsVault KVEnv var
JWT validationJWKS from auth-service via KongNo keys stored

No secret is ever written to logs, events, or config files. Pre-commit gitleaks scan blocks accidental commits.


8. Threat model

ThreatMitigation
Unauthorised broadcast via stolen PKI certHSM-bound private key (cert holder cannot export); OCSP revocation on every handshake; dual-control for P0
Replay of valid signatureNonce + 120-s clock window + 24-h nonce retention
Prompt injectionN/A — no LLM in hot path (AI_INTEGRATION)
Compromised admin account adds callerplatform.cbc.admin MFA mandatory; caller create requires counter-signed mouRef; audit log + SOC event
Audit-log tamperingPostgres rule reject UPDATE/DELETE + daily hash-chain verifier + cold-archive Object Lock (WORM 7 y)
CRL/OCSP MITMCRL/OCSP transport via HTTPS with pinned CA; stapled OCSP preferred
Cell-DB poisoning (wrong coordinates → wrong targeting)MNO exports delivered over IPSec/leased link; schema validation + coverage-delta anomaly check before atomic swap; manual admin approval required if delta > 10%
Signature-oracle (probing HSM for timing)Constant-time verification path (HSM native); rate limits per cert; PagerDuty on anomalous failure rate
Side-channel body capture on outbox relayRelay runs in same pod as service; TLS to JetStream; no unencrypted egress
NOC-role release of fraudulent broadcastNo release path exists in CBC (unlike compliance-engine). Broadcasts are either dispatched or cancelled; cancellation is dual-control
Regulator-channel breachregulator.ca.trust.updated.v1 is signed by regulator HSM; we verify signature before reloading trust anchor

9. GDPR & regulatory

  • Broadcast bodies are public data — GDPR Art. 17 erasure does not apply; retention is regulator-mandated (7 years cold).
  • Authorised-caller registry fields (cert_subject, MOU refs, dual-control partners) are operational metadata of a state actor, not "personal data" under GDPR Art. 4(1); nonetheless, redaction-on-erasure available.
  • Right of access: government callers can export their own broadcasts via GET /v1/cbc/broadcasts?callerId=me.
  • Audit evidence window: ≥ 7 years cold (exceeds GDPR-mandated minimums for incident evidence).
  • Data residency: Afghan regions (kbl, mzr) hold authoritative state. Sovereign cold archive in dxb is sealed and governed by platform's sovereign-cloud DPA per ADR-0004 §5.

10. Security testing

  • Contract tests per API_CONTRACTS §6.
  • ZAP baseline + API scan on every main-branch build.
  • Quarterly penetration test scoped to gRPC (signature bypass, replay, scope escalation) + REST admin.
  • Role-matrix integration test — every endpoint × every role — verifies 200/403 behaviour.
  • PKI-bypass test — attempt to verify a signature with a file-mounted key; MUST fail at start-up guard.
  • Replay test — submit the same (certFp, nonce) twice → second MUST be CBC_REPLAY_DETECTED.
  • Chain-tamper test — manually rewrite a cbc.broadcasts row; verifier MUST detect on next run.
  • Secret scanning (gitleaks); dependency scanning (osv-scanner); container scanning (trivy).
  • HSM fail-closed drill — kill HSM connectivity in staging; verify no broadcast accepted.

11. Dual-control surfaces

SurfaceInitiatorApproverWindow
CancelBroadcastCaller ACaller B ∈ A.dualControlPartners60 s
Severity escalation P1→P0Caller ACaller B ∈ A.dualControlPartners60 s
Emergency HSM bypass (prohibited — documented only)CISOCTOn/a — requires joint ADR
Cell-DB rollbackplatform.cbc.adminCISO5 min approval
authorised_callers createplatform.cbc.adminLegal (signed approval in audit log)

12. Runbook references

  • runbooks/cbc-hsm-out.md
  • runbooks/cbc-all-cbes-out.md
  • runbooks/cbc-pki-probing.md
  • runbooks/cbc-audit-chain-broken.md
  • runbooks/cbc-drill-overdue.md
  • runbooks/cbc-cell-db-stale.md