Security Hardening
Security Hardening
This page covers the production security measures built into Message Center and the additional configuration required for a hardened deployment.
Content Security Policy (CSP)
Message Center enforces a strict Content Security Policy in production. CSP headers are set by the Next.js middleware and block inline scripts, untrusted origins, and resource loading from unexpected domains.
The CSP is configured in src/middleware.ts. The default policy:
default-src 'self'— blocks all external resources by defaultscript-src 'self'— no inline scripts, noeval()style-src 'self' 'unsafe-inline'— inline styles permitted (required by Tailwind/shadcn)frame-src— expanded to includeGRAFANA_PUBLIC_URLwhen set (for the monitoring iframe)connect-src 'self'— API calls restricted to same origin
Verifying CSP in production
make test-csp # runs the CSP guard test suite
The CSP guard tests (tests/csp*.test.*) verify that the policy headers are present on all routes and that no scripts are loaded from unexpected origins.
If you add an external embed (e.g., a new iframe), update src/middleware.ts to extend the relevant frame-src or script-src directive.
RBAC Lockdown
Message Center uses role-based access control with four roles: author, moderator, admin, super_admin. In addition, the is_moderator flag on an author membership grants elevated permissions within one workspace.
Hardening recommendations:
-
Minimize super_admin accounts. Super admins see all workspaces and can delete campaigns in any workspace. Assign super_admin only to system operators, not workspace owners or content managers.
-
Assign authors rather than admins. Authors can create campaigns but cannot manage members or sender names. Assign the
adminrole only where workspace management is needed. -
Use
is_moderatorflag instead of promoting authors to full admin when moderator permissions are needed within one workspace. -
Audit role assignments. The audit log records all membership changes (
member.invited,member.role_changed). Review via the Audit tab on the Settings page or directly in MongoDB.
See the full permission matrix at Roles & Permissions.
Secret Rotation
| Secret | Rotation cadence | How to rotate |
|---|---|---|
NEXTAUTH_SECRET | Quarterly | Generate new value; existing sessions invalidated on next request (users must re-login) |
MONGODB_URI (credentials) | Quarterly | Update MongoDB user password; update Secret in k8s; rolling restart |
BFF_PROXY_EMAIL / BFF_PROXY_PASSWORD | Quarterly | Update service account in Proxy; update k8s Secret; the JWT cache refreshes automatically within 60 s |
CORE_ADMIN_API_KEY | Quarterly | Rotate in Core config; update k8s Secret; rolling restart |
| mTLS certificates | Before expiry (check NotAfter) | Replace files; see [[v1.0.0/en/public/04-administration/02-configuration/04-mtls-certificates]] for hot-reload procedure — no restart needed |
Rotating NEXTAUTH_SECRET
All active user sessions are signed with the current NEXTAUTH_SECRET. Rotating it immediately invalidates all sessions — all users will be logged out. Coordinate with users or schedule rotation during a maintenance window.
# Generate a new secret
openssl rand -base64 32
# Update Kubernetes Secret
kubectl patch secret message-center-secrets \
-p '{"stringData":{"NEXTAUTH_SECRET":"<new-value>"}}'
# Trigger rolling restart to pick up the new secret
kubectl rollout restart deployment/core-admin
Network Security
Kubernetes NetworkPolicy
Restrict pod communication at the network level (see Kubernetes Deployment for the full NetworkPolicy example). The policy should:
- Allow inbound only on port 3000 (web UI)
- Allow outbound only to MongoDB (27017), Core API (8080/8092), and Proxy (8088/8089)
- Block all other outbound — prevents data exfiltration via server-side request forgery
TLS everywhere
- The web UI should be served behind TLS termination (nginx Ingress with a valid certificate, or a load balancer).
- Core and Proxy connections already use mTLS — do not downgrade to plain HTTP in production.
- The health endpoint (
CORE_HEALTH_URL) runs on plain HTTP by design (port 8092). Restrict access to this port to the Message Center pod only via NetworkPolicy.
MongoDB Security
-
Use authentication. The default development URI (
mongodb://localhost:27018/core_admin) has no authentication — production must use credentials. -
Restrict network access. Only the Message Center pod should reach MongoDB port 27017. In Kubernetes, enforce this with NetworkPolicy.
-
Enable TLS. Configure MongoDB to require TLS connections and set the URI to
mongodb+srv://...ormongodb://...?tls=true. -
Least privilege. The MongoDB user for Message Center needs only
readWriteon the$MONGODB_DBdatabase. It does not needadminorclusterAdminroles.
Session Security
- Sessions are stored as HTTP-only,
Secure,SameSite=Laxcookies managed by next-auth. - The session JWT expires after 8 hours (next-auth default). Users must re-authenticate after expiry.
- Rate limiting is applied to the login endpoint — multiple failed attempts temporarily block the IP.
- Passwords are hashed with bcrypt (cost factor 12). Password comparison is timing-safe via bcrypt's constant-time comparison.
Audit Logging
Message Center records compliance-grade audit logs for all significant actions (26 write-points). In a hardened deployment:
- Set
AUDIT_RETENTION_DAYSaccording to your compliance requirements (e.g., 365 for one year). - Back up
audit_logsas part of your standard MongoDB backup — these records may be required for regulatory audits. - Monitor
[audit-fallback-overflow]— this event means compliance events are being lost.
Supply Chain Security
- The Docker image is built from the official
node:20-alpinebase image. - All Node.js dependencies are pinned via
yarn.lockand installed with--frozen-lockfile. NEXT_TELEMETRY_DISABLED=1is set in both builder and runner stages of the Dockerfile — no data is sent to Vercel.
Next Steps
- mTLS Certificates — certificate rotation
- Kubernetes Deployment — NetworkPolicy setup
- Monitoring & Alerts — detect security anomalies via log sentinels