Some links on this page are affiliate links: if you sign up through them, this site earns a commission at no extra cost to you. Prices and recommendations are not affected.
Somebody hands you the root password to a machine that has been running for years. It serves real sites, nobody remembers how it was set up, and the person who did set it up is gone.
The temptation is to do the big thing first: rebuild it, migrate it, containerise it. Do the small thing first. One hour, five questions, and you will know whether the machine is an inconvenience or an emergency.
This is the audit we ran on exactly such a box last week, with what it actually turned up. Every finding below is real, and not one of them was visible from the outside.
1. What is listening, and what does the firewall think?
These two answers are always compared, never read separately. The first is the truth about the machine; the second is what somebody once intended.
ss -tlnp # every listening TCP socket, with the process
iptables -L INPUT -n --line-numbers
Our finding, and it is the one to steal from this article: the firewall had a careful allowlist for the admin panel port, three trusted subnets, written by somebody who knew what they were doing. Four lines above it sat a blanket ACCEPT for the same port from 0.0.0.0/0, added later.
iptables evaluates in order and stops at the first match. The allowlist had never, from the moment that line was added, allowed anything less than everybody. The admin login had been open to the internet for years behind a rule that read, to a casual glance, like a restriction.
Read your rules top to bottom, with line numbers, and ask what matches first. A rule list is a program, not a set.
2. Who is actually using the thing you are about to close?
The audit’s single most useful habit. Before closing any path, read its access log and count real users.
# top talkers against one path, last 10k lines
grep -h 'phpmyadmin' /var/log/nginx/*access* | awk '{print $1}' \
| sort | uniq -c | sort -rn | head
Two of our findings came straight out of this:
- A phpMyAdmin from 2017, reachable from the internet, on a web server whose PHP had no upstream patches left. The log showed 201 requests from one address and 38 from another, both scanners walking a wordlist. Not one human session in the window.
- proftpd running, in plaintext, no TLS.
xferlogwas empty since June. The only connections wereanonymouslogin attempts from a botnet.
Both were closed in minutes, with no risk of breaking anyone’s workflow, because the logs proved there was no workflow. Had the logs shown daily sessions from one office IP, the answer would have been an allowlist and a conversation rather than a shutdown.
The rule: never close a service on the belief that nobody uses it. Close it on evidence that nobody uses it. Those feel the same right up until the invoicing department calls.
3. Do not lock yourself out
Everything in this section is firewall work on a machine you reach only through that firewall. Two precautions, both cheap.
Arm an auto-revert before you commit anything. A timer that restores the known-good ruleset in ten minutes, cancelled by hand once you have confirmed you can still get in:
iptables-save > /root/iptables.good
echo 'iptables-restore < /root/iptables.good' | at now + 10 minutes
Apply your change, open a brand new SSH session from somewhere else, and only then cancel the job with atrm. Testing on the session you already have proves nothing: an established connection survives rules that block new ones.
Then make it survive a reboot, which is a separate step and the one people forget:
service iptables save # or: netfilter-persistent save
systemctl is-enabled iptables
Ours rebooted on a weekly schedule we did not know about. The hardening was still there afterwards, because the rules had been saved, and we knew it was still there because we checked after the reboot rather than assuming.
4. What renews itself, and what only appears to?
The quiet killer. On this box, TLS renewal was a job inside a control panel whose licence had expired two months earlier. The renewal cron died with the licence. Four sites had already been failing their TLS handshake for five days before the audit found it, and seven more were scheduled to expire within three weeks.
# every scheduled job on the box, in one pass
crontab -l; ls -l /etc/cron.*/ /etc/cron.d/; systemctl list-timers --all
Read all of it, including the commented lines, because they tell you what used to run. And look for duplicates: we disabled one renewal cron and only on a second pass found a second one, same job, different hour, four lines further down.
Taking renewal back is a short job even on an end-of-life distro - we wrote it up in acme.sh on a server where certbot will not install.
5. What is installed that nothing uses?
Legacy boxes accumulate runtimes. Ours had eleven separate PHP builds, going back to 5.2. The interesting question is not what is installed but what is wired up:
grep -rhoE 'php[0-9]+' /etc/nginx/conf.d/*.conf /etc/httpd/conf.d/*.conf \
| sort | uniq -c | sort -rn
Eight sites were on 7.4, three on 8.0, and every build older than that was dead weight. That single command changed the migration plan: a machine that looks like it needs a hosting platform supporting PHP 5.2 actually needs 7.4 and 8.x, which is to say anything modern.
An audit is not only about what to close. It is about discovering that the scary part of the job does not exist.
What an hour bought
Four findings on one machine: an allowlist that allowed everyone, a nine-year-old admin tool exposed to scanners, an FTP daemon serving nobody, and certificate renewal that had been dead for two months with four sites already down.
None of it required a rebuild. None of it required a maintenance window. Three of the four were fixed the same hour, and the fourth - the distro itself being past end of life - is the one honest finding you cannot patch, which is exactly why it belongs in the migration plan rather than in today’s work.
If the audit sends you shopping for a replacement box, we compared the options with live prices in Best VPS for self-hosting in 2026, and wrote up four years on one provider in our Hetzner Cloud review.