Practical Linux server hardening — every item is failure → fix → verify. SSH, firewall, service binding, secrets, DB auth, and more.
14
stars
7
commits
Shell
primary language
Sep 7, 2026
updated
Практический чек-лист для укрепления защиты production Linux-сервера — каждый пункт по схеме проблема → как исправить → как проверить. Покрывает SSH, firewall, привязку сервисов к интерфейсам, секреты, аутентификацию БД и остальное. Полный текст — на английском ниже.
⚠️ Применяйте команды осознанно и сначала проверяйте не на боевом сервере — некоторые шаги (например отключение пароля SSH) могут заблокировать доступ, если сделаны не по порядку.
A practical, battle-tested checklist for locking down a production Linux server — written after learning some of these lessons the hard way.
Most "hardening guides" are either a wall of sysctl flags nobody applies, or a vendor benchmark you skim once and forget. This one is different: every item here maps to a real way servers get owned — an exposed port, a weak remote-desktop password, a secret in git, a database with no auth — and gives you the exact command to close it, plus how to verify it actually closed.
If you run a VPS, a side-project box, or a small fleet and you don't have a security team, this is for you.
The one lesson that matters most: a service is only as safe as the interface it listens on. A strong password on a service exposed to
0.0.0.0is a weak setup. No password on a service bound to127.0.0.1behind a default-deny firewall is a strong one. Reachability beats secrecy. Start there.
If you do nothing else, do these five. They close the most common real-world entry points.
# 1. Is anything listening on all interfaces that shouldn't be?
ss -tlnp | grep -vE '127.0.0.1|::1'
# 2. Is SSH password login disabled? (should print: no)
sshd -T | grep -i '^passwordauthentication'
# 3. Is the firewall default-deny? (should show DROP/deny policy)
sudo iptables -S INPUT | head -1 # -P INPUT DROP == good
# 4. Any database answering with no auth?
redis-cli ping 2>/dev/null # PONG with no password == bad
mysql -u root -h 127.0.0.1 -e 'SELECT 1' 2>&1 | grep -q denied && echo "mysql: TCP root blocked (good)"
# 5. Any secrets committed to git?
git log -p | grep -iE 'api[_-]?key|secret|password|private[_-]?key|token' | head
Anything that comes back "wrong" above has a dedicated section below.
⚠️ Apply consciously, test before prod. These are real commands that change what's reachable on a live box — some (disabling SSH password auth, tightening firewall defaults) can lock you out if applied out of order. Confirm key-based login works before disabling passwords, and try unfamiliar steps on a disposable/staging box first, not directly on production.
| # | Topic | The failure it prevents |
|---|---|---|
| 1 | SSH | Password brute-force → shell |
| 2 | Firewall | Forgotten service exposed to the internet |
| 3 | Service binding | App on 0.0.0.0 reachable by anyone |
| 4 | Remote desktop (VNC/RDP) | Weak-password screen-share → full desktop |
| 5 | Secrets | Key in git / hardcoded → stolen credential |
| 6 | Database auth | Auth-less redis / empty DB password |
| 7 | Passwords | Guessable / short credentials |
| 8 | Attack-surface audit | Drift — a new hole opens and nobody notices |
| 9 | Incident response basics | Making a breach worse while reacting to it |
| 10 | Change discipline | Breaking prod while trying to secure it |
| 11 | Integrity sentinel | Persistence sitting in plain sight for days |
| 12 | Supply chain | Install-time code execution — the hole you open yourself |
There's a condensed one-page version in CHECKLIST.md.
Reachability first, then auth, then everything else. The order is deliberate. A misconfigured firewall or a service on the wrong interface is worth more to an attacker than a weak password, because it turns a local problem into a remote one. Fix what's reachable before you polish what's secret.
Every control needs a verification. "I disabled password login" is a belief. sshd -T | grep passwordauthentication returning no is a fact. This playbook pairs every change with the command that proves it took effect — because the gap between "I edited the config" and "the running service actually changed" is where most hardening quietly fails. (Reloading isn't restarting; a config on disk isn't a config in memory.)
Defense in depth, not defense in one place. A database should be bound to localhost and behind a firewall and password-protected. Any single layer can fail — a firewall gets flushed, a bind address gets reverted in a deploy — and the others still hold.
Least surface. The most secure service is the one that isn't running. Before you harden something, ask whether it needs to be exposed — or exist — at all.
Solo developers, indie hackers, small teams, and anyone running their own boxes without a dedicated security function. It assumes a Debian/Ubuntu-family server and root (or sudo) access. The concepts transfer to any Linux; the exact commands are written for the common case.
It is not a compliance framework, a pentest methodology, or a substitute for a real security review of a high-value target. It's the practical 80% that stops the opportunistic 99% of attacks.
Found a sharper command, a missing failure mode, or a distro-specific gotcha? PRs and issues welcome — see CONTRIBUTING.md. Keep the format: failure → fix → verify.
MIT — see LICENSE. Use it, fork it, ship it.
Maintained by Aleksandr Shulgin (@Aleksandr_NFA) · GitHub @Sanexxxx777
7 commits
Shell
81.4%
HTML
18.6%
Practical Linux server hardening — every item is failure → fix → verify. SSH, firewall, service binding, secrets, DB auth, and more.
14
stars
7
commits
Shell
primary language
Sep 7, 2026
updated
Практический чек-лист для укрепления защиты production Linux-сервера — каждый пункт по схеме проблема → как исправить → как проверить. Покрывает SSH, firewall, привязку сервисов к интерфейсам, секреты, аутентификацию БД и остальное. Полный текст — на английском ниже.
⚠️ Применяйте команды осознанно и сначала проверяйте не на боевом сервере — некоторые шаги (например отключение пароля SSH) могут заблокировать доступ, если сделаны не по порядку.
A practical, battle-tested checklist for locking down a production Linux server — written after learning some of these lessons the hard way.
Most "hardening guides" are either a wall of sysctl flags nobody applies, or a vendor benchmark you skim once and forget. This one is different: every item here maps to a real way servers get owned — an exposed port, a weak remote-desktop password, a secret in git, a database with no auth — and gives you the exact command to close it, plus how to verify it actually closed.
If you run a VPS, a side-project box, or a small fleet and you don't have a security team, this is for you.
The one lesson that matters most: a service is only as safe as the interface it listens on. A strong password on a service exposed to
0.0.0.0is a weak setup. No password on a service bound to127.0.0.1behind a default-deny firewall is a strong one. Reachability beats secrecy. Start there.
If you do nothing else, do these five. They close the most common real-world entry points.
# 1. Is anything listening on all interfaces that shouldn't be?
ss -tlnp | grep -vE '127.0.0.1|::1'
# 2. Is SSH password login disabled? (should print: no)
sshd -T | grep -i '^passwordauthentication'
# 3. Is the firewall default-deny? (should show DROP/deny policy)
sudo iptables -S INPUT | head -1 # -P INPUT DROP == good
# 4. Any database answering with no auth?
redis-cli ping 2>/dev/null # PONG with no password == bad
mysql -u root -h 127.0.0.1 -e 'SELECT 1' 2>&1 | grep -q denied && echo "mysql: TCP root blocked (good)"
# 5. Any secrets committed to git?
git log -p | grep -iE 'api[_-]?key|secret|password|private[_-]?key|token' | head
Anything that comes back "wrong" above has a dedicated section below.
⚠️ Apply consciously, test before prod. These are real commands that change what's reachable on a live box — some (disabling SSH password auth, tightening firewall defaults) can lock you out if applied out of order. Confirm key-based login works before disabling passwords, and try unfamiliar steps on a disposable/staging box first, not directly on production.
| # | Topic | The failure it prevents |
|---|---|---|
| 1 | SSH | Password brute-force → shell |
| 2 | Firewall | Forgotten service exposed to the internet |
| 3 | Service binding | App on 0.0.0.0 reachable by anyone |
| 4 | Remote desktop (VNC/RDP) | Weak-password screen-share → full desktop |
| 5 | Secrets | Key in git / hardcoded → stolen credential |
| 6 | Database auth | Auth-less redis / empty DB password |
| 7 | Passwords | Guessable / short credentials |
| 8 | Attack-surface audit | Drift — a new hole opens and nobody notices |
| 9 | Incident response basics | Making a breach worse while reacting to it |
| 10 | Change discipline | Breaking prod while trying to secure it |
| 11 | Integrity sentinel | Persistence sitting in plain sight for days |
| 12 | Supply chain | Install-time code execution — the hole you open yourself |
There's a condensed one-page version in CHECKLIST.md.
Reachability first, then auth, then everything else. The order is deliberate. A misconfigured firewall or a service on the wrong interface is worth more to an attacker than a weak password, because it turns a local problem into a remote one. Fix what's reachable before you polish what's secret.
Every control needs a verification. "I disabled password login" is a belief. sshd -T | grep passwordauthentication returning no is a fact. This playbook pairs every change with the command that proves it took effect — because the gap between "I edited the config" and "the running service actually changed" is where most hardening quietly fails. (Reloading isn't restarting; a config on disk isn't a config in memory.)
Defense in depth, not defense in one place. A database should be bound to localhost and behind a firewall and password-protected. Any single layer can fail — a firewall gets flushed, a bind address gets reverted in a deploy — and the others still hold.
Least surface. The most secure service is the one that isn't running. Before you harden something, ask whether it needs to be exposed — or exist — at all.
Solo developers, indie hackers, small teams, and anyone running their own boxes without a dedicated security function. It assumes a Debian/Ubuntu-family server and root (or sudo) access. The concepts transfer to any Linux; the exact commands are written for the common case.
It is not a compliance framework, a pentest methodology, or a substitute for a real security review of a high-value target. It's the practical 80% that stops the opportunistic 99% of attacks.
Found a sharper command, a missing failure mode, or a distro-specific gotcha? PRs and issues welcome — see CONTRIBUTING.md. Keep the format: failure → fix → verify.
MIT — see LICENSE. Use it, fork it, ship it.
Maintained by Aleksandr Shulgin (@Aleksandr_NFA) · GitHub @Sanexxxx777
7 commits
Shell
81.4%
HTML
18.6%