Security Best Practices


v0.4.8Updated for nself v0.4.8

Security is a top priority in nself deployments. This guide covers security best practices, hardening configurations, and the tools nself provides to help secure your backend infrastructure.

Security Disclaimer

Security is a shared responsibility. While nself provides secure defaults and hardening tools, you are responsible for proper configuration, regular updates, and monitoring of your deployments.

Security Quick Start

# Run security audit
nself prod check

# Apply all security hardening
nself prod harden

# Generate strong secrets
nself prod secrets generate

# Configure SSL
nself prod ssl request yourdomain.com

# Configure firewall
nself prod firewall configure

Production Security Audit

Running Security Checks

# Comprehensive security audit
nself prod check

# Output:
# Production Security Audit
# ════════════════════════════════════════════════
#
# Authentication & Authorization
# ─────────────────────────────────────
# ✓ Admin UI disabled
# ✓ Hasura console disabled
# ✓ Dev mode disabled
# ✓ Strong JWT secret (64+ characters)
# ✓ Password policy configured
#
# Network Security
# ─────────────────────────────────────
# ✓ SSL/TLS enabled
# ✓ HSTS enabled
# ✓ Secure headers configured
# ✗ Rate limiting not configured
#
# Data Protection
# ─────────────────────────────────────
# ✓ Database password strong
# ✓ Backups enabled
# ✓ Encryption at rest enabled
# ✗ Audit logging not enabled
#
# Infrastructure
# ─────────────────────────────────────
# ✓ Firewall configured
# ✓ SSH key authentication
# ✓ Non-root containers
# ✗ Container resource limits not set
#
# Score: 85/100
# Critical Issues: 0
# Warnings: 3
# Recommendations: 4

Verbose Audit

# Detailed audit with recommendations
nself prod check --verbose

# Export audit report
nself prod check --output security-report.json

# Check specific category
nself prod check --category authentication
nself prod check --category network
nself prod check --category data

Authentication Security

JWT Configuration

# JWT settings for production
HASURA_JWT_KEY=your-secret-key-minimum-64-characters-for-production-use
HASURA_JWT_TYPE=HS256

# Token expiration
AUTH_ACCESS_TOKEN_EXPIRY=15m        # Short-lived access tokens
AUTH_REFRESH_TOKEN_EXPIRY=7d        # Longer refresh tokens

# Audience and issuer validation
AUTH_JWT_AUDIENCE=https://api.yourdomain.com
AUTH_JWT_ISSUER=https://auth.yourdomain.com

Password Policy

# Password requirements
AUTH_PASSWORD_MIN_LENGTH=12
AUTH_PASSWORD_REQUIRE_UPPERCASE=true
AUTH_PASSWORD_REQUIRE_LOWERCASE=true
AUTH_PASSWORD_REQUIRE_NUMBERS=true
AUTH_PASSWORD_REQUIRE_SYMBOLS=true
AUTH_PASSWORD_PREVENT_COMMON=true

# Password history
AUTH_PASSWORD_HISTORY_COUNT=5
AUTH_PASSWORD_MAX_AGE_DAYS=90

Multi-Factor Authentication

# Enable MFA for all users
AUTH_MFA_ENABLED=true
AUTH_MFA_REQUIRED_FOR_ROLES=admin,moderator

# TOTP configuration
AUTH_MFA_TOTP_ENABLED=true
AUTH_MFA_TOTP_ISSUER="Your App Name"

# Recovery codes
AUTH_MFA_RECOVERY_CODES_COUNT=10

Rate Limiting

# Authentication rate limiting
AUTH_RATE_LIMIT_ENABLED=true
AUTH_RATE_LIMIT_LOGIN_ATTEMPTS=5
AUTH_RATE_LIMIT_LOGIN_WINDOW=900        # 15 minutes
AUTH_RATE_LIMIT_SIGNUP_ATTEMPTS=3
AUTH_RATE_LIMIT_SIGNUP_WINDOW=3600      # 1 hour

