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

bash
# Apply security updates regularly
sudo apt update
sudo apt upgrade -y

Subscribe to distro security notices. For production, schedule maintenance windows.

Minimal install: disable services you do not use.

bash
# 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-daemon

Fewer 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):

plaintext
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes

Apply:

bash
sudo sshd -t
sudo systemctl reload sshd

Warning

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

bash
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/id_ed25519

Never commit private keys or production .env files to Git.

bash
# Server-side env file readable only by app user
sudo install -m 600 -o myapp -g myapp /tmp/app.env /opt/myapp/app.env

Use EnvironmentFile= in systemd units instead of exporting secrets in shell history.

Firewall Recap

Only open what you need:

PortTypical use
22SSH (restrict source IP in cloud SG when possible)
80 / 443HTTP(S) via Nginx
8080Often not public—proxy via Nginx
bash
sudo ufw status verbose

Database port 3306 should stay on private network or 127.0.0.1, not the public internet.

Users and sudo

  • Daily work as normal user; sudo for admin tasks
  • Run apps as dedicated user (myapp), not root
  • sudoers grants only required commands in teams
bash
# See who has sudo
getent group sudo

Application-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:8 not latest in prod)

Backups and Recovery

Security includes availability:

  • Database dumps on schedule
  • Test restore once
  • Off-site copy (object storage), encrypted
bash
# Example MySQL dump (credentials via config file)
mysqldump --defaults-extra-file=~/.my.cnf appdb > backup-$(date +%F).sql

Incident Response Sketch

  1. Block attack vector (firewall, disable account, rotate keys)
  2. Preserve logs: journalctl, /var/log/auth.log, nginx logs
  3. Patch vulnerability, restore from clean backup if compromised
  4. 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.

What comes next?

Linux book recommendations.