Files
mesh-lab/scenarios/the-ordinary-shape.yml
T
jschoubben 5d01006eab Transit, host firewalls, and the whole topology raising
The full topology now raises: four machines, three routers, a transit
router, six segments, in 35 seconds. Everything the declaration model can
express except `place`, which is refused because the node host it would
place does not exist yet.

Transit was a real gap, not a bug. The design says public networks are
unrelated and routed to each other, never bridged — and I built the
segments and never built the thing that routes between them, so three
public networks were islands and nothing crossed. A transit router now
holds an interface on every public segment, forwarding and no translation:
the closest thing the lab has to the internet, deliberately dumb.

Proven rather than asserted, by ping TTL across the raised topology:

  within one segment                     ttl=64   no hops
  across two unrelated public networks   ttl=62   gateway + transit
  multicast between public networks      0 replies

A flat internet would have shown ttl=64 and answered multicast — which
would let a node discover a peer it could never reach in production, and
report success. That is the fault the as-is layer records the mesh already
hitting with multicast name resolution.

inbound: deny is implemented as a host firewall on the machine, read back
after applying. A declared refusal that silently did not load leaves the
machine wide open, which looks exactly like a machine that is working.
Established and related traffic is accepted, so a defended machine can
still dial out rather than being a disconnected one.

Verified by running, all of it:

  home -> devices (policy allow)               reachable
  devices -> home (policy deny)                blocked
  behind unforwardable NAT -> out              reachable
  in -> behind unforwardable NAT               unreachable
  inbound: deny, dialling out                  reachable
  reaching a machine that denies inbound       refused

The two routers differ exactly as declared: the forwardable one carries the
policy rule and no inbound drop, the unforwardable one carries `ct state
new drop` and no DNAT.
2026-08-24 01:49:30 +02:00

82 lines
2.0 KiB
YAML

# One machine with a routable address, one publicly named but behind a household
# connection, one stationary machine on that network, one that roams.
#
# Three unrelated public networks, routed to each other and never bridged — putting them
# in one prefix would make ARP adjacency, non-decrementing TTL and crossing multicast
# true in the lab and false in production.
scenario: the-ordinary-shape
segments:
hosting:
kind: public
cidr: [192.0.2.0/24, "2001:db8:a::/48"]
isp-home:
kind: public
cidr: [198.51.100.0/24, "2001:db8:b::/48"]
isp-mobile:
kind: public
cidr: [203.0.113.0/24, "2001:db8:c::/48"]
home:
kind: private
cidr: [192.168.1.0/24, "2001:db8:b:1::/64"]
mtu: 1492
gateway:
to: isp-home
address: [198.51.100.7, "2001:db8:b::7"]
nat: [v4]
forwardable: true
mapping_ttl: 120s
devices:
kind: private
cidr: [192.168.30.0/24]
gateway:
to: isp-home
address: [198.51.100.7]
nat: [v4]
forwardable: true
mapping_ttl: 120s
cafe:
kind: private
cidr: [10.50.0.0/16]
mtu: 1400
gateway:
to: isp-mobile
address: [203.0.113.129]
nat: [v4]
forwardable: false
mapping_ttl: 30s
policy:
- { from: devices, to: home, allow: false }
- { from: home, to: devices, allow: true }
machines:
anchor:
at: { segment: hosting, address: [192.0.2.10, "2001:db8:a::10"] }
inbound: allow
home-server:
at: { segment: home, address: [192.168.1.135, "2001:db8:b:1::135"] }
published:
- { port: 443, on: home }
inbound: allow
workstation:
at: { segment: home, address: [192.168.1.250, "2001:db8:b:1::250"] }
inbound: deny
laptop:
at: { segment: home, address: [192.168.1.98, "2001:db8:b:1::98"] }
inbound: deny
# No `place:` yet. The node host it would place does not exist — this lab is being built to
# develop it, and the lab refuses declarations it cannot materialise rather than raising a
# mesh that silently lacks them.
snapshot: raised