Unix network security: services, firewalls, SSH, NFS and DNS

Updated November 2026

A Unix server is only as exposed as the services it runs. Find out what listens, remove what you do not need, and filter the rest with a firewall.

Find what is listening

Start with an inventory. ss lists every listening socket and the program that owns it:

sudo ss -tulpn
Netid State  Local Address:Port  Process
udp   UNCONN 127.0.0.1:323       users:(("chronyd",pid=612))
tcp   LISTEN 0.0.0.0:22          users:(("sshd",pid=801))
tcp   LISTEN 127.0.0.1:3306      users:(("mariadbd",pid=950))
tcp   LISTEN 0.0.0.0:443         users:(("nginx",pid=1022))

Read the Local Address column carefully. 127.0.0.1 or ::1 means only local programs can connect. 0.0.0.0, * or [::] means every network interface. In this example the database correctly listens only on localhost.

On older systems without ss, use netstat -tulpn (Linux), sockstat -l (FreeBSD) or netstat -an | grep LISTEN (AIX, Solaris), and lsof -i -P to see the process.

Remove what you do not need

Every service you switch off is one less thing to patch and one less way in. On a typical server you can usually remove printing (CUPS), mDNS (Avahi), Bluetooth, the RPC portmapper unless you use NFS, and any desktop software.

systemctl list-units --type=service --state=running
sudo systemctl disable --now avahi-daemon.service avahi-daemon.socket cups.service
sudo apt purge telnetd rsh-server tftpd-hpa vsftpd       # if any of them are installed

Services that should never run on a modern system

ServiceWhyUse instead
telnet, rlogin, rsh, rexecPasswords and sessions travel in clear text; rsh trusts host namesSSH
FTPClear-text passwordsSFTP or HTTPS
TFTPNo authentication at allOnly on an isolated install network
finger, rusers, rwhoTell an attacker your user namesNothing
NIS (YP)Password hashes readable over the networkLDAP or FreeIPA with Kerberos
inetd services (echo, chargen, daytime)Useful only for attacksNothing

On AIX and HP-UX, check /etc/inetd.conf, comment out everything you do not use, then run refresh -s inetd (AIX) or inetd -c (HP-UX). On Solaris 10 and 11, inetd services are managed by SMF: list them with inetadm and switch them off with inetadm -d or svcadm disable.

Bind services to the right address

A database, cache or admin panel that only local programs use should listen on 127.0.0.1, not on every interface. Examples:

# MariaDB / MySQL: /etc/mysql/mariadb.conf.d/50-server.cnf
bind-address = 127.0.0.1

# Redis: /etc/redis/redis.conf
bind 127.0.0.1 -::1
protected-mode yes

# PostgreSQL: /etc/postgresql/*/main/postgresql.conf
listen_addresses = 'localhost'

Host firewall

A firewall on the server itself is your last line of defence if something starts listening by mistake. Allow only what you mean to offer, and deny everything else coming in. Modern Linux uses nftables in the kernel; ufw (Ubuntu) and firewalld (RHEL, Fedora, SUSE) are friendlier front ends to it. Use one of them, not several.

Ubuntu and Debian with ufw

sudo apt install ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow from 10.0.0.0/24 to any port 5432 proto tcp   # PostgreSQL from the office network only
sudo ufw allow 80,443/tcp
sudo ufw limit OpenSSH                                       # rate-limit new SSH connections
sudo ufw enable
sudo ufw status verbose

RHEL, Fedora and SUSE with firewalld

sudo firewall-cmd --get-active-zones
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --permanent --remove-service=cockpit
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.0/24" port port="5432" protocol="tcp" accept'
sudo firewall-cmd --reload
sudo firewall-cmd --list-all

Plain nftables

A complete server rule set in /etc/nftables.conf. Load it with sudo nft -f /etc/nftables.conf and enable it at boot with sudo systemctl enable --now nftables (on RHEL the service reads /etc/sysconfig/nftables.conf):

#!/usr/sbin/nft -f
flush ruleset

table inet filter {
  chain input {
    type filter hook input priority 0; policy drop;
    ct state established,related accept
    ct state invalid drop
    iif "lo" accept
    meta l4proto { icmp, ipv6-icmp } accept
    tcp dport 22 ct state new limit rate 10/minute accept
    tcp dport { 80, 443 } accept
  }
  chain forward {
    type filter hook forward priority 0; policy drop;
  }
  chain output {
    type filter hook output priority 0; policy accept;
  }
}
Do not lock yourself out

When you change firewall rules on a remote server, schedule an automatic rollback first, for example echo "nft flush ruleset" | sudo at now + 5 minutes (install the at package first), and cancel it with sudo atrm once you have confirmed you can still log in.

On the BSDs the firewall is pf (configured in /etc/pf.conf, loaded with pfctl -f). AIX has IP filtering in the genfilt and mkfilt commands, and Solaris 11.4 uses the PF packet filter as well.

SSH

SSH is usually the only service open to the internet for administration. Turn off password logins, block root, limit who may log in and slow down password guessing, as shown in the security guide. If only a few places need to reach SSH, allow just their addresses in the firewall, or put SSH behind a VPN such as WireGuard.

NFS

NFS trusts the client to tell the truth about who the user is, so treat NFS exports carefully.

# /etc/exports
/srv/share  10.0.0.21(rw,sync,root_squash,no_subtree_check) 10.0.0.22(ro,sync,root_squash)

sudo exportfs -ra       # apply changes
sudo exportfs -v        # show what is exported, with options

Time and name services

Kernel network settings

Linux defaults are reasonable on current distributions. On a server that is not a router, check these values in /etc/sysctl.d/90-network.conf:

net.ipv4.ip_forward = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.conf.all.log_martians = 1

Apply them with sudo sysctl --system. Do not set ip_forward = 0 on hosts that run containers or virtual machines with routed networking, because that breaks their network.

Watch the network

sudo ss -tnp state established        # who is connected right now
sudo tcpdump -ni eth0 port 22          # watch SSH traffic on one interface
sudo journalctl -k | grep -i 'nft\|ufw'   # packets the firewall logged

Check your server from the outside as well: from another machine, scan it with nmap to see what an attacker would see. Unix security testing explains how.