mirror of
https://github.com/jmcorgan/fips.git
synced 2026-07-30 19:46:15 +00:00
fipstop: add "Listening on fips0" panel to Node tab
Surfaces local services reachable from the mesh, paired with their
current `inet fips` baseline filter classification. Lands to the
right of the existing TUN section in the Traffic block.
A new daemon control query `show_listening_sockets` returns IPv6
listeners bound to either `::` (wildcard) or the node's fd00::/8
address, each classified as Accept / Drop / Unknown / NoFirewall
against the running inbound chain. fipstop renders the result as a
table beside the Traffic counters: Accept rows in default White,
Drop / Unknown in DarkGray, a yellow banner above the table when
`fips-firewall.service` is inactive, and a trailing `*` on
wildcard binds to remind the operator the bind is not
fips0-specific.
Daemon side:
- `src/control/listening.rs` walks `/proc/net/tcp6` and
`/proc/net/udp6` via the procfs crate (LISTEN state for TCP,
wildcard remote for UDP), filters to fips0-reachable binds, and
resolves inodes to PID / comm via `/proc/<pid>/fd`.
- `src/control/firewall_state.rs` shells out to
`nft -j list table inet fips` and walks the inbound chain.
Recognises canonical accepts (`tcp/udp dport N accept`,
`dport { ... } accept`, `dport A-B accept`), the iifname-scoping
line, conntrack and icmpv6 lines (skipped). Any rule with
unrecognised matchers (saddr filters, jumps, daddr filters) or
non-terminal verdicts forces Unknown classification for the
ports it references. Eleven unit tests cover the classification
logic; the listening enumerator carries a /proc-parsing test of
its own.
- `show_listening_sockets` emits
`{fips0_addr, firewall_active, sockets[]}` with per-row
`{proto, local_addr, port, pid, process, filter, wildcard_bind}`.
fipstop side:
- `src/bin/fipstop/ui/dashboard.rs` splits the Traffic block into
a 50/50 horizontal layout; the existing TUN + Forwarded panel
occupies the left half.
- `src/bin/fipstop/ui/listening.rs` renders the right half.
- `main.rs` fetches the new query each tick when the Node tab is
active. Errors are non-fatal: an old daemon without the query
leaves the payload at None and the panel renders "loading...".
`Cargo.toml` gains `procfs = "0.18"` on the Linux target. IPv4
listeners are not enumerated — fips0 is IPv6-only.
Folded in: revert the default-socket lookup from writability-probe
back to existence-based selection. The previous tempfile-probe on
`/run/fips` silently steered fipstop / fipsctl onto an XDG path
the daemon never bound for any user in the `fips` group whose
shell session had not yet picked up the supplementary group (no
re-login after `usermod -aG`). `XDG_RUNTIME_DIR` is set on every
modern systemd-managed user session, so this hit the common case.
The kernel checks actual group membership at `connect(2)`, so a
user who genuinely cannot connect now gets a clear `EACCES`
rather than a silent path mismatch. Drops the now-unused
`is_writable_dir` helper. `XDG_RUNTIME_DIR` existence validation
is preserved.
Documentation:
- `docs/reference/cli-fipstop.md` — Node-tab row updated, new
"Listening on fips0 panel" section.
- `docs/reference/control-socket.md` — `show_listening_sockets`
added to the read-only queries table.
- `docs/how-to/enable-mesh-firewall.md` — new "Verify with
fipstop" section.
- `docs/tutorials/host-a-service.md` — fipstop callouts at
Steps 3, 5, 6 + Troubleshooting bullet + wildcard-bind reminder
under "What you've learned".
- `CHANGELOG.md` — new bullet under `Added / Operator Tooling`,
resolver `Fixed` entry rewritten to describe the
existence-based final shape.
This commit is contained in:
+23
-5
@@ -187,6 +187,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
`fipsctl stats` subcommands, and a `fipstop` Graphs tab with
|
||||
btop-style sparklines
|
||||
([#64](https://github.com/jmcorgan/fips/pull/64)).
|
||||
- `fipstop` Node tab now carries a "Listening on fips0" panel
|
||||
(right-half of the Traffic block) that lists local IPv6 listening
|
||||
sockets reachable from the mesh interface, paired with the
|
||||
`inet fips` baseline filter classification for each (proto, port).
|
||||
Rows render in default White (`OPEN` — the chain has a canonical
|
||||
unrestricted accept rule), DarkGray (`filt` — chain falls through
|
||||
to `counter drop`), or DarkGray with a `?` State suffix (`filt?` —
|
||||
the chain references the port but with matchers the panel cannot
|
||||
fully decompose, e.g. saddr filters or jumps). When the
|
||||
`fips-firewall.service` is not active, the panel renders a yellow
|
||||
banner reminding the operator that all listeners are
|
||||
mesh-exposed. Wildcard binds (`local_addr == ::`) carry a `*`
|
||||
suffix in the Process column. Powered by a new
|
||||
`show_listening_sockets` control query (Linux-only).
|
||||
|
||||
#### Packaging and Deployment
|
||||
|
||||
@@ -321,11 +335,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
`fipstop` could connect to a socket the daemon never bound (notably
|
||||
on dev runs with `XDG_RUNTIME_DIR` set, or after a prior packaged
|
||||
install left a root-owned `/run/fips` behind). Canonical order is
|
||||
`/run/fips` → `$XDG_RUNTIME_DIR/fips/` → `/tmp/fips-<name>`, with
|
||||
writability of `/run/fips` probed via tempfile create (ACL- and
|
||||
group-aware) and `XDG_RUNTIME_DIR` validated as an existing
|
||||
directory before being used. The deployed fleet is unaffected:
|
||||
packaged configs set `node.control.socket_path` explicitly.
|
||||
`/run/fips` → `$XDG_RUNTIME_DIR/fips/` → `/tmp/fips-<name>`. The
|
||||
`/run/fips` arm is selected by directory existence; the kernel
|
||||
enforces actual access at `connect(2)` time, surfacing a clear
|
||||
`EACCES` for users not yet in the `fips` group rather than silently
|
||||
steering them to a path the daemon never bound. `XDG_RUNTIME_DIR` is
|
||||
validated as an existing directory before being used so stale
|
||||
post-logout values are treated as missing. The deployed fleet is
|
||||
unaffected: packaged configs set `node.control.socket_path`
|
||||
explicitly.
|
||||
- UDP transport with `advertise_on_nostr: true` + `public: true` +
|
||||
a wildcard `bind_addr` (e.g. `0.0.0.0:2121`) is now advertised
|
||||
with its STUN-discovered public IPv4 instead of being silently
|
||||
|
||||
Reference in New Issue
Block a user