Commit 8a4adee
authored
fix(agent): detect IPv6-bound listening ports (#27765)
Closes #15675
Dev servers that bind to the IPv6 wildcard address (Next.js, Node's
default `http.Server`, Go's `net.Listen` on wildcard addresses, and
others) never showed up in the dashboard Ports panel, even though they
were listening and reachable. The agent's port scanner only called
`netstat.TCPSocks`, which reads `/proc/net/tcp` (IPv4 only); an `[::]`
dual-stack socket lives exclusively in `/proc/net/tcp6`, so the panel
reported "No open ports were detected."
Also scan `netstat.TCP6Socks` and merge the results into the existing
dedupe loop. The IPv6 scan failure is non-fatal so hosts with IPv6
disabled (missing `/proc/net/tcp6`, Windows with the v6 stack off) keep
their IPv4 results instead of the Ports panel 500ing.
Both captures below: `python3 -m http.server 5123 --bind ::` running in
the same workspace, built from main vs this branch ([full
recordings](https://github.com/coder/coder/tree/recordings/recordings/ipv6-listening-ports)).
## Before

## After

<details>
<summary>Debugging and decision log</summary>
- Reproduced on a dogfood workspace: `next-server` listening on `*:3000`
per `ss -tlnp`, `curl` returning 200, but `GET
/api/v2/workspaceagents/{agent}/listening-ports` returning `{"ports":
[]}`. Port 3000 (hex `0BB8`) present in `/proc/net/tcp6`, absent from
`/proc/net/tcp`.
- Confirmed `agent/ports_supported.go` only calls `netstat.TCPSocks`;
the library's `TCP6Socks` (parses `/proc/net/tcp6` on Linux,
`GetTcp6Table2` on Windows) was never referenced.
- Control test: an IPv4-bound (`0.0.0.0`) listener was detected
correctly, isolating the bug to the missing IPv6 scan.
- Made the IPv6 scan failure non-fatal after review: returning an error
would break the entire ports endpoint (HTTP 500 on every poll) on
IPv6-disabled hosts, a regression from the current behavior of "IPv4
ports only." Silent fallback matches the spirit of
`ports_unsupported.go`, which returns an empty list rather than
erroring.
- The fix lives in the OS-backed `osListeningPortsGetter` behind the
`ListeningPortsGetter` interface from #20842, so the fake used by coderd
tests is unaffected.
- Dedupe by port in the existing `seen` map covers dual-stack sockets
that appear in both tables on Windows.
- `TestOSListeningPortsGetter_IPv6` listens on `[::]:0` and asserts
detection; it skips when the host can't bind IPv6. Verified it fails
against the pre-fix code.
</details>
> 🤖 This PR was opened by [Coder
Agents](https://coder.com/docs/ai-coder/agents) on behalf of @bpmct.1 parent 71c7e92 commit 8a4adee
2 files changed
Lines changed: 48 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
33 | | - | |
| 33 | + | |
34 | 34 | | |
35 | | - | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
36 | 38 | | |
37 | 39 | | |
38 | 40 | | |
39 | 41 | | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
40 | 56 | | |
41 | 57 | | |
42 | 58 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
0 commit comments