ports-checker — Detect unexpected externally reachable TCP ports
PORTS-CHECKER ASDF System Details
Description: A targeted port checker that finds what’s listening on your servers—and flags what shouldn’t be.
Licence: Unlicense
Author: Alexander Artemenko <svetlyak.40wt@gmail.com>
Homepage: https://40ants.com/ports-checker/
Bug tracker: https://github.com/40ants/ports-checker/issues
Source control: GIT
ports-checker verifies that no unexpected TCP ports have become reachable
after a server configuration change or software installation. It does not scan
a range of ports. Instead, it:
connects to the specified server over
SSH;runs
ss -H -lntto obtain theTCPsockets listening on wildcard and non-loopback interfaces;closes the
SSHconnection;attempts to connect from the local machine only to the discovered ports;
exits with code
1if a reachable port is not on the allowlist.
A service blocked by an external firewall is therefore not considered
reachable, even if its process listens on a non-loopback interface. Startup and
SSH errors cause the command to exit with code 2.
Why Not Nmap?
ports-checker complements general-purpose port scanners such as nmap with
a narrower, policy-oriented check:
It probes only ports that can actually be open. Instead of scanning a range, it first obtains the listening
TCPports from the server overSSHand then tests only those ports from the outside.It avoids broad scan-like network activity. In some environments, endpoint protection or network security tooling may classify an
nmaprun as a port-scanning or attack attempt.ports-checkerperforms ordinarySSHaccess followed by targeted connection attempts, which is less likely to trigger rules intended specifically for broad port scans. These checks are still normal network activity and may remain visible in security logs.It checks policy, not just reachability.
nmapreports scan results;ports-checkercompares reachable ports with an explicit allowlist and returns a non-zero exit code when it finds an unauthorized port. This makes it straightforward to use in automated checks, deployment verification, andCIjobs.
Requirements
Common Lisp (the project is tested with
SBCL);Qlot;
a local
sshcommand;the
sscommand fromiproute2on the Linux server being checked;configured non-interactive
SSHauthentication.
The SSH host key must already be present in known_hosts. The command
intentionally does not accept unknown host keys automatically.
Installation
Install the command directly from GitHub with Roswell:
ros install 40ants/ports-checker
ports-checker --allow 22,80,443 example.comTo run the command from a local checkout, install and use the Qlot dependencies:
qlot install
chmod +x roswell/ports-checker.ros
qlot exec ./roswell/ports-checker.ros --allow 22,80,443 example.comUsage
The --allow (-a) option accepts a comma-separated list of ports. The SSH
user and SSH port can be specified separately:
qlot exec ./roswell/ports-checker.ros --ssh-user deploy --ssh-port 2222 \
--allow 2222,443 server.example.comSet the external TCP probe timeout with --timeout and the SSH connection
timeout with --ssh-timeout. Both values are specified in seconds.
Exit Codes
0— no unapproved ports are reachable;1— at least one unapproved port is reachable;2— invalid arguments, anSSHerror, or another runtime error.
Development
Project dependencies are pinned with Qlot. Tests use Rove:
qlot install
qlot exec ros -Q -e '(asdf:load-asd (truename "ports-checker.asd"))' \
-e '(asdf:test-system "ports-checker")' -qThe network-logic tests do not require a real SSH server and do not open actual
network connections.
Install the project-local documentation builder once, then regenerate
README.md, ChangeLog.md, and the HTML site after changing documentation or
public API docstrings:
qlot exec ros install 40ants/docs-builder
CL_SOURCE_REGISTRY=$(pwd)/ .qlot/bin/build-docs ports-checker-docsInitial Release Limitations
Only listening
TCPports are checked.The remote server must run Linux and provide the
sscommand.UDPis not checked yet.Reachability is tested from the machine running
ports-checker; a firewall may behave differently for clients on another network.
API
PORTS-CHECKER/CHECKER
Functions
Return unexpected reachable ports among DISCOVERED-PORTS on HOST.
ALLOWED-PORTS are never probed. PROBE-FUNCTION accepts HOST and PORT plus a
TIMEOUT keyword, which makes the network boundary replaceable in tests.
Return true when a TCP connection to HOST and PORT succeeds within TIMEOUT.
PORTS-CHECKER/REMOTE
Classes
REMOTE-COMMAND-ERROR
An error reported when remote listener discovery over SSH fails.
Readers
Functions
Return externally bound TCP ports reported by DESTINATION over SSH.
SSH-USER and SSH-PORT select the SSH account and port. CONNECT-TIMEOUT is
passed to OpenSSH. Authentication is deliberately non-interactive.