Repository navigation
Enforce delegated-prefix ownership and bidirectional traffic policy #979
Description
Parent: #970
Deliverable
Packet-path policy enforcement that confines a connector to its delegated /96 and authorized gVPC traffic in both directions.
Dependencies
Galactic Edge integration (#992)
Implement this milestone as a consumer of the Galactic Edge provider contract. #992 owns provider authorization, host attachment lifecycle, shared-state fencing and maintenance, independently enforced forwarding expiry, prefix conflict arbitration/publication, and gVPC traffic policy. MASQUE owns client authentication, connector /96 assignment and retention, session packet checks, transport behavior and its provider adapter. Use the foundation's versioned contract and existing provider helpers; do not introduce a second host-networking controller, lease store or BGP publisher. Any required foundation change must be tracked and qualified under #992 before this milestone claims integrated acceptance.
Work
- Integrate connector session policy with the existing gVPC policy boundary using the accepted internal interfaces.
- Validate inner source addresses before admitting client traffic into the gVPC and enforce destination/protocol/port policy as applicable.
- Apply return-direction policy before selecting and writing to a connector tunnel. Never use another tenant as a fallback for missing state.
- Handle malformed IPv6 packets, extension headers, fragments and required control traffic with bounded parsing and explicit rules; policy updates must affect active sessions.
Acceptance criteria
- Tests reject spoofed sources outside the /96, cross-connector impersonation, cross-tenant traffic and traffic denied in either direction.
- Identical addresses in two tenant contexts never select the other tenant's route or tunnel.
- Policy updates affect established sessions, and malformed or unevaluable packets cannot bypass enforcement.
- Required CONNECT-IP control and ICMP behavior remains functional under the enforced policy; denial reasons are available to observability.
Additional integration acceptance
- Consume Integrate gVPC traffic policy #999 attachment policy binding and updates; keep only connector/session-specific restrictions in MASQUE, and never bypass a closed foundation gate or missing/invalid policy.
- Key packet demultiplexing by explicit VPC/attachment context plus connector prefix and session generation. A destination prefix alone is not a global tenant identity.
- Validate IPv6 source ownership and authorized route/destination use in both directions, including malformed packets/fragments/extension headers and required control traffic; explicitly reject unsupported IPv4 rather than implying IPv4 support.
- Prove denied packets arrive at neither workload nor other connector, including same-instance connectors, overlapping tenants and live policy updates. Verify the relied-on library checks rather than assuming them; cover Edge pod attachments: unchecked caller inputs and weak isolation tests #989/CONNECT-IP server lacks overload limits and tenant isolation checks #991 findings.
Scope follows #970: CONNECT-IP with HTTP/2 fallback; no public API development, plugin/portal integration, product-client development or production rollout.
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Activity
[-]MASQUE: enforce delegated-prefix ownership and bidirectional traffic policy[/-][+]Enforce delegated-prefix ownership and bidirectional traffic policy[/+]privateip commentedon Oct 28, 2026
Verdict: valid. Reproduced on
mainat 2dcdb3e.The CONNECT-IP proxy runs no packet checks of its own. It relies on connect-ip-go v0.6.0, which checks a packet's source against the assigned /96 and its destination against the advertised routes. The library does not apply the parsing profile in the MASQUE contract, and it counts no drops.
A test in
internal/edge/connectipsent raw datagrams over an admitted request with the test fixture's addresses. All three of these packets got through:Other findings:
log.Printf, with no reason counter.privateip commentedon Oct 28, 2026
Approved fix plan.
Root cause.
Server.proxySessionininternal/edge/connectip/server.gomoves packets between the CONNECT-IP stream and the backend with no checks of its own. connect-ip-go v0.6.0 accepts an ICMP error from any source, does not walk extension headers or refuse fragments, and logs drops instead of counting them.Fix.
internal/edge/connectip, built from the lease's /96 and routes:proxySessionapplies it in both directions, and to the Packet Too Big the library composes for the client.galactic_connectip_packets_denied_total{direction,reason}counts every refusal, including IPv4 and unmatched packets the TUN backend reads from its device.docs/edge/masque-provider.mddocuments the policy, its reasons and the metric.A grant refresh that changes the routes already ends the session, so MASQUE-side policy updates reach live sessions. gVPC traffic policy stays with the foundation (#999).
Tests. A table test for every reason in both directions. The reproduction becomes a server test that fails on
main: spoofed and cross-connector sources, fragments in both directions, the refusal counters, and the IPv4 route grant. A socketpair TUN test shows two VPCs holding the same /96 never receive each other's packets.Risk. Stricter than RFC 9484 for client ICMP errors: a client must send its errors from its own /96. Fragmented inner packets are refused, as the contract already states. HTTP/2 (#981) and the lab rows (#985) are out of scope.