Skip to content

--allow-host alone fails on Linux: DNS unreachable from guest #94

@nemtsov

Description

@nemtsov

Hi — I've been running matchlock 0.2.8 on Arch and ran into something that looks like a bug around --allow-host on Linux. Writing it up in case it's useful; the diagnosis is tentative (I'm not an expert; exploring this with Claude Opus 4.6).

What I observed

Clean clone, git checkout v0.2.8 (f3f5bb8), go build ./cmd/matchlock ./cmd/guest-init. Arch Linux x86_64, Firecracker backend, kernel 6.16.7-arch1-1.

Manual repro:

$ matchlock run --image alpine:latest --allow-host httpbin.org -- \
    sh -c 'nslookup httpbin.org; wget -q -O - http://httpbin.org/get'
;; connection timed out; no servers could be reached
wget: bad address 'httpbin.org'

Inside the guest, /etc/resolv.conf points at 8.8.8.8/8.8.4.4, TCP/443 to raw IPs succeeds, UDP/53 times out, and /etc/hosts has no entry for the allowlisted name.

Running the affected acceptance tests against the same clean build:

MATCHLOCK_BIN=$PWD/bin/matchlock go test -tags acceptance -v ./tests/acceptance/ \
  -run 'TestAllowlistPermitsHTTP$|TestAllowlistPermitsHTTPS|TestAllowlistGlobPattern|TestAllowlistMultipleHosts|TestPassthroughAllowsPermittedHost|TestCustomDNSServersStillResolveDomains|TestSDKAddHostEtcHosts|TestDNSResolutionWorksWithInterception'

--- FAIL: TestAllowlistPermitsHTTP
--- FAIL: TestAllowlistPermitsHTTPS
--- FAIL: TestAllowlistGlobPattern
--- FAIL: TestAllowlistMultipleHosts
--- FAIL: TestPassthroughAllowsPermittedHost
--- FAIL: TestCustomDNSServersStillResolveDomains
--- PASS: TestSDKAddHostEtcHosts
--- PASS: TestDNSResolutionWorksWithInterception

All six failures share the shape "wget: bad address 'httpbin.org'" does not contain "\"url\"".

TestDNSResolutionWorksWithInterception passes despite the manual nslookup inside the guest printing ;; connection timed out; no servers could be reached. The assertion at tests/acceptance/network_test.go:445 checks that the nslookup output contains neither SERVFAIL nor can't resolve — neither substring matches the "connection timed out" line, so the assertion passes.

The test-linux job in .github/workflows/ci.yml runs unit tests only; the acceptance suite runs on the acceptance-linux self-hosted runner. I haven't reproduced there and don't know why these tests are green on it.

Workaround for manual use: pair --allow-host with --add-host <name>:192.0.2.1.

Possible root cause

Reading the source, a shape that seems consistent with what I saw:

  • The Linux allowlist enforcement installs nftables DNAT on ports 80/443 (pkg/net/nftables.go); non-80/443 traffic appears to be routed to the pass-through deny added in Stricter networking #11, which would include UDP/53.
  • guest-init doesn't seem to be told about allowlisted names, so /etc/hosts isn't populated from the allow-list.
  • That would leave the guest with no path to turn a hostname into an IP, matching the bad address symptom.
  • There's a gVisor UDP forwarder at pkg/net/stack_darwin.go:407 with a handleDNS path at :419; I didn't test on macOS, so I don't know whether the same failure reproduces there.

This is just a hypothesis from exploring the code with Claude.

Possible fix direction (high level)

(A) Populate /etc/hosts from the allow-list: synthesize a placeholder HostIPMapping for each concrete allow-host entry and reuse the existing AddHosts → guest-init plumbing.

(B) Alternative: host-side DNS forwarder mirroring handleDNS in stack_darwin.go, wired via nftables. I haven't explored this.

Happy to open a PR for (A). This approach worked for me in https://github.com/nemtsov/matchlock/tree/fix/allow-host-dns-resolution — ~54 lines in one file, unit tests pass, and the manual repro command above works end-to-end after it. All tests except TestAllowlistGlobPattern (which is a limitation of this approach) pass on that branch.

Environment

matchlock 0.2.8 (f3f5bb8) / Arch Linux x86_64 / kernel 6.16.7-arch1-1 / Firecracker v1.10.1.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions