verify kai-server:30122 is not publicly reachable - Dowel's turn endpoint refuses nobody #866
Labels
No labels
burndown-2026-06
burndown-2026-08
autonomy
async-consult
autonomy
epic
autonomy
headless
autonomy
live-collab
coherence-core
priority
P0
priority
P1
priority
P2
priority
P3
priority
P4
qa-fixture
role/advocate
role/director
role/exec
role/frontend
role/gamedev
role/human
role/platform
role/qa
role/science
role/sysadmin
state
ambient
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
coilyco-flight-deck/infrastructure#866
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Verify whether
kai-server:30122is reachable from outside the LAN, and from the router rather than from a doc.What is on that port
The
sirens-dowellane publishes NodePort 30122 on kai-server (services/sirens-echo/deploy/sirens-dowel-values.yaml,nodeSelector: kubernetes.io/hostname: kai-server). The pod bindsSIRENS_ECHO_HTTP_ADDR: 0.0.0.0:8080, moved off the process default of127.0.0.1:8080by the deployment.That one port serves both
/mcp, which exposes the lane as a singleturntool, andPOST /v1/turn. Both run a full agent turn on the same path Discord uses.The lane runs
tailnet.enabled: false, so there is no Tailscale sidecar and no MagicDNS name. The NodePort is its only inbound path, and Tailscale ACLs are not in front of it.The endpoint authenticates nobody
SIRENS_ECHO_HTTP_TOKENreads as an auth control and is not one.internal/community/httpidentity.go:22-callerTrusteddoes a correct constant-time compare of theX-Sirens-Truststyle header against the configured token.internal/community/http.go:227- the result is assigned, written to the span attributehttp.caller.trusted, and set on the turn struct. That span attribute is the only consumer in the tree. There is no401, no403, no early return.So an anonymous caller and a token-bearing caller both get their turn run. The only difference is how the trace is labelled. The SSM description says as much: "records who called rather than refusing anyone."
What does gate is shared admission and rate limiting keyed on
X-Sirens-Caller, plus a4xxcontract for malformed requests. Neither is an authorization control.Why this needs checking rather than assuming
sirens-echo/docs/sirens-echo-http.mdstates the boundary as network-only: "the NodePort is LAN and tailnet only, the home router forwards nothing to it."infrastructure#103 found the opposite for port 22 and closed with "this was b/c of DMZ config." A DMZ host takes every inbound port, not port 22 alone. If kai-server is still in the router's DMZ, 30122 is reachable from the public internet and the doc's stated boundary does not hold. #103 closed with an explanation rather than a recorded remediation, so whether the DMZ was removed is not answerable from any repo.
ufwwas active on kai-server at the time of #103 with rules unverified. k3s installs its own iptables chains for NodePort traffic, which commonly bypass ufw's INPUT filtering, so an active ufw is not by itself an answer either.What to check
ufw/iptables actually drops 30122 from non-LAN, non-tailnet sources, tested rather than read.ss -lntpon kai-server for what else the DMZ answer would expose, since 30120 and 30121 carry the Echo and Deep lanes on the same footing.Impact if it is reachable
An unauthenticated caller runs turns as Sirens Dowel. That lane holds Discord write grants, so a turn can post into the owl.glass guild. It also spends inference per call, bounded only by the shared admission limiter.
Timing note: Dowel is staged for a public Temporal livestream on 2026-08-19, 11:00 to 11:50 PDT.
Not in scope here
Making the token refuse instead of annotate is a
sirens-echochange againstinternal/community/http.go:227plus a rebuild and rollout. Worth a separate issue there. This one is the network question only.Filed by Claude. Investigated read-only, nothing changed.
Resolved by deprovision. The endpoint no longer exists, so the exposure question is closed rather than answered.
Dowel and owl.glass were fully deprovisioned on 2026-08-25 (
coilyco-bridge/deploy#718). NodePort30122was that lane's turn endpoint.The allocated NodePorts on the cluster now run 30081-30121 with 30122 unassigned.
Worth being precise about what this does and does not settle
This issue asked whether
30122was reachable from the public internet, and I have not answered that. I established that nothing is listening on it at all, which makes the question moot for this port, but it is not the same as having tested the public path and found it closed.So the general concern behind the issue survives: NodePorts on kai-server are reachable on whatever interfaces the host exposes, and "Dowel's turn endpoint refuses nobody" was a statement about the service's own auth, not about the network. If any other NodePort in the 30081-30121 range serves something that authenticates weakly, the same finding applies to it and this closure does not cover it.
infrastructure#157(public-port allowlist must live in the repo and be audited externally) and#110(treat LAN as semi-hostile) are the issues that own that general form.Closing this one, since the specific port is gone.