Here's a Minimal & Practical Guide to Server Hygiene
If you run a server long enough, you will eventually open a directory, spot a file you don't recognise, and think: “I did not put this here.”
I had my "moment" during the React2shell (CVE-2025-55182) outbreak. I found a file named Agtisx.exe sitting in a temp folder. A Windows executable on a Linux box is a red flag big enough to cover a stadium.
It wasn't just a stray file; it was the payload for a campaign turning my infrastructure into a distribution hub to infect Windows servers. My clean server had become a staging ground for someone else’s war.
This guide exists so that the moment becomes mildly interesting instead of emotionally devastating. We aren't aiming for "military-grade" complexity. We want clean, boring, predictable systems that let you sleep at night.
1. SSH: Make Logging In Aggressively Boring
If logging into your server feels exciting, you’ve already failed. Your job isn't to defeat "hackers"; it’s to make your server so unrewarding that they move on to someone else.
Your config file at (/etc/ssh/sshd_config) should at the very least enforce:
- Key-based login only. No passwords.
- No root login. Force a standard user.
- Minimal attempts. Give them three tries, then kick them.
#Edit /etc/ssh/sshd_config with these settings:
PasswordAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
MaxAuthTries 3
Why it works: Bots love low-hanging fruit (passwords and root). They hate keys and effort. Be the effort.
2. Keys & Secret Rotation is Hygiene, Not Panic
SSH keys live forever unless you actively rotate them. They sit on old laptops, archived backups, CI machines you no longer use, and devices you forgot ever had access.
Environment variables behave the same way.
Once a secret exists, it tends to spread. It gets copied into .env files, pasted into dashboards, shared with teammates, backed up, and sometimes logged by mistake. Over time, you stop remembering where it lives, but it still works.
Rotating keys and secrets is not a response to an incident. It is routine maintenance. It limits the damage of forgotten access and reduces the blast radius of mistakes.
If you are thinking, “But nothing bad happened,” that is exactly the point. Rotation is what keeps it that way.
3. The Local Firewall: Trust but Verify
Even if your cloud provider has a "Security Group," run a local firewall. It’s your second line of defence against human error—specifically, your own misclicks.
The Rule: Expose SSH, HTTP, HTTPS, and your specific app ports. Block everything else.
# Example using UFW (Uncomplicated Firewall)
sudo ufw default deny incoming
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
sudo ufw enable
4. Fail2ban: Outsource the Annoyance
Brute-force attempts are a fact of life. You aren't being targeted; you just exist. Fail2ban watches your logs and quietly jails IPs that behave poorly.
# Check status of blocked IPs
sudo fail2ban-client status sshd
5. Audit Your Services
Malware loves persistence, and persistence loves services. Run this command:
systemctl list-unit-files --state=enabled
Ask one question for every line: “Do I know why this is here?”
If the answer is No: Investigate.
If the answer is still No: Disable it.
6. The Post-Moment Forensic Routine
When you find a file like Agtisx.exe, don't just delete it. You need to know how it got there.
Step 1: Check the Metadata
Before deleting, look at the file's "birth certificate":
stat Agtisx.exe
This tells you the exact second it was created (Birth) or modified. Note the timestamp.
Step 2: Check Permissions and Ownership
Who "owns" the malware?
ls -la Agtisx.exe
Owned by www-data? Your web app was the entry point (e.g., React2shell).
Owned by root?This implies privilege escalation.
Step 3: Confirm via Logs
Match the stat timestamp against your logs:
# Search web logs for activity around the 'stat' timestamp
grep "2025-02-04 10:30" /var/log/nginx/access.log
7. Recovery: The "Burn It Down" Philosophy
Once a server is compromised enough to host attack payloads, you can rarely trust the OS again.
- Snapshot the evidence: Save logs and the malware sample to an isolated folder.
- Rotate everything:
- SSH Keys: Generate new ones; revoke all old ones.
- Secrets: Change DB passwords, API keys, and .env files.
- Delete the instance: Terminate the server. Do not try to "clean" it.
- Redeploy Fresh: Provision a new instance, apply your "Boring Config," and patch the vulnerability (e.g., update React) before going live.
8. Predictability Beats Fancy Tools
Your server should only talk to places you expect. Check your active connections:
ss -antp
Normal: Web traffic, database connections, updates.
Not Normal: Random IPs, unknown processes, strange ports.
9. File Hygiene: Stop the Clutter
Your server is not a downloads folder. Make sure to check if there are
- No random binaries in home directories.
- No world-writable directories.
- No executable permissions unless required.
# Find world-writable directories
find / -xdev -type d \( -perm -0002 -a ! -perm -1000 \) -print
10. Backups: The Disaster Circuit-Breaker
Backups don’t prevent incidents; they prevent disasters. They turn a "compromise" into a "recovery."
The Golden Rule: If the thought of restoring your server feels scary, your backups aren't finished yet.
A clean server has:
- A few ways in.
- A few things are running.
- Few places to hide.
- Predictable behaviour.
The best compliment a professional server can receive is: “Nothing interesting happened today.”
The goal is to build for boredom & to Sleep better.
And as always Happy Coding!


Top comments (0)