Security Basics
Introduction
A default VPS on the public internet is scanned within minutes. Good Linux hygiene—patching, SSH keys, firewall, least privilege—blocks most automated attacks. This chapter covers habits for solo developers and small teams, without duplicating a full security engineering curriculum.
Prerequisites
Patch and Minimize Surface
# Apply security updates regularly
sudo apt update
sudo apt upgrade -ySubscribe to distro security notices. For production, schedule maintenance windows.
Minimal install: disable services you do not use.
# Example: list enabled services
systemctl list-unit-files --type=service --state=enabled
# Disable unused example (only if you know you do not need it)
# sudo systemctl disable --now avahi-daemonFewer open ports and daemons means fewer CVE targets.
SSH Hardening (Concepts)
Prefer key-based login over passwords—already covered when you copied id_ed25519.pub to authorized_keys.
Recommended /etc/ssh/sshd_config tweaks (edit with care, keep a second session open):
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yesApply:
sudo sshd -t
sudo systemctl reload sshdWarning
Lockout Risk
Test SSH in a new terminal before closing your current session. Keep provider console access as backup.
Use non-default SSH port only as obscurity—firewall and keys matter more.
File Permissions for SSH and Secrets
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/id_ed25519Never commit private keys or production .env files to Git.
# Server-side env file readable only by app user
sudo install -m 600 -o myapp -g myapp /tmp/app.env /opt/myapp/app.envUse EnvironmentFile= in systemd units instead of exporting secrets in shell history.
Firewall Recap
Only open what you need:
| Port | Typical use |
|---|---|
| 22 | SSH (restrict source IP in cloud SG when possible) |
| 80 / 443 | HTTP(S) via Nginx |
| 8080 | Often not public—proxy via Nginx |
sudo ufw status verboseDatabase port 3306 should stay on private network or 127.0.0.1, not the public internet.
Users and sudo
- Daily work as normal user;
sudofor admin tasks - Run apps as dedicated user (
myapp), not root sudoersgrants only required commands in teams
# See who has sudo
getent group sudoApplication-Level Habits
- Rotate API keys and DB passwords after leaks
- JDBC URLs with credentials only in env—JDBC track
- Keep dependencies updated (
npm audit, Maven/Gradle dependency checks) - Docker: pull trusted images, pin tags (
mysql:8notlatestin prod)
Backups and Recovery
Security includes availability:
- Database dumps on schedule
- Test restore once
- Off-site copy (object storage), encrypted
# Example MySQL dump (credentials via config file)
mysqldump --defaults-extra-file=~/.my.cnf appdb > backup-$(date +%F).sqlIncident Response Sketch
- Block attack vector (firewall, disable account, rotate keys)
- Preserve logs:
journalctl,/var/log/auth.log, nginx logs - Patch vulnerability, restore from clean backup if compromised
- Document timeline for your future self
FAQ
Is fail2ban required?
Helpful on SSH-exposed hosts—bans IPs after failed attempts. Not a substitute for keys and firewall.
Root login with key only?
Still discouraged—use sudo audit trail on a named account.
SELinux / AppArmor?
Enforcing MAC on RHEL (SELinux) and Ubuntu (AppArmor) defaults—learn basics before disabling policies.
Containers secure the app?
They isolate packaging, not all threats—update images, scan CVEs, do not run as root inside container when avoidable.