Security Best Practices
QueryGlow connects directly to your production data. Treating it as critical infrastructure is non-negotiable.
Rule #1: Network Isolation
Never expose your database port (5432, 3306) directly to the internet.
QueryGlow should run inside your private network or connect via SSH Tunnel.
# Check your firewall - database ports should NOT be listed
sudo ufw status | grep -E "5432|3306"
# Should return: nothing
# QueryGlow port should also NOT be exposed
curl http://your-server-ip:3000
# Should return: Connection refusedRule #2: Keep Safe Mode Enabled
Safe Mode blocks dangerous queries in the Query Editor. Leave it ON unless you have a specific reason to disable it.
Blocked by default:
DROP TABLE, TRUNCATE, DELETE/UPDATE without WHERE
Always blocked (can't disable):
DROP DATABASE, SHUTDOWN, filesystem operations
Verify: grep QUERYGLOW_ALLOW_DESTRUCTIVE .env → should be false or not set.
Access Control Layers
Layer 1: Reverse Proxy
Always put QueryGlow behind Nginx, Caddy, or Traefik. This handles SSL termination and Basic Auth (bcrypt hashed, 10 rounds).
The deploy.sh script sets this up automatically.
Layer 2: Private Network
For maximum security, don't expose QueryGlow to the public web at all. Use a mesh VPN like Tailscale or WireGuard to access it securely.
This eliminates the need for Basic Auth entirely.
Credential Storage
QueryGlow stores your database credentials securely:
- ✓Passwords and SSH keys are encrypted with AES-256-GCM (authenticated encryption with tamper detection)
- ✓Encryption key is derived from your
SESSION_SECRETusing scrypt - ✓Credentials are never exposed via API responses
- ✓Modified ciphertext fails to decrypt (GCM tamper detection)
⚠️ Critical: Back up your .env file securely. Without SESSION_SECRET, you cannot decrypt saved database passwords.
Rate Limiting (Automatic)
The deploy.sh script configures Nginx rate limiting:
Authentication
10 requests/minute per IP
Prevents brute-force password attacks
AI Endpoints
30 requests/minute per IP
Prevents API cost abuse
Database Permissions
Principle of Least Privilege
Avoid connecting with the postgres or root superuser. If a credential leak occurs, damage is limited to what that user can access.
Viewer Role (Read-Only)
For users who only need to query data.
GRANT SELECT ON ALL TABLES IN SCHEMA public TO viewer;Operator Role
Can modify data but cannot drop tables or alter schema.
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO operator;Host Server Hardening
- Disable Password Auth: Only allow SSH key authentication to your VPS.
- Enable UFW/Firewall: Only allow ports 22, 80, and 443. Block 3000 (QueryGlow internal) and 5432/3306 (DB).
- Automatic Updates: Enable
unattended-upgradesfor security patches. - Fail2ban: Install for SSH brute-force protection.
Verify Your Security
Run these commands after deployment:
# 1. HTTPS works
curl https://your-domain.com/api/health -u admin:password# Should return: {"status":"ok"}
# 2. Auth is required
curl https://your-domain.com# Should return: 401 Unauthorized
# 3. Port 3000 is NOT exposed
curl http://your-server-ip:3000# Should return: Connection refused
# 4. Database ports are closed
sudo ufw status | grep -E "5432|3306"# Should return: nothing
# 5. Safe Mode is ON
grep QUERYGLOW_ALLOW_DESTRUCTIVE .env# Should return: false (or nothing = defaults to false)