Follow-up from a review thread on #71: #71 (comment)
The gap
honmoon run on Linux spawns the child into an empty network namespace, and close_inherited_descriptors() closes every inherited descriptor so a leaked non-CLOEXEC socket cannot carry traffic out of it. That sweep deliberately starts at fd 3, because the child needs a stdin, stdout and stderr to be a usable command at all.
So if honmoon itself is launched with a connected network socket as one of those three, the child inherits it. A socket keeps its binding across unshare, so it stays a live channel to that peer — a real exception to the "reaches nothing over the network" claim.
Why #71 did not change behavior
The two obvious remedies are both user-visible behavior changes that a review-apply pass should not decide unilaterally:
- Refuse to run when stdio is socket-valued — breaks a legitimate
honmoon run whose output the operator deliberately wired to a socket.
- Replace socket stdio with
/dev/null — silently discards the child's output, which is arguably worse than the hole.
It is also not something the child can arrange for itself: it requires an operator to have already connected honmoon's stdio to a network peer, and the reachable surface is that one peer. That puts it in the same class as the filesystem-Unix-socket caveat (/var/run/docker.sock) the README already documents.
#71 therefore documents it in both places rather than changing behavior:
crates/honmoon-cli/src/isolate/linux.rs module doc
README.md, in the privilege / docker.sock paragraph
The decision to make
- Keep documenting only. Consistent with how the
docker.sock caveat is handled today.
- Detect and warn.
fstat stdio and, when S_ISSOCK, print a prominent stderr warning naming the bypass but still run. Consistent with how Isolation::Advisory already surfaces a weaker-than-expected posture rather than hard-failing.
- Detect and refuse. Fail closed, consistent with the README's position that a client which cannot use the proxy failing closed "is the correct default for a firewall". Would need an escape-hatch flag.
Option 2 looks like the best fit for this project's existing habit of stating weaknesses plainly rather than silently tolerating or hard-failing on them, but this is an author call.
Notes
- Detection is cheap:
fstat(fd) and test S_ISSOCK(st_mode), or getsockopt(fd, SOL_SOCKET, SO_TYPE, ...) succeeding.
- A pipe to a socket is not affected — only a socket handed directly as fd 0, 1 or 2.
- Related: TD-003 in
.please/docs/tracks/tech-debt-tracker.md.
Follow-up from a review thread on #71: #71 (comment)
The gap
honmoon runon Linux spawns the child into an empty network namespace, andclose_inherited_descriptors()closes every inherited descriptor so a leaked non-CLOEXEC socket cannot carry traffic out of it. That sweep deliberately starts at fd 3, because the child needs a stdin, stdout and stderr to be a usable command at all.So if
honmoonitself is launched with a connected network socket as one of those three, the child inherits it. A socket keeps its binding acrossunshare, so it stays a live channel to that peer — a real exception to the "reaches nothing over the network" claim.Why #71 did not change behavior
The two obvious remedies are both user-visible behavior changes that a review-apply pass should not decide unilaterally:
honmoon runwhose output the operator deliberately wired to a socket./dev/null— silently discards the child's output, which is arguably worse than the hole.It is also not something the child can arrange for itself: it requires an operator to have already connected honmoon's stdio to a network peer, and the reachable surface is that one peer. That puts it in the same class as the filesystem-Unix-socket caveat (
/var/run/docker.sock) the README already documents.#71 therefore documents it in both places rather than changing behavior:
crates/honmoon-cli/src/isolate/linux.rsmodule docREADME.md, in the privilege /docker.sockparagraphThe decision to make
docker.sockcaveat is handled today.fstatstdio and, whenS_ISSOCK, print a prominent stderr warning naming the bypass but still run. Consistent with howIsolation::Advisoryalready surfaces a weaker-than-expected posture rather than hard-failing.Option 2 looks like the best fit for this project's existing habit of stating weaknesses plainly rather than silently tolerating or hard-failing on them, but this is an author call.
Notes
fstat(fd)and testS_ISSOCK(st_mode), orgetsockopt(fd, SOL_SOCKET, SO_TYPE, ...)succeeding..please/docs/tracks/tech-debt-tracker.md.