Record which ports a host is supposed to listen on, then let a timer tell you when that changes.
portop --save-baseline # record what listens now portop --diff # exit 3 if anything changed portop --diff --json # the same, for scripts
The baseline is a small JSON file in the OS config directory; --baseline-path chooses another.
Each listening TCP and UDP port is recorded with its scope: loopback, every interface or one address. A service listening on IPv4 and IPv6 is one entry. --diff reports:
127.0.0.1 to 0.0.0.0.Baselines saved by versions before 0.3.0 have no scope and are compared by protocol and port only.
# /etc/systemd/system/portop-diff.service [Unit] Description=Compare listening ports with the baseline [Service] Type=oneshot ExecStart=/usr/bin/portop --diff --baseline-path /var/lib/portop/baseline.json # /etc/systemd/system/portop-diff.timer [Timer] OnCalendar=hourly [Install] WantedBy=timers.target
A failed run (exit 3) shows up in systemctl --failed and can trigger OnFailure= alerting.