How to Harden Debian Linux

A Debian-first baseline hardening guide for real systems. This is not an enterprise lockdown or a complete security framework. It is a practical checklist that reduces unnecessary exposure, tightens common defaults, and gives you a cleaner foundation for servers, homelabs, and small-business systems.

What this is

Linux hardening is the process of reducing attack surface, tightening access, and making a system harder to misuse. In practical terms, that means limiting exposed services, enforcing safer defaults, improving account security, and making sure you can still manage the system cleanly after the changes are applied.

In this guide, the focus is a Debian system that already works and now needs a stronger baseline. The goal is not to turn Debian into a complicated security project. The goal is to make it safer without breaking normal administration.

Why build this baseline first?

Why this matters:

• A working system is not automatically a secure one.

• Default settings are often broader than they need to be.

• Basic hardening helps reduce common abuse like brute-force attempts, unnecessary service exposure, and weak remote access practices.

• This becomes the security foundation for later builds like NAS systems, remote access servers, personal cloud platforms, and monitoring stacks.

How this guide is structured

This page uses one full baseline hardening path. It starts with safe checks, then moves into account security, firewall control, SSH tightening, service review, and persistence testing.

This is intentional. It is better to apply baseline protections in a controlled order than to stack random security changes and hope nothing breaks.

Important

This is baseline hardening, not full lockdown

This guide improves the default security posture of a Debian system, but it does not make the system invulnerable. It does not replace network segmentation, backups, VPN design, patch management discipline, monitoring, or application-specific security work.

Most automated attacks target default configurations, weak credentials, and exposed services. This baseline directly reduces those risks, but it does not eliminate targeted or advanced threats.

Why: Hardening is a layer, not the whole strategy. Treat this as the clean starting point that everything else builds on.

Assumptions

What you should already have

• A Debian-based system installed

• Sudo access to an administrative account

• A stable local or remote console path in case you need to recover from a mistake

• A system that is already functioning normally before hardening begins

Free Build

Apply a practical Debian hardening baseline

Step 1: Update the system fully

sudo apt update
sudo apt full-upgrade -y

What this does:
Refreshes Debian’s package lists and installs all currently available upgrades.

Why this matters:
There is no point hardening around outdated packages if known fixes are already available. You want your starting point to be current before you tighten anything else.

What to expect:
Package lists should refresh, updates should install if any are available, and the command should finish without package errors.

Step 2: Reboot if the update process requires it

sudo reboot

What this does:
Restarts the system so kernel, service, and library updates fully take effect.

Why this matters:
A system may show updated packages while still running older components in memory. Rebooting gives you a clean baseline before continuing.

What to expect:
Your SSH session or console session will disconnect, the system will restart, and you will reconnect after it comes back up.

Step 3: Confirm who has administrative access

getent group sudo

What this does:
Shows which local accounts currently belong to the sudo group.

Why this matters:
Hardening starts with knowing which accounts already have elevated privileges. If too many accounts can administer the system, the risk is broader than it needs to be.

What to expect:
The command should return the sudo group line and list any accounts that currently have sudo access.

Step 4: Review local login-capable user accounts

awk -F: '$7 !~ /(nologin|false)$/ {print $1 ":" $7}' /etc/passwd

What this does:
Lists accounts whose shells are not set to nologin or false, which helps identify users that may be able to log in interactively.

Why this matters:
You should know which accounts can actually be used to access the system. Security work is harder when you do not know what is normal.

What to expect:
You should see a short list of normal interactive accounts. Service accounts should generally not appear here.

Step 5: Lock any account you do not want used for password login

sudo passwd -l username

What this does:
Locks the password for the specified account so it cannot be used for password-based login.

Why this matters:
Unused or legacy accounts expand your exposure. If an account does not need interactive password access, it should not remain casually usable.

What to expect:
Debian should confirm that the password was locked. Replace username with the real account name you intend to restrict.

This prevents password-based login for the account. Other authentication methods, such as SSH keys, may still function depending on configuration.

Step 6: Install the basic hardening tools

sudo apt install -y ufw fail2ban unattended-upgrades apt-listchanges

What this does:
Installs the Uncomplicated Firewall, Fail2Ban, automatic security update tooling, and package change notification support.

Why this matters:
These are practical baseline tools. UFW controls network exposure, Fail2Ban helps respond to repeated abusive login behavior, and unattended upgrades help close the gap between patch release and patch installation.

What to expect:
Debian should download and install the packages. If they are already installed, it may report that no changes were needed.

Step 7: Set firewall defaults before allowing anything

sudo ufw default deny incoming
sudo ufw default allow outgoing

What this does:
Sets UFW to deny unsolicited inbound traffic by default while still allowing normal outbound traffic from the system.