# Account lockout
AUTH_ACCOUNT_LOCKOUT_ENABLED=true
AUTH_ACCOUNT_LOCKOUT_ATTEMPTS=5
AUTH_ACCOUNT_LOCKOUT_DURATION=1800      # 30 minutes

Network Security

SSL/TLS Configuration

# Enable SSL with Let's Encrypt
SSL_ENABLED=true
SSL_PROVIDER=letsencrypt
LETSENCRYPT_EMAIL=admin@yourdomain.com

# TLS protocol versions (disable old versions)
SSL_PROTOCOLS="TLSv1.2 TLSv1.3"

# Strong cipher suites
SSL_CIPHERS="ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384"
SSL_PREFER_SERVER_CIPHERS=true

# HSTS (HTTP Strict Transport Security)
HSTS_ENABLED=true
HSTS_MAX_AGE=31536000           # 1 year
HSTS_INCLUDE_SUBDOMAINS=true
HSTS_PRELOAD=true

Security Headers

# Enable security headers
SECURITY_HEADERS_ENABLED=true

# Content Security Policy
CSP_ENABLED=true
CSP_DEFAULT_SRC="'self'"
CSP_SCRIPT_SRC="'self' 'unsafe-inline'"
CSP_STYLE_SRC="'self' 'unsafe-inline'"
CSP_IMG_SRC="'self' data: https:"
CSP_CONNECT_SRC="'self' https://api.yourdomain.com wss://api.yourdomain.com"

# Other security headers
X_FRAME_OPTIONS=DENY
X_CONTENT_TYPE_OPTIONS=nosniff
X_XSS_PROTECTION="1; mode=block"
REFERRER_POLICY=strict-origin-when-cross-origin
PERMISSIONS_POLICY="geolocation=(), microphone=(), camera=()"

CORS Configuration

# CORS settings
CORS_ENABLED=true
CORS_ALLOWED_ORIGINS=https://yourdomain.com,https://app.yourdomain.com
CORS_ALLOWED_METHODS=GET,POST,PUT,DELETE,OPTIONS
CORS_ALLOWED_HEADERS=Authorization,Content-Type,X-Requested-With
CORS_ALLOW_CREDENTIALS=true
CORS_MAX_AGE=86400

Firewall Configuration

# Configure firewall via nself
nself prod firewall configure --dry-run
nself prod firewall configure

# Manual firewall rules (UFW)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable

# Docker network isolation
DOCKER_NETWORK_INTERNAL=true
EXPOSE_INTERNAL_PORTS=false

Data Protection

Database Security

# Strong database credentials
POSTGRES_PASSWORD=your-strong-password-32-chars-minimum

# Connection security
POSTGRES_SSL_MODE=require
POSTGRES_SSL_CERT=/path/to/server.crt
POSTGRES_SSL_KEY=/path/to/server.key

# Connection restrictions
POSTGRES_ALLOWED_HOSTS=hasura,api-service
POSTGRES_MAX_CONNECTIONS=100

# Audit logging
POSTGRES_LOG_STATEMENT=ddl
POSTGRES_LOG_CONNECTIONS=true
POSTGRES_LOG_DISCONNECTIONS=true

Encryption at Rest

# PostgreSQL encryption
POSTGRES_DATA_ENCRYPTION=true

# MinIO encryption
MINIO_SSE_ENABLED=true
MINIO_SSE_MASTER_KEY=your-32-character-master-key

# Backup encryption
BACKUP_ENCRYPTION=true
BACKUP_ENCRYPTION_KEY=your-backup-encryption-key

Secrets Management

# Generate secure secrets
nself prod secrets generate

# Rotate secrets
nself prod secrets rotate POSTGRES_PASSWORD
nself prod secrets rotate --all

# Validate secrets
nself prod secrets validate

