From d60c81ebda5861cdd2ddb7c9fb1bb2f7b23774a7 Mon Sep 17 00:00:00 2001 From: Hermes VM Date: Wed, 27 May 2026 21:25:38 +0000 Subject: [PATCH] docs: record internal port loopback hardening --- docs/vm-exposure-inventory.md | 10 +++++----- docs/vm-security-blind-spots-roadmap.md | 25 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/docs/vm-exposure-inventory.md b/docs/vm-exposure-inventory.md index e403f0b..61cba55 100644 --- a/docs/vm-exposure-inventory.md +++ b/docs/vm-exposure-inventory.md @@ -69,7 +69,7 @@ These listeners were bound on `0.0.0.0` and/or `[::]` during review. | `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 | | `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 | `private-admin` | Remove public host bind; keep Docker/internal or Tailscale only | +| `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 | @@ -83,10 +83,10 @@ These listeners were bound on `0.0.0.0` and/or `[::]` during review. | `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 | | `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 | `/root/bytelyst.ai/repos/learning_ai_common_plat/docker-compose.yml` | none found in Caddy | `private-admin` / `docker-internal` | Remove public host bind | -| `8025` | `mailpit` UI | `/root/bytelyst.ai/repos/learning_ai_common_plat/docker-compose.yml` | none found in Caddy | `private-admin` | Bind loopback/Tailscale or remove | -| `10000` | `azurite` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `docker-internal` | Remove public host bind | -| `1234`, `8081` | `cosmos-emulator` | `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` | none found in Caddy | `docker-internal` | Remove public host bind | +| `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 | +| `10000` | `azurite` | `/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 | +| `1234`, `8081` | `cosmos-emulator` | `/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 | | `11434` | `ollama` host process | host service | `ollama.bytelyst.com` | `private-admin` | Bind loopback/private or auth-gate; do not leave raw public | ## Non-Public / Internal Listeners diff --git a/docs/vm-security-blind-spots-roadmap.md b/docs/vm-security-blind-spots-roadmap.md index 34c03a5..3ce9cae 100644 --- a/docs/vm-security-blind-spots-roadmap.md +++ b/docs/vm-security-blind-spots-roadmap.md @@ -181,6 +181,7 @@ Effective `sshd -T` settings showed: - [ ] Create a canonical exposure inventory: service, container, host port, public hostname, required audience, auth requirement. - [ ] 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. - [ ] 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. @@ -389,6 +390,7 @@ Effective `sshd -T` settings showed: ### Phase 1 — Immediate security hardening - [ ] Close or loopback-bind non-public Docker host ports. + - [x] Loopback-bound internal emulator/mail/observability ports `1025`, `8025`, `10000`, `1234`, `8081`, and `3100`. - [ ] 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. @@ -540,6 +542,29 @@ Minimum post-checks for Phase 1: - Local Gitea mirror push for `learning_ai_common_plat` failed at Git HTTP transport even though fetch and health checks work; retry/fix mirror push separately. - This fixed health state, not public exposure. Several direct published ports remain to be loopback-bound or blocked in Phase 1. +### 2026-05-27 — Phase 1 internal port loopback + +**Changed:** + +- Updated `/opt/bytelyst/learning_ai_common_plat/docker-compose.ecosystem.yml` so `cosmos-emulator`, `azurite`, `mailpit`, and `loki` publish host ports only on `127.0.0.1`. +- Recreated only `cosmos-emulator`, `azurite`, `mailpit`, and `loki` with `docker compose ... up -d --no-build`. + +**Verified:** + +- `docker compose -f docker-compose.ecosystem.yml --env-file .env.ecosystem config --quiet` passed. +- Docker reports the target services healthy. +- `ss -ltnp` shows `1025`, `8025`, `10000`, `1234`, `8081`, and `3100` listening on `127.0.0.1` only, with no `0.0.0.0` or IPv6 wildcard bind for that group. +- Local smoke checks returned HTTP `200` for Mailpit UI, Loki readiness, and Cosmos explorer. Azurite returned HTTP `400` on the raw blob endpoint while its container healthcheck remained healthy, which is expected for an unauthenticated root request. + +**Committed/pushed:** + +- `learning_ai_common_plat`: `1c09e479` (`fix: bind internal infra ports to loopback`) pushed to GitHub. + +**Residual risk:** + +- 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. + ## Do Not Start With - Rootless Docker migration.