You are currently viewing Linux Server Hardening: A Practical Baseline and Verification Guide

Linux Server Hardening: A Practical Baseline and Verification Guide

An unused web service listening on every interface gives people a way to reach a Linux server that they do not need. Hardening begins with a simpler question than “Which security settings can I enable?”: what must this machine do, and which paths into it are necessary? Remove the rest without breaking the work it is meant to perform.

Use these checks only on machines you own or administer. Commands and configuration locations vary by distribution, release, and machine type. Before changing a remote host, record its current configuration and arrange console or recovery access. A firewall or SSH mistake can turn a security improvement into an outage.

Start with a baseline and a recovery path

Write down the system’s purpose and the services it needs. A small application server might accept HTTPS from clients and SSH from an administration network, but have no reason to expose its database port. A developer workstation is different: it may need local build tools and desktop services while requiring no inbound connections.

Collect a baseline before editing configuration. On a systemd-based distribution, systemctl --type=service --state=running shows running services. ss -lntup lists listening TCP and UDP sockets, with process details where permissions allow. Use cat /etc/os-release to record the distribution and release; also note installed software, active firewall rules, administrative users, and scheduled jobs. Inspect these results locally. There is no need to probe other hosts.

Keep a change log: the setting, its previous value, why you changed it, and how you tested it. Take an appropriate snapshot or verified backup before risky work, but have a separate recovery plan. Know how to reach a provider console, boot a rescue environment, or revert a configuration file if remote access fails.

Administrator checks services before changing a Linux host

Keep the software inventory small and current

Security updates fix known defects. A smaller package inventory and fewer externally reachable components make those updates easier to manage. Use the distribution’s supported repositories and package manager rather than arbitrary installation scripts. Debian-based systems commonly use apt; Fedora and some related systems use dnf. Check when the release will stop receiving security maintenance, and plan an upgrade before then.

Do not remove a package until you know what depends on it. systemctl stop stops a service for the current run; disabling it prevents normal startup; removing its package removes the software. If a service appears unnecessary, confirm its role, disable it where appropriate, then reboot during a maintenance window and check that the workload still runs. An unfamiliar package name alone is not a reason for broad removal commands.

Schedule updates that may restart services or require a reboot. Afterward, review the package manager’s output, service health, and running kernel version. Automatic security updates can shorten the delay on suitable systems, but someone still needs to monitor failures and handle unsuccessful upgrades. Patching vulnerable code will not close an unnecessarily exposed port or reduce an account’s excessive privileges.

Limit accounts and administrative authority

List local accounts and identify who can administer the machine. Give human administrators individual accounts so actions can be attributed to a person; avoid sharing the root password. Use sudo for approved tasks and grant access only where needed. Review the distribution’s administrator groups and entries under /etc/sudoers.d/. Edit sudo policy with visudo, which checks syntax before the change takes effect.

Service accounts should run applications without interactive administrative access unless there is a documented reason. When an employee leaves or a temporary lab account is no longer needed, disable or remove access through the established offboarding process. Before deleting an account, check for files and running services that depend on it.

For password-based local access, use unique, strong passwords and the distribution’s supported authentication policy. Multifactor authentication can help protect privileged remote access where the SSH or identity integration supports it. When introducing an authentication requirement, test it in a second session and leave the existing administrator session open until the test succeeds.

Harden SSH without locking yourself out

SSH is often the administrative entry point. Restrict both who can use it and where connections can come from. Create a normal administrative account and confirm it can elevate privileges before disabling direct root login. For key-based access, put each administrator’s public key in that account’s authorized-keys file, protect the matching private key, and test a fresh login. Consider disabling SSH password authentication only after accounting for every legitimate access path, including emergency access.

Server settings typically live in /etc/ssh/sshd_config or included files. PermitRootLogin no and PasswordAuthentication no may be appropriate once those prerequisites are met. Where available, check the effective configuration with sshd -T and syntax with sshd -t, then reload SSH using the distribution’s service name. Keep the original session open until a separate login succeeds. A different SSH port may reduce incidental noise, but it is no substitute for strong authentication and access controls.

Control network exposure

