# Security Model

## Overview

TOD (The Other Dude) implements a 1Password-inspired zero-knowledge security architecture. The server never stores or sees user passwords. All data is stored on infrastructure you own and control — no external telemetry, analytics, or third-party data transmission.

## Authentication: SRP-6a Zero-Knowledge Proof

TOD uses the Secure Remote Password (SRP-6a) protocol for authentication, ensuring the server never receives, transmits, or stores user passwords.

- **SRP-6a protocol:** Password is verified via a zero-knowledge proof — only a cryptographic verifier derived from the password is stored on the server, never the password itself.
- **Two-Secret Key Derivation (2SKD):** Combines the user password with a 128-bit Secret Key using a multi-step derivation process, ensuring that compromise of either factor alone is insufficient.
- **Key derivation pipeline:** PBKDF2 with 650,000 iterations + HKDF expansion + XOR combination of both factors.
- **Secret Key format:** `A3-XXXXXX` (128-bit), stored exclusively in the browser's IndexedDB. The server never sees or stores the Secret Key.
- **Emergency Kit:** Downloadable PDF containing the Secret Key for account recovery. Generated client-side.
- **Vault key decryption on login:** After successful SRP authentication, the client decrypts the user's vault key using the derived session key. This enables client-side decryption of encrypted data without the server ever handling the plaintext vault key.
- **Session management:** JWT tokens with 15-minute access token lifetime and 7-day refresh token lifetime. Access tokens are returned in the response body; refresh tokens are delivered via httpOnly cookies to enable silent token refresh without exposing the refresh token to JavaScript.
- **SRP session state:** Ephemeral SRP handshake data stored in Redis with automatic expiration.

### Authentication Flow

```
Client                                Server
  |                                     |
  |  POST /auth/srp/init {email}        |
  |------------------------------------>|
  |  {salt, server_ephemeral_B}         |
  |<------------------------------------|
  |                                     |
  |  [Client derives session key from   |
  |   password + Secret Key + salt + B] |
  |                                     |
  |  POST /auth/srp/verify {A, M1}      |
  |------------------------------------>|
  |  [Server verifies M1 proof]         |
  |  {M2, access_token, refresh_token}  |
  |<------------------------------------|
```

## Credential Encryption

Device credentials (RouterOS usernames and passwords) are encrypted at rest using envelope encryption:

- **Encryption algorithm:** Fernet symmetric encryption (AES-128-CBC + HMAC-SHA256).
- **Key management:** OpenBao Transit secrets engine provides the master encryption keys.
- **Per-tenant isolation:** Each tenant has its own encryption key in OpenBao Transit.
- **Envelope encryption:** Data is encrypted with a data encryption key (DEK), which is itself encrypted by the tenant's Transit key.
- **Go poller decryption:** The poller service decrypts credentials at runtime via the Transit API, with an LRU cache (1,024 entries, 5-minute TTL) to reduce KMS round-trips.
- **CA private keys:** Encrypted with AES-256-GCM before database storage. PEM key material is never logged.

## Tenant Isolation

Multi-tenancy is enforced at the database level, making cross-tenant data access structurally impossible:

- **PostgreSQL Row-Level Security (RLS):** All data tables have RLS policies that filter rows by `tenant_id`.
- **`app_user` database role:** All application queries run through a non-superuser role that enforces RLS. Even a SQL injection attack cannot cross tenant boundaries.
- **Session context:** `tenant_id` is set via PostgreSQL session variables (`SET app.current_tenant`) on every request, derived from the authenticated user's JWT.
- **`super_admin` role:** Users with NULL `tenant_id` can access all tenants for platform administration. Represented as `'super_admin'` in the RLS context.
- **`poller_user` role:** Bypasses RLS by design — the polling service needs cross-tenant device access to poll all devices. This is an intentional security trade-off documented in the architecture.

## Role-Based Access Control (RBAC)

| Role | Scope | Capabilities |
|------|-------|-------------|
| `super_admin` | Global | Full system access, tenant management, user management across all tenants |
| `admin` | Tenant | Manage devices, users, settings, certificates within their tenant |
| `operator` | Tenant | Device operations, configuration changes, monitoring |
| `viewer` | Tenant | Read-only access to devices, metrics, and dashboards |

- RBAC is enforced at both the API middleware layer and database level.
- API keys inherit the `operator` permission level and are scoped to a single tenant.
- API key tokens use the `mktp_` prefix and are stored as SHA-256 hashes (the plaintext token is shown once at creation and never stored).

## Internal Certificate Authority

TOD includes a per-tenant Internal Certificate Authority for managing TLS certificates on RouterOS devices:

