Skip to content

[LXC] An allowedHosts hostname is pinned to the IPs the host resolved, so a container resolving it differently is blocked #992

Description

A hostname in network.allowedHosts is enforced as the set of IP addresses the
host
resolved when the rules were installed. The container resolves the same
name itself when it connects. When those two answers differ, the allowed
destination is dropped, and the policy still reports as enforced.

resolve_host at src/backends/lxc/common/src/network_iptables.rs:835 expands
each entry through to_socket_addrs and programs the resulting addresses as
literal iptables destinations. Nothing re-resolves afterward, and nothing
reconciles the host's answer with the container's.

Evidence

CI run
32534393886 ran the
LXC E2E suite three times against the same commit (5ea1bc80) with no code
change between attempts:

Attempt Failed suites
1 LXC Network, LXC Network Enforcement, LXC Network Deny Precedence
2 LXC Network
3 none

The failing assertion is always a timeout reaching api.github.com, which
tests/configs/lxc_network_test.json names in allowedHosts under
defaultPolicy: block:

  • attempt 1: wget: can't connect to remote host (140.82.114.6): Operation timed out
  • attempt 2: wget: can't connect to remote host (172.182.252.137): Operation timed out

Those are different addresses for the same name, and they are not in the same
range -- 140.82.112.0/20 is GitHub, 172.182.0.0/16 is Azure. In the same
attempt that failed, the suite reached dns.google over HTTPS successfully, so
container egress was working.

The suites report this themselves rather than as a bare timeout: "an explicitly
allowed destination was unreachable. The policy is over-blocking, so the deny
case above proves nothing."

Why this is not just a test problem

The fixture is an ordinary hostname allowlist, which is the documented way to
express one. Any name behind a rotating, geo-balanced, or migrating DNS record
carries the same exposure: the caller is refused a destination the policy
allows, with no diagnostic saying why. api.github.com is currently moving
between GitHub-owned and Azure-owned address space, which is why it surfaced
here.

Related but distinct: #877 covers blockedHosts entries that do not resolve at
all. This is about entries that resolve successfully and to a different
answer than the container gets.

Not verified

  • Whether the container and the host use the same resolver under the default
    lxcbr0 setup. That would narrow the window but not close it, since the two
    lookups still happen at different times against a rotating record.
  • Whether Bubblewrap is exposed the same way. It reaches this module through
    NetworkIptablesManager::apply_firewall_rules, so it plausibly is, but that
    was not tested.
  • Nothing here establishes how often this fires outside CI.

Ruled out

This is not a regression from #849. The fixture and resolve_host are both
byte-identical to main, and the same commit passed on the third attempt.

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions