- audit/phase-one + phase-two: security audit briefs, findings, credential-rotation plan, Docker-USER hardening scripts, rollback refs - disaster-recovery/restore-test-log.md + backup-dr-audit-2026-08-10.md - clients/ (modelortho SEO audit, ai-biz-dev competitive landscape), notes/ (tiktok strategy) - projects/: front-desk-voice-agent, seo-visibility-checker product plan, hotnow-savannah HTML, resend-transactional-email, backup-dashboard-enhancements, code-review-graph, seo-ci-architecture - proposals/verdicttank/: architecture v4.0, methodology, judge-pool review, consolidation reasoning, cross-check review - docs/super-search/firecrawl-provider-strategy.md - updates: CHANGELOG, model-chain, projects-master-readme, intelsight.io - .gitignore: exclude nested standalone repos (seo-tool, venturebuilt)
389 lines
25 KiB
Markdown
389 lines
25 KiB
Markdown
# Sys-A Phase One Inventory & Findings: Core + app1
|
|
|
|
Auditor: Sys-A (sysadmin inventory auditor)
|
|
Scope: Core (this host, localhost) and app1 (152.53.36.131)
|
|
Date: 2026-08-13
|
|
Mode: READ-ONLY. No mutation performed. All remediations are Phase Two items.
|
|
|
|
---
|
|
|
|
## 1. Host Profiles
|
|
|
|
| Attribute | Core (localhost) | app1 (152.53.36.131) |
|
|
|---|---|---|
|
|
| Provider / model | Netcup RS 2000 G12 | Netcup RS 4000 G12 |
|
|
| OS | Debian 13 (trixie) | Debian 13 (trixie) |
|
|
| Kernel | 6.12.94+deb13-amd64 | 6.12.95 |
|
|
| vCPU | 8 | 12 |
|
|
| RAM | 15 GB, NO swap | 31 GB, NO swap |
|
|
| RAM in use | ~12 GB used / 467 MB free (2.8 GB available) | (healthy) |
|
|
| Disk | 503 GB, 73 GB used (15%) | 1007 GB, 107 GB used (11%) |
|
|
| Uptime | not recorded | not recorded |
|
|
| Public IP | 152.53.192.33 | 152.53.36.131 |
|
|
| Docker | present | present |
|
|
| Firewall | ufw active (default policy not confirmed) | ufw active |
|
|
|
|
Total services inventoried: Core ~55 systemd units (running) + 10 Docker containers; app1 19 systemd units (running) + 23 Docker containers. Counts below are of running/active workloads only.
|
|
|
|
---
|
|
|
|
## 2. Core Service Inventory (localhost)
|
|
|
|
### 2.1 Critical / Tier-0 services
|
|
|
|
| Service | Software + Version | Port | Runs as | Restart | Backup | Notes |
|
|
|---|---|---|---|---|---|---|
|
|
| Hermes gateway | hermes-agent (python) | 8642, 8787 (socat), 9119 (dashboard) | root | user unit enabled | hermes-full-backup daily 01:00 + live-sync | runs under systemd USER manager (user@0), not a system unit; 3.7 GB RSS, 182 tasks |
|
|
| Caddy | caddy (deb) | 80/443 on 152.53.192.33, 2019 admin 127.0.0.1 | caddy | systemd enabled | Caddyfile in hermes-full-backup + system-config-sync | single ingress for Core |
|
|
| Grafana | grafana/grafana:11.4.0 | 3002 (0.0.0.0) | root (container) | always | grafana db in core-services-backup 01:30 | image 11.4.0 is ~20 months old |
|
|
| Prometheus | prom/prometheus:latest | 9090 (0.0.0.0) | root (container) | always | prometheus_data volume in core-services-backup | config /root/docker/monitoring/prometheus/prometheus.yml |
|
|
| Super Search MCP | /root/docker/super-search (python) | 8899 (0.0.0.0) | root | systemd | NOT covered by any backup script (see F-18) | depends on searxng + exa/firecrawl/open-corporates APIs |
|
|
| PostgreSQL | postgres (deb) | 5432 (127.0.0.1) | postgres | systemd | no dedicated dump; only hotnow-app DB implied | single instance, no replica |
|
|
| Redis | redis (deb) | 6379 (127.0.0.1) | redis | systemd | none found | single instance |
|
|
|
|
### 2.2 Hermes ecosystem systemd units (all as root unless noted)
|
|
|
|
| Unit | Purpose | Port | Notes |
|
|
|---|---|---|---|
|
|
| hermes-assistant | Hermes Assistant PWA backend | (8080-ish) | root |
|
|
| hermes-browser | Headless Chromium CDP | 9222 (127.0.0.1) | runs chrome with `--no-sandbox` as root |
|
|
| hermes-control-deck | Control Deck backend API | 8200 | **port collides with pipeline-api** (F-10) |
|
|
| hermes-socat-8787 | port forward 8787 -> localhost:8642 | 8787 (0.0.0.0) | `After=hermes-gateway.service` references non-existent SYSTEM unit (gateway is a user unit) |
|
|
| hermes-voice | Hermes Voice (SvelteKit) | 4331 (127.0.0.1) | runs as non-root (best practice) |
|
|
| hermes-gateway (user) | gateway run | 8642 | root, user manager |
|
|
| hermes-gateway-anita (user) | Anita messaging gateway | - | root, user manager |
|
|
|
|
### 2.3 Application / MCP / API systemd units (all as root)
|
|
|
|
| Unit | Purpose | Port | Hardcoded secret? |
|
|
|---|---|---|---|
|
|
| auth-api | auth backend | 8500 | no (uses /root/projects/auth/.env) |
|
|
| ops-portal | operations portal | 8090 | - |
|
|
| osint-api | OSINT API | 8100 | - |
|
|
| osint-person | OSINT person MCP | 8902 | - |
|
|
| diglocate-api | diglocate | 8000 | - |
|
|
| intelsight-api | intelsight | 8099 | - |
|
|
| hotnow-api | HotNow backend | 8001 | - (Postgres + Redis) |
|
|
| shopping-cart | shopping cart | 8101 (127.0.0.1) | - |
|
|
| seemytrip | SeeMyTrip backend | 8113 | **ADMIN_AI_KEY hardcoded** |
|
|
| rally | rally backend | 8105 (0.0.0.0) | **JWT_SECRET, DEEPSEEK_API_KEY, ADMIN_AI_KEY hardcoded** |
|
|
| shark-game | shark-game backend | 8083 (0.0.0.0) | - |
|
|
| pipeline-api | pipeline API | 8200 | **collides with hermes-control-deck** |
|
|
| verdicttank-api | verdicttank | 8201 | - |
|
|
| verdicttank-worker | worker | - | - |
|
|
| status-page / node | status page | 8210 | node |
|
|
| pry | PRY API | 8905 | - |
|
|
| dre-mcp | DRE MCP | 8900/8901 | - |
|
|
| ft360-mcp | FleetTracker360 MCP | 8903 | - |
|
|
| twilio-mcp | Twilio MCP | 8910/8911 | - |
|
|
| crawl4ai | crawler | - | - |
|
|
| voice-agent | voice agent | 9101 | - |
|
|
| voice-agent-stt | voice STT | 9000 | - |
|
|
| gitea-runner | Gitea actions runner | - | - |
|
|
| host-metrics-export | metrics textfile | - | - |
|
|
| mysql-tunnel | SSH tunnel to wphost02 MySQL | 33060 (127.0.0.1) | `StrictHostKeyChecking=no`, `UserKnownHostsFile=/dev/null` |
|
|
| hear-read | audio TTS/read | 8240 (127.0.0.1) | - |
|
|
|
|
### 2.4 Core Docker containers
|
|
|
|
| Container | Image:Tag | Port | Image age | Restart | Purpose |
|
|
|---|---|---|---|---|---|
|
|
| browserless | browserless/chrome:latest | 3000 (0.0.0.0) | 2 years | always | headless chrome API |
|
|
| uptime-kuma | louislam/uptime-kuma:1 | 3001 (0.0.0.0) | current | always | status monitoring |
|
|
| grafana | grafana/grafana:11.4.0 | 3002 | 20 months | always | dashboards |
|
|
| prometheus | prom/prometheus:latest | 9090 | 6 weeks | always | metrics |
|
|
| searxng | searxng/searxng:latest | 8888 (127.0.0.1) | 6 weeks | always | meta search (Super Search dep) |
|
|
| timetrex | timetrex | 8085 (127.0.0.1) | - | always | time tracking |
|
|
| microbin | microbin | 8260 (127.0.0.1) | - | always | paste bin |
|
|
| camofox | camofox | 9377 (0.0.0.0) | - | always | stealth browsing |
|
|
| mikrotik-exporter | mikrotik-exporter | 9436 (127.0.0.1) | - | always | router metrics |
|
|
| node_exporter (host) | prom/node-exporter | 9100 (0.0.0.0) | - | systemd | node metrics |
|
|
| telegraf (host) | telegraf | 9273 (0.0.0.0) | - | systemd | metrics |
|
|
|
|
### 2.5 Ad-hoc / unmanaged processes (Core)
|
|
|
|
| Process | Port | CWD | Concern |
|
|
|---|---|---|---|
|
|
| `python3 -m http.server 8080` | 8080 (0.0.0.0) | /var/www/mockup/anita-consulting | ad-hoc web server as root, no systemd |
|
|
| `python3 -m http.server 8934` | 8934 (0.0.0.0) | /var/www/mockup/itpropartner | ad-hoc web server as root |
|
|
| `python3 -m http.server 9876` | 9876 (0.0.0.0) | /tmp | **serving /tmp as root, public bind** |
|
|
| hermes dashboard | 9119 (0.0.0.0) | - | part of Hermes |
|
|
|
|
---
|
|
|
|
## 3. app1 Service Inventory (152.53.36.131)
|
|
|
|
### 3.1 Critical / Tier-0 services
|
|
|
|
| Service | Software + Version | Port | Runs as | Restart | Backup | Notes |
|
|
|---|---|---|---|---|---|---|
|
|
| LiteLLM / admin-ai | ghcr.io/berriai/litellm:v1.92.0 | 4000 | root (container) | always | **DB NOT backed up** (F-1); config YAML daily | master key + admin_ai provider |
|
|
| Caddy | caddy (systemd) | 80/443 | caddy | enabled | **Caddyfile NOT backed up** (F-12) | single ingress for all app1 |
|
|
| Open WebUI | ghcr.io/open-webui/open-webui:latest | 3000 (0.0.0.0) | root (container) | always | daily 1.47 GB | depends on LiteLLM |
|
|
| Wazuh manager/indexer/dashboard | wazuh 4.9.2 (indexer, 21 months old) | 1514/1515, 9200, 5601 | root | always | daily | SIEM/XDR |
|
|
| n8n | n8n | 5678 | root (container) | always | daily | automation |
|
|
| Vaultwarden | vaultwarden latest | 8081 | root (container) | always | daily | password manager |
|
|
| Twenty CRM | twentycrm | 3003 | root (container) | always | daily | CRM |
|
|
| Komodo | komodo-core | 9120 (0.0.0.0) | root (container) | always | daily | infra automation |
|
|
| Browserless | browserless/chrome:latest | 3005 (0.0.0.0), 3006 (ufw-limited) | root (container) | always | - | 2 year old image |
|
|
| super-search (host) | /root/docker/super-search | 8899 | root | systemd | .env only (373 bytes) | depends on searxng (Core) |
|
|
| giftaroast | /root/giftaroast | 8100 | root | systemd | - | **runaway fix_dict.py** (F-8) |
|
|
|
|
### 3.2 app1 Docker containers (full list)
|
|
|
|
LiteLLM v1.92.0 + litellm_postgres, mcp-browser / mcp-email / mcp-git / mcp-filesystem / mcp-super-search, litellm-super-search, open-webui, n8n + n8n-postgres, twenty-server + twenty-db + twenty-redis, komodo-core + komodo-mongo, vaultwarden, wazuh single-node (manager+indexer+dashboard), browserless, docuseal.
|
|
|
|
Key stale/very-old image tags on app1: browserless/chrome:latest (2 years), wazuh/wazuh-indexer:4.9.2 (21 months), vaultwarden/server:1.33.2 (18 months, old tag present alongside latest), ollama/ollama (5 weeks, not running), mattermost (not running).
|
|
|
|
### 3.3 app1 systemd units
|
|
|
|
super-search (8899), giftaroast (8100), caddy, sshd, docker, containerd, fail2ban, cron, rsyslog, ufw, unattended-upgrades, qemu-guest-agent, chrony, plus base system units.
|
|
|
|
### 3.4 Runaway / orphaned processes (app1)
|
|
|
|
| PID | Process | CWD | Elapsed | CPU |
|
|
|---|---|---|---|---|
|
|
| 3656093 | python3 fix_dict.py | /root/giftaroast | 14d 17h | 99.4% |
|
|
| 3657668 | python3 fix_dict.py | /root/giftaroast | 14d 17h | 99.4% |
|
|
| 3658103 | python3 fix_dict.py | /root/giftaroast | 14d 17h | 99.4% |
|
|
|
|
Parent of PID 3656093 is `bash -c "cd /root/giftaroast && python3 fix_dict.py 2>&1 echo '=== Restart service ===' systemctl dae..."`, an aborted/interrupted manual deployment. Three cores permanently pegged for 14+ days.
|
|
|
|
---
|
|
|
|
## 4. Dependency Map
|
|
|
|
### 4.1 Core (dot)
|
|
|
|
```dot
|
|
digraph Core {
|
|
// Ingress
|
|
caddy -> ops_portal; caddy -> osint_api; caddy -> diglocate; caddy -> intelsight;
|
|
caddy -> hotnow_api; caddy -> shopping_cart; caddy -> seemytrip; caddy -> rally;
|
|
caddy -> shark_game; caddy -> pipeline_api; caddy -> verdicttank_api; caddy -> auth_api;
|
|
caddy -> hermes_voice; caddy -> voice_agent; caddy -> pry; caddy -> uptime_kuma;
|
|
caddy -> microbin; caddy -> timetrex; caddy -> status_page;
|
|
|
|
// Data layer
|
|
hotnow_api -> postgres; hotnow_api -> redis;
|
|
osint_api -> redis;
|
|
|
|
// Hermes gateway fan-out
|
|
hermes_gateway -> super_search; hermes_gateway -> dre_mcp; hermes_gateway -> ft360_mcp;
|
|
hermes_gateway -> osint_person; hermes_gateway -> twilio_mcp; hermes_gateway -> clearfront_mcp;
|
|
hermes_gateway -> browserless; hermes_gateway -> camofox; hermes_gateway -> chrome_cdp;
|
|
|
|
// Search chain
|
|
super_search -> searxng; super_search -> "exa/firecrawl/open-corporates APIs";
|
|
|
|
// Voice chain
|
|
voice_agent -> voice_agent_stt;
|
|
|
|
// Observability
|
|
grafana -> prometheus;
|
|
prometheus -> node_exporter; prometheus -> telegraf; prometheus -> mikrotik_exporter; prometheus -> snmp;
|
|
|
|
// Cross-host tunnel
|
|
mysql_tunnel -> "wphost02 MySQL (152.53.62.38)";
|
|
}
|
|
```
|
|
|
|
### 4.2 app1 (dot)
|
|
|
|
```dot
|
|
digraph app1 {
|
|
caddy -> litellm; caddy -> open_webui; caddy -> n8n; caddy -> vaultwarden;
|
|
caddy -> docuseal; caddy -> twenty; caddy -> komodo; caddy -> wazuh_dashboard;
|
|
caddy -> giftaroast; caddy -> browserless;
|
|
|
|
litellm -> litellm_postgres;
|
|
litellm -> mcp_browser; litellm -> mcp_email; litellm -> mcp_git; litellm -> mcp_filesystem;
|
|
litellm -> mcp_super_search;
|
|
open_webui -> litellm;
|
|
|
|
n8n -> n8n_postgres;
|
|
twenty_server -> twenty_db; twenty_server -> twenty_redis;
|
|
komodo_core -> komodo_mongo;
|
|
wazuh_manager -> wazuh_indexer; wazuh_dashboard -> wazuh_indexer;
|
|
|
|
super_search_host -> "searxng on Core (152.53.192.33:8888)";
|
|
mcp_super_search -> super_search_host;
|
|
}
|
|
```
|
|
|
|
### 4.3 Dependency map highlights
|
|
|
|
- Single ingress per host: Caddy is the sole HTTP(S) entry point. A Caddy misconfiguration or crash takes down every web service on that host (single point of failure).
|
|
- LiteLLM is the AI backbone: Open WebUI, all mcp-* tool servers, and Super Search (via MCP) route through it. admin-ai.itpropartner.com is the model gateway for every AI consumer.
|
|
- Hermes gateway has a wide fan-out (8+ MCP/tool servers, browserless, camofox, Chrome CDP). Its failure cascades into all agent tooling.
|
|
- Cross-host dependency: app1 super-search depends on Core searxng (port 8888) over the WAN. If Core is down, app1 search is degraded.
|
|
- mysql-tunnel (Core) depends on wphost02 (152.53.62.38) reachability; a host key change breaks it silently.
|
|
- HotNow is the only app with a real DB dependency chain (Postgres + Redis) on Core.
|
|
|
|
---
|
|
|
|
## 5. Cron Jobs
|
|
|
|
### 5.1 Core crontab (root)
|
|
|
|
| Schedule | Job | User | Purpose | Failure mode if silent |
|
|
|---|---|---|---|---|
|
|
| 01:00 daily | hermes-full-backup.sh | root | full Hermes home + Caddyfile + systemd units + state.db to Wasabi | no DR copy of Hermes state; RPO grows unbounded |
|
|
| */15 min | hermes-live-sync.sh | root | aws s3 sync of .hermes to `live/` prefix | incremental state lost (mitigated by daily full) |
|
|
| 01:30 daily | core-services-backup.sh | root | Grafana/Prometheus/etc volumes | monitoring history loss |
|
|
| 02:00 daily | root-essentials-backup.sh | root | .hermes/.ssh/.aws/himalaya/shark-game/projects | credential/config loss |
|
|
| 03:00 daily | docker-volume-sync.sh | root | **script does not exist** (F-13) | always fails silently |
|
|
| 00:05 daily | system-config-sync.sh | root | system configs to Wasabi | config drift backups lost |
|
|
| 15:00 daily | status-page-refresh.sh | root | status page data | stale status |
|
|
| various | wphost02-backup.sh | root | SSHes to 152.53.62.38 (wphost02) with root key | wphost02 backups lost |
|
|
| various | docuseal/timetrex/gitea/hudu/dawarich/ragflow/twenty/stack-auth/hexclave/komodo/litellm/technitium/unifi/unms/vaultwarden-backup.sh | root | per-service backups (many target remote hosts) | per-service backup loss |
|
|
|
|
Hermes-managed jobs live in /root/.hermes/cron/jobs.json (jobs.json + per-job dirs). Parsing jobs.json programmatically failed during the audit (malformed JSON); a grep fallback confirmed the job list above. The exact schedule set should be re-verified by the DR owner.
|
|
|
|
### 5.2 app1 crontab (root)
|
|
|
|
| Schedule | Job | Purpose | Failure mode |
|
|
|---|---|---|---|
|
|
| 02:00 daily | /root/backup.sh | litellm config, n8n, openwebui, mcp .env, ollama, mattermost, wazuh, twenty | see F-1 (litellm DB), F-12 (Caddyfile) |
|
|
|
|
app1 /root/backup.sh references `mattermost-backup.sh` and `wazuh-backup.sh` in /root/.hermes/scripts/. Mattermost is not running (no container), so that backup is a no-op. Ollama is not installed, so its backup step fails each night.
|
|
|
|
### 5.3 /etc/cron.* (both hosts)
|
|
|
|
Standard Debian logrotate/man-db/dpkg jobs. No custom third-party cron drops observed beyond the system crontabs above. No evidence of a rotating backup retention job; S3 listings show daily objects accumulating without obvious lifecycle policy (Phase Two item).
|
|
|
|
---
|
|
|
|
## 6. Backup Status Matrix
|
|
|
|
| Service | Backup script | Last S3 object | Restore test documented? |
|
|
|---|---|---|---|
|
|
| Hermes (full) | hermes-full-backup.sh | 2026-08-13 (daily tar.gz ~1.43 GB) | restore.sh generated; DR-PLAN.md present (600) |
|
|
| Hermes (live) | hermes-live-sync.sh -> `live/` prefix | last state.db `live/` unclear; `live-sync/` (old prefix) stale since 2026-07-05 | partial |
|
|
| LiteLLM config | app1 /root/backup.sh | 2026-08-13 (litellm-config-*.yaml, 333 B) | no |
|
|
| **LiteLLM Postgres DB** | (should be pg_dump) | **ZERO .sql.gz objects ever** (F-1) | no |
|
|
| Grafana/Prometheus | core-services-backup.sh | 2026-08-13 (grafana 55 KB, prometheus 51 MB) | no |
|
|
| Open WebUI | app1 /root/backup.sh | 2026-08-13 (1.47 GB) | no |
|
|
| n8n / Twenty / Vaultwarden / Wazuh | app1 /root/backup.sh | 2026-08-13 | no |
|
|
| Core Caddyfile | hermes-full-backup + system-config-sync | 2026-08-13 | via restore.sh |
|
|
| **app1 Caddyfile** | **none** | **never** (F-12) | no |
|
|
| Super Search (both hosts) | none meaningful | .env only (373 B) | no |
|
|
| Postgres / Redis (Core) | none dedicated | none | no |
|
|
|
|
Restore-test documentation: DR-PLAN.md and migration-recovery.md exist on Core (root-only, mode 600) but no evidence of an actual periodic restore drill being executed (no drill logs found in backup dirs).
|
|
|
|
---
|
|
|
|
## 7. Severity-Rated Findings
|
|
|
|
### CRITICAL
|
|
|
|
**F-1. LiteLLM Postgres database is not being backed up (data-loss risk).**
|
|
Evidence: `aws s3 ls s3://hermes-vps-backups/app1/litellm/` shows only `litellm-config-*.yaml` objects (240-333 bytes). Zero `.sql`/`.sql.gz` objects since inception. app1 /root/backup.sh dumps `pg_dump -U litellm litellm`, but the live config (`config.yaml`) sets database_url to database `litellm_db`, so the dump targets a nonexistent database and fails silently every night.
|
|
Rationale: LiteLLM's Postgres holds every API key, model routing table, spend/budget records, and the admin-ai provider config. A database or volume failure means total loss of the AI gateway state. This is the single highest-impact finding.
|
|
|
|
**F-2. Plaintext secrets hardcoded in world-readable systemd unit files.**
|
|
Evidence: `/etc/systemd/system/rally.service` contains `JWT_SECRET`, `DEEPSEEK_API_KEY`, and `ADMIN_AI_KEY` as literal `Environment=` values; `/etc/systemd/system/seemytrip.service` contains `ADMIN_AI_KEY`; `/etc/systemd/system/giftaroast.service` (app1) contains Twilio `AUTH_TOKEN`/`SID` and a `ADMIN_AI_KEY`. Unit files are 0644 (world-readable).
|
|
Rationale: Any local user (or any service compromise) can read live production API keys for DeepSeek, the admin-ai gateway, and Twilio. Secrets belong in root-only env files (600), not unit files.
|
|
|
|
### HIGH
|
|
|
|
**F-3. Every custom service runs as root.**
|
|
Evidence: docker inspect of all Core/app1 containers shows `User=""` (root); all `python3`/`node` listeners show `user root` in ss/ps. Only hermes-voice and Caddy run non-root.
|
|
Rationale: A single compromised service (e.g. a 2-year-old browserless) yields full root on the host, no privilege boundary.
|
|
|
|
**F-4. Very old / unpatched image tags in active service.**
|
|
Evidence: browserless/chrome:latest = 2 years old on both hosts; grafana/grafana:11.4.0 = ~20 months old (Core, internet-exposed via ufw); wazuh/wazuh-indexer:4.9.2 = 21 months old (app1); vaultwarden/server:1.33.2 = 18 months old tag still present on app1.
|
|
Rationale: These images predate many published CVEs and receive no updates. Browserless (headless Chrome) is a high-value attack surface and is also a Hermes tool dependency.
|
|
|
|
**F-5. Hermes gateway (most critical service) supervised by root's systemd USER manager, not a system unit.**
|
|
Evidence: `systemctl --user status hermes-gateway.service` shows active (user@0), while `systemctl status hermes-gateway.service` is `not-found`. `hermes-socat-8787.service` declares `After=hermes-gateway.service` against a unit that does not exist at the system level.
|
|
Rationale: The gateway is the core of ITPP automation. Its lifecycle depends on a user session staying alive (no lingering system-level restart guarantee) and its unit wiring is inconsistent (socat references a nonexistent system unit). Fragile single point of failure.
|
|
|
|
**F-6. Port 8200 collision between hermes-control-deck and pipeline-api.**
|
|
Evidence: both units are `active (running)` and both configure 127.0.0.1:8200. `ss -tlnp` shows 8200 held by pipeline-api (PID 3787644). hermes-control-deck Main PID (1962662) is not the socket owner.
|
|
Rationale: One of the two services is silently shadowed (the Control Deck API). Traffic routed by Caddy reaches whichever holds the port; the other is effectively down while appearing healthy.
|
|
|
|
**F-7. Three runaway `fix_dict.py` processes pegging 3 cores for 14+ days on app1.**
|
|
Evidence: PIDs 3656093/3657668/3658103 at 99.4% CPU, elapsed 14d 17h, cwd /root/giftaroast. Parent is an aborted `bash -c` deployment (truncated command visible in /proc).
|
|
Rationale: 25% of app1 CPU permanently wasted; indicates a bug in fix_dict.py and an interrupted deployment that was never cleaned up. Load average 3.26 on 12 cores.
|
|
|
|
**F-8. app1 Caddyfile (ingress for every app1 service) is not backed up.**
|
|
Evidence: grep of /root/backup.sh for Caddyfile returns nothing; no app1 script references /etc/caddy. Only Core's Caddyfile is backed up (by Core scripts).
|
|
Rationale: app1's entire reverse-proxy routing config (every site, TLS policy, upstream mapping) would need to be reconstructed by hand after a host failure. High blast radius, zero coverage.
|
|
|
|
### MEDIUM
|
|
|
|
**F-9. Ad-hoc `python -m http.server` running as root on public interfaces, one serving /tmp.**
|
|
Evidence: `python3 -m http.server 8080` (cwd /var/www/mockup/anita-consulting), `... 8934` (/var/www/mockup/itpropartner), `... 9876` (/tmp), all bound 0.0.0.0, none under systemd.
|
|
Rationale: Undocumented, unsupervised web servers run as root; serving /tmp is a direct path to accidental data exposure. These are "shadow IT" that bypasses the standard service lifecycle.
|
|
|
|
**F-10. Grafana exposed to Anywhere by firewall rule.**
|
|
Evidence: `ufw status` shows `3002/tcp ALLOW IN Anywhere` (Grafana). Prometheus (9090) and node_exporter (9100) also bind 0.0.0.0.
|
|
Rationale: Grafana (an old 11.4.0 with auth) and raw metrics endpoints are reachable from the public internet, not just the Tailscale/management network.
|
|
|
|
**F-11. World-readable credential files.**
|
|
Evidence: /root/projects/auth/.env = 0644, /etc/caddy/dre-passwd = 0644 (basic-auth password), /root/anita-key.json = 0644 (LiteLLM key alias), on app1.
|
|
Rationale: Live credentials readable by any local account, compounding F-3 (everything is root anyway, but defense-in-depth is absent).
|
|
|
|
**F-12. mysql-tunnel disables host key verification.**
|
|
Evidence: unit uses `-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null` to wphost02.
|
|
Rationale: The MySQL tunnel to wphost02 is susceptible to MITM/host-spoofing; a changed host would be silently accepted.
|
|
|
|
**F-13. Dead cron job: docker-volume-sync.sh does not exist.**
|
|
Evidence: crontab references `/root/.hermes/scripts/docker-volume-sync.sh` at 03:00 daily; the file does not exist (`ls` fails). Docker volume sync was reportedly moved to hermes-docker-sync.sh (different bucket), but the old crontab entry was never removed.
|
|
Rationale: A scheduled job that can never succeed, silently. Indicates cron hygiene drift.
|
|
|
|
**F-14. Pending security updates on both hosts (including kernel).**
|
|
Evidence: Core has 14 upgradable packages including linux-image-amd64 6.12.94 -> 6.12.101 (security) and chromium security update; app1 has 14 including docker-ce 29.6.1 -> 29.7.2.
|
|
Rationale: Kernel security updates pending on both hosts. Docker engine on app1 is a minor version behind. (Both run unattended-upgrades, but security packages remain pending.)
|
|
|
|
**F-15. No swap on either host.**
|
|
Evidence: `free -h` shows Swap 0 on both. Core sits at ~12 GB/15 GB used with 467 MB free.
|
|
Rationale: Under memory pressure the OOM killer will terminate arbitrary services (likely the biggest consumer: the 3.7 GB Hermes gateway). No graceful pressure relief.
|
|
|
|
**F-16. Dead reverse-proxy route: noc.itpropartner.com.**
|
|
Evidence: Caddyfile maps noc -> 127.0.0.1:8065, but no mattermost container is running on app1; mattermost-backup.sh runs daily as a no-op.
|
|
Rationale: A configured, monitored-adjacent route points at a service that is not running. Drift between routing config and reality.
|
|
|
|
**F-17. Super Search (designated critical) has no meaningful backup.**
|
|
Evidence: only a 373-byte .env snippet is uploaded; the code/config/venv under /root/docker/super-search (Core) and app1 is not covered by any backup script.
|
|
Rationale: Rebuilding the search MCP requires re-cloning and re-provisioning keys by hand; RPO effectively zero for its configuration.
|
|
|
|
### LOW
|
|
|
|
**F-18. /root/.aws directory is group/world writable on app1 (drwxrwxr-x).**
|
|
Evidence: `ls -ld /root/.aws` = 775. Credentials file itself is 600, but the directory is too open for a secrets dir.
|
|
|
|
**F-19. Leftover Docker volumes / image drift.**
|
|
Evidence: three Grafana volumes (grafana_data, grafana_data_final, grafana_data_v3) indicate config churn; multiple stale images (vaultwarden 1.33.2, litellm v1.84.0, ollama, mattermost) not running but present. Nine accumulated Caddyfile backup files in /etc/caddy.
|
|
|
|
**F-20. Single points of failure (no HA anywhere).**
|
|
Evidence: one Caddy per host, one Postgres (no replica), one Redis (no replica), one Hermes gateway, one LiteLLM + one litellm_postgres, one Wazuh single-node.
|
|
Rationale: No redundancy for any Tier-0 component. Acceptable for the environment size but must be documented and covered by DR runbooks.
|
|
|
|
---
|
|
|
|
## 8. RTO / RPO for Critical Services
|
|
|
|
| Service | RPO (est.) | RTO (est.) | Basis |
|
|
|---|---|---|---|
|
|
| Hermes gateway | ~24 h (daily full backup; 15-min live-sync status uncertain) | 2-4 h | restore.sh + DR-PLAN.md exist; warm standby at app1-bu (5.161.225.131) referenced but out of scope/unverified |
|
|
| LiteLLM / admin-ai | **config 24 h; database 0 (not backed up)** | hours to days | F-1: DB must be reconstructed; keys re-added manually |
|
|
| Caddy ingress (Core) | 24 h (Caddyfile) | 30 min - 1 h | reinstall caddy + restore Caddyfile |
|
|
| Caddy ingress (app1) | **none (unbacked)** | 1-4 h | F-8: full manual reconstruction of routing |
|
|
| Grafana / Prometheus | 24 h | 1-2 h | volume restore from core-services-backup |
|
|
| Super Search | **none (code/config unbacked)** | 1-3 h | re-clone + re-provision .env keys |
|
|
|
|
---
|
|
|
|
## 9. Top 5 Highest-Severity Findings (for parent summary)
|
|
|
|
1. **F-1 (Critical):** LiteLLM Postgres DB never backed up; dump targets wrong database name, so the AI gateway's keys/routing/spend are unprotected.
|
|
2. **F-2 (Critical):** Plaintext API keys (DeepSeek, admin-ai, Twilio) hardcoded in world-readable systemd unit files (rally, seemytrip, giftaroast).
|
|
3. **F-3 (High):** Every custom service runs as root, so any single compromise is full-host takeover.
|
|
4. **F-4 (High):** browserless/chrome (2 years old), grafana 11.4.0 (20 months), wazuh 4.9.2 (21 months) in active internet-facing service.
|
|
5. **F-5 (High):** Hermes gateway supervised by user manager only, with inconsistent unit wiring (socat references nonexistent system unit); fragile SPOF.
|
|
|
|
*End of Sys-A Phase One findings. No remediation performed. All items deferred to Phase Two.*
|