# File permissions for secrets
chmod 600 .environments/prod/.env.secrets

Secret Management Rules

  • Never commit secrets to git
  • Use different secrets for each environment
  • Rotate secrets regularly (quarterly minimum)
  • Use a secrets manager for production (Vault, AWS Secrets Manager)

Infrastructure Security

Container Security

# Run containers as non-root
CONTAINER_USER=1000:1000

# Resource limits
POSTGRES_MEMORY_LIMIT=2GB
POSTGRES_CPU_LIMIT=2.0
HASURA_MEMORY_LIMIT=1GB
HASURA_CPU_LIMIT=1.0

# Read-only root filesystem
CONTAINER_READ_ONLY_ROOT=true

# Drop unnecessary capabilities
CONTAINER_DROP_CAPABILITIES=ALL
CONTAINER_ADD_CAPABILITIES=NET_BIND_SERVICE

# Security options
CONTAINER_NO_NEW_PRIVILEGES=true

SSH Security

# SSH configuration recommendations
# /etc/ssh/sshd_config

# Disable password authentication
PasswordAuthentication no
PubkeyAuthentication yes

# Disable root login
PermitRootLogin no

# Use SSH key authentication only
ChallengeResponseAuthentication no

# Limit SSH to specific users
AllowUsers deploy

# Change default port (optional)
Port 2222

# Limit authentication attempts
MaxAuthTries 3
LoginGraceTime 60

Server Hardening

# Apply nself hardening
nself prod harden

# This applies:
# - Disable unnecessary services
# - Configure automatic security updates
# - Set up fail2ban
# - Configure sysctl security parameters
# - Set file permissions
# - Enable audit logging

# Manual hardening steps
# 1. Keep system updated
sudo apt update && sudo apt upgrade

# 2. Install fail2ban
sudo apt install fail2ban
sudo systemctl enable fail2ban

# 3. Configure automatic updates
sudo apt install unattended-upgrades
sudo dpkg-reconfigure unattended-upgrades

Hasura Security

Production Configuration

# Disable development features
HASURA_GRAPHQL_ENABLE_CONSOLE=false
HASURA_GRAPHQL_DEV_MODE=false
HASURA_GRAPHQL_ENABLE_TELEMETRY=false

# Strong admin secret
HASURA_GRAPHQL_ADMIN_SECRET=your-strong-admin-secret-32-chars

# Unauthorized role
HASURA_GRAPHQL_UNAUTHORIZED_ROLE=anonymous

# Connection limits
HASURA_GRAPHQL_WS_CONNECTION_INIT_TIMEOUT=10s
HASURA_GRAPHQL_MAX_CONNECTIONS=50

Row-Level Security

-- Enable row-level security on sensitive tables
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;

-- Create policies
CREATE POLICY users_own_data ON users
  FOR ALL
  USING (id = current_setting('hasura.user')::uuid);

CREATE POLICY posts_author_access ON posts
  FOR ALL
  USING (
    author_id = current_setting('hasura.user')::uuid
    OR status = 'published'
  );

-- Admin bypass
CREATE POLICY admin_full_access ON users
  FOR ALL
  USING (current_setting('hasura.role') = 'admin');

Hasura Permissions

// Example Hasura permission configuration
{
  "role": "user",
  "table": "posts",
  "permissions": {
    "select": {
      "filter": {
        "_or": [
          { "author_id": { "_eq": "X-Hasura-User-Id" } },
          { "status": { "_eq": "published" } }
        ]
      },
      "columns": ["id", "title", "content", "created_at"],
      "limit": 100
    },
    "insert": {
      "check": {
        "author_id": { "_eq": "X-Hasura-User-Id" }
      },
      "columns": ["title", "content"]
    },
    "update": {
      "filter": { "author_id": { "_eq": "X-Hasura-User-Id" } },
      "columns": ["title", "content"]
    },
    "delete": {
      "filter": { "author_id": { "_eq": "X-Hasura-User-Id" } }
    }
  }
}

