Skip to content

Refuse the private addresses written the other way - #27

Open
beardthelion wants to merge 1 commit into
CopilotKit:mainfrom
beardthelion:fix/navigation-floor-ipv6
Open

Refuse the private addresses written the other way#27
beardthelion wants to merge 1 commit into
CopilotKit:mainfrom
beardthelion:fix/navigation-floor-ipv6

Conversation

@beardthelion

Copy link
Copy Markdown

Closes #25.

The navigation floor compared exact hostnames and dotted-quad IPv4, so every IPv6 spelling of the same destination went past it, cloud metadata included. docs/architecture.md:160 says metadata is refused under every configuration; before this it was reachable as http://[::ffff:169.254.169.254]/ whatever the configuration said.

The same function backs checkAgentEndpoint, where a registered agent is a URL this server POSTs to on every run, so the hole was reachable from agent registration as well as from the browser.

What it does

Reduce the hostname to one form before comparing anything:

  • Drop the root dot, so metadata.google.internal. is the name it resolves to.
  • Unwrap the IPv4 an IPv6 address carries in its low 32 bits, under each of the three prefixes that actually reach it: ::ffff:0:0/96 for a dual-stack socket, 64:ff9b::/96 for NAT64 (translated by the gateway on an IPv6-only network), and the deprecated ::/96.
  • Classify IPv6 loopback, fe80::/10 and fc00::/7 the way RFC1918 is already classified, so they sit behind the same private-host opt-in as their IPv4 equivalents.
  • Add AWS's fd00:ec2::254 to the never-allowed set, alongside the quad-form metadata address.

:: and ::1 keep their existing handling rather than being read as embedded IPv4, since their low bits are 0.0.0.0 and 0.0.0.1, which are not addresses anybody routes to. Anything in 0.0.0.0/8 is left alone for the same reason.

The bracketed "[::1]" entry comes out of INTERNAL_HOSTNAMES: canonicalization strips the brackets, so it can no longer be reached.

Verification

12 test cases added to server/tests/computer-target.test.ts, all failing before the change and passing after. They cover mapped metadata, AWS IPv6 metadata, the trailing root dot, NAT64, the IPv4-compatible form, mapped loopback and RFC1918, link-local and unique-local.

The other direction is covered too, because a floor that refuses too much is its own outage: public IPv6 ([2606:4700::1111]) and example.com. stay allowed, mapped loopback is allowed when the deployment opts in, and metadata stays refused under both settings.

Existing tests unchanged and passing. The server suite has the same 71 failures before and after this branch, all of them integration tests wanting a Postgres this machine does not have. bun run typecheck and bunx biome check are clean.

The navigation floor matched exact hostnames and dotted-quad IPv4, so every IPv6
spelling of the same destination went straight past it. A Bot talked into opening
http://[::ffff:169.254.169.254]/ reached the cloud metadata endpoint and
screenshotted the deployment's credentials back into the transcript, which is the
one thing the architecture doc promises cannot happen under any configuration.

The same hole covered mapped loopback and RFC1918, AWS's IPv6 metadata address,
link-local and unique-local IPv6, and a trailing root dot on any of the refused
names.

Reduce the hostname to one form before comparing anything: drop the root dot, and
unwrap the IPv4 an IPv6 address carries in its low 32 bits under any of the three
prefixes that reach it, the dual-stack ::ffff:0:0/96, the NAT64 well-known
64:ff9b::/96, and the deprecated compatible ::/96. Then classify IPv6 the way
RFC1918 is already classified, so loopback, link-local and unique-local sit behind
the same opt-in as their IPv4 equivalents while public IPv6 stays reachable.

:: and ::1 keep their own handling: their low bits are 0.0.0.0 and 0.0.0.1, which
are not addresses anybody routes to, so 0.0.0.0/8 is left alone rather than read as
an embedded address.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Navigation floor misses every IPv6 spelling of a private address, including cloud metadata

1 participant