Skip to content

Repository files navigation

ports-checker — Detect unexpected externally reachable TCP ports

PORTS-CHECKER ASDF System Details

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:

  1. connects to the specified server over SSH;
  2. runs ss -H -lnt to obtain the TCP sockets listening on wildcard and non-loopback interfaces;
  3. closes the SSH connection;
  4. attempts to connect from the local machine only to the discovered ports;
  5. exits with code 1 if 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:

  1. It probes only ports that can actually be open. Instead of scanning a range, it first obtains the listening TCP ports from the server over SSH and then tests only those ports from the outside.
  2. It avoids broad scan-like network activity. In some environments, endpoint protection or network security tooling may classify an nmap run as a port-scanning or attack attempt. ports-checker performs ordinary SSH access 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.
  3. It checks policy, not just reachability. nmap reports scan results; ports-checker compares 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, and CI jobs.

Requirements

  • Common Lisp (the project is tested with SBCL);
  • Roswell;
  • Qlot;
  • a local ssh command;
  • the ss command from iproute2 on the Linux server being checked;
  • configured non-interactive SSH authentication.

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.com

To 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.com

Usage

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.com

Set 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, an SSH error, 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")' -q

The 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-docs

Initial Release Limitations

  • Only listening TCP ports are checked.
  • The remote server must run Linux and provide the ss command.
  • UDP is 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

package ports-checker/checker

Functions

function ports-checker/checker:check-ports host discovered-ports allowed-ports &key (timeout 3) (probe-function #'probe-port)

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.

function ports-checker/checker:probe-port host port &key (timeout 3)

Return true when a TCP connection to HOST and PORT succeeds within TIMEOUT.

PORTS-CHECKER/REMOTE

package ports-checker/remote

Classes

REMOTE-COMMAND-ERROR

condition ports-checker/remote:remote-command-error (error)

An error reported when remote listener discovery over SSH fails.

Readers

reader ports-checker/remote:remote-command-error-destination (remote-command-error) (:destination)

reader ports-checker/remote:remote-command-error-status (remote-command-error) (:status)

reader ports-checker/remote:remote-command-error-stderr (remote-command-error) (:stderr)

Functions

function ports-checker/remote:discover-ports destination &key ssh-user ssh-port (connect-timeout 10)

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.


[generated by 40ANTS-DOC]

About

A targeted port checker that finds what’s listening on your servers—and flags what shouldn’t be.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages