4.5 KiB
Multi-Server Inventory Audit Pattern
Walkthrough for performing a comprehensive SSH-based audit across a fleet of servers.
Approach
-
Create a self-contained audit script that collects everything in a single SSH session per host:
- System info: hostname, OS, kernel, uptime, load, CPU cores/model, RAM, disk usage
- Docker:
docker ps(names, images, status, ports),docker images(repositories, tags, sizes),docker-compose.ymllocations - Systemd: all running services, custom services under
/etc/systemd/system/(filter out base services like getty, sshd, cron, dbus, systemd-*, networking) - Listening ports:
ss -tlnp(TCP),ss -ulnp(UDP) — note which services/pids own each port - Web servers: which binary exists (nginx/apache2/caddy), vhost/site configs, Caddyfile excerpts
- Databases: which binaries exist (mysql, mysqld, psql, mongod, redis-server), which systemd services are active
- Users: list
/home/*directories, note non-standard users vs root/standard - Cron:
crontab -lfor root - Custom scripts:
ls /root/*.sh /root/*.py,ls /usr/local/bin/*.sh /usr/local/bin/*.py
-
Run in parallel — all hosts are independent, so batch the SSH calls with background processes or a simple loop with
&andwait. Use the common SSH key (itpp-infraat~/.ssh/itpp-infra). -
Compile the report as a single markdown file under
/root/.hermes/references/<name>.mdwith:- Quick summary table (hostname, IP, specs, OS, Docker Y/N, key apps)
- Per-server sections with the full field-by-field inventory
- Cross-cutting summary tables (Docker hosts, non-Docker hosts, database engines across fleet, VPN types, disk warnings)
Audit Script
Location: /root/.hermes/scripts/audit-server.sh
# Usage
/root/.hermes/scripts/audit-server.sh <ip> [username]
The script collects all fields listed above in a single SSH call. Output is clearly delimited with --- SECTION --- headers for parsing.
Common Fields Per Server Entry
Each inventory entry should document:
| Field | Source |
|---|---|
| IP | Known address |
| Hostname | hostname |
| Plan type | From known inventory (CPX11, CPX21, etc.) |
| CPU/RAM/Disk | nproc, free -h, df -h / |
| OS + Kernel | /etc/os-release, uname -a |
| Uptime + Load | uptime |
| Docker status | docker ps, docker images, compose file locations |
| Systemd services | systemctl list-units --type=service --state=running |
| Custom systemd | /etc/systemd/system/*.service minus base services |
| TCP listening | ss -tlnp |
| UDP listening | ss -ulnp |
| Web servers | binary check + config files |
| Databases | binary check + systemd status |
| Users | /home/* + /etc/passwd filtered for home dirs |
| Cron | crontab -l |
| Custom scripts | ls /root/*.sh /root/*.py /usr/local/bin/*.sh /usr/local/bin/*.py |
Disk Warning Flags
Servers above 70% disk usage should be flagged. At 90%+, mark as critical.
Known disk warnings (as of 2026-07-09):
- ai.itpropartner.com (178.156.167.181): 92% — 199G of 226G used. Potential cause: large Ollama model images (10GB+), stale Docker images, old Open WebUI versions.
- docker (178.156.168.35): 73% — 26G of 38G used. Small disk (CPX11), trending up.
Full Audit Snapshot
The live full audit report lives at /root/.hermes/references/hetzner-server-audit.md — last refreshed 2026-07-09 covering all 10 servers.
Pitfalls
- Known hosts keys: The first SSH to a new server prompts for host key verification. Use
-o StrictHostKeyChecking=nofor automation, but be aware of the security trade-off. - Large output: Some commands (like
dpkg -l | grep) can produce long output. Keep queries targeted. - Docker not installed: Check for
docker psreturning "command not found" gracefully — don't treat it as a failure, just note "No Docker installed." - Custom vs base services: Filter out standard systemd units (getty, sshd, cron, dbus, systemd-*, polkit, multipathd, etc.) to surface what's actually custom. Keep the full list in raw output but highlight custom ones separately.
- Traccar ports: The Traccar GPS tracking server uses a large range of ports (5001-5032 TCP+UDP) for various GPS protocols. Don't mistake these for security issues — they're expected for a GPS tracking platform.
- UniFi ports: UniFi controller uses 8080 (HTTP), 8443 (HTTPS), 8880/8843 (portal redirects), 6789 (speed test), 27117 (MongoDB local), 3478 (STUN UDP), 5514 (syslog UDP), 10001 (UBNT discovery UDP).