Server
Open ports and server hardening
FTP and control panel ports open to the internet are found quickly during an expertise. A practical guide to closing unused services, firewalls and panel allowlists.
· 7 min read

No matter how carefully the web application code is written, the server it runs on is checked too. One of the first steps in an official expertise is scanning the server's ports from the outside. If the internet can reach not only ports 80 and 443 but also FTP, database or control panel ports, that goes into the report as a separate finding.
The expertise of our own product (a tax and finance SaaS platform) had exactly this finding: FTP and hosting panel ports were reachable from the internet. In this post we describe how we closed it and walk through the main steps of server hardening.
Why an open port is a risk
Every open port is a program talking to the outside world. The more there are, the larger the attack surface:
- FTP transfers data, including the login and password, unencrypted. It is also a common target for password guessing (brute force) attacks.
- Control panels (a hosting panel, a database web interface) give full access to the server. If the panel itself has a vulnerability or a password leaks, the consequences are serious.
- Database ports (for example MySQL 3306, PostgreSQL 5432, Redis 6379) should not be open to the internet; the application connects to them from inside the server.
- Forgotten test services are often never updated and keep old vulnerabilities.
The principle: only the service your users need should be visible from the outside. Usually that is 443 (HTTPS) and 80 for redirects. Everything else is closed, or open only to approved addresses.
Step 1: find out what is open
First, see from inside the server which services listen on which addresses:
sudo ss -tlnp
Look at the Local Address column:
127.0.0.1:...or[::1]:...means reachable only from inside the server, which is good;0.0.0.0:...,*:...or[::]:...means it listens on all network interfaces; without a firewall it is open to the internet.
Then check from the outside. Do this from another machine and only against your own server:
nmap -Pn -p- your-server-ip
Compare the two lists. For every open port, ask: "who needs this service, and why?"
Step 2: disable unused services
The most reliable protection is a service that is not running at all. If FTP was installed once to upload some files, disable it and transfer files over SFTP (via SSH) or through your deploy process:
sudo systemctl disable --now vsftpd
In our case FTP was not needed at all; files were already delivered over SSH and git. Stopping it closed part of the finding.
Bind database and cache services to the local address only. For PostgreSQL, in postgresql.conf:
listen_addresses = 'localhost'
For Redis, redis.conf should contain bind 127.0.0.1 ::1.
Step 3: a firewall that is closed by default
On Ubuntu the simplest route is ufw. The idea: incoming connections are denied by default and only the required ports are opened.
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose
Important: before ufw enable, make sure the SSH port is allowed, or you may lock yourself out. If SSH runs on a non-standard port, allow that port instead.
If the server is hosted with a cloud provider, remember the provider also has its own network firewall (security group). Configuring both is good practice.
Step 4: put the control panel behind an allowlist
Tools such as a hosting panel are often needed, and removing them entirely is not always possible. In that case, open the panel port only to specific IP addresses:
sudo ufw allow from 203.0.113.10 to any port 8888 proto tcp
Here 203.0.113.10 is the static IP of the office or administrator and 8888 is the panel port (use your own). Without a static IP, reach the panel only through a VPN or an SSH tunnel:
ssh -L 8888:127.0.0.1:8888 user@your-server
The panel then opens in the browser at http://127.0.0.1:8888 and is invisible from the outside. That is the route we took: the panel port was closed to everyone and left open only to a list of approved addresses.
Step 5: harden SSH
SSH has to stay open, so make it robust. In /etc/ssh/sshd_config:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
Before changing this, confirm that key-based login works, then run sudo systemctl reload ssh. A tool such as fail2ban additionally blocks repeated failed attempts.
Further hardening
- Update system packages regularly; on Ubuntu,
unattended-upgradesinstalls security updates automatically. - Run each service under its own restricted user, not as root.
- Hide the server version in responses (
server_tokens off). - Watch the logs: SSH logins, panel logins, connections rejected by the firewall.
- After any change, run
ss -tlnpand the external scan again.
Standards
These requirements fall under A05 "Security Misconfiguration" in the OWASP Top 10 (2021). The CIS Benchmarks provide detailed hardening checklists for Ubuntu and other systems and are a convenient reference.
Summary
Open ports do not require code changes, but they are easy to forget. In our process, the Claude AI agent reviews the server configuration and the list of listening services, and the expert applies firewall rules in a safe order and re-checks the result from the outside.
If you want to know how your server looks from the outside, request a security scan. We will help you close unnecessary services before the official expertise.


