Every address that is this host, as this host currently reports itself
(029 T014's supply half, 029-FR-015, D103).
Why this exists as a separate module
ExSandbox.Egress.Allowlist.parse/2 takes the alias set as data and must
keep doing so: it is the module that transfers whole to a second mechanism
precisely because it names no mechanism, and a capability query from inside it
would be the re-coupling that argument forbids. So somebody has to produce
the list, and that somebody is here — on the platform side of the seam, where
knowing about interfaces and routing tables is allowed.
⚠️ Discovered, never configured, and the difference is the whole point. A configured constant would be right in the config file and wrong in the namespace, and the drift is the defect: every parse-time test stays green while the address actually in use is one no entry is refused for. The list is read from the running kernel at the moment it is needed.
What counts as the host
| source | why |
|---|---|
every address on every interface (:inet.getifaddrs/0) | these are literally this machine, under every name it answers to |
| the default gateway | see below |
⚠️ The default gateway is included even though --no-map-gw is passed,
and that is belt-and-braces rather than confusion. pasta's default is to map
the namespace's gateway address onto the host, which makes it a second name
for the host that is not 127.0.0.1 — the case D103 was written about. The
flag turns that default off, so today the gateway is the real upstream router
instead. Refusing it anyway costs an operator nothing they should have wanted
(a sandbox has no business dialling the host's own next hop), and it means the
exclusion does not quietly evaporate if the flag is ever dropped — which is
exactly the kind of silent re-widening netns.ex documents --no-map-gw
against.
⚠️ :host_alias wins over :rfc1918_private when both match, which is
the ordering Allowlist.host_class/2 already fixes. Nearly every address here
is also private, so without that ordering this whole list would be invisible
in the error an operator reads.
What this cannot see
Read at the moment of the call, so an address added afterwards is not in a list already produced. That is a real gap and is stated rather than papered over: the alternative — re-reading per comparison — would put a syscall inside a pure policy function, which is the coupling the seam exists to prevent. Provisioning is where the list is taken, and provisioning is also where the refusal must land, so the two are the same moment.
⚠️ It also cannot see a name for the host (host.docker.internal and
friends). Allowlist.parse/2 accepts names in the alias set and nothing here
produces one, so an operator who writes a host name is not refused as
:host_alias today. Unfixed here on purpose: guessing at names would produce
a list that is wrong in a different direction, and the answer-side filter in
ExSandbox.Egress.Resolver catches the name once it resolves.
Summary
Types
An address that is this host.
Functions
Every address that is this host, right now.
The gateway addresses in ip route show default output.
Types
@type host_alias() :: :inet.ip_address()
An address that is this host.
Functions
@spec detect() :: [host_alias()]
Every address that is this host, right now.
Returns [] on a host whose interfaces cannot be read — which is a loss of
a guard, so it is logged rather than passing silently. It is not fatal: the
static classes (:loopback, :rfc1918_private, …) still refuse most of what
this list would have caught, so an empty answer narrows the guard rather than
removing it.
@spec parse_default_via(String.t()) :: [host_alias()]
The gateway addresses in ip route show default output.
Public so the parsing is testable on a host that has no ip(8) and no default
route — which is every machine this is written on. A parser exercised only
where the command runs is one whose failure mode is a silently empty guard.