Browse Source

base: resync /etc/resolv.conf into Network=host containers

With Network=host, Podman snapshots /etc/resolv.conf at container creation
and bind-mounts the copy, so a later DNS change (DHCP renewal, renumbering,
router replacement) never reaches running containers: they keep querying the
old, now-dead resolvers until recreated, silently.

Add a host-level mechanism in the base cookbook:

- a systemd.path unit watching /etc/resolv.conf (catches any writer, including
  atomic rename-into-place);
- a oneshot service running a dedicated script that rewrites each Network=host
  container's snapshot in place (same inode, so the bind-mount stays valid).

Rewriting the content in place is picked up at runtime with no restart by both
glibc (reloads on mtime change) and musl (stateless, re-reads every lookup),
verified empirically. Scoped to NetworkMode=host only: bridged containers
legitimately point at aardvark-dns and must not be touched.

Fixes #1

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix/network-host-resolv-conf
Nicolas Massé 2 weeks ago
parent
commit
9771f18de7
  1. 5
      cookbooks/base/Makefile
  2. 6
      cookbooks/base/README.md
  3. 43
      cookbooks/base/config/podman-resolv-conf-sync.sh
  4. 12
      cookbooks/base/podman-resolv-conf-sync.path
  5. 8
      cookbooks/base/podman-resolv-conf-sync.service

5
cookbooks/base/Makefile

@ -2,8 +2,13 @@
SYSTEMD_MAIN_UNIT_NAMES := var-lib-virtiofs-data.mount SYSTEMD_MAIN_UNIT_NAMES := var-lib-virtiofs-data.mount
SYSTEMD_MAIN_UNIT_NAMES += rpm-ostree-install-qemu-guest-agent.service SYSTEMD_MAIN_UNIT_NAMES += rpm-ostree-install-qemu-guest-agent.service
SYSTEMD_MAIN_UNIT_NAMES += install-tools.service SYSTEMD_MAIN_UNIT_NAMES += install-tools.service
# .path units are not picked up by common.mk's wildcard (only .service/.target/
# .timer/.mount), so install and enable/start it explicitly. It triggers the
# path-scoped podman-resolv-conf-sync.service oneshot.
SYSTEMD_MAIN_UNIT_NAMES += podman-resolv-conf-sync.path
TARGET_FILES += $(TARGET_CHROOT)/etc/quadlets/base/install-tools.d TARGET_FILES += $(TARGET_CHROOT)/etc/quadlets/base/install-tools.d
TARGET_FILES += $(TARGET_CHROOT)/etc/systemd/system/podman-resolv-conf-sync.path
# Include common Makefile # Include common Makefile
include ../../scripts/common.mk include ../../scripts/common.mk

6
cookbooks/base/README.md

@ -8,6 +8,12 @@ The base cookbook provides foundational configuration for Fedora CoreOS systems.
- **tmpfiles configuration**: Sets up required directories such as `/var/lib/virtiofs/data`. - **tmpfiles configuration**: Sets up required directories such as `/var/lib/virtiofs/data`.
- **QEMU guest agent**: Optional installation for better VM integration. - **QEMU guest agent**: Optional installation for better VM integration.
- **SSH key persistence**: Backs up and restores SSH host keys across reboots. - **SSH key persistence**: Backs up and restores SSH host keys across reboots.
- **resolv.conf resync**: Keeps `Network=host` containers in sync with the host's DNS.
With `Network=host`, Podman snapshots `/etc/resolv.conf` at container creation and bind-mounts
the copy, so a later DNS change (e.g. a DHCP renewal) never reaches running containers. A
`systemd.path` unit watches `/etc/resolv.conf` and a oneshot service rewrites each host-network
container's snapshot **in place** (same inode, no restart); bridged containers are left untouched.
See [issue #1](https://github.com/nmasse-itix/podman-quadlet-cookbook/issues/1).
This cookbook is used as an implicit dependency for other cookbooks that run on Fedora CoreOS. This cookbook is used as an implicit dependency for other cookbooks that run on Fedora CoreOS.

43
cookbooks/base/config/podman-resolv-conf-sync.sh

@ -0,0 +1,43 @@
#!/bin/bash
#
# Resync the host's /etc/resolv.conf into the frozen snapshot that Podman
# bind-mounts into every Network=host container.
#
# Why: with Network=host, Podman copies /etc/resolv.conf at container *creation*
# time into .../userdata/resolv.conf and bind-mounts that copy. It is a snapshot,
# not a view: when the host's DNS servers change (DHCP renewal, renumbering,
# router replacement, ...), every running container keeps querying the old,
# now-dead resolvers — silently, until it is recreated.
#
# Fix: overwrite the snapshot's *content* in place (same inode, so the bind-mount
# stays valid). Both glibc (reloads when the file's mtime changes) and musl
# (stateless, re-reads on every lookup) then pick up the new resolvers at runtime
# with no restart and no downtime.
#
# Scope: Network=host containers ONLY. Bridged containers legitimately point at
# aardvark-dns (e.g. nameserver 10.89.0.1), which already follows the host's DNS
# live; overwriting their resolv.conf would break inter-container name resolution.
set -Eeuo pipefail
host_resolv=/etc/resolv.conf
for id in $(podman ps -q); do
# Only touch containers that share the host network stack.
mode="$(podman inspect --format '{{.HostConfig.NetworkMode}}' "$id" 2>/dev/null)" || continue
[ "$mode" = host ] || continue
snapshot="$(podman inspect --format '{{.ResolvConfPath}}' "$id" 2>/dev/null)" || continue
[ -n "$snapshot" ] && [ -f "$snapshot" ] || continue
if ! cmp -s "$host_resolv" "$snapshot"; then
name="$(podman inspect --format '{{.Name}}' "$id" 2>/dev/null || echo "$id")"
echo "Refreshing resolv.conf for container ${name} (${snapshot})"
# In-place truncation: preserves the inode the container has bind-mounted.
if ! cat "$host_resolv" > "$snapshot"; then
echo "Failed to refresh resolv.conf for container ${name}!" >&2
fi
fi
done
exit 0

12
cookbooks/base/podman-resolv-conf-sync.path

@ -0,0 +1,12 @@
[Unit]
Description=Watch /etc/resolv.conf and resync it into Network=host containers
Documentation=https://github.com/nmasse-itix/podman-quadlet-cookbook/issues/1
[Path]
# Fires on any writer (NetworkManager, manual edit, ...), including atomic
# rename-into-place, since systemd also watches the parent directory.
PathChanged=/etc/resolv.conf
Unit=podman-resolv-conf-sync.service
[Install]
WantedBy=multi-user.target

8
cookbooks/base/podman-resolv-conf-sync.service

@ -0,0 +1,8 @@
[Unit]
Description=Resync /etc/resolv.conf into running Network=host Podman containers
Documentation=https://github.com/nmasse-itix/podman-quadlet-cookbook/issues/1
# Triggered by podman-resolv-conf-sync.path whenever /etc/resolv.conf changes.
[Service]
Type=oneshot
ExecStart=/etc/quadlets/base/podman-resolv-conf-sync.sh
Loading…
Cancel
Save