From 91ff65cbd5ac9ccfa16c3ef47fd342c5fa525294 Mon Sep 17 00:00:00 2001 From: Martin Pitt Date: Tue, 3 Nov 2026 07:50:23 +0000 Subject: [PATCH] imds: turn off IPv4 reverse path filtering on IMDS links in locked mode With `systemd.imds.network=locked`, imdsd cannot reach IMDS at all. Our sysctl.d/50-default.conf enables IPv4 reverse path filtering for all links. Locked mode adds a "prohibit" route for the IMDS address to the main table. imdsd's marked sockets use their own table, so its _requests_ get out. But IMDS's _replies_ carry no mark: the reverse path filter checks their source against the main table, finds the prohibit route, and drops them. To fix this, turn off reverse path filtering on the configured links in locked mode. The prohibit route still blocks direct access for everybody else. Caveat: This does not help where `net.ipv4.conf.all.rp_filter` is on, as the kernel applies the maximum of that and the link's setting. Ubuntu does that by default, so for now this only works out of the box on Debian, Fedora, etc. Cover that case in the tests, and expect a timeout/failure with `all.rp_filter > 0`. Fixes #44602 Signed-off-by: Martin Pitt (cherry picked from commit 9d7ae503d9e997e2ea4fd0b54f18dbf72e3e99f0) (cherry picked from commit 69d45358bfd429584bd79b0465ec17493f0b4fc0) --- man/systemd-imdsd@.service.xml | 5 ++- src/imds/imdsd.c | 12 +++++- test/units/TEST-74-AUX-UTILS.imds.sh | 63 ++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 3 deletions(-) diff --git a/man/systemd-imdsd@.service.xml b/man/systemd-imdsd@.service.xml index d4007b7c87f..76d94dd68ac 100644 --- a/man/systemd-imdsd@.service.xml +++ b/man/systemd-imdsd@.service.xml @@ -71,7 +71,10 @@ access. Unless set to off early boot networking is enabled, ensuring that the IMDS endpoint can be reached. If set to locked direct access to the IMDS endpoint by regular unprivileged processes is disabled via a "prohibit" route, so that any - access must be done through systemd-imdsd@.service or its associated tools. If + access must be done through systemd-imdsd@.service or its associated tools. + This also turns off IPv4 reverse path filtering on all Ethernet links that the early boot networking + configures (see IPv4ReversePathFilter= in + systemd.network5). If set to unlocked (the default) this "prohibit" route is not created, and regular unprivileged processes can directly contact IMDS. diff --git a/src/imds/imdsd.c b/src/imds/imdsd.c index 95bd54fb6dd..f90044ca8c5 100644 --- a/src/imds/imdsd.c +++ b/src/imds/imdsd.c @@ -1679,8 +1679,16 @@ static int setup_network(void) { "\n" "[Network]\n" "DHCP=yes\n" - "LinkLocalAddressing=ipv6\n" - "\n" + "LinkLocalAddressing=ipv6\n", f); + + /* IMDS's replies to imdsd carry no firewall mark. Reverse path filtering would check their source + * address against the main table, find the prohibit route below, and drop them. IPv6 has no + * reverse path filter. */ + if (arg_network_mode != IMDS_NETWORK_UNLOCKED && in4_addr_is_set(&arg_address_ipv4)) + fputs("# Reverse path filtering would drop IMDS's replies due to the prohibit route\n" + "IPv4ReversePathFilter=no\n", f); + + fputs("\n" "[DHCP]\n" "UseTimezone=yes\n" "UseHostname=yes\n" diff --git a/test/units/TEST-74-AUX-UTILS.imds.sh b/test/units/TEST-74-AUX-UTILS.imds.sh index bc62a45eb3f..0bce84280d2 100755 --- a/test/units/TEST-74-AUX-UTILS.imds.sh +++ b/test/units/TEST-74-AUX-UTILS.imds.sh @@ -15,6 +15,10 @@ fi at_exit() { set +e systemctl stop fake-imds systemd-imdsd.socket + # Unmanage the link before removing it, so that networkd also removes its prohibit route and rule + rm -rf /run/systemd/network/85-imds-early.network /run/systemd/network/85-imds-early.network.d \ + /run/systemd/resolve/static.d/imds-endpoint.rr + networkctl reload ip link del veth-imds ip netns del imds rm -f /run/credstore/firstboot.hostname /run/credstore/acredtest /run/systemd/system/systemd-imdsd@.service.d/50-env.conf /run/fake-imds-flaky @@ -156,3 +160,62 @@ systemctl daemon-reload (! /usr/lib/systemd/systemd-imds --import 2>"$WORK_DIR/import.err") cat "$WORK_DIR/import.err" grep -F "Failed to parse IP address 'piff'" "$WORK_DIR/import.err" >/dev/null + +# Lock down IMDS access to imdsd (systemd.imds.network=locked), with the networking which +# "systemd-imdsd --setup-network" generates and networkd applies. This needs IMDS at its usual link-local +# address, as the prohibit route covers only that. +ip -n imds addr add 169.254.169.254/32 dev veth-imds-peer +IMDSD_LOCKED=("${IMDSD[@]}" --data-url=http://169.254.169.254:8088 --address-ipv4=169.254.169.254) + +# Unlocked mode has no prohibit route, and keeps reverse path filtering +SYSTEMD_PROC_CMDLINE=systemd.imds.network=unlocked "${IMDSD[@]}" --address-ipv4=169.254.169.254 --setup-network +cat /run/systemd/network/85-imds-early.network +(! grep -F -e "Type=prohibit" -e "IPv4ReversePathFilter=" /run/systemd/network/85-imds-early.network >/dev/null) + +SYSTEMD_PROC_CMDLINE=systemd.imds.network=locked "${IMDSD_LOCKED[@]}" --setup-network +cat /run/systemd/network/85-imds-early.network +grep -Fx "Type=prohibit" /run/systemd/network/85-imds-early.network >/dev/null +grep -Fx "IPv4ReversePathFilter=no" /run/systemd/network/85-imds-early.network >/dev/null + +# The generated .network matches physical Ethernet links only, and asks for DHCP. Apply it to the veth +# instead, with its static address. +mkdir -p /run/systemd/network/85-imds-early.network.d +cat >/run/systemd/network/85-imds-early.network.d/50-test.conf < DROPS )) +fi + +# Everybody else gets blocked +(! bash -c 'echo >/dev/tcp/169.254.169.254/8088' 2>"$WORK_DIR/connect.err") +cat "$WORK_DIR/connect.err" +grep -F "Permission denied" "$WORK_DIR/connect.err" >/dev/null