Skip to content

Commit 3b95adf

Browse files
committed
docs(security): expand security docs to cover database-scoped auth and new event types
Authentication, RBAC, audit, and tenancy docs are updated throughout to reflect that security primitives are now database-aware: - auth.md: API keys can be narrowed to specific databases; service accounts are per-database; session binding ties a connection to one database for its lifetime. - rbac.md: documents the ClusterAdmin role for cross-database operations and the database-scoped DatabaseAdmin / DatabaseReadOnly roles; updates GRANT/REVOKE examples to show database-level permission grants. - rls.md: documents the $auth.database_id context variable so RLS policies can filter by the session's bound database. - audit.md: expands the event catalogue with database lifecycle events (DatabaseCreated, DatabaseDropped, DatabaseRenamed, DatabaseQuotaChanged, DatabaseCloned, DatabaseMirrored, DatabasePromoted, DatabaseMaterialized, TenantMoved, DatabaseBackedUp, DatabaseRestored), session events (SessionRevoked, LockoutTriggered, LoginRateLimited), and the opt-in DmlAudit event; renames RlsDenied → RlsRejected for consistency with the error catalogue; adds AuthzDenied as a legacy alias for PermissionDenied. - tenants.md: clarifies the database vs tenant distinction and the MOVE TENANT DDL path. - security/README.md: adds a Database Scoping overview section listing which security primitives are database-aware and links the new guides. Adds docs/security/oidc.md covering OIDC/JWT bearer authentication: provider registration (CREATE OIDC PROVIDER), claim-mapping rules, multi-provider precedence, pgwire exclusion rationale, token validation steps, and troubleshooting. Adds docs/security/sessions.md covering session lifecycle management: SHOW SESSIONS with database/tenant/user filters, KILL SESSION, idle timeout enforcement (per-database ALTER DATABASE SET IDLE_TIMEOUT), automatic revocation on permission changes, login rate limiting, account lockout policy, and the audit events emitted for each transition.
1 parent 554d957 commit 3b95adf

8 files changed

Lines changed: 816 additions & 44 deletions

File tree

docs/security/README.md

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,28 @@ NodeDB has a defense-in-depth security model covering authentication, authorizat
44

55
## Guides
66

7-
- [Authentication](auth.md) — Users, passwords, API keys, JWKS, mTLS
8-
- [Roles & Permissions (RBAC)](rbac.md) — CREATE ROLE, GRANT, REVOKE, permission hierarchy
7+
- [Authentication](auth.md) — Users, passwords, API keys, service accounts, OIDC, mTLS
8+
- [OIDC Single Sign-On](oidc.md) — JWT bearer authentication, claim mapping, provider setup
9+
- [Roles & Permissions (RBAC)](rbac.md) — CREATE ROLE, GRANT, REVOKE, permission hierarchy, ClusterAdmin
10+
- [Session Management](sessions.md) — SHOW SESSIONS, KILL SESSION, idle timeout, lockout, rate limiting
911
- [Row-Level Security (RLS)](rls.md) — Per-row filtering based on auth context
10-
- [Audit Log](audit.md) — Hash-chained audit trail, change tracking, SIEM export
11-
- [Multi-Tenancy](tenants.md) — Tenant isolation, quotas, purge
12+
- [Audit Log](audit.md) — Hash-chained audit trail, database-scoped events, DML audit, SIEM export
13+
- [Multi-Tenancy](tenants.md)Database vs Tenant, tenant isolation, quotas, purge
1214
- [Encryption](encryption.md) — At-rest cipher per storage tier, key management, TLS
1315

16+
## Database Scoping
17+
18+
Authentication and access control are now database-aware:
19+
20+
- **API keys** can be narrowed to specific databases
21+
- **Service accounts** are created per database
22+
- **RLS policies** can reference `$auth.database_id`
23+
- **Session management** binds connections to one database
24+
- **Audit events** include `database_id` for filtering
25+
- **Admin DDL** is gated by role (ClusterAdmin for cross-database ops)
26+
27+
See [Authentication](auth.md), [RBAC](rbac.md), [Audit Log](audit.md), and [Session Management](sessions.md).
28+
1429
## Encryption (summary)
1530