Why this matters:
This creates a clean default stance. Instead of exposing whatever happens to be listening, you explicitly allow only what you intend to use.

What to expect:
UFW should confirm that the default policies were updated.

Step 8: Allow only the management and service ports you actually need

sudo ufw allow OpenSSH

What this does:
Allows SSH traffic using UFW’s built-in OpenSSH application profile.

Why this matters:
If you manage the system remotely, you must allow SSH before enabling the firewall or you may lock yourself out.

What to expect:
UFW should confirm that the OpenSSH rule was added.

This allows SSH from any source. For more restrictive setups, limit access to specific IP ranges instead of opening it broadly.

If this machine also hosts a local web service, add only the ports you actually need. For example:

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

What this does:
Opens HTTP and HTTPS traffic for web services.

Why this matters:
A firewall should support your intended service design, not fight it. Only allow these if the system actually serves web traffic.

What to expect:
UFW should confirm each new rule that gets added.

Step 9: Enable the firewall

sudo ufw enable

What this does:
Activates the firewall rules you configured.

Why this matters:
Rules that are never enabled do not protect anything. This is the point where the system begins enforcing your chosen network policy.

What to expect:
UFW may warn that enabling the firewall can disrupt SSH sessions. If you already allowed OpenSSH first, your remote administration path should remain available.

Step 10: Confirm firewall status

sudo ufw status verbose

What this does:
Displays the current firewall state, default policy, and active allow rules.

Why this matters:
Hardening should be verified, not assumed. This confirms whether the firewall is active and whether the policy looks the way you intended.

What to expect:
You should see UFW marked as active, incoming denied by default, outgoing allowed by default, and only the specific ports you added.

Step 11: Back up the SSH server configuration before editing it

sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak

What this does:
Creates a backup copy of your current SSH daemon configuration file.

Why this matters:
SSH is one of the most sensitive parts of hardening. A backup gives you a clean rollback point if you make a mistake.

What to expect:
The copy command should produce no output if it succeeds.

Step 12: Open the SSH configuration file

sudo nano /etc/ssh/sshd_config

What this does:
Opens the SSH daemon configuration so you can tighten baseline access settings.

Why this matters:
SSH is often the most direct administrative entry point into a Linux system. It deserves more attention than the default configuration alone.

What to expect:
Nano should open the SSH configuration file for editing.

Find or add these lines:

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
X11Forwarding no
AllowUsers youradminuser

What this does:
PermitRootLogin no prevents direct root login over SSH. PasswordAuthentication no disables password-based SSH login. PubkeyAuthentication yes keeps key-based access enabled. MaxAuthTries 3 limits repeated login attempts per connection. X11Forwarding no disables a feature most servers do not need. AllowUsers youradminuser explicitly limits SSH access to the administrative user you name.

Why this matters:
This is one of the highest-value baseline changes you can make. Disabling root SSH login and removing password-based SSH access significantly reduces the usefulness of brute-force attacks.

What to expect:
The file should save normally. Replace youradminuser with the actual administrative account you intend to use.

If multiple administrative users need SSH access, list all of them on the same line separated by spaces.

Important

Do not disable password SSH access until your key login works

Do not disable password authentication unless you have already confirmed that SSH key-based login works in a separate session.

Why: SSH hardening is valuable, but access recovery is harder if you remove your only working login path too early.

Step 13: Test the SSH configuration for syntax errors

sudo /usr/sbin/sshd -t

What this does:
Checks the SSH daemon configuration file for syntax problems before you reload the service.

Why this matters:
This is a safety check. It is much better to catch a bad directive before restarting SSH than after losing remote access.

What to expect:
No output usually means the configuration is valid. Errors will point you to the line that needs correction.

Step 14: Restart SSH after validation

sudo systemctl restart ssh

What this does:
Restarts the SSH daemon so your updated settings take effect.

Why this matters:
Editing the configuration alone changes nothing until the service reloads or restarts.

What to expect:
The restart should complete silently if successful. Your current session may stay open, but new sessions will use the new rules.

Step 15: Confirm SSH is running normally

sudo systemctl status ssh

What this does:
Displays the SSH service status and recent startup details.

Why this matters:
This confirms that the service is active after your hardening changes and did not fail during restart.

What to expect:
You want to see the service marked as active and running.

Step 16: Enable automatic security updates

sudo dpkg-reconfigure -plow unattended-upgrades

What this does:
Opens Debian’s unattended-upgrades configuration prompt so you can enable automatic package installation for allowed update sources.

Why this matters:
Patching only helps when it actually happens. Automatic security updates reduce the time your system stays exposed to already-fixed vulnerabilities.

What to expect:
A text prompt should ask whether unattended upgrades should be automatically downloaded and installed. Choose Yes.

