Check which ports are really open

There are two questions here, and people often answer only one. What is listening on the server? And what can the internet actually reach? You need both answers, and the second one has to come from outside.

Listening is not the same as reachable

A service can listen on a port that the firewall blocks. That is fine. A service can also be reachable even though your firewall rules say the port is closed, for example when Docker publishes it. That is not fine.

So you check twice. On the server, you list what listens and on which address. From another machine, you scan to see what actually answers. Then you compare the two lists.

See what is listening

sudo ss -tulpn

The flags, from the ss manual:

Run it with sudo so you see the process for every socket, including ones owned by other users. Add -4 or -6 to show only IPv4 or IPv6 sockets.

Read the local address column

The column that matters is the local address. It tells you who can connect:

Anything on loopback is out of reach from the internet whatever your firewall does. Anything on 0.0.0.0, [::] or * is only as safe as the firewall in front of it.

For each line on all addresses, ask: does this need to be public? SSH, a web server on 80 and 443, maybe a mail server. A database, a cache or an admin panel almost never does. Those should listen on 127.0.0.1, or sit behind a firewall rule that only lets specific addresses in.

Why checking from the server misleads

It's tempting to test with nmap localhost or curl on the server. That doesn't tell you what the internet sees:

The only check that counts is one that comes from outside, over the same path an attacker would use.

Scan from outside with nmap

Run nmap on another machine: your laptop on a home connection, or a second server. Only scan servers you own or have permission to test. Scanning other people's machines can break your provider's terms or the law.

# the most common 1,000 TCP ports
nmap -Pn your.server.example

# all TCP ports, 1 to 65535
nmap -Pn -p- your.server.example

-Pn skips host discovery and scans anyway. Without it, nmap may decide a server that ignores pings is down and skip it. -p- scans every port from 1 to 65535. It takes longer, but services on unusual ports are exactly the ones people forget.

UDP needs a separate scan, and root, because unprivileged users can only run connect scans. UDP is slow, so pick the ports you care about:

sudo nmap -Pn -sU -p 53,123,161 your.server.example

Read the results

nmap reports a state for each port:

Now compare with the ss list:

Close what shouldn't be open

The best fix is to stop the service from listening publicly. Bind it to 127.0.0.1 in its own config, restart it, and run ss again. If it must stay on all addresses, block or narrow it in the firewall, as in setting up UFW safely. For the two most common cases, see PostgreSQL exposed to the internet and Redis exposed.

Then scan from outside once more. A fix you haven't checked from outside is only a guess.

Stenvakt checks this for every server. Its Review lists what listens on all addresses, and the database guard flags databases that are exposed to the internet or have risky settings, with Back up now before you change anything. It's a Mac app and isn't out yet: get early access.

Sources

  1. ss(8) manual page (man7.org)
  2. Nmap Reference Guide: Port Scanning Basics
  3. Nmap Reference Guide: Port Specification and Scan Order
  4. Nmap Reference Guide: Port Scanning Techniques
  5. Nmap Reference Guide: Host Discovery
Next guideUFW, iptables and nftables