1631
- **At rest** — AES-256-GCM for WAL and columnar/timeseries segments (per-collection KEK + per-segment SEGP envelope). Filesystem-level encryption (LUKS / dm-crypt / FileVault) covers redb catalogs and HNSW / Vamana mmap segments. Full per-tier breakdown: [`encryption.md`](encryption.md).

docs/security/audit.md

Lines changed: 92 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,44 @@ Each entry also records `auth_user_id`, `auth_user_name`, and `session_id` for c
1616

1717
## Audit Events
1818

19-
| Event | Level | Description |
20-
| ------------------- | -------- | --------------------------- |
21-
| `AuthSuccess` | Minimal | Successful authentication |
22-
| `AuthFailure` | Minimal | Failed login attempt |
23-
| `AuthzDenied` | Minimal | Permission denied |
24-
| `PrivilegeChange` | Standard | GRANT, REVOKE, role changes |
25-
| `SessionConnect` | Standard | New connection |
26-
| `SessionDisconnect` | Standard | Connection closed |
27-
| `AdminAction` | Standard | DDL, config changes |
28-
| `TenantCreated` | Standard | New tenant |
29-
| `TenantDeleted` | Standard | Tenant removed |
30-
| `SnapshotBegin/End` | Standard | Backup lifecycle |
31-
| `RestoreBegin/End` | Standard | Restore lifecycle |
32-
| `CertRotation` | Standard | TLS cert rotated |
33-
| `KeyRotation` | Standard | Encryption key rotated |
34-
| `NodeJoined/Left` | Standard | Cluster membership |
35-
| `QueryExec` | Full | Every query executed |
36-
| `RlsDenied` | Full | Row filtered by RLS |
37-
| `RowChange` | Forensic | Individual row mutations |
19+
| Event | Level | Description |
20+
| ---------------------------- | -------- | ---------------------------------------------- |
21+
| `AuthSuccess` | Minimal | Successful authentication |
22+
| `AuthFailure` | Minimal | Failed login attempt |
23+
| `PermissionDenied` | Minimal | Permission check failed |
24+
| `AuthzDenied` | Minimal | Legacy: permission denied |
25+
| `PrivilegeChange` | Standard | GRANT, REVOKE, role changes |
26+
| `SessionConnect` | Standard | New connection |
27+
| `SessionDisconnect` | Standard | Connection closed |
28+
| `SessionRevoked` | Standard | Admin terminated session |
29+
| `LockoutTriggered` | Standard | Account locked after failed logins |
30+
| `LoginRateLimited` | Standard | Too many login attempts |
31+
| `RlsRejected` | Standard | RLS policy blocked row |
32+
| `AdminAction` | Standard | DDL, config changes |
33+
| `TenantCreated` | Standard | New tenant |
34+
| `TenantDeleted` | Standard | Tenant removed |
35+
| `DatabaseCreated` | Standard | New database |
36+
| `DatabaseDropped` | Standard | Database deleted |
37+
| `DatabaseRenamed` | Standard | Database renamed |
38+
| `DatabaseQuotaChanged` | Standard | Quota limit changed |
39+
| `DatabaseCloned` | Standard | Database cloned |
40+
| `DatabaseMirrored` | Standard | Database mirrored |
41+
| `DatabasePromoted` | Standard | Replica promoted to primary |
42+
| `DatabaseMaterialized` | Standard | Materialized view materialized |
43+
| `TenantMoved` | Standard | Tenant reassigned to database |
44+
| `DatabaseBackedUp` | Standard | Backup created |
45+
| `DatabaseRestored` | Standard | Backup restored |
46+
| `DatabaseAuditDmlChanged` | Standard | DML audit setting changed |
47+
| `DatabaseIdleTimeoutChanged` | Standard | Idle timeout changed |
48+
| `OidcProviderChanged` | Standard | OIDC provider created/altered/dropped |
49+
| `DmlAudit` | Forensic | Individual DML operation (opt-in per database) |
50+
| `SnapshotBegin/End` | Standard | Backup lifecycle |
51+
| `RestoreBegin/End` | Standard | Restore lifecycle |
52+
| `CertRotation` | Standard | TLS cert rotated |
53+
| `KeyRotation` | Standard | Encryption key rotated |
54+
| `NodeJoined/Left` | Standard | Cluster membership |
55+
| `QueryExec` | Full | Every query executed |
56+
| `RowChange` | Forensic | Individual row mutations |
3857