A host firewall provides a local boundary even if a router or cloud firewall filters traffic. Work from the service inventory: allow required inbound connections and deny unsolicited inbound traffic the host does not need. Preserve established traffic and essential local communication according to the tool’s defaults. Commands differ among nftables, firewalld, and ufw. Use the tool supported by your distribution, and do not run overlapping rule managers without understanding how they interact.

On a remote server, permit the current administration path before enabling a restrictive inbound policy. Where practical, allow SSH only from an approved administration network rather than the entire internet. Test from an authorized location, and confirm that application clients can still reach the service they need. Make sure the rules persist across a reboot, then verify them again after restarting.

Firewall rules are only part of the picture. If a database serves only a local application, configure it to bind to a local interface or Unix socket where supported. Check ss -lntup afterward: listening on 127.0.0.1 is not the same as listening on 0.0.0.0. Check IPv6 listeners and firewall policy on IPv6-enabled systems too. Containers and virtual-machine networking can add paths, so assess exposure in the host’s actual deployment context rather than assuming one local rule covers every interface.

Firewall rules limit connections to required services

Protect files, processes, and persistent data

Set permissions according to who needs each file, not with a blanket command across the filesystem. Check ownership and modes on application configuration, private keys, backups, and directories containing secrets. A process that reads one configuration file may not need write access to its entire directory. Avoid world-writable application paths, and do not fix access errors by recursively granting permissions to everyone. Shared locations such as /tmp need the operating system’s intended permissions, not the treatment you would give an ordinary application directory.

Run services under dedicated, unprivileged identities where supported. A systemd unit can restrict capabilities, make parts of the filesystem read-only, and isolate temporary files. Settings such as NoNewPrivileges= and ProtectSystem= are useful, but safe values depend on where the application needs to read and write. Test one restriction at a time outside production. Review the logs and check that normal tasks, including updates and log rotation, still work.

Mandatory access control adds another boundary. SELinux and AppArmor policies can constrain a process even when traditional Unix permissions allow an action. Use the policy supported by the distribution and keep it enforcing once the application works with it. If a denial appears, inspect the relevant audit record and deliberately adjust the application layout or policy. Disabling the control globally because one service fails removes protection from unrelated services.

Encrypt disks or volumes that could be lost, stolen, or retired, and store recovery material separately from the host. Full-disk encryption mainly protects data while the system is powered off; an authorized process on a running host can still read data it has permission to access. Apply suitable access controls and encryption to backups as well. Restore a representative file as a test, and test whole-service recovery for critical systems before relying on those backups.

Make logging useful without collecting everything

Logs can show what changed, why a service failed, or which access attempts need investigation. On systemd systems, journalctl shows service messages; distribution-specific authentication and application logs may add detail. Check time synchronization so events on different machines can be compared. Set retention according to storage and operational needs, and restrict log access: records may contain usernames, IP addresses, or application data.

Watch for events relevant to the machine’s role, such as unexpected administrator-account changes, repeated authentication failures, a stopped service, or a new listener after deployment. Centralized logging can preserve evidence if a host becomes unavailable, provided its transport and storage are protected. Alerts should identify conditions someone can check. For instance, repeated SSH authentication failures are more useful as an alert when an administrator can compare them with approved maintenance and inspect source details in the logs.

Verify each change against the intended workload

A hardening change is not finished until you check both its security effect and its operational effect. After a firewall change, confirm through an approved testing path that unwanted access is blocked and legitimate traffic still works. After restricting a service account, run the application’s normal startup, write, and shutdown tasks. After an update, check service health and whether a reboot is pending. Perform network tests only against systems you are authorized to administer.

Security baselines and auditing tools can flag settings worth reviewing, but their findings are not automatic fixes. A rule suited to a single-purpose server might break a developer workstation. Record each finding as corrected, accepted with a documented reason, or scheduled for later work. Recheck after major upgrades, when package defaults, service units, and network rules may change.

For a new application server, a short verification record could say that only HTTPS and administration SSH are required, the database listens locally, the application runs without root privileges, an administrator can log in with an approved key, and the latest backup restores a test file. Keep the supporting command output or configuration references with the date and change ticket. After the next reboot or deployment, the next administrator has a concrete baseline to check against.