# Multi-Server Inventory Audit Pattern

Walkthrough for performing a comprehensive SSH-based audit across a fleet of servers.

## Approach

1. **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.yml` locations
   - 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 -l` for root
   - Custom scripts: `ls /root/*.sh /root/*.py`, `ls /usr/local/bin/*.sh /usr/local/bin/*.py`

2. **Run in parallel** — all hosts are independent, so batch the SSH calls with background processes or a simple loop with `&` and `wait`. Use the common SSH key (`itpp-infra` at `~/.ssh/itpp-infra`).

3. **Compile the report** as a single markdown file under `/root/.hermes/references/<name>.md` with:
   - 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`

```bash
# 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=no` for 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 ps` returning "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).