- **Per-tenant CA:** Each tenant can generate its own self-signed Certificate Authority.
- **Device certificate lifecycle:** Certificates follow a state machine: `issued` -> `deploying` -> `deployed` -> `expiring`/`revoked`/`superseded`.
- **Deployment:** Certificates are deployed to devices via SFTP.
- **Three-tier TLS fallback:** The Go poller attempts connections in order:
  1. CA-verified TLS (using the tenant's CA certificate)
  2. InsecureSkipVerify TLS (for self-signed RouterOS certs)
  3. Plain API connection (fallback)
- **Key protection:** CA private keys are encrypted with AES-256-GCM before database storage. PEM key material is never logged or exposed via API responses.
- **Certificate rotation and revocation:** Supported via the certificate lifecycle state machine.

## Remote Access Security

TOD includes on-demand WinBox tunnels and browser-based SSH terminals for devices behind NAT.

- **Single-use session tokens:** SSH sessions are initiated with a short-lived token stored in Redis (`GETDEL`, 120-second TTL). The token is consumed on first use and cannot be replayed.
- **RBAC enforcement:** Opening a tunnel or starting an SSH session requires the `operator` role or higher. `viewer` accounts have no access to remote access features.
- **Audit trail:** Tunnel open/close events and SSH session start/end events are recorded in the immutable audit log with device ID, user ID, source IP, and timestamp.
- **WinBox tunnel binding:** TCP proxies for WinBox connections are bound to `127.0.0.1` only. Tunnels are never exposed on `0.0.0.0` and cannot be reached from outside the host without explicit port forwarding.
- **Idle-timeout cleanup:** Inactive tunnels are closed automatically after `TUNNEL_IDLE_TIMEOUT` seconds (default 300). SSH sessions time out after `SSH_IDLE_TIMEOUT` seconds (default 900). Resources are reclaimed immediately on disconnect.

## Network Security

- **RouterOS communication:** All device communication uses the RouterOS binary API over TLS (port 8729). InsecureSkipVerify is enabled by default because RouterOS devices typically use self-signed certificates. To eliminate this risk, use the Internal Certificate Authority feature to issue verified TLS certificates to your devices.
- **CORS enforcement:** Strict CORS policy in production, configured via `CORS_ORIGINS` environment variable.
- **Rate limiting:** Authentication endpoints are rate-limited to 5 requests per minute per IP to prevent brute-force attacks.
- **Cookie security:** httpOnly cookies prevent JavaScript access to session tokens. The `Secure` flag is auto-detected based on whether CORS origins use HTTPS. If you switch from HTTP to HTTPS, existing sessions will be invalidated — users will need to log in again.

## Data Protection

- **Config backups:** Encrypted at rest via OpenBao Transit envelope encryption before database storage.
- **Audit logs:** Encrypted at rest via Transit encryption — audit log content is protected even from database administrators.
- **Subresource Integrity (SRI):** SHA-384 hashes on JavaScript bundles prevent tampering with frontend code.
- **Content Security Policy (CSP):** Strict CSP headers prevent XSS, code injection, and unauthorized resource loading.
- **No external dependencies:** Fully self-hosted with no external analytics, telemetry, CDNs, or third-party services. The only outbound connections are:
  - RouterOS firmware update checks (no device data sent)
  - SMTP for email notifications (if configured)
  - Webhooks for alerts (if configured)

## Security Headers

The following security headers are enforced on all responses:

| Header | Value | Purpose |
|--------|-------|---------|
| `Strict-Transport-Security` | `max-age=31536000; includeSubDomains` | Force HTTPS connections |
| `X-Content-Type-Options` | `nosniff` | Prevent MIME-type sniffing |
| `X-Frame-Options` | `DENY` | Prevent clickjacking via iframes |
| `Content-Security-Policy` | Strict policy | Prevent XSS and code injection |
| `Referrer-Policy` | `strict-origin-when-cross-origin` | Limit referrer information leakage |

## Audit Trail

- **Immutable audit log:** All significant actions are recorded in the `audit_logs` table — logins, configuration changes, device operations, admin actions.
- **Fire-and-forget logging:** The `log_action()` function records audit events asynchronously without blocking the main request. Each call opens a dedicated database session and self-commits, ensuring audit entries are persisted regardless of whether the caller's transaction commits or rolls back.
- **Per-tenant access:** Tenants can only view their own audit logs (enforced by RLS).
- **Encryption at rest:** Audit log content is encrypted via OpenBao Transit.
- **CSV export:** Audit logs can be exported in CSV format for compliance and reporting.
- **Account deletion:** When a user deletes their account, audit log entries are anonymized (PII removed) but the action records are retained for security compliance.

## Data Retention

| Data Type | Retention | Notes |
|-----------|-----------|-------|
| User accounts | Until deleted | Users can self-delete from Settings |
| Device metrics | 90 days | Purged by TimescaleDB retention policy |
| Configuration backups | Indefinite | Stored in git repositories on your server |
| Audit logs | Indefinite | Anonymized on account deletion |
| API keys | Until revoked | Cascade-deleted with user account |
| Encrypted key material | Until user deleted | Cascade-deleted with user account |
| Session data (Redis) | 15 min / 7 days | Auto-expiring access/refresh tokens |
| Password reset tokens | 30 minutes | Auto-expire |
| SRP session state | Short-lived | Auto-expire in Redis |

## GDPR Compliance

TOD provides built-in tools for GDPR compliance:

- **Right of Access (Art. 15):** Users can view their account information on the Settings page.
- **Right to Data Portability (Art. 20):** Users can export all personal data in JSON format from Settings.
- **Right to Erasure (Art. 17):** Users can permanently delete their account and all associated data. Audit logs are anonymized (PII removed) with a deletion receipt generated for compliance verification.
- **Right to Rectification (Art. 16):** Account information can be updated by the tenant administrator.

As a self-hosted application, the deployment operator is the data controller and is responsible for compliance with applicable data protection laws.
