Open ports have a way of accumulating over time. A test environment gets deployed and never removed. An administrative interface is exposed for troubleshooting and left in place. A database that was supposed to listen internally ends up reachable from the internet.
Attackers look for these mistakes constantly. Redis, Elasticsearch, MongoDB, Jenkins, and similar services still show up on internet-facing systems where they were never meant to be exposed. Sometimes it's a temporary change that becomes permanent. Sometimes a firewall rule was missed during deployment. Sometimes nobody realized the service was listening externally.
The result is the same. A service intended for internal use ends up answering requests from anywhere.
The first step is figuring out what's actually reachable. From there, it's usually obvious what belongs on the internet and what doesn't.
Before disabling services or modifying firewall rules, establish a baseline of the system's current configuration. These records can help with troubleshooting, rollback planning, and future audits.
Collect the following information:
hostnamectl
ip addr
sudo ss -tulpn
sudo lsof -i -n -P
sudo systemctl list-unit-files --state=enabled
sudo systemctl --failed
Save the output to a secure location.
At a minimum, you should document:
Create a baseline immediately after provisioning a new server. Comparing future scans against a known-good state makes it easier to identify unexpected changes.
The first step in reducing the attack surface is understanding what is currently listening for connections.
Modern Linux distributions include the ss utility, which is the preferred replacement for netstat.
ss -tulpn
Example output:
Netid State Recv-Q Send-Q Local Address:Port
tcp LISTEN 0 128 0.0.0.0:22
tcp LISTEN 0 128 127.0.0.1:3306
tcp LISTEN 0 128 0.0.0.0:8080
Key fields to review include:
Pay particular attention to services in the LISTEN state that are bound to all interfaces.
To map open ports directly to processes:
sudo lsof -i -n -P
This command shows which applications own active network connections and listening sockets.
Many administrators still encounter systems that use netstat.
sudo netstat -tulpn 2>/dev/null || echo "netstat is not installed"
Although considered legacy, it remains common in documentation and troubleshooting workflows.
Not every open port is a security problem. However, every exposed service should have a documented owner and business justification.
The following ports frequently deserve additional review:
|
Port |
Service |
Why Review It |
|
22 |
SSH |
Direct internet exposure |
|
21 |
FTP |
Legacy protocol with security concerns |
|
23 |
Telnet |
Unencrypted remote access |
|
3306 |
MySQL |
Often unintentionally exposed |
|
5432 |
PostgreSQL |
Common cloud misconfiguration |
|
6379 |
Redis |
Frequent attack target |
|
9200 |
Elasticsearch |
Data exposure risk |
|
27017 |
MongoDB |
Associated with numerous breaches |
|
8080 |
Web/Admin Services |
Often forgotten after deployment |
An open port does not automatically indicate a vulnerability.
Instead, ask:
If nobody can answer these questions, further investigation is warranted.
Many production servers accumulate services over time as teams deploy software, perform testing, and forget to remove temporary components.
Identify the process associated with a listening port:
sudo ss -tulpn
Example:
LISTEN 0 128 *:8080 *:* users:(("java",pid=1234))
Inspect the process:
ps -fp 1234
Then review the service:
sudo systemctl status
Ask the following questions:
Unused services should be removed or disabled.
One of the most common exposure issues occurs when applications listen on every network interface.
Find services listening on all IPv4 interfaces:
sudo ss -tulpn | grep "0.0.0.0"
Find services listening on IPv6 interfaces:
sudo ss -tulpn | grep "::"
Compare these examples:
127.0.0.1:3306
and
0.0.0.0:3306
The first accepts connections only from the local host.
The second accepts connections from any reachable network.
For database servers, message brokers, and management interfaces, this distinction is often the difference between a secure configuration and an unnecessary exposure.
If a service is not required, disable it completely.
For example:
sudo systemctl stop rpcbind
sudo systemctl disable rpcbind
sudo systemctl status rpcbind
Verify the service is disabled:
sudo systemctl is-enabled rpcbind
Confirm the listening port has disappeared:
sudo ss -tulpn
Removing unnecessary services not only reduces attack surface but also decreases maintenance and patching requirements.
Not every service can be removed.
In many environments, the better solution is to limit where the service listens.
# my.cnf
bind-address = 127.0.0.1
Verify:
sudo ss -tulpn | grep 3306
Why: Verify the change actually took effect.
Expected result:
127.0.0.1:3306
not:
0.0.0.0:3306
# postgresql.conf
listen_addresses = 'localhost'
Apply and verify:
sudo systemctl restart postgresql
sudo ss -tulpn | grep 5432
Why: Configuration changes without verification create support headaches.
Expected result:
127.0.0.1:5432
listen 127.0.0.1:8080;
This approach allows local applications to function normally while preventing external access.
For many organizations, restricting exposure provides nearly the same security benefit as removing the service entirely.
A service may be listening, but that does not necessarily mean it is reachable.
Review firewall policies and compare them against listening ports.
sudo firewall-cmd --list-all
sudo ufw status numbered
sudo iptables -L -n -v
sudo nft list ruleset
Compare:
Any discrepancy should be investigated.
Internal checks alone do not provide a complete picture.
Administrators should periodically perform scans from a separate host to see what external users can actually reach.
Basic full TCP port scan:
nmap -Pn -p-
Identify service versions and common configurations:
nmap -sV -sC
Review results for:
This step frequently reveals exposures that internal reviews miss.
Numerous Elasticsearch, Redis, and MongoDB exposure incidents have occurred because services intended for internal use were reachable from the internet due to firewall, cloud security group, or binding misconfigurations.
Attack surface extends beyond traditional services.
Review systems for:
Administrative tools often provide direct access to sensitive systems and should rarely be exposed publicly.
Look for:
These services are frequently deployed without security controls.
Inspect running containers:
docker ps
docker port
Depending on your environment, you may need sudo or membership in the docker group.
Why: Many production environments still require root or Docker group membership.
Review access controls for:
Improper access controls can increase the impact of server compromise.
Old staging systems and proof-of-concept deployments often become forgotten attack vectors.
Periodically inventory all externally reachable hosts and retire systems that are no longer required.
Attack surface management is not a one-time project.
New software deployments, containers, updates, and configuration changes continually alter exposure.
Regularly review listening services:
sudo ss -tulpn
Consider automated auditing tools such as:
sudo lynis audit system
osqueryi "SELECT pid, port, protocol, address FROM listening_ports;"
Why: Produces cleaner output and is more useful in a hardening workflow.
Additional options include:
Continuous monitoring helps detect exposure drift before attackers do.
How often you should review public Linux systems depends on your risk profile, but they should be reviewed regularly and continuously monitored as part of attack surface management.
Always reassess exposure after:
The attack surface changes whenever the environment changes.
Most exposure issues aren't discovered during an incident response engagement. They're found later, when someone notices a service listening where it shouldn't be, a firewall rule that was never removed, or a system that changed over time without anyone revisiting the original configuration.
Redis, Elasticsearch, MongoDB, Jenkins, administrative interfaces, internal dashboards, test environments. The technology changes, but the underlying problem tends to look familiar. Something that was meant to stay internal became reachable from somewhere it shouldn't.
Public Linux systems rarely stay static for long. Services get deployed, containers come and go, firewall rules change, and cloud infrastructure evolves with them. Knowing what is exposed today is often more useful than knowing what was exposed six months ago.
For more Linux hardening guidance, vulnerability coverage, and practical security administration tips, subscribe to the LinuxSecurity newsletter.