Step 1: Bind to Localhost Only (5 minutes)
The most common mistake: binding services to 0.0.0.0 (all interfaces). Fix this first.
# In your .env or config file:
MCP_HOST=127.0.0.1 # NOT 0.0.0.0
MCP_PORT=8080
ADMIN_HOST=127.0.0.1
ADMIN_PORT=8081
# Verify with:
ss -tlnp | grep 8080
# Should show: 127.0.0.1:8080, NOT 0.0.0.0:8080
Step 2: Add Authentication (15 minutes)
Install and configure Caddy (simplest) or Nginx with basic auth:
# Caddy (auto-TLS, recommended):
# /etc/caddy/Caddyfile
mcp.yourdomain.com {
basicauth {
admin $2a$14$your_bcrypt_hash_here
}
reverse_proxy 127.0.0.1:8080
}
# Generate password hash:
caddy hash-password --plaintext "your-strong-password"
Step 3: Configure Firewall (10 minutes)
# UFW setup (Ubuntu/Debian)
sudo ufw reset
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow from YOUR_OFFICE_IP to any port 22 # SSH from your IP only
sudo ufw allow 80/tcp # HTTP (for Let's Encrypt)
sudo ufw allow 443/tcp # HTTPS
sudo ufw deny 8080/tcp # Explicitly block direct MCP access
sudo ufw deny 8081/tcp # Explicitly block direct admin access
sudo ufw enable
sudo ufw status verbose
Step 4: HTTPS/TLS (10 minutes)
# With Caddy (automatic TLS via Let's Encrypt)
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install caddy
sudo systemctl enable --now caddy
Step 5: Secrets Management (10 minutes)
# Create a .env file with restricted permissions
touch /app/.env
chmod 600 /app/.env
chown appuser:appuser /app/.env
# NEVER commit .env to git
echo ".env" >> .gitignore
echo "*.env" >> .gitignore
# Rotate all secrets now if you've had any exposure:
# 1. Revoke old OpenAI/Anthropic API keys in their dashboards
# 2. Generate new keys
# 3. Update .env
# 4. Restart services
Step 6: Rate Limiting (5 minutes with Caddy)
# In Caddyfile, add rate limiting:
mcp.yourdomain.com {
rate_limit {
zone mcp {
key {remote_host}
events 100
window 1m
}
}
basicauth { ... }
reverse_proxy 127.0.0.1:8080
}
Step 7: Input Validation
In your MCP server configuration, enable input validation for tool parameters:
# clawdbot config.yml
security:
input_validation:
enabled: true
max_message_length: 10000
block_patterns:
- "ignore previous instructions"
- "system:"
- "SYSTEM:"
- "/etc/passwd"
- "../"
sanitize_html: true
Step 8: Audit Logging
# clawdbot config.yml
logging:
level: info
audit:
enabled: true
format: json
output: /var/log/clawdbot/audit.log
include:
- tool_calls
- authentication
- configuration_changes
rotate:
max_size: 100MB
max_age: 90d
compress: true
Step 9: Tool Allowlisting
# Only enable tools you actually need
tools:
enabled:
- search_web # If you need web search
- read_file # If you need file reading
# - write_file # Comment out unless needed
# - shell_execute # NEVER enable in production
# - database_write # Enable only with restrictions
file_read:
allowed_paths:
- "/app/data/"
- "/app/public/"
# Explicitly block sensitive paths
blocked_paths:
- "/etc/"
- "/root/"
- "~/"
Step 10: Monitoring Setup
# Simple monitoring with a cron job + Uptime Kuma or similar
# Check for exposed ports every hour:
0 * * * * ss -tlnp | grep -v 127.0.0.1 | grep -E '(8080|8081|3000)' | mail -s "PORT EXPOSURE ALERT" admin@yourdomain.com
# Check auth log for failures:
0 * * * * grep "authentication failure" /var/log/auth.log | tail -100 | mail -s "Auth failures" admin@yourdomain.com