By default, unattended-upgrades installs security updates only. Full system upgrades should still be performed manually or through a controlled maintenance process.

Step 17: Verify the unattended-upgrades configuration file exists

sudo ls /etc/apt/apt.conf.d/50unattended-upgrades

What this does:
Checks that the main unattended-upgrades configuration file is present.

Why this matters:
This confirms the package is installed and its core configuration file is available if you need to review or tune it later.

What to expect:
The command should print the file path if it exists.

Step 18: Enable Fail2Ban at boot

sudo systemctl enable fail2ban
sudo systemctl start fail2ban

What this does:
Configures Fail2Ban to start automatically and launches it immediately.

Why this matters:
Fail2Ban does not help if it is installed but inactive. This makes sure it is part of the system’s normal startup behavior.

What to expect:
Systemd should confirm the service was enabled, and the start command should complete without errors.

Step 19: Create a local Fail2Ban override file

sudo nano /etc/fail2ban/jail.local

What this does:
Opens a local Fail2Ban configuration file where you can define your own settings without modifying the package defaults directly.

Why this matters:
Local override files survive package updates more cleanly and make your intended policy easier to understand later.

What to expect:
Nano should open a new or empty file if it does not already exist.

Paste this:

[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
backend = systemd

[sshd]
enabled = true

What this does:
Sets a one-hour ban window, tracks repeated failed events over ten minutes, bans after five failures, uses systemd logs as the backend, and enables SSH protection.

Why this matters:
This gives you a simple, readable baseline for protecting SSH from repeated failed login attempts without introducing complicated policy tuning.

What to expect:
The file should save normally. The configuration remains inactive until Fail2Ban is restarted.

On most modern Debian systems, systemd is the correct backend. If your system uses traditional log files instead, this may need to be adjusted.

Step 20: Restart and verify Fail2Ban

sudo systemctl restart fail2ban
sudo fail2ban-client status
sudo fail2ban-client status sshd

What this does:
Reloads Fail2Ban with your local settings, shows the overall service status, and then shows the status of the SSH jail specifically.

Why this matters:
Installing or editing a protection tool is not the same thing as confirming it is actually watching the service you care about.

What to expect:
You should see Fail2Ban active and the sshd jail listed as enabled.

Step 21: Review which services are actually listening on the network

sudo ss -tulpn

What this does:
Lists listening TCP and UDP sockets along with the owning processes.

Why this matters:
You cannot meaningfully reduce exposure if you do not know what is exposed. This is one of the fastest ways to see what the system is currently listening on.

What to expect:
You should see a list of listening services and their ports. Investigate anything you do not recognize or do not actually need.

Step 22: Disable any unnecessary service from starting automatically

sudo systemctl disable servicename
sudo systemctl stop servicename

What this does:
Prevents a service from starting automatically at boot and stops it immediately in the current session.

Why this matters:
One of the cleanest hardening actions is simply removing unnecessary exposure. A service you do not need should not stay running by habit.

What to expect:
Systemd should confirm the unit was disabled. Replace servicename with the real service you intentionally want to remove from normal operation.

Step 23: Check for failed services

systemctl --failed

What this does:
Shows services that are currently in a failed state.

Why this matters:
Hardening should not quietly damage system stability. This check helps make sure your baseline security work did not leave broken units behind.

What to expect:
Ideally the list is empty. If anything is failed, investigate it before moving on.

Step 24: Review recent authentication-related log entries

sudo journalctl -u ssh --since "today"

What this does:
Displays SSH-related log entries from today through the systemd journal.

Why this matters:
Basic visibility is part of hardening. You should know how to review login-related events so suspicious behavior does not go unnoticed.

What to expect:
You should see SSH service events and login-related activity for the current day.

Testing

Make sure the hardening actually works

• Open a new SSH session before closing your current one

• Confirm key-based login works if password SSH access was disabled

• Run sudo ufw status verbose and confirm only intended ports are open

• Run sudo fail2ban-client status sshd and confirm the jail is active

• Reboot the system

• Confirm SSH, UFW, and Fail2Ban all come back normally after boot

• Confirm any hosted services you intentionally use still function

Why this matters: Security changes that break administration or normal service are not complete. Hardening should reduce risk without making the system unreliable.

Hardening

Keep the baseline honest

Do not treat this checklist as a one-time event. Baseline hardening only stays useful if the system remains updated, unnecessary services stay removed, and remote access rules are reviewed whenever the system’s purpose changes.

If you later expose the system to the public internet, add stronger controls around remote access, application security, monitoring, backups, and recovery planning.

What comes next

Natural follow-up builds

• Harden a Debian NAS after the base system is locked down

• Harden a remote access server with the same baseline controls

• Add centralized logging and monitoring for visibility

• Build a more mature patching, backup, and recovery process