feat(macos): give nested containers working egress
tracker-policy-pr / check-pr (pull_request) Successful in 11s
test / integration-docker (pull_request) Successful in 17s
test / unit (pull_request) Successful in 43s
lint / lint (push) Successful in 56s
test / integration-firecracker (pull_request) Successful in 3m32s
test / coverage (pull_request) Successful in 23s
test / publish-infra (pull_request) Has been skipped

Answers the last open question in #392: DNS from inside a nested
container was not a phantom, but it was also not a DNS problem. Public
resolution fails there by design — everything egresses through the
proxy — and the actual breakage was two missing pieces:

- The proxy URL names `bot-bottle-gateway`, which resolves only through
  the bottle's own /etc/hosts. Containers got their own hosts file, so
  they failed at "Could not resolve proxy", which reads like broken DNS.
  base_hosts_file propagates the bottle's entries.
- The gateway TLS-intercepts, so a container that does not trust the
  bottle's CA bundle fails with "unable to get local issuer
  certificate". The bundle is now mounted read-only and the usual env
  vars point at it, which covers curl, wget, python, and node without
  distro-specific trust commands.

Verified on the macOS host by reproducing each failure and confirming
that these three conditions applied by hand produced HTTP 200 from
inside a nested container. podman already forwards the proxy env, so
that needed nothing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-21 19:37:39 -04:00
parent 41959fe2a2
commit f9f27788db
2 changed files with 65 additions and 1 deletions
@@ -39,6 +39,14 @@ done
# Short by necessity, not by accident: conmon's attach socket lives under
# this directory and must fit in a 108-byte sun_path. See nested_containers.py.
# Must stay in step with AGENT_CA_BUNDLE in bot_bottle/backend/util.py; a unit
# test pins the two together.
CA_BUNDLE="/etc/ssl/certs/ca-certificates.crt"
[ -r "$CA_BUNDLE" ] || {
echo "gateway CA bundle $CA_BUNDLE is missing or unreadable" >&2
exit 1
}
export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/tmp/bbp}"
config="$HOME/.config/containers"
mkdir -p "$XDG_RUNTIME_DIR" "$config"
@@ -57,9 +65,32 @@ CONF
# No cgroup delegation reaches this guest, so asking podman to manage cgroups
# fails; events_logger=file avoids the journald socket that is equally absent.
cat > "$config/containers.conf" <<'CONF'
#
# The rest of this block is what makes a nested container reach the network at
# all. podman already forwards the proxy env, but that alone is useless here:
#
# base_hosts_file the proxy URL names `bot-bottle-gateway`, which resolves
# only through the bottle's own /etc/hosts. Without this a
# container fails at "Could not resolve proxy", which reads
# like broken DNS but is a missing hosts entry — public DNS
# is *supposed* to fail, since everything egresses via the
# proxy.
# volumes/env the gateway TLS-intercepts, so a container that does not
# trust the bottle's CA bundle gets "unable to get local
# issuer certificate". Mounting the bundle read-only and
# pointing the usual env vars at it covers curl, wget,
# python, and node without distro-specific trust commands.
cat > "$config/containers.conf" <<CONF
[containers]
cgroups="disabled"
base_hosts_file="/etc/hosts"
volumes=["${CA_BUNDLE}:${CA_BUNDLE}:ro"]
env=[
"SSL_CERT_FILE=${CA_BUNDLE}",
"CURL_CA_BUNDLE=${CA_BUNDLE}",
"REQUESTS_CA_BUNDLE=${CA_BUNDLE}",
"NODE_EXTRA_CA_CERTS=${CA_BUNDLE}",
]
[engine]
cgroup_manager="cgroupfs"
events_logger="file"