diff --git a/docs/vm-exposure-inventory.md b/docs/vm-exposure-inventory.md index 61cba55..074d40a 100644 --- a/docs/vm-exposure-inventory.md +++ b/docs/vm-exposure-inventory.md @@ -57,31 +57,31 @@ These listeners were bound on `0.0.0.0` and/or `[::]` during review. | `22` | `sshd` | host systemd | direct SSH | `public-direct` | Keep public only after SSH key hardening | | `80`, `443` | `caddy` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | public ingress | `public-caddy` | Keep public | | `3000` | `notelett-web` | `/opt/bytelyst/learning_ai_notes/docker-compose.yml` | `notes.bytelyst.com` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke | -| `3002` | `lysnrai-dashboard` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `needs-decision` | Private/admin or retire direct exposure | -| `3003` | `tracker-web` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `tracker.bytelyst.com` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke | +| `3002` | `lysnrai-dashboard` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `loopback-only` | Bound to `127.0.0.1` on 2026-05-27; still needs public/private product decision | +| `3003` | `tracker-web` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `tracker.bytelyst.com` | `public-caddy` | Bound to `127.0.0.1` on 2026-05-27; keep Caddy route | | `3030` | `chronomind-web` | `/root/bytelyst.ai/repos/learning_ai_clock/docker-compose.yml` | `clock.bytelyst.com` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke | -| `3035` | `jarvisjr-web` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `needs-decision` | Unhealthy; classify as private/admin or retire | -| `3040` | `flowmonk-web` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `needs-decision` | Unhealthy; classify as private/admin or retire | +| `3035` | `jarvisjr-web` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `loopback-only` | Bound to `127.0.0.1` on 2026-05-27; still needs public/private product decision | +| `3040` | `flowmonk-web` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `loopback-only` | Bound to `127.0.0.1` on 2026-05-27; still needs public/private product decision | | `3049` | `devops-web` | `/opt/bytelyst/bytelyst-devops-tools/dashboard/docker-compose.yml` | `devops.bytelyst.com` | `private-admin` with direct bypass | Fix old repo path drift, then bind loopback/private | -| `3050` | `mindlyst-web` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `needs-decision` | Unhealthy; classify as private/admin or retire | +| `3050` | `mindlyst-web` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `loopback-only` | Bound to `127.0.0.1` on 2026-05-27; still needs public/private product decision | | `3055` | `nomgap-web` | orphan from older `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `retire` | Retired on 2026-05-27; current Compose says Nomgap web is deployed to Vercel | -| `3060` | `actiontrail-web` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `needs-decision` | Unhealthy; classify as private/admin or retire | -| `3070` | `localmemgpt-web` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `needs-decision` | Unhealthy; classify as private/admin or retire | -| `3075` | `llmlab-dashboard` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `llmlab.bytelyst.com` | `private-admin` with direct bypass | Dashboard unhealthy; gate or retire | +| `3060` | `actiontrail-web` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `loopback-only` | Bound to `127.0.0.1` on 2026-05-27; still needs public/private product decision | +| `3070` | `localmemgpt-web` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `loopback-only` | Bound to `127.0.0.1` on 2026-05-27; still needs public/private product decision | +| `3075` | `llmlab-dashboard` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `llmlab.bytelyst.com` | `private-admin` | Bound to `127.0.0.1` on 2026-05-27; still needs auth/private gate for Caddy route | | `3085` | `invttrdg-web` | `/opt/bytelyst/learning_ai_invt_trdg/docker-compose.yml` | `invttrdg.bytelyst.com` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke | | `3100` | `loki` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `loopback-only` | Bound to `127.0.0.1` on 2026-05-27 | | `3300` | `gitea-npm-registry` | non-Compose container labels absent | `gitea.bytelyst.com` | `public-caddy` with direct bypass | Bind loopback or private; keep Caddy route | | `4004` | `devops-backend` | `/opt/bytelyst/learning_ai_devops_tools/dashboard/docker-compose.yml` | `api.bytelyst.com/devops/*` | `private-admin` with direct bypass | Bind loopback/private | -| `4010` | `peakpulse-backend` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `api.bytelyst.com/peakpulse/*` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke | +| `4010` | `peakpulse-backend` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `api.bytelyst.com/peakpulse/*` | `public-caddy` | Host port removed by Compose recreate on 2026-05-27; keep Caddy route | | `4011` | `chronomind-backend` | `/root/bytelyst.ai/repos/learning_ai_clock/docker-compose.yml` | `api.bytelyst.com/chronomind/*` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke | -| `4012` | `jarvisjr-backend` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `api.bytelyst.com/jarvisjr/*` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke | -| `4013` | `nomgap-backend` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `api.bytelyst.com/nomgap/*` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke | -| `4014` | `mindlyst-backend` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `api.bytelyst.com/mindlyst/*` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke | -| `4015` | `lysnrai-backend` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `api.bytelyst.com/lysnrai/*` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke | +| `4012` | `jarvisjr-backend` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `api.bytelyst.com/jarvisjr/*` | `public-caddy` | Host port removed by Compose recreate on 2026-05-27; keep Caddy route | +| `4013` | `nomgap-backend` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `api.bytelyst.com/nomgap/*` | `public-caddy` | Host port removed by Compose recreate on 2026-05-27; keep Caddy route | +| `4014` | `mindlyst-backend` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `api.bytelyst.com/mindlyst/*` | `public-caddy` | Host port removed by Compose recreate on 2026-05-27; keep Caddy route | +| `4015` | `lysnrai-backend` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `api.bytelyst.com/lysnrai/*` | `public-caddy` | Host port removed by Compose recreate on 2026-05-27; keep Caddy route | | `4016` | `notelett-backend` | `/opt/bytelyst/learning_ai_notes/docker-compose.yml` | `api.bytelyst.com/notelett/*` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke | -| `4017` | `flowmonk-backend` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `api.bytelyst.com/flowmonk/*` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke | -| `4019` | `localmemgpt-backend` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `api.bytelyst.com/localmemgpt/*` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke | -| `4020` | `actiontrail-backend` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `api.bytelyst.com/actiontrail/*` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke | +| `4017` | `flowmonk-backend` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `api.bytelyst.com/flowmonk/*` | `public-caddy` | Host port removed by Compose recreate on 2026-05-27; keep Caddy route | +| `4019` | `localmemgpt-backend` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `api.bytelyst.com/localmemgpt/*` | `public-caddy` | Host port removed by Compose recreate on 2026-05-27; keep Caddy route | +| `4020` | `actiontrail-backend` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | `api.bytelyst.com/actiontrail/*` | `public-caddy` | Bound to `127.0.0.1` on 2026-05-27; route mapping still needs Caddy/product verification | | `4025` | `invttrdg-backend` | `/opt/bytelyst/learning_ai_invt_trdg/docker-compose.yml` | `api.bytelyst.com/invttrdg/*` | `public-caddy` with direct bypass | Bind loopback or remove host port after Caddy smoke | | `1025` | `mailpit` SMTP | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `loopback-only` | Bound to `127.0.0.1` on 2026-05-27 | | `8025` | `mailpit` UI | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `loopback-only` | Bound to `127.0.0.1` on 2026-05-27 | diff --git a/docs/vm-security-blind-spots-roadmap.md b/docs/vm-security-blind-spots-roadmap.md index 3ce9cae..5d95efb 100644 --- a/docs/vm-security-blind-spots-roadmap.md +++ b/docs/vm-security-blind-spots-roadmap.md @@ -182,6 +182,7 @@ Effective `sshd -T` settings showed: - [ ] For each service, decide one of: public via Caddy, private via Tailscale/SSH, loopback-only host port, Docker-internal only, or remove. - [ ] Bind non-public Compose ports to `127.0.0.1` or remove host port mapping entirely. - [x] Internal emulator/mail/observability ports `1025`, `8025`, `10000`, `1234`, `8081`, and `3100` are loopback-bound. + - [x] Common-platform direct app/API bypasses are loopback-bound or removed from host publishing. - [ ] Add a `DOCKER-USER` chain policy to drop unsolicited traffic to non-approved published ports before Docker's accept rules. - [ ] Keep only `80/443` and intentionally public SSH exposed at the provider/firewall layer. - [ ] Add a recurring check that compares `ss -ltn` and Docker published ports against the approved inventory. @@ -391,6 +392,7 @@ Effective `sshd -T` settings showed: - [ ] Close or loopback-bind non-public Docker host ports. - [x] Loopback-bound internal emulator/mail/observability ports `1025`, `8025`, `10000`, `1234`, `8081`, and `3100`. + - [x] Closed/loopback-bound common-platform direct app/API bypasses. - [ ] Add `DOCKER-USER` default-deny rules for non-approved ports. - [ ] Harden SSH root/password access after key-based access is verified. - [ ] Put `ollama.bytelyst.com`, admin dashboards, and dev tooling behind private/auth-gated access unless explicitly approved as public. @@ -565,6 +567,36 @@ Minimum post-checks for Phase 1: - Public direct bypass remains for app/API ports, Gitea direct port `3300`, devops/admin surfaces, and Ollama `11434`. - Add a `DOCKER-USER` fallback policy after the remaining allowlist is reviewed. +### 2026-05-27 — Phase 1 common-platform app/API bypasses + +**Changed:** + +- Updated `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` so remaining published common-platform web/dashboard ports bind to `127.0.0.1`. +- Recreated the common-platform web/dashboard services that previously published on `0.0.0.0`: `tracker-web`, `lysnrai-dashboard`, `jarvisjr-web`, `flowmonk-web`, `mindlyst-web`, `actiontrail-web`, `localmemgpt-web`, and `llmlab-dashboard`. +- Recreated stale common-platform backend containers `peakpulse-backend`, `lysnrai-backend`, and `nomgap-backend`; their current Compose definitions do not publish host ports, so the old direct `4010`, `4015`, and `4013` mappings were removed. + +**Verified:** + +- `docker compose -f docker-compose.ecosystem.yml --env-file .env.ecosystem config --quiet` passed. +- `docker ps --filter name=learning_ai_common_plat ... | grep 0.0.0.0` returned no common-platform wildcard-published containers. +- `docker ps --filter health=unhealthy` returned no unhealthy containers. +- `ss -ltnp` shows `3002`, `3003`, `3035`, `3040`, `3050`, `3060`, `3070`, and `3075` bound to `127.0.0.1`. +- Host smoke checks returned HTTP `200` for `3002`, `3003`, `3035`, `3040`, `3050`, `3060`, `3070`, and `3075`. + +**Committed/pushed:** + +- `learning_ai_common_plat`: `e29cc58a` (`fix: bind app host ports to loopback`) pushed to GitHub. + +**Remaining wildcard Docker publishes after this checkpoint:** + +- Caddy public ingress: `80`, `443`. +- Local Gitea direct port: `3300`. +- DevOps dashboard/API: `3049`, `4004`. +- Notes direct ports: `3000`, `4016`. +- Clock direct ports: `3030`, `4011`. +- InvtTrdg direct ports: `3085`, `4025`. +- Host Ollama still listens on wildcard `11434`. + ## Do Not Start With - Rootless Docker migration.