Files
Dashboard/docs/SECURITY.md

7.9 KiB

Security & Best Practices

Credential Management

⚠️ Critical Security Rules

  1. Never commit .env.local to Git

    • It contains passwords and API keys
    • Use .env.example for template only
    • Add to .gitignore (already configured)
  2. Rotate credentials regularly

    • Change Synology password every 90 days
    • Rotate UniFi credentials quarterly
    • Update Grafana API keys if compromised
  3. Use strong passwords

    • Minimum 16 characters
    • Mix of uppercase, lowercase, numbers, special characters
    • Unique per service

Credential Storage

Best Practice: Use a secrets manager

Option 1: HashiCorp Vault

# Store credentials in Vault
vault kv put secret/dashboard/atlas \
  unifi_password="..." \
  synology_password="..."

# Load in container startup script
export UNIFI_PASSWORD=$(vault kv get -field=unifi_password secret/dashboard/atlas)

Option 2: AWS Secrets Manager

# Store and retrieve
aws secretsmanager get-secret-value --secret-id dashboard/credentials

Option 3: GitHub Actions Secrets (for automation)

env:
  UNIFI_PASSWORD: ${{ secrets.UNIFI_PASSWORD }}

Network Security

Docker API Security

⚠️ Current Setup: Docker API exposed to internal network only

# Verify Docker API is not publicly exposed
curl http://100.104.196.38:2375/containers/json

# Should NOT be accessible from external networks
# If it is, restrict with firewall:
sudo ufw allow from 100.104.196.0/24 to any port 2375
sudo ufw deny from any to any port 2375

HTTPS/SSL Configuration

Recommended: Use reverse proxy with SSL

# Nginx example
server {
    listen 443 ssl http2;
    server_name dashboard.yourdomain.com;
    
    ssl_certificate /etc/ssl/certs/your_cert.crt;
    ssl_certificate_key /etc/ssl/private/your_key.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    
    location / {
        proxy_pass http://localhost:3001;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

VPN/Network Access

Recommended Setup:

  1. Dashboard accessible only via VPN
  2. Or restrict to specific IP ranges:
# UFW firewall rules
sudo ufw allow from 100.104.196.0/24 to any port 3001
sudo ufw deny from any to any port 3001

Authentication & Authorization

Basic Auth (Simple)

Add basic authentication with Nginx/Traefik:

# Traefik example
labels:
  - "traefik.http.middlewares.auth.basicauth.users=admin:your_hashed_password"
  - "traefik.http.routers.dashboard.middlewares=auth"

Generate hashed password:

echo $(htpasswd -nB admin) | sed -r 's/:.*//'
# Use output in Traefik config

OAuth2 (Advanced)

Using Oauth2-proxy:

# docker-compose.yml addition
oauth2-proxy:
  image: quay.io/oauth2-proxy/oauth2-proxy:v7.4.0
  environment:
    OAUTH2_PROXY_PROVIDER: github
    OAUTH2_PROXY_CLIENT_ID: your_client_id
    OAUTH2_PROXY_CLIENT_SECRET: your_client_secret
    OAUTH2_PROXY_COOKIE_SECRET: your_secret
  ports:
    - "4180:4180"

API Security

Rate Limiting

Add rate limiting to API endpoints:

// src/app/api/containers/route.ts
import rateLimit from 'express-rate-limit';

const limiter = rateLimit({
  windowMs: 1 * 60 * 1000, // 1 minute
  max: 100, // 100 requests per minute
});

export const GET = limiter(async (req) => {
  // ... existing code
});

Input Validation

Always validate external inputs:

// Validate environment variables
function validateEnv() {
  const required = ['DOCKER_HOST', 'UNIFI_HOST', 'SYNOLOGY_HOST'];
  const missing = required.filter(key => !process.env[key]);
  
  if (missing.length > 0) {
    throw new Error(`Missing env vars: ${missing.join(', ')}`);
  }
}

API Key Rotation

For Grafana API key:

# Generate new key in Grafana UI
# Update in .env.local
# Revoke old key in Grafana

# Script to automate
#!/bin/bash
NEW_KEY=$(curl -X POST https://grafana/api/auth/keys \
  -H "Authorization: Bearer $OLD_KEY" \
  -d '{"name": "dashboard", "role": "Viewer"}')

# Update .env.local
sed -i "s/GRAFANA_API_KEY=.*/GRAFANA_API_KEY=$NEW_KEY/" /opt/dashboard/.env.local

Logging & Monitoring

Enable Audit Logging

# Docker daemon audit log
echo '{"log-driver": "json-file"}' | sudo tee /etc/docker/daemon.json
sudo systemctl restart docker

Monitor Access Logs

# View nginx/reverse proxy logs
tail -f /var/log/nginx/access.log | grep dashboard

# Monitor failed authentication attempts
grep "401\|403" /var/log/nginx/access.log

Alert on Anomalies

# Example: Alert on excessive API errors
docker logs atlas-dashboard | grep -c "error" | awk '{if ($1 > 10) print "ALERT: High error rate"}'

Vulnerability Management

Scan for CVEs

# Scan Docker image
trivy image atlas-dashboard:latest

# Scan dependencies
npm audit

# Fix vulnerabilities
npm audit fix

Keep Images Updated

# Update base image
docker-compose build --pull

# Update Node.js version regularly
# Edit Dockerfile to latest LTS version

Monitor for Vulnerabilities

# GitHub Dependabot - enabled by default
# Review and merge dependabot PRs regularly

# Manual check
npm outdated

Data Privacy

GDPR/Data Protection

The dashboard:

  • Does NOT store personal data
  • Does NOT use cookies or tracking
  • Does NOT collect user information
  • ⚠️ Logs contain IP addresses

To anonymize logs:

# Redact IPs from logs
docker logs atlas-dashboard | sed 's/\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}/[REDACTED]/g'

Compliance Checklist

  • All credentials use strong passwords
  • .env.local is NOT committed to Git
  • Docker API is not publicly exposed
  • HTTPS/SSL configured for production
  • Authentication layer in place
  • Audit logs are enabled
  • Dependencies are up-to-date
  • Security scanning (trivy) runs regularly
  • Access is restricted by firewall/VPN
  • Backup strategy is documented
  • Incident response plan is prepared
  • Regular security reviews scheduled

Incident Response

If Credentials Are Compromised

  1. Immediately change passwords:

    # Synology
    # UniFi
    # Any API keys
    
  2. Update in .env.local:

    ssh soadmin@100.104.196.38
    nano /opt/dashboard/.env.local
    
  3. Restart container:

    docker-compose restart dashboard
    
  4. Check logs for unauthorized access:

    docker logs atlas-dashboard | grep error
    
  5. Review API call history in Synology/UniFi

If Container Is Compromised

  1. Isolate the container:

    docker-compose down
    
  2. Rebuild from source:

    cd /opt/dashboard
    git fetch origin
    git reset --hard origin/main
    docker-compose build --no-cache
    
  3. Verify integrity:

    git log -1
    docker images atlas-dashboard
    
  4. Redeploy:

    docker-compose up -d
    

If Server Is Compromised

  1. Migrate to new server (see MONITORING.md - Disaster Recovery)
  2. Rotate ALL credentials
  3. Conduct security audit of infrastructure
  4. Review access logs from before incident

Additional Resources