Hardened Multi-Service Linux Server
You will run several services (web, DNS, database) safely on one hardened Linux host behind a default-deny firewall, each managed by systemd. By the end the server delivers all three services with least-privilege exposure.
Learning Objectives
- Install and manage web, DNS, and database services.
- Expose only the required ports with nftables.
- Monitor service health with systemd and logs.
- Time: ~12 hours · Difficulty: Advanced · Prereqs: the Linux server labs.
Architecture Overview
graph LR
Net((Clients)) -->|443/53| FW[nftables default-deny]
FW --> Web[Nginx]
FW --> DNS[BIND9]
Web --> DB[(PostgreSQL local-only)]
Environment Setup
- A Linux server with systemd and nftables.
- Nginx, BIND9, and PostgreSQL packages.
Step-by-Step Execution
01
Install and enable the services
sudo apt install -y nginx bind9 postgresql && sudo systemctl enable --now nginx named postgresql02
Expose only required ports
The database stays local-only; only web and DNS are reachable.
sudo nft add rule inet filter input tcp dport {80,443} accept && sudo nft add rule inet filter input udp dport 53 accept03
Verify all services are healthy
$ systemctl is-active nginx named postgresql
active
active
active
Progress So Far
graph LR
A[01 Install services] -->|done| B[02 Firewall scope]
B -->|done| C[03 Health check]
style A fill:#1a4a1a,stroke:#00ff00,color:#fff
style B fill:#1a4a1a,stroke:#00ff00,color:#fff
style C fill:#1a4a1a,stroke:#00ff00,color:#fff
Testing & Validation
curl -s -o /dev/null -w "%{http_code}\n" http://localhost && dig @localhost lab +short && nft list ruleset | grep 5432 || echo "db not exposed"Web returns 200, DNS resolves, and the database port is not exposed externally. If so, the server runs all services with least privilege.
Troubleshooting
- Service won't start: inspect
journalctl -u <service>for the failure. - Port unreachable: confirm the nftables accept rule precedes the default drop.
- DB reachable externally: bind PostgreSQL to localhost and block 5432 at the firewall.
Extension Ideas
- Add TLS to Nginx and DNSSEC to BIND9.
- Centralize logs (see Log Management).
- Containerize the services with Docker Compose.
Key Results
- Ran web, DNS, and database on one hardened host.
- Exposed only the required ports via default-deny nftables.
- Kept the database local-only with no external exposure.
- Verified health of all three services.