3.1 KiB
3.1 KiB
Deployment security workflow
A fresh VPS with a public IP is attacked within hours. This workflow hardens the server before the app is deployed, and re-verifies the state on every deploy.
The two phases
1. One-time hardening (on the fresh VPS)
# From your machine, immediately after first login as root:
ssh root@<vps-ip> 'bash -s' < deploy/hardening.sh
The script refuses to run twice, and performs, in order:
| Step | What it does |
|---|---|
| 1 | Updates system packages, installs ufw, fail2ban, unattended-upgrades |
| 2 | Creates a non-root deploy user with sudo privileges |
| 3 | Copies your root SSH key to the deploy user |
| 4 | Rewrites sshd_config: no root login, no password auth, AllowUsers deploy, MaxAuthTries 4, keeps your SSH port |
| 5 | Validates and reloads sshd (does not drop your session) |
| 6 | UFW: deny all incoming except SSH + app ports (default 8080) |
| 7 | fail2ban: bans 5 failed attempts within 10 min for 1 hour |
| 8 | Enables unattended security upgrades |
Critical: after the script prints its summary, open a new terminal and test
ssh -p 22 deploy@<vps-ip>before closing the root session. Only after that login works should you deploy the app.
2. Verify on every deploy (CI + manual)
Automated (GitHub Actions)
.github/workflows/predeploy-security.yml runs on every push to main / PR and blocks
deploy if any job fails:
- gitleaks – scans for leaked secrets in the repo
- shellcheck – lints both deploy scripts
- cargo audit – checks dependencies for known vulnerabilities
- release build – confirms
broadcast_serverbuilds with--features b_server - hardening verify – syntax-checks and smoke-runs the verification script
Manual (on the active VPS, any time)
# Audits the live server state; exit 0 = hardened, non-zero = something reverted.
sudo bash deploy/verify-hardening.sh
It prints [PASS]/[FAIL] per check, plus the last 10 unique brute-force usernames
from /var/log/auth.log.
Configuration
Both scripts honour environment variables:
DEPLOY_USER=deploy # the sudo user to create / check
APP_PORTS="8080" # extra firewall ports (space-separated)
SSH_PORT=22 # your SSH port
Example with custom values:
SSH_PORT=2222 APP_PORTS="8080 9090" ssh root@<vps-ip> 'bash -s' < deploy/hardening.sh
Monitoring (the step you already started)
# Tail for brute-force attempts
sudo tail -f /var/log/auth.log | grep -E 'Failed|Invalid user'
# Watch fail2ban bans
sudo fail2ban-client status sshd
# Blocklist grows here as attackers get banned
sudo cat /etc/fail2ban/jail.local
Order of operations for a new deploy
- Provision VPS → get public IP.
ssh root@<ip> 'bash -s' < deploy/hardening.sh(hardening first, before the app).- In a new terminal:
ssh deploy@<ip>→ confirm it works, then close root session. - Deploy the app binary.
sudo bash deploy/verify-hardening.sh→ confirm all[PASS].- Push code → CI re-runs pre-deploy checks automatically.