imds: warn if net.ipv4.conf.all.rp_filter breaks locked mode

Make that caveat from the previous commit more obvious with a logged
warning, and document the workaround (or really, the fix) of disabling
`net.ipv4.conf.all.rp_filter` globally.

Signed-off-by: Martin Pitt <martin@amutable.com>
This commit is contained in:
Martin Pitt committed 2026-11-03 12:05:59 +01:00
1 parent 9d7ae503d9
commit cec6bc4ea6
4 files changed
+46 -5

No files matched your search

+10
View File
@@ -78,6 +78,16 @@
set to <literal>unlocked</literal> (the default) this "prohibit" route is not created, and regular unprivileged
processes can directly contact IMDS.</para>
<para>Some systems set <varname>net.ipv4.conf.all.rp_filter</varname>. With
<literal>locked</literal>, IMDS then cannot be reached. <command>systemd-imdsd</command> warns about
this when setting up the network. To allow IMDS access, turn it off with a
<citerefentry><refentrytitle>sysctl.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
drop-in, e.g. <filename>/etc/sysctl.d/90-imds.conf</filename>:</para>
<programlisting>net.ipv4.conf.all.rp_filter = 0</programlisting>
<para>systemd's <filename>50-default.conf</filename> already turns on reverse path filtering for every
individual link, so all other links keep it.</para>
<xi:include href="version-info.xml" xpointer="v261"/>
</listitem>
</varlistentry>
+17 -1
View File
@@ -47,6 +47,7 @@
#include "socket-util.h"
#include "string-util.h"
#include "strv.h"
#include "sysctl-util.h"
#include "time-util.h"
#include "tmpfile-util.h"
#include "utf8.h"
@@ -1637,6 +1638,19 @@ static int imds_configured(int level) {
return 0;
}
static void warn_rp_filter_all(void) {
int r, v;
/* The kernel applies the maximum of the link's and the "all" setting, so turning off reverse path
* filtering on the link does not help if "all" turns it on. */
r = sysctl_read_ip_property_int(AF_INET, "all", "rp_filter", &v);
if (r < 0)
return (void) log_debug_errno(r, "Failed to read net.ipv4.conf.all.rp_filter, ignoring: %m");
if (v != 0)
log_warning("net.ipv4.conf.all.rp_filter=%i turns on IPv4 reverse path filtering on all links, "
"which drops IMDS replies in locked mode. Set it to 0 to allow IMDS access.", v);
}
static int setup_network(void) {
int r;
@@ -1691,9 +1705,11 @@ static int setup_network(void) {
/* 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))
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);
warn_rp_filter_all();
}
fputs("\n"
"[DHCP]\n"
+18 -3
View File
@@ -25,6 +25,10 @@ at_exit() {
rmdir /run/systemd/system/systemd-imdsd@.service.d
# cached negative entries would short-circuit the /flaky retry on a rerun
rm -rf /run/systemd/imds/cache/veth-imds
if [[ -v RP_FILTER_ALL ]]; then
rm -f /run/sysctl.d/90-imds.conf
sysctl -w net.ipv4.conf.all.rp_filter="$RP_FILTER_ALL"
fi
rm -rf "$WORK_DIR"
}
@@ -168,11 +172,12 @@ 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
SYSTEMD_PROC_CMDLINE=systemd.imds.network=unlocked "${IMDSD[@]}" --address-ipv4=169.254.169.254 --setup-network 2>&1 | tee "$WORK_DIR/setup-network-unlocked.log"
cat /run/systemd/network/85-imds-early.network
(! grep -F -e "Type=prohibit" -e "IPv4ReversePathFilter=" /run/systemd/network/85-imds-early.network >/dev/null)
(! grep -F "net.ipv4.conf.all.rp_filter" "$WORK_DIR/setup-network-unlocked.log" >/dev/null)
SYSTEMD_PROC_CMDLINE=systemd.imds.network=locked "${IMDSD_LOCKED[@]}" --setup-network
SYSTEMD_PROC_CMDLINE=systemd.imds.network=locked "${IMDSD_LOCKED[@]}" --setup-network 2>&1 | tee "$WORK_DIR/setup-network.log"
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
@@ -204,15 +209,25 @@ assert_eq "$(sysctl -n net.ipv4.conf.veth-imds.rp_filter)" 0
# applies the maximum of that and net.ipv4.conf.all.rp_filter, which some distributions turn on (e.g.
# Ubuntu). Then the reverse path filter still drops the replies.
if [[ "$(sysctl -n net.ipv4.conf.all.rp_filter)" == 0 ]]; then
(! grep -F "net.ipv4.conf.all.rp_filter" "$WORK_DIR/setup-network.log" >/dev/null)
assert_eq "$("${IMDSD_LOCKED[@]}" --interface=veth-imds /hostname)" "piff"
else
echo "WARNING: net.ipv4.conf.all.rp_filter is on, imdsd cannot reach IMDS in locked mode"
grep -F "net.ipv4.conf.all.rp_filter=" "$WORK_DIR/setup-network.log" >/dev/null
# Do not wait for imdsd's overall 40s timeout (too annoying); instead, check that reverse path
# filtering drops the replies
rp_filter_drops() { nstat -asz TcpExtIPReversePathFilter | awk '/IPReversePathFilter/ { print $2 }'; }
DROPS="$(rp_filter_drops)"
assert_rc 124 timeout 5 "${IMDSD_LOCKED[@]}" --interface=veth-imds /hostname
(( "$(rp_filter_drops)" > DROPS ))
# The documented workaround. Only apply this file: all sysctl.d files would also reset the link's
# setting, which networkd configures after them during boot.
RP_FILTER_ALL="$(sysctl -n net.ipv4.conf.all.rp_filter)"
mkdir -p /run/sysctl.d
echo "net.ipv4.conf.all.rp_filter = 0" >/run/sysctl.d/90-imds.conf
/usr/lib/systemd/systemd-sysctl /run/sysctl.d/90-imds.conf
sysctl net.ipv4.conf.all.rp_filter net.ipv4.conf.veth-imds.rp_filter
assert_eq "$("${IMDSD_LOCKED[@]}" --interface=veth-imds /hostname)" "piff"
fi
# Everybody else gets blocked
+1 -1
View File
@@ -15,7 +15,7 @@ Before=network-pre.target
Wants=network-pre.target
Conflicts=shutdown.target initrd-switch-root.target
Before=shutdown.target initrd-switch-root.target
After=sys-devices-virtual-dmi-id.device
After=sys-devices-virtual-dmi-id.device systemd-sysctl.service
[Service]
ExecStart={{LIBEXECDIR}}/systemd-imdsd --setup-network