3958
## Audit Levels
4059

@@ -53,10 +72,62 @@ level = "standard"
5372

5473
Higher levels include everything from lower levels.
5574

75+
## Per-Database Filtering
76+
77+
Filter audit events by database to reduce noise when operating a single database:
78+
79+
```sql
80+
-- Show audit events for a specific database
81+
SHOW AUDIT IN DATABASE prod LIMIT 50;
82+
83+
-- Combine with event type filter
84+
SHOW AUDIT IN DATABASE prod WHERE event_type = 'DmlAudit' LIMIT 100;
85+
```
86+
87+
Database-scoped DDL (CLONE, MIRROR, PROMOTE, etc.) include `database_id` in the audit entry, enabling per-database filtering and forensics.
88+
5689
## Hash Chain Integrity
5790

5891
Every `AuditEntry` contains `prev_hash` — the SHA-256 of the previous entry. This creates a tamper-evident chain verified on startup. If an entry is modified or deleted, the chain breaks and is flagged.
5992

93+
Hash chain is extended with `database_id` when present (new events include this field; legacy events without database scope are preserved for compatibility).
94+
95+
## DML Audit (Opt-In)
96+
97+
Track individual data modifications (INSERT, UPDATE, DELETE) per database. **Disabled by default** — enable only when required for compliance or forensics, as the volume can be substantial.
98+
99+
```sql
100+
-- Enable DML audit for writes only (INSERT, UPDATE, DELETE)
101+
ALTER DATABASE prod SET AUDIT_DML = 'writes';
102+
103+
-- Enable all DML including reads
104+
ALTER DATABASE prod SET AUDIT_DML = 'all';
105+
106+
-- Disable (default)
107+
ALTER DATABASE prod SET AUDIT_DML = 'none';
108+
109+
-- View DML audit entries
110+
SHOW AUDIT IN DATABASE prod WHERE event_type = 'DmlAudit' LIMIT 100;
111+
```
112+
113+
**DML audit entry includes:**
114+
115+
| Field | Type | Description |
116+
| ------------------ | ---------- | ---------------------------------- |
117+
| `database_id` | DatabaseId | Scoped to database |
118+
| `tenant_id` | TenantId | Tenant context |
119+
| `user_id` | u32 | Who performed the operation |
120+
| `statement_digest` | String | Hash of the SQL statement |
121+
| `collection` | String | Target collection name |
122+
| `op` | String | Operation (insert, update, delete) |
123+
| `row_id` | u32 | Surrogate key of affected row |
124+
| `lsn` | u64 | Log sequence number (WAL position) |
125+
| `timestamp` | Timestamp | When the operation occurred |
126+
127+
**Storage cost:** Estimate ~1 KB per DML operation. For a production database with 1M writes/day, expect ~1 GB of audit storage per month.
128+
129+
**Implementation:** DML audit events are emitted by the Event Plane (not the request path), so audit fanout does not block writes. Events are buffered in the event bus and persisted asynchronously.
130+
60131
## SIEM Export
61132

62133
Export audit events to external security information and event management systems via CDC webhook:
@@ -69,6 +140,8 @@ CREATE CHANGE STREAM audit_export ON _system.audit
69140

