server/index.js:
- Import http and https modules
- Resolve TLS_ENABLED early (before Helmet) so upgradeInsecureRequests
CSP directive fires when TLS is active directly (not only via proxy)
- loadTlsCredentials() reads TLS_CERT/TLS_KEY (defaulting to bundled
snakeoil) and returns null on failure (graceful HTTP fallback)
- Start https.createServer or http.createServer depending on credentials
- Startup banner now shows protocol, TLS cert path, and snakeoil warning
certs/:
- Add bundled snakeoil self-signed certificate (RSA 2048, 10yr, SAN for
localhost + 127.0.0.1) for out-of-the-box HTTPS without configuration
- .gitignore allows only snakeoil.{crt,key} — real certs must not be
committed
Dockerfile:
- COPY certs/ into image so snakeoil default is always available
- HEALTHCHECK updated to https:// with --no-check-certificate
docker-compose.yaml:
- Port now exposes HTTPS directly by default
- TLS_CERT/TLS_KEY/TLS_ENABLED/TRUST_PROXY documented with Option A/B
- cert volume mount examples added (commented out)
- healthcheck updated to https with --no-check-certificate
.env.sample:
- New TLS/HTTPS section with TLS_ENABLED, TLS_CERT, TLS_KEY
- openssl self-signed cert generation example included
docs/ARCHITECTURE.md:
- Configuration table: TLS_ENABLED, TLS_CERT, TLS_KEY env vars added
- Docker image section: TLS default behaviour documented
- Docker Compose example: Option A (direct TLS) / Option B (proxy) layout
- Security checklist: HTTPS now first item, updated for TLS modes
125 lines
5.8 KiB
Plaintext
125 lines
5.8 KiB
Plaintext
# sofarr Configuration
|
|
# Copy this file to .env and update with your values
|
|
|
|
# =============================================================================
|
|
# SERVER SETTINGS
|
|
# =============================================================================
|
|
PORT=3001
|
|
|
|
# Logging level: debug, info, warn, error, silent
|
|
# - debug: Verbose logging for troubleshooting
|
|
# - info: Standard operational logging (default)
|
|
# - warn: Only warnings and errors
|
|
# - error: Only errors
|
|
# - silent: No logging
|
|
LOG_LEVEL=info
|
|
|
|
# Cookie signing secret for tamper-proof session cookies
|
|
# Required in production (server exits on startup if unset).
|
|
# Generate with: openssl rand -hex 32
|
|
COOKIE_SECRET=your-cookie-secret-here
|
|
|
|
# =============================================================================
|
|
# TLS / HTTPS
|
|
# =============================================================================
|
|
|
|
# TLS is enabled by default using the bundled snakeoil self-signed certificate
|
|
# (valid for localhost/127.0.0.1, 10-year expiry).
|
|
# Set TLS_CERT and TLS_KEY to use your own certificate (recommended).
|
|
# Set TLS_ENABLED=false to run in plain HTTP mode (e.g. behind a TLS proxy).
|
|
#
|
|
# To generate a self-signed cert for your own hostname:
|
|
# openssl req -x509 -newkey rsa:2048 -keyout server.key -out server.crt \
|
|
# -days 365 -nodes -subj "/CN=yourhostname" \
|
|
# -addext "subjectAltName=DNS:yourhostname,IP:192.168.x.x"
|
|
#
|
|
# TLS_ENABLED=true
|
|
# TLS_CERT=/path/to/server.crt
|
|
# TLS_KEY=/path/to/server.key
|
|
|
|
# =============================================================================
|
|
# REVERSE PROXY & DEPLOYMENT
|
|
# =============================================================================
|
|
|
|
# Set to 1 when running behind a reverse proxy (Nginx, Caddy, Traefik).
|
|
# This makes Express trust X-Forwarded-For and X-Forwarded-Proto so that
|
|
# req.ip reflects the real client IP and cookies are marked secure correctly.
|
|
# Leave unset if sofarr is exposed directly to the internet.
|
|
# TRUST_PROXY=1
|
|
|
|
# Directory for persistent data (SQLite token store, server logs).
|
|
# Must be writable by the process user (UID 1000 in the container).
|
|
# Defaults to ./data relative to the project root.
|
|
# DATA_DIR=/app/data
|
|
|
|
# Background polling interval in milliseconds (default: 5000)
|
|
# sofarr polls all services in the background and caches results so
|
|
# dashboard requests are near-instant.
|
|
# Set to 0, "off", "false", or "disabled" to disable background polling.
|
|
# When disabled, data is fetched on-demand when a user opens the dashboard
|
|
# and cached for 30 seconds so other users benefit from the same fetch.
|
|
# POLL_INTERVAL=5000
|
|
|
|
# =============================================================================
|
|
# EMBY (Authentication - Required)
|
|
# =============================================================================
|
|
EMBY_URL=https://emby.example.com
|
|
EMBY_API_KEY=your-emby-api-key-here
|
|
|
|
# =============================================================================
|
|
# SABNZBD INSTANCES (JSON Array Format)
|
|
# Add one or more SABnzbd instances as a single-line JSON array
|
|
# Format: [{"name":"instance-name","url":"https://...","apiKey":"..."}]
|
|
# =============================================================================
|
|
SABNZBD_INSTANCES=[{"name":"primary","url":"https://sabnzbd.example.com","apiKey":"your-sabnzbd-api-key"}]
|
|
|
|
# Legacy single-instance format (optional - still supported)
|
|
# SABNZBD_URL=https://sabnzbd.example.com
|
|
# SABNZBD_API_KEY=your-sabnzbd-api-key
|
|
|
|
# =============================================================================
|
|
# QBITTORRENT INSTANCES (JSON Array Format)
|
|
# Add one or more qBittorrent instances as a single-line JSON array
|
|
# Uses username/password authentication (not API key)
|
|
# Format: [{"name":"instance-name","url":"https://...","username":"...","password":"..."}]
|
|
# =============================================================================
|
|
QBITTORRENT_INSTANCES=[{"name":"main","url":"https://qbittorrent.example.com","username":"admin","password":"your-password"}]
|
|
|
|
# Legacy single-instance format (optional - still supported)
|
|
# QBITTORRENT_URL=https://qbittorrent.example.com
|
|
# QBITTORRENT_USERNAME=admin
|
|
# QBITTORRENT_PASSWORD=your-password
|
|
|
|
# =============================================================================
|
|
# SONARR INSTANCES (JSON Array Format)
|
|
# Add one or more Sonarr instances as a single-line JSON array
|
|
# Format: [{"name":"instance-name","url":"https://...","apiKey":"..."}]
|
|
# =============================================================================
|
|
SONARR_INSTANCES=[{"name":"main","url":"https://sonarr.example.com","apiKey":"your-sonarr-api-key"}]
|
|
|
|
# Legacy single-instance format (optional - still supported)
|
|
# SONARR_URL=https://sonarr.example.com
|
|
# SONARR_API_KEY=your-sonarr-api-key
|
|
|
|
# =============================================================================
|
|
# RADARR INSTANCES (JSON Array Format)
|
|
# Add one or more Radarr instances as a single-line JSON array
|
|
# Format: [{"name":"instance-name","url":"https://...","apiKey":"..."}]
|
|
# =============================================================================
|
|
RADARR_INSTANCES=[{"name":"main","url":"https://radarr.example.com","apiKey":"your-radarr-api-key"}]
|
|
|
|
# Legacy single-instance format (optional - still supported)
|
|
# RADARR_URL=https://radarr.example.com
|
|
# RADARR_API_KEY=your-radarr-api-key
|
|
|
|
# =============================================================================
|
|
# NOTES
|
|
# =============================================================================
|
|
# 1. All JSON arrays must be on a single line (no line breaks)
|
|
# 2. Instance "name" can be anything descriptive (e.g., "main", "4k", "backup")
|
|
# 3. URLs should include protocol (http:// or https://)
|
|
# 4. For qBittorrent, ensure Web UI is enabled in settings
|
|
# 5. User downloads are matched by tags in Sonarr/Radarr - tag your media!
|
|
# 6. Background polling keeps data fresh; disable it for low-resource setups
|
|
# =============================================================================
|