# TREK Security Hardening Guide: Production Deployment Options

> Learn how to harden your TREK production deployment. Secure your system with encryption, TLS, 2FA, and IP spoofing prevention. Protect your data effectively.

- Repository: [Maurice/TREK](https://github.com/mauriceboe/TREK)
- Tags: best-practices
- Published: 2026-07-04

---

**To secure a production TREK deployment, generate a 256-bit `ENCRYPTION_KEY`, terminate TLS behind a reverse proxy with `FORCE_HTTPS=true`, enable two-factor authentication for admin accounts, and verify that `TRUST_PROXY` is configured to prevent IP spoofing attacks.**

TREK is a self-hosted travel-planning platform that requires explicit security configuration when exposed to production workloads. The following hardening options cover data encryption, network transport, authentication controls, and session management as documented in the official [`wiki/Security-Hardening.md`](https://github.com/mauriceboe/TREK/blob/main/wiki/Security-Hardening.md) and implemented in [`src/config/config.schema.ts`](https://github.com/mauriceboe/TREK/blob/main/src/config/config.schema.ts).

## Data Encryption and Secrets Management

### Generate and Protect the ENCRYPTION_KEY

TREK uses a 256-bit encryption key to protect API credentials and sensitive data at rest. Generate a secure key using OpenSSL and provide it via the `ENCRYPTION_KEY` environment variable:

```bash
openssl rand -hex 32

```

On first startup, TREK stores this key in `data/.encryption_key`. **Back up this file separately from your database backups**—losing the key renders all encrypted secrets permanently unreadable. Never commit the key to version control or include it in automated database dumps.

### Rotate Keys After Exposure

If the encryption key is compromised, follow the procedure documented in [`wiki/Encryption-Key-Rotation.md`](https://github.com/mauriceboe/TREK/blob/main/wiki/Encryption-Key-Rotation.md) to rotate the key without losing existing encrypted data. This process decrypts existing data with the old key and re-encrypts it with the new key atomically.

## HTTPS and Reverse Proxy Configuration

### Terminate TLS at the Edge

TREK expects TLS termination to occur at a reverse proxy (nginx, Caddy, or Traefik). The proxy must forward the original protocol via the `X-Forwarded-Proto` header so TREK can enforce secure redirects.

### Force HTTPS and Trust Proxy Settings

Set `FORCE_HTTPS=true` to enable three critical security features:
- **301 redirects** from HTTP to HTTPS
- **HSTS header** with `max-age=31536000`
- **Secure cookie flag** automatically applied to the `trek_session` cookie

Configure `TRUST_PROXY=1` (or the appropriate hop count) so Express.js reads the real client IP from `X-Forwarded-For`. This prevents attackers from bypassing rate limits by spoofing headers and avoids redirect loops behind load balancers.

### Nginx Configuration Example

Configure your reverse proxy to handle WebSocket upgrades and large file uploads for backup restoration:

```nginx
server {
    listen 443 ssl http2;
    server_name trek.example.com;

    ssl_certificate /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;

    location /ws {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 86400;
        client_max_body_size 500m;
    }

    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        client_max_body_size 500m;
    }
}

```

*Source:* [`wiki/Reverse-Proxy.md`](https://github.com/mauriceboe/TREK/blob/main/wiki/Reverse-Proxy.md)

## Authentication and Access Controls

### Enable Two-Factor Authentication

Navigate to **Admin → Settings → Two-Factor Authentication** to enable **TOTP-based 2FA** for the admin account. Once enabled, all administrative actions require a time-based code from an authenticator app, preventing credential-stuffing attacks even if the password is compromised.

### Rotate JWT Secrets on Demand

If session tokens are suspected of being compromised, invalidate all active sessions immediately by rotating the JWT signing secret:

```bash
curl -X POST http://localhost:3000/api/admin/rotate-jwt-secret \
     -H "Authorization: Bearer <admin-jwt>"

```

This endpoint is documented in [`wiki/Security-Hardening.md`](https://github.com/mauriceboe/TREK/blob/main/wiki/Security-Hardening.md) and forces all users to re-authenticate.

### Disable Open Registration

Unless intentionally running a public instance, disable open registration in the admin panel to prevent uncontrolled account creation. Restrict registration to invite-only or manual admin creation.

## Session and Password Security

### Secure Cookie Configuration

TREK stores sessions as JWTs in an `httpOnly` cookie named `trek_session`. The `secure` flag is automatically set when `NODE_ENV=production` or `FORCE_HTTPS=true`. **Never set `COOKIE_SECURE=false` in production**—this flag exists only for temporary LAN testing without TLS.

Session duration defaults to 24 hours for standard logins and 30 days for "Remember Me" sessions, configurable via `SESSION_DURATION` and `SESSION_DURATION_REMEMBER`.

### Password Policy Enforcement

TREK enforces a strict password policy requiring minimum 8 characters with mixed case, digits, and special characters. Common passwords and repetitive strings are rejected at the API level. Passwords are hashed using **bcrypt with a cost factor of 12**, providing robust protection against brute-force attacks without additional configuration.

## Network Security and Rate Limiting

### Built-In Rate Limiting

TREK implements in-memory rate limiting on authentication endpoints. Default limits per source IP are:

- **Login / Register / Invite:** 10 attempts per 15 minutes
- **MFA verification / enable:** 5 attempts per 15 minutes
- **Password change:** 5 attempts per 15 minutes
- **MCP token creation:** 5 attempts per 15 minutes

Accurate rate limiting requires `TRUST_PROXY` to be set correctly so TREK identifies the true client IP rather than the proxy's internal address.

### Content Security Policy Headers

Helmet.js enforces a strict CSP that mitigates XSS and data injection attacks:

- `default-src 'self'`
- `script-src 'self' 'wasm-unsafe-eval'` (no `unsafe-inline`)
- `object-src 'none'`
- `frame-src 'none'`
- `frame-ancestors 'self'`

When `FORCE_HTTPS=true`, the `upgrade-insecure-requests` directive is automatically injected to force mixed-content upgrades.

## Backup and Monitoring Strategies

### Encrypted Backup Procedures

Enable automatic backups via the admin panel and store them off-site. Backup archives contain the encrypted `data/.encryption_key` file, but you must maintain a separate backup of the key itself. Without the key, database backups containing encrypted API credentials are useless.

### Audit Logging and Updates

Review the **Audit Log** (documented in [`wiki/Audit-Log.md`](https://github.com/mauriceboe/TREK/blob/main/wiki/Audit-Log.md)) periodically for unexpected admin actions or logins from unusual IPs. Subscribe to the GitHub releases feed to apply security updates promptly, as TREK does not auto-update.

## Production Environment Variables

Configure these variables for a hardened deployment:

| Variable | Production Value |
|----------|------------------|
| `ENCRYPTION_KEY` | 64-character hex string (256-bit) |
| `FORCE_HTTPS` | `true` |
| `TRUST_PROXY` | `1` (or proxy hop count) |
| `ALLOWED_ORIGINS` | `https://trek.example.com` |
| `ALLOW_INTERNAL_NETWORK` | `false` |
| `SESSION_DURATION` | `24h` |
| `SESSION_DURATION_REMEMBER` | `30d` |

*Source:* [`wiki/Environment-Variables.md`](https://github.com/mauriceboe/TREK/blob/main/wiki/Environment-Variables.md)

### Docker Compose Example

```yaml
version: "3.8"
services:
  trek:
    image: ghcr.io/mauriceboe/trek:latest
    restart: unless-stopped
    environment:
      - NODE_ENV=production
      - ENCRYPTION_KEY=${ENCRYPTION_KEY}
      - FORCE_HTTPS=true
      - TRUST_PROXY=1
      - ALLOWED_ORIGINS=https://trek.example.com
      - SESSION_DURATION=24h
      - SESSION_DURATION_REMEMBER=30d
    ports:
      - "3000:3000"
    volumes:
      - trek-data:/app/data
volumes:
  trek-data:

```

## Summary

- **Generate a 256-bit `ENCRYPTION_KEY`** using `openssl rand -hex 32` and back it up separately from database backups
- **Terminate TLS at a reverse proxy** and set `FORCE_HTTPS=true` to enable HSTS and secure cookies
- **Configure `TRUST_PROXY`** to ensure rate limiting and logging see the actual client IP
- **Enable two-factor authentication** for admin accounts and disable open registration unless required
- **Monitor the audit log** and rotate JWT secrets immediately if session compromise is suspected
- **Use the provided nginx configuration** to properly handle WebSocket upgrades and large file uploads

## Frequently Asked Questions

### How do I rotate the encryption key if it was exposed?

Follow the procedure in [`wiki/Encryption-Key-Rotation.md`](https://github.com/mauriceboe/TREK/blob/main/wiki/Encryption-Key-Rotation.md) to perform an atomic rotation that preserves existing encrypted data. The process decrypts all secrets with the old key and re-encrypts them with the new key without service interruption. After rotation, verify that `data/.encryption_key` contains the new value and update your offline backups immediately.

### What reverse proxy settings are required for WebSocket support?

Configure your proxy to upgrade connections for the `/ws` endpoint by setting `proxy_http_version 1.1` and forwarding the `Upgrade` and `Connection` headers. Set `client_max_body_size` to at least 500m to accommodate backup restore uploads. Always forward `X-Forwarded-Proto` so TREK can detect HTTPS correctly.

### How do I invalidate all active user sessions immediately?

Send a POST request to `/api/admin/rotate-jwt-secret` with an admin JWT in the Authorization header. This invalidates all existing session tokens instantly, forcing every user to re-authenticate. This is the recommended response to suspected session hijacking or credential leaks.

### Is TREK's built-in rate limiting sufficient for production?

The built-in memory-based rate limiting (10 attempts per 15 minutes for login, 5 for MFA) is sufficient for single-instance deployments. However, if running multiple TREK instances behind a load balancer, you must implement rate limiting at the reverse proxy or load balancer level, as the in-memory store does not share state between nodes.