Securing your Mercury Core deployment is critical for protecting user data and maintaining service integrity. This guide covers essential security measures for production deployments.
Critical Security Items
Before deploying Mercury Core to production, you MUST complete these security tasks:
- Change all default passwords in environment variables
- Restrict database port access to localhost only
- Use strong authentication keys for all services
- Enable HTTPS with valid TLS certificates
- Configure firewall rules to limit exposed ports
- Set up regular automated backups
Environment Variables Security
Default Credentials
The Site/.env.example file contains default passwords that must be changed:
ORIGIN=https://mercs.dev
PORT=4443
BODY_SIZE_LIMIT=1G
EMAIL_PASSWORD=password # CHANGE THIS
RCC_KEY=password # CHANGE THIS
GAMESERVER_KEY=password # CHANGE THIS
OPEN_CLOUD_KEY=whatever # CHANGE THIS
Generating Strong Passwords
Generate cryptographically secure passwords:
# Generate random 32-character password
openssl rand -base64 32
# Generate random hex string
openssl rand -hex 24
# Using /dev/urandom
tr -dc 'A-Za-z0-9!@#$%^&*' < /dev/urandom | head -c 32
Secure Production .env File
Example production configuration:
ORIGIN=https://yourdomain.com
PORT=4443
BODY_SIZE_LIMIT=1G
EMAIL_PASSWORD=Xy9#mK2$pL8@vN4qR7&jT1wZ5fH3
RCC_KEY=aB8$dE2#gH6@kL9mP3qR7sT1vW4yZ5
GAMESERVER_KEY=cD9#fG3$hJ7@lM1nP5rS8tV2wX6
OPEN_CLOUD_KEY=eF1$gH5#jK9@mN3pQ7rT2uW6xY0
File Permissions
Protect your .env file from unauthorized access:
# Set restrictive permissions (owner read/write only)
chmod 600 Site/.env
# Verify permissions
ls -l Site/.env
# Should show: -rw------- (600)
# Ensure proper ownership
chown $(whoami):$(whoami) Site/.env
Never commit .env files to version control. Ensure .env is listed in .gitignore.
Database Security
Change Default Database Credentials
The database service in compose.yml uses default credentials:
# INSECURE DEFAULT - CHANGE THIS
database:
command:
- start
- -u=root
- -p=root
- surrealkv://database
Update to secure credentials:
database:
command:
- start
- -u=dbadmin
- -p=${DB_PASSWORD} # Use environment variable
- surrealkv://database
environment:
- DB_PASSWORD=${DB_PASSWORD}
Store DB_PASSWORD in a .env file at the repository root:
DB_PASSWORD=your_secure_database_password_here
Restrict Database Port Access
By default, the database port is exposed to all interfaces. Restrict to localhost:
database:
ports:
- 127.0.0.1:8000:8000 # Only accessible from host
# ... rest of config
Or remove port exposure entirely if only accessed via Docker network:
database:
expose:
- 8000 # Accessible only from other containers
# Remove ports: directive
Enable Database TLS
For encrypted database connections:
database:
command:
- start
- -u=dbadmin
- -p=${DB_PASSWORD}
- --web-crt=/certs/db.crt
- --web-key=/certs/db.key
- surrealkv://database
volumes:
- ./data/surreal:/database
- ./certs:/certs:ro
Generate self-signed certificates for internal use:
mkdir -p certs
openssl req -x509 -newkey rsa:4096 -nodes \
-keyout certs/db.key \
-out certs/db.crt \
-days 365 \
-subj "/CN=mercury-database"
chmod 600 certs/db.key
Network Security
Firewall Configuration
Configure UFW (Uncomplicated Firewall) to restrict access:
# Enable UFW
sudo ufw enable
# Allow SSH (critical - don't lock yourself out!)
sudo ufw allow 22/tcp
# Allow HTTP and HTTPS (for Caddy)
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Deny all other incoming traffic
sudo ufw default deny incoming
# Allow all outgoing traffic
sudo ufw default allow outgoing
# Check status
sudo ufw status verbose
Always allow SSH (port 22) before enabling the firewall, or you may lock yourself out of the server.
Block Direct Access to Application Ports
Ensure backend services are not directly accessible:
# Deny direct access to application port
sudo ufw deny 4443/tcp
# Deny database port
sudo ufw deny 8000/tcp
# Deny economy service port
sudo ufw deny 2009/tcp
Access should only be through Caddy reverse proxy on ports 80/443.
Docker Network Isolation
Create isolated networks for service communication:
networks:
frontend:
# Exposed to internet via Caddy
backend:
internal: true # No external access
services:
database:
networks:
- backend
# No ports exposed to host
economy:
networks:
- backend
site:
networks:
- frontend
- backend
caddy:
networks:
- frontend
ports:
- 80:80
- 443:443
TLS/SSL Configuration
Caddy Automatic HTTPS
Caddy automatically obtains and renews certificates from Let’s Encrypt. Ensure:
- Domain DNS points to your server
- Ports 80 and 443 are accessible
- Valid email configured for certificate notifications
{
email [email protected]
}
yourdomain.com {
# Caddy handles TLS automatically
# ... reverse_proxy config ...
}
```caddy
### Enforce HTTPS
Caddy redirects HTTP to HTTPS by default. Verify with:
curl -I http://yourdomain.com
Should see: HTTP/1.1 308 Permanent Redirect
### TLS Version and Cipher Suites
Configure strong TLS settings in Caddyfile:
```caddy
yourdomain.com {
tls {
protocols tls1.2 tls1.3
ciphers TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
}
# ... reverse_proxy config ...
}
HSTS (HTTP Strict Transport Security)
yourdomain.com {
header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# ... reverse_proxy config ...
}
Add security headers in Caddy configuration:
yourdomain.com {
header {
# Prevent XSS attacks
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
X-XSS-Protection "1; mode=block"
# Content Security Policy
Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';"
# Referrer policy
Referrer-Policy "strict-origin-when-cross-origin"
# Permissions policy
Permissions-Policy "geolocation=(), microphone=(), camera=()"
# HSTS
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
}
# ... rewrites and reverse_proxy ...
}
Container Security
Run Containers as Non-Root
Modify Dockerfiles to use non-root users:
# Site Dockerfile example
FROM oven/bun AS release
# Create non-root user
RUN addgroup --system --gid 1001 mercury && \
adduser --system --uid 1001 mercury
# ... copy files ...
USER mercury
ENTRYPOINT ["bun", "-b", "./build"]
Limit Container Resources
Prevent resource exhaustion attacks:
services:
site:
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
reservations:
cpus: '0.5'
memory: 512M
database:
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
economy:
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
Read-Only Root Filesystem
For containers that don’t need write access:
services:
site:
read_only: true
tmpfs:
- /tmp
- /var/run
Docker Security Scanning
Scan images for vulnerabilities:
# Scan all images
docker scout cves mercury-core-site
docker scout cves mercury-core-database
docker scout cves mercury-core-economy
# Or use Trivy
trivy image mercury-core-site:latest
Access Control
SSH Hardening
Secure SSH access to your server:
# Edit SSH config
sudo nano /etc/ssh/sshd_config
Recommended settings in /etc/ssh/sshd_config:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
Port 22 # Consider changing to non-standard port
AllowUsers your_username
Restart SSH:
sudo systemctl restart sshd
Use SSH Keys Only
Disable password authentication:
# Generate SSH key pair (on your local machine)
ssh-keygen -t ed25519 -C "[email protected]"
# Copy public key to server
ssh-copy-id user@your-server
# Test login
ssh user@your-server
# Disable password auth in /etc/ssh/sshd_config
# PasswordAuthentication no
Sudo Access Control
Limit sudo access to specific commands:
# Edit sudoers file
sudo visudo
# Allow user to restart services without password
username ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart caddy
username ALL=(ALL) NOPASSWD: /usr/bin/docker compose
```caddy
## Monitoring and Logging
### Centralized Logging
Configure Docker logging driver:
services:
site:
logging:
driver: “json-file”
options:
max-size: “10m”
max-file: “3”
### Monitor Failed Login Attempts
Install and configure fail2ban:
sudo apt install fail2ban
Configure for SSH
sudo nano /etc/fail2ban/jail.local
[sshd]
enabled = true
port = 22
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
### Security Audit Logs
Enable Docker events logging:
```bash
# Monitor Docker events
docker events --filter 'type=container' --format '{{.Time}} {{.Action}} {{.Actor.Attributes.name}}'
# Log to file
docker events >> /var/log/docker-events.log &
Application Logging
Ensure application logs don’t contain sensitive data:
- Don’t log passwords or API keys
- Sanitize user input before logging
- Use structured logging (JSON format)
- Implement log rotation
Backup Security
Encrypt Backups
Encrypt backup files before storing:
# Create encrypted backup
tar -czf - ./data/surreal | openssl enc -aes-256-cbc -pbkdf2 -out backup-encrypted.tar.gz.enc
# Decrypt and restore
openssl enc -d -aes-256-cbc -pbkdf2 -in backup-encrypted.tar.gz.enc | tar -xzf -
Secure Backup Storage
- Store backups on separate physical server
- Use encrypted cloud storage (S3 with SSE, encrypted Google Drive)
- Implement access controls on backup storage
- Regularly test backup restoration
Backup Access Control
# Restrict backup directory permissions
chmod 700 ./backups
chown root:root ./backups
# Individual backup files
chmod 600 ./backups/*
Security Updates
System Updates
Keep system packages up to date:
# Update package list
sudo apt update
# Upgrade packages
sudo apt upgrade -y
# Enable automatic security updates
sudo apt install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades
Docker Image Updates
Regularly update base images:
# Pull latest base images
docker pull oven/bun:latest
docker pull surrealdb/surrealdb:v3.0.1
docker pull golang:latest
# Rebuild containers
docker compose build --no-cache
docker compose up -d
Dependency Updates
Keep application dependencies current:
# Update Bun dependencies
cd Site
bun update
# Update Go dependencies
cd Economy
go get -u ./...
go mod tidy
Security Checklist
Before deploying to production:
Incident Response
In case of security incident:
- Isolate: Disconnect affected services from network
- Assess: Review logs to determine scope of breach
- Contain: Stop malicious activity
- Eradicate: Remove threat (patch vulnerabilities, remove malware)
- Recover: Restore from clean backups
- Document: Record timeline and actions taken
- Improve: Update security measures to prevent recurrence
Emergency Shutdown
# Immediately stop all services
docker compose down
# Block all network traffic
sudo ufw default deny incoming
sudo ufw default deny outgoing
# Review logs
sudo journalctl -xe
docker compose logs
Security Resources
Security is an ongoing process, not a one-time task. Regularly review and update your security measures to protect against emerging threats.