Skip to content

Cookie settings

Choose which kinds of cookies you allow. You can change your choice at any time via Cookie settings at the bottom of every page. Privacy statement

Necessary

Always on

Needed to log in, keep your cart and remember your choices. These are always on.

Which cookies?
  • weboke_sessie, weboke_klant, weboke_remember: your session and staying logged in
  • weboke_basket: your cart
  • wkBtwIncl: showing prices with or without VAT
  • wok_cookies: your cookie choice, 12 months

Measures which ad leads to a visit or order, so we do not pay for ads that do nothing.

Which cookies?
  • Google Ads via Google Tag Manager (_gcl_au, _gcl_aw), up to 90 days
  • OpenAI Ads, ads in ChatGPT
  • wok_aff: which partner referred you to us (affiliate programme), 60 days

4.6

Country and language

Each country has its own site, with prices and VAT for that country.

View

Module 5 · 5.1

Ports and listening services

Module 5: Shielding the network · Reading time: 10 minutes

Learning objective: After this lesson you can see which services are listening on your server and on which address, explain the difference between 0.0.0.0 and 127.0.0.1, and check from outside what anyone on the internet can see of your server.

Key concepts: port, TCP and UDP, listening, ss -tulpn, 0.0.0.0 and [::], 127.0.0.1 and [::1], bind-address, open, closed and filtered, attack surface, scanners

One IP address with 65,535 doors

Your server has an IP address, for example 192.0.2.10 and 2001:db8::10. Several programs can be reachable on that address at the same time, each on its own port: a number from 1 to 65,535. A program that accepts connections is listening on a port. A few well-known ones:

PortProtocolService
22TCPSSH
80TCPhttp (web server)
443TCP and UDPhttps (UDP for HTTP/3)
25TCPmail between mail servers (SMTP)
3306TCPMySQL and MariaDB
5432TCPPostgreSQL
6379TCPRedis

TCP is the protocol for connections where data must arrive reliably (web, SSH, databases). UDP is for fast, separate packets (DNS, VPNs such as WireGuard, HTTP/3). A service listens on TCP, UDP or both.

Every listening port is a door into your server. Behind every door is software that may contain bugs. All the doors that can be reached from outside together form your attack surface. The smaller, the better: a server with a website only needs 22, 80 and 443 from outside.

Seeing what is listening: ss

The command ss (socket statistics) shows what is listening on your server. Use the options -t (TCP), -u (UDP), -l (listening only), -p (which program) and -n (numbers, not names):

anna@web1:~$ sudo ss -tulpn
Netid State  Local Address:Port  Peer Address:Port Process
udp   UNCONN 127.0.0.54:53       0.0.0.0:*         users:(("systemd-resolve",pid=402,fd=16))
tcp   LISTEN 0.0.0.0:22          0.0.0.0:*         users:(("sshd",pid=611,fd=3))
tcp   LISTEN 0.0.0.0:80          0.0.0.0:*         users:(("nginx",pid=902,fd=6))
tcp   LISTEN 0.0.0.0:443         0.0.0.0:*         users:(("nginx",pid=902,fd=7))
tcp   LISTEN 127.0.0.1:3306      0.0.0.0:*         users:(("mariadbd",pid=755,fd=21))
tcp   LISTEN 0.0.0.0:6379        0.0.0.0:*         users:(("redis-server",pid=780,fd=6))
tcp   LISTEN [::]:22             [::]:*            users:(("sshd",pid=611,fd=4))
tcp   LISTEN [::]:80             [::]:*            users:(("nginx",pid=902,fd=8))
tcp   LISTEN [::]:443            [::]:*            users:(("nginx",pid=902,fd=9))

The most important column is Local Address: the address the program is listening on.

AddressMeaning
0.0.0.0All IPv4 addresses of the server, including the public one: reachable from outside (unless a firewall blocks it)
[::]All IPv6 addresses: the same for IPv6
127.0.0.1 or [::1]Only the server itself (loopback): never reachable from outside
127.0.0.53, 127.0.0.54Also loopback; this is where the local DNS helper of systemd-resolved listens
192.0.2.10Only that one address

In the example above, MariaDB is fine: it listens on 127.0.0.1, so only programs on the server itself (your website) can reach it. But Redis listens on 0.0.0.0:6379. If the firewall isn't set up properly, the whole internet can reach it. That is exactly the kind of mistake this lesson is about.

Listening on the right address

A database, cache or admin panel should almost never be reachable from outside. The first and best defence is for the service to listen only on loopback. A firewall (lesson 5.2) is the second layer; it can be switched off by accident or not apply (for example with Docker, lesson 9.3).

Where you set this differs per program:

ProgramSettingFile (Debian/Ubuntu)
MariaDB/MySQLbind-address = 127.0.0.1/etc/mysql/mariadb.conf.d/50-server.cnf
PostgreSQLlisten_addresses = 'localhost'/etc/postgresql/<versie>/main/postgresql.conf
Redisbind 127.0.0.1 -::1 and protected-mode yes/etc/redis/redis.conf

The Debian and Ubuntu packages already set these services to localhost by default. Things usually go wrong when someone follows a guide that says "set bind-address to 0.0.0.0 to connect from outside". If you want to reach the database from your own laptop, use an SSH tunnel (lesson 2.3): the port stays closed, and only someone who comes in via SSH can reach it.

