Skip to content

Security Configuration

JWT Authentication

SLOzy uses HMAC-SHA256 signed JWT tokens for API authentication.

VariableDefaultRecommendation
JWT_SECRETdev_change_me_in_production_min_32_charsGenerate with openssl rand -base64 48
JWT_EXPIRATION8h8h for web apps, 1h for high-security environments
REFRESH_TOKEN_EXPIRATION30dRotate refresh tokens periodically

Important: The default JWT_SECRET is hardcoded for development only. In production, always set it to a cryptographically random value. The secret must be at least 32 characters.

Tokens are sent via the Authorization: Bearer <token> header. The backend validates every protected route using a middleware in internal/middleware/.

Two-Factor Authentication (2FA)

2FA support is available in the UI but requires SMTP configuration in .env to send verification codes:

VariableDefaultDescription
SMTP_HOSTSMTP server hostname
SMTP_PORT587SMTP server port
SMTP_USERSMTP username
SMTP_PASSWORDSMTP password
SMTP_FROMSender email address

Without these variables, 2FA will show "Enable 2FA" in the UI but cannot deliver codes. Planned support for authenticator apps (TOTP) is not yet implemented.

CORS

Cross-Origin Resource Sharing is controlled by:

VariableDefaultDescription
CORS_ENABLEDtrueEnable/disable CORS headers
CORS_ORIGINShttp://localhost:3000,http://localhost:8080Comma-separated allowed origins

In production, restrict CORS_ORIGINS to your actual frontend domain:

CORS_ORIGINS=https://slozy.yourdomain.com

Rate Limiting

Rate limiting is enforced at two levels:

Application Level (Go backend)

VariableDefaultDescription
RATE_LIMIT_REQUESTS_PER_SECOND10General API per-second limit
RATE_LIMIT_SLO_CREATES_PER_SECOND5Stricter limit for the SLO creation endpoint

The provided deploy/nginx.conf sets up zone-based rate limiting:

limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=slo_create:10m rate=5r/s;

Two zones are defined:

  • api — 10 requests/second with burst 20, applied to all /api/ routes
  • slo_create — 5 requests/second with burst 10, applied to /api/slo/create

Security Headers

The production Nginx config (deploy/production/nginx.prod.conf) sets these headers:

HeaderValue
X-Frame-OptionsDENY
X-Content-Type-Optionsnosniff
X-XSS-Protection1; mode=block
Referrer-Policystrict-origin-when-cross-origin
Content-Security-Policydefault-src 'self'
Strict-Transport-Securitymax-age=31536000; includeSubDomains; preload

The dev Nginx config (deploy/nginx.conf) uses a slightly more permissive CSP to allow API calls during development.

Additional Security Settings (.env.production)

VariableDefaultDescription
ENABLE_SECURITYtrueMaster toggle for all security middleware
ENABLE_RATE_LIMITtrueEnables Go-side rate limiting
GLOBAL_RATE_LIMIT1000Global requests/second across all clients
PER_IP_RATE_LIMIT100Per-IP requests/second
ENABLE_IP_WHITELISTfalseRestrict access to specific IPs/CIDRs
IP_WHITELIST127.0.0.1,10.0.0.0/8Allowed IP ranges

Secrets Management

For production, never store secrets in environment variables in plain text. Recommended approaches:

  1. Kubernetes Secrets — store JWT_SECRET, POSTGRES_PASSWORD, REDIS_PASSWORD as k8s secrets
  2. External Secrets Operator — sync from AWS Secrets Manager / GCP Secret Manager
  3. HashiCorp Vault — inject secrets via Vault Agent sidecar
bash
kubectl create secret generic slozy-secrets \
  --from-literal=jwt-secret="$(openssl rand -base64 48)" \
  --from-literal=postgres-password="<strong-password>" \
  -n slozy