Minimize services & exposure
Every listener you remove is risk removed.
The most reliable way to secure a service is to not run it. Every listening service is attack surface — code that can have a vulnerability, a port that can be reached — so a core hardening step is auditing what is installed and running, and removing or disabling everything the host does not actually need. A minimal server has a small, understood set of processes and open ports, which is both more secure and easier to reason about.
$ ss -tlnp # what is listening, and which program owns it$ systemctl list-units --type=service --state=running # what is running$ systemctl disable --now avahi-daemon # stop + never start a service you do not need$ systemctl mask cups # harder: prevent it being started at all$ sudo apt purge telnetd rsh-server # remove insecure packages entirely
Bind services to the right interface
For services you must run but only need locally — a database, a metrics endpoint, an admin socket — bind them to 127.0.0.1 rather than 0.0.0.0 so they are reachable only from the host itself. This is the ss -tlnp habit from the essentials course turned into a rule: every 0.0.0.0 listener must be justified, and the default for anything not serving external clients is local-only. Between a local bind and a default-deny firewall, an internal service has two independent reasons it is unreachable from outside.
Remove the toolkit an attacker would use
Minimization extends to the tools sitting on the host. Compilers, interpreters, and network utilities that a production server does not need are exactly what an attacker who lands a foothold reaches for to build and pivot. You will not strip a host to nothing, but removing obviously unnecessary build tools and legacy clients (telnet, rsh, ftp) shrinks both the vulnerability surface and the post-exploitation toolkit. It is the bare-metal echo of the distroless idea from the container course: the less that is present, the less there is to attack and to abuse.