Monitoring and Audit

Audit Logging

# Enable audit logging
AUDIT_LOG_ENABLED=true
AUDIT_LOG_LEVEL=info
AUDIT_LOG_RETENTION_DAYS=90

# What gets logged:
# - Authentication events (login, logout, failed attempts)
# - Authorization failures
# - Data access (sensitive tables)
# - Configuration changes
# - Admin actions
-- Audit log table
CREATE TABLE audit_logs (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  timestamp TIMESTAMPTZ DEFAULT NOW(),
  user_id UUID,
  action VARCHAR(50) NOT NULL,
  resource VARCHAR(100),
  resource_id UUID,
  details JSONB,
  ip_address INET,
  user_agent TEXT,
  success BOOLEAN NOT NULL
);

CREATE INDEX idx_audit_logs_timestamp ON audit_logs(timestamp);
CREATE INDEX idx_audit_logs_user_id ON audit_logs(user_id);
CREATE INDEX idx_audit_logs_action ON audit_logs(action);

Security Monitoring

# Enable security monitoring
SECURITY_MONITORING_ENABLED=true

# Alert thresholds
ALERT_FAILED_LOGIN_THRESHOLD=10
ALERT_FAILED_LOGIN_WINDOW=300    # 5 minutes
ALERT_RATE_LIMIT_THRESHOLD=100
ALERT_UNUSUAL_ACCESS_ENABLED=true

# Alert destinations
ALERT_EMAIL=security@yourdomain.com
ALERT_SLACK_WEBHOOK=https://hooks.slack.com/...
ALERT_PAGERDUTY_KEY=your-pagerduty-key

Intrusion Detection

# Install and configure fail2ban
sudo apt install fail2ban

# Create jail configuration
# /etc/fail2ban/jail.local
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600

[nginx-http-auth]
enabled = true
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 3
bantime = 3600

# Restart fail2ban
sudo systemctl restart fail2ban

Security Checklist

Production Security Checklist

  • [ ]Run nself prod check and address all issues
  • [ ]Admin UI and console disabled
  • [ ]Strong secrets generated and unique per environment
  • [ ]SSL/TLS configured with valid certificates
  • [ ]Security headers configured
  • [ ]Firewall configured to allow only necessary ports
  • [ ]Rate limiting enabled
  • [ ]Database backups configured and tested
  • [ ]Audit logging enabled
  • [ ]Monitoring and alerting configured
  • [ ]SSH hardened (key auth only, non-root user)
  • [ ]Container resource limits set
  • [ ]Hasura permissions properly configured
  • [ ]Automatic security updates enabled

Security Updates

Staying Updated

# Check for nself updates
nself update check

# Update nself CLI
nself update

# Update container images
nself update images

# Review security advisories
nself security advisories

Vulnerability Scanning

# Scan container images for vulnerabilities
docker scan hasura/graphql-engine:latest
docker scan postgres:16

# Use Trivy for comprehensive scanning
trivy image hasura/graphql-engine:latest
trivy image postgres:16

Incident Response

Security Incident Procedures

  1. Detection: Monitor alerts, logs, and anomaly detection
  2. Containment: Isolate affected systems, revoke compromised credentials
  3. Investigation: Analyze logs, determine scope and impact
  4. Remediation: Fix vulnerabilities, rotate secrets, patch systems
  5. Recovery: Restore from clean backups if needed
  6. Documentation: Document incident and lessons learned

Emergency Commands

# Immediately rotate all secrets
nself prod secrets rotate --all --force

# Block all external access (emergency)
nself prod firewall lockdown

# Take system offline for maintenance
nself stop

# Restore from clean backup
nself db restore --clean latest-verified-backup.sql.gz

Next Steps

Security is an ongoing process, not a one-time configuration. Regularly audit your deployment, stay updated on security best practices, and monitor for potential threats.