Appearance
Configuration Drift Detection
Monitors security-relevant configuration (env vars, DB settings, admin roles) and detects when values change from the captured baseline.
How It Works
Trigger Points
- Startup: Captures the initial baseline when
initDriftDetection()runs - Scheduled: Periodic checks via cron (default: every 5 minutes)
- Event-driven: Immediately after
SettingsService.updateSingleton()modifies settings
Monitored Configurations
Environment Variables
| Key | Category |
|---|---|
SSRF_PROTECTION_ENABLED | security |
RATE_LIMITER_ENABLED | security |
RATE_LIMITER_STORE | security |
RATE_LIMITER_POINTS | security |
RATE_LIMITER_DURATION | security |
RATE_LIMITER_GLOBAL_ENABLED | security |
AUDIT_HASH_ENABLED | security |
SECURITY_EVENTS_ENABLED | security |
CORS_ENABLED | security |
CORS_ORIGIN | security |
SESSION_COOKIE_ENABLED | auth |
ACCESS_TOKEN_TTL | auth |
REFRESH_TOKEN_TTL | auth |
WEBSOCKETS_REST_AUTH | auth |
WEBSOCKETS_GRAPHQL_AUTH | auth |
Database Settings (odp_settings)
| Column | Category |
|---|---|
auth_login_attempts | auth |
auth_password_policy | auth |
public_registration | auth |
public_registration_verify_email | auth |
public_registration_role | permission |
module_bar | security |
Admin Roles
All roles with admin_access = true are tracked (ID + name). Adding, removing, or renaming an admin role triggers a drift event.
Baseline & Hash
The baseline is a sorted array of { key, value, category } entries. The hash is computed as:
SHA-256( JSON.stringify( entries.map(e => [e.key, e.value]) ) )This produces a single hash that summarizes the entire security configuration state.
Cross-Node Comparison
When Redis is available, each node publishes its config hash to odp:drift:{nodeId} with a 10-minute TTL. If two nodes have different hashes, their configurations are inconsistent — typically due to different env vars across pods.
Without Redis, only local drift detection runs (comparing current state vs the stored baseline on the same node).
Drift Report
When drift is detected, the security event includes:
json
{
"type": "DRIFT_DETECTED",
"severity": "warning",
"details": {
"nodeId": "pod-api-abc123",
"driftCount": 2,
"items": [
{
"key": "env:RATE_LIMITER_POINTS",
"category": "security",
"expected": "50",
"actual": "100"
},
{
"key": "db:auth_login_attempts",
"category": "auth",
"expected": "25",
"actual": "10"
}
]
}
}Node Identification
Node ID is resolved in order:
HOSTNAMEenv var (set automatically in Kubernetes as the pod name)os.hostname()crypto.randomUUID()fallback
Configuration
| Variable | Type | Default | Description |
|---|---|---|---|
DRIFT_DETECTION_ENABLED | boolean | false | Enable drift detection |
DRIFT_CHECK_SCHEDULE | string | '*/5 * * * *' | Cron schedule for periodic checks |
Source Files
| File | Purpose |
|---|---|
src/security/drift/types.ts | SecurityBaseline, DriftReport, DriftItem interfaces |
src/security/drift/baseline.ts | captureCurrentBaseline(), compareBaselines() |
src/security/drift/monitor.ts | checkDrift() — compare + publish |
src/security/drift/scheduler.ts | initDriftDetection() — startup + cron |
src/security/drift/node-id.ts | getNodeId() |
src/services/settings.ts | Event-driven drift check on settings update |