After a change, restart the service and check again with ss -tulpn.

Open, closed and filtered

Seen from outside, a port can be in one of three states:

  • Open: something is listening and the connection succeeds.
  • Closed: nothing is listening, or a firewall actively refuses. The server immediately sends back a "no" (with TCP an RST packet). A connection fails straight away with "Connection refused".
  • Filtered: a firewall silently drops the packet. The connection hangs until it times out.

For your database, closed and filtered are both fine: nobody gets in. The difference only tells you why: filtered almost always means your firewall is blocking it.

Looking from outside

ss shows what is listening on the server, but not what can be reached from outside. There is still a firewall in between, and sometimes a network firewall at your provider too. So the only real check is: look from outside, from another computer.

From your own computer (not from the server itself, because then you go via loopback) you can test a single port with nc (netcat):

anna@laptop:~$ nc -vz -w 3 192.0.2.10 6379
nc: connect to 192.0.2.10 port 6379 (tcp) failed: Connection refused
anna@laptop:~$ nc -vz -w 3 192.0.2.10 22
Connection to 192.0.2.10 22 port [tcp/ssh] succeeded!

On Windows, PowerShell does the same with Test-NetConnection 192.0.2.10 -Port 6379. A port scanner such as nmap can check several ports at once; only use a scanner like that on your own server.

Don't forget IPv6. A common mistake is a firewall that only has rules for IPv4, while the service also listens on [::]. So test the IPv6 address too: nc -6 -vz -w 3 2001:db8::10 6379.

Why this matters so much

Your server is scanned from the very first minute. Search engines for devices and services on the internet, as well as criminals' bots, constantly probe all IPv4 addresses on well-known ports. An open Redis without a password, an Elasticsearch cluster or a MongoDB without access control is found within a short time. Data breaches caused by open databases and open search clusters make the news regularly, and they almost always come down to exactly this mistake: a service meant for internal use was listening on all addresses.

The SSH port gets knocked on all day too. That's normal and no problem with keys (lesson 2.2) and fail2ban (lesson 5.3).

The course check

In the exercise for this lesson, the course program checks five fixed database ports of your server from outside: 3306 (MySQL/MariaDB), 5432 (PostgreSQL), 6379 (Redis), 9200 (Elasticsearch/OpenSearch) and 27017 (MongoDB). The program only tries whether a connection can be made, sends no data and doesn't log in anywhere. It doesn't scan any other ports. The checks come from the IP address shown in the exercise, so you can recognise them in your own logs. An address can only be checked a few times per hour.

On AlmaLinux and Rocky Linux

ss works the same. Service files are in other places (for example /etc/my.cnf.d/ for MariaDB, /var/lib/pgsql/data/postgresql.conf for PostgreSQL). In addition, SELinux can block a service that wants to listen on an unusual port; you'll see that in journalctl (lesson 6.2) and not in ss.

Summary

  • Every listening port is a door; from outside, only keep open what you really need (usually 22, 80 and 443).
  • sudo ss -tulpn shows which program is listening on which address and port.
  • 0.0.0.0 and [::] mean "all addresses", 127.0.0.1 and [::1] mean "only the server itself". Databases, caches and search clusters belong on loopback.
  • Closed and filtered are both fine for a database; open is wrong.
  • Always check from outside, for IPv4 and IPv6, because ss can't see the firewall.

Quiz

1. ss -tulpn shows 127.0.0.1:3306 for mariadbd. What does that mean?



2. Redis listens on 0.0.0.0:6379. Your firewall doesn't allow 6379. What is the best approach?



3. You test port 5432 from outside and the connection hangs for 3 seconds and then fails. What is the likely status?



4. Why should you check an open port from outside and not only with ss on the server?



5. Your firewall has rules for IPv4, but your server also has an IPv6 address. Redis listens on [::]:6379. What is the risk?



Exercise

Exercise Z5.1: Database ports closed from outside. Run sudo ss -tulpn on your server and find out which addresses your services are listening on. Then let the course program check from outside that ports 3306, 5432, 6379, 9200 and 27017 of your server can't be reached, for IPv4 and (if your server has an AAAA record) IPv6. Also fill in which of your services listen on 0.0.0.0 or [::]. The program only makes a connection to those five ports and sends nothing.

Exercise Z5.1: Database ports closed from outside

We don't log in and we don't try any password or key. First you show that the server is yours: click "Show my code" and put a file /.well-known/weboke-cursus-<code>.txt containing only that code on the web server of your hostname. We fetch it directly from every IP address we check (IPv4 and IPv6 separately); a TXT record doesn't count here. The check comes from heavy.webawere.nl (185.158.167.16 and 2a07:ae80:100:0:185:158:167:16), not from our web server. Only for registered students; at most 6 checks per hour per server. We only make a TCP connection to ports 3306, 5432, 6379, 9200, 27017 and 2375 (IPv4 and IPv6) and send nothing.




What you enter and what we find isn't stored. Your browser remembers your answers and whether you've passed the exercise. If you're signed in, we only save that you got the exercise (almost) right in My courses; you can delete that there too.

Further reading

One moment…

Set up your domains

For each domain, arrange the nameservers, the link to your hosting and who becomes the owner.

Loading your domains…