70141
HMAC signatures allow the receiving system to verify event authenticity.
71142

143+
Database-scoped events include `database_id` field for SIEM correlation.
144+
72145
---
73146

74147
# Document Change Tracking

docs/security/auth.md

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,71 @@ For service-to-service communication without passwords.
3636
-- Create an API key (returns the key once — store it securely)
3737
CREATE API KEY 'my-service' ROLE readwrite;
3838

39+
-- Create a key scoped to specific databases
40+
CREATE API KEY 'analytics-svc' FOR service_account WITH DATABASES (analytics, logs);
41+
3942
-- Revoke
4043
DROP API KEY 'my-service';
4144
```
4245

46+
**Database scoping:**
47+
48+
```sql
49+
-- Create a key accessible only to the 'prod' database
50+
CREATE API KEY 'prod-reader' FOR alice WITH DATABASES (prod);
51+
52+
-- Attempt to use key on a different database is rejected
53+
-- Error: DATABASE_NOT_AUTHORIZED
54+
```
55+
56+
API keys default to the owner's accessible databases. Narrow with `WITH DATABASES (db1, db2)` to restrict to a subset.
57+
4358
Use in HTTP requests:
4459

4560
```bash
4661
curl -H "Authorization: Bearer <api-key>" http://localhost:6480/v1/query
4762
```
4863

49-
## JWKS (JWT Auto-Discovery)
64+
## Service Accounts
65+
66+
Credentials for daemons, batch jobs, and application backends. Service accounts are scoped to a database.
67+
68+
```sql
69+
-- Create a service account for a database
70+
CREATE SERVICE ACCOUNT 'batch-processor' FOR DATABASE analytics;
71+
72+
-- Create an API key on the service account
73+
CREATE API KEY 'batch-key' FOR 'batch-processor' WITH DATABASES (analytics);
74+
75+
-- Adjust accessible databases
76+
ALTER SERVICE ACCOUNT 'batch-processor' SET DATABASES (analytics, logs);
77+
78+
-- View all service accounts
79+
SHOW SERVICE ACCOUNTS;
80+
```
81+
82+
Service accounts are isolated from user accounts and cannot authenticate via password.
83+
84+
## OIDC Single Sign-On
85+
86+
Authenticate via external identity providers (Auth0, Okta, Keycloak) using JWT bearer tokens. One-time setup per provider, then users authenticate directly with the provider.
87+
88+
```sql
89+
-- Admin registers provider
90+
CREATE OIDC PROVIDER auth0 WITH (
91+
issuer = 'https://your-domain.auth0.com/',
92+
jwks_url = 'https://your-domain.auth0.com/.well-known/jwks.json',
93+
audience = 'nodedb-api'
94+
);
95+
```
96+
97+
**Supported on:** Native protocol and HTTP. **Not on pgwire** — the Postgres wire protocol has no standard bearer-token framing; pgwire stays SCRAM-SHA-256 only.
5098

51-
Multi-provider support for Auth0, Clerk, Supabase, Firebase, Keycloak, and Cognito.
99+
See [OIDC Single Sign-On](oidc.md) for full setup and claim mapping.
100+
101+
## JWKS (JWT Auto-Discovery) — Legacy
102+
103+
Multi-provider support for Auth0, Clerk, Supabase, Firebase, Keycloak, and Cognito via `nodedb.toml` configuration.
52104

53105
Configure in `nodedb.toml`:
54106

@@ -70,6 +122,8 @@ JWT claims map to `$auth.*` session variables:
70122

71123
Supported algorithms: ES256, ES384, RS256. Built-in JWKS cache with disk fallback and circuit breaker for provider outages.
72124

125+
**Note:** The OIDC provider mechanism (above) is the recommended approach for modern SSO; this `nodedb.toml` method is supported for backward compatibility.
126+
73127
## mTLS (Mutual TLS)
74128

75129
For zero-trust environments. Both client and server present certificates.

0 commit comments

Comments
 (0)