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.
A hostname in
network.allowedHostsis enforced as the set of IP addresses thehost 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_hostatsrc/backends/lxc/common/src/network_iptables.rs:835expandseach entry through
to_socket_addrsand programs the resulting addresses asliteral 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 codechange between attempts:
LXC Network,LXC Network Enforcement,LXC Network Deny PrecedenceLXC NetworkThe failing assertion is always a timeout reaching
api.github.com, whichtests/configs/lxc_network_test.jsonnames inallowedHostsunderdefaultPolicy: block:wget: can't connect to remote host (140.82.114.6): Operation timed outwget: can't connect to remote host (172.182.252.137): Operation timed outThose are different addresses for the same name, and they are not in the same
range --
140.82.112.0/20is GitHub,172.182.0.0/16is Azure. In the sameattempt that failed, the suite reached
dns.googleover HTTPS successfully, socontainer 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.comis currently movingbetween GitHub-owned and Azure-owned address space, which is why it surfaced
here.
Related but distinct: #877 covers
blockedHostsentries that do not resolve atall. This is about entries that resolve successfully and to a different
answer than the container gets.
Not verified
lxcbr0setup. That would narrow the window but not close it, since the twolookups still happen at different times against a rotating record.
NetworkIptablesManager::apply_firewall_rules, so it plausibly is, but thatwas not tested.
Ruled out
This is not a regression from #849. The fixture and
resolve_hostare bothbyte-identical to
main, and the same commit passed on the third attempt.