fix(selfhost): open Grafana's port for --profile observability, opt-in - #6108
Conversation
terraform/main.tf's firewall opened 22, 80, 443/tcp, 443/udp and 8787, but nothing for 3000. docker-compose.yml's grafana service (--profile observability) publishes 3000:3000 on the host, so an operator following main.tf's own documented flow -- provision the VPS, then run docker compose --profile observability up -d -- ended up with Grafana bound to the public interface and no path through the cloud firewall to reach it, not even from their own IP. The failure mode is a timed-out connection with no explanation. Add the rule gated behind a new var.expose_grafana (bool, default false) rather than opening it unconditionally: the observability profile is itself opt-in, so a default-open port for a service most operators never start would widen the attack surface for nothing. When enabled the rule is scoped to var.admin_ip_allowlist, matching port 8787's existing pattern -- never 0.0.0.0/0 like the public Caddy ports. Left at the default, Grafana stays reachable over an SSH tunnel. The new terraform/README.md (this module had none, unlike the miner module) documents both paths, including the ssh -L command, alongside what gets provisioned and the deploy steps main.tf's header already described. The test locks the invariants a syntax check can't see -- the port is opt-in and can never be public -- and pins the rule to the compose service it exists for, mirroring test/unit/miner-terraform-module.test.ts. Closes JSONbored#5818
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
|
Tip 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩 ✅ LoopOver review result - approve/merge recommendedReview updated: 2026-07-15 09:51:25 UTC
✅ Suggested Action - Approve/Merge
Review summary Nits — 4 non-blocking
Linked issue satisfactionAddressed Review context
Contributor next steps
Signal definitions
🧪 Chat with LoopOverAsk LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://gittensory.aethereal.dev/docs/loopover-commands 🧪 Experimental — new and may change. 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #6108 +/- ##
=======================================
Coverage 95.24% 95.24%
=======================================
Files 595 595
Lines 47035 47046 +11
Branches 15015 15015
=======================================
+ Hits 44797 44808 +11
Misses 1493 1493
Partials 745 745
Flags with carried forward coverage won't be shown. Click here to find out more. |
Summary
Closes #5818
terraform/main.tf'shcloud_firewall.gittensoryopened exactly four things: TCP/22 (SSH), TCP/80 + TCP/443 + UDP/443 (Caddy), and TCP/8787 (admin_ip_allowlist-scoped). Verified against the file: no rule for port 3000.docker-compose.yml'sgrafanaservice —profiles: ["observability"],ports: ["3000:3000"]— publishes Grafana directly on the host. So an operator followingmain.tf's own documented flow (provision the VPS, thendocker compose --profile observability up -d, a combination the self-hosting docs list as supported) ends up with Grafana bound to the public interface and no path through the cloud firewall to reach it — not even from their own IP. The failure mode is a timed-out connection with no explanation.Fix — opt-in, never public. The rule is gated behind a new
var.expose_grafana(bool, defaultfalse) rather than opened unconditionally: the observability profile is itself opt-in, so a default-open port for a service most operators never start would widen the attack surface for nothing. When enabled, the rule is scoped tovar.admin_ip_allowlist, matching port 8787's existing pattern — never0.0.0.0/0/::/0like the public Caddy ports. This is the config-as-code resolution the issue's first requirement asks for, with the toggle its second requirement asks for.Left at the default, Grafana stays reachable over an SSH tunnel. The new
terraform/README.md(this module had none, unlikepackages/loopover-miner/terraform/) documents both access paths — including the runnablessh -L 3000:localhost:3000 …command — alongside what gets provisioned, the deploy stepsmain.tf's header already described, and the outputs. It mirrors the miner module README's shape.Scope
fix(selfhost): …).expose_grafanaat itsfalsedefault, the generated plan is byte-identical to today's — no existing rule, resource, or output is touched.CONTRIBUTING.md; nosite//CNAME/lovablechanges; no changelog edit.Validation
git diff --checkclean.terraformis not installed in this environment and — as the issue notes — the repo has noterraform validate/planCI step for the root module, so a syntax error here would ship silently. I parsed all three.tffiles withpython-hcl2:main.tf,variables.tf,outputs.tfall parse, and the new block parses to exactly the intended shape:dynamic "rule"→for_each = ${var.expose_grafana ? [1] : []}→content={direction "in", protocol "tcp", port "3000", source_ips ${var.admin_ip_allowlist}}.miner-terraform-module,self-host-ops-rename-residue— 16 tests passed..tfchanges and confirmed the three fix-dependent tests fail (rule missing, not allowlist-scoped, variable absent), then pass again with the fix restored.main.The test locks the invariants a syntax check cannot see, mirroring
test/unit/miner-terraform-module.test.ts:var.expose_grafana.admin_ip_allowlist-scoped and contains no0.0.0.0/0/::/0— it can never be silently made public.expose_grafanais abooldefaulting tofalse, with a description (matching this file's self-documenting style).3000:3000underprofiles: ["observability"]), so if that drifts, the rule guarding the wrong port fails CI.If any required check was skipped, explain why:
test:cinot run end-to-end locally (Linux-only steps on Windows).terraform/**(HCL + README) andtest/**, both outside Codecov'scoverage.include. Noting that explicitly per the issue's Test Coverage Requirements rather than omitting the section.Safety
hcloud_token/ssh_public_keyhandling is untouched.expose_grafana = false) opens nothing, so applying this changes no operator's firewall until they opt in. The port can only ever open toadmin_ip_allowlist, and the test enforces that it can never be widened to0.0.0.0/0without failing CI. The README warns to restrictadmin_ip_allowlistbefore enabling, since its own default is permissive.