Add interim mitigations for CVE-2026-98432
CVE-2026-98432 is a use-after-free in the Linux IPv4 TCP option handling (inet_opt/ireq_opt are freed with a plain kfree()) that is reachable by sending a TCP SYN carrying IP options. No fixed kernel has been released for any supported Ubuntu release yet, so mitigate in role[base] until one is: * Drop inbound IPv4 TCP segments that carry IP options (no legitimate traffic uses them) and log the drops, rate limited, so probing is visible. The rules are added to /etc/ufw/before.rules so they survive ufw reloads, and are idempotent. * Disable Multipath TCP where the kernel supports it (5.6+). Go programs built with Go >= 1.24 open MPTCP listeners by default, which is the precondition of the published exploit; nothing here needs MPTCP. Hosts on older kernels (5.4, no MPTCP) skip this. The sysctl drop-in is written directly rather than via the core sysctl resource, whose reload runs `sysctl -p` against /etc/sysctl.conf - a file that no longer exists on Ubuntu 26.04. Opt node_exporter (Go >= 1.24, so it opened an MPTCP listener) out of MPTCP with GODEBUG=multipathtcp=0, and restart it when the unit changes so the change takes effect without a separate restart.
This commit is contained in:
3 files changed
+85
No files matched your search
@@ -0,0 +1,78 @@
|
||||
#
|
||||
# Cookbook:: kosmos-base
|
||||
# Recipe:: cve_2026_98432
|
||||
#
|
||||
# Interim mitigations for CVE-2026-98432: a use-after-free in the Linux IPv4
|
||||
# TCP option handling (inet_opt/ireq_opt are freed with a plain kfree()) that
|
||||
# is reachable by sending a TCP SYN carrying IP options. No fixed kernel has
|
||||
# been released for Ubuntu yet, so until every host runs a patched kernel we:
|
||||
#
|
||||
# 1. drop inbound IPv4 TCP segments that carry IP options (no legitimate
|
||||
# traffic uses them), and
|
||||
# 2. disable Multipath TCP where the running kernel supports it, so that Go
|
||||
# programs built with Go >= 1.24 (e.g. node_exporter) stop opening MPTCP
|
||||
# listeners - the precondition of the published exploit.
|
||||
#
|
||||
# This recipe (and the matching change in kosmos_prometheus::node_exporter) can
|
||||
# be removed once all hosts run a kernel containing the fix.
|
||||
#
|
||||
|
||||
# --- 1. Drop inbound IPv4 TCP segments that carry IP options ----------------
|
||||
#
|
||||
# ufw rebuilds its ruleset from /etc/ufw/before.rules on every start/reload, so
|
||||
# the restriction has to live in that file to survive. The u32 expression
|
||||
# `0&0x0F000000=0x05000000` matches a segment whose IP header length (IHL) is
|
||||
# 5, i.e. one *without* IP options; negating it drops any segment that carries
|
||||
# them. Drops are logged (rate limited) so probing is visible.
|
||||
before_rules = "/etc/ufw/before.rules"
|
||||
cve_marker = "CVE-2026-98432"
|
||||
ipopts_match = '-p tcp -m u32 ! --u32 "0&0x0F000000=0x05000000"'
|
||||
|
||||
ruby_block "ufw: drop IPv4 TCP segments with IP options (#{cve_marker})" do
|
||||
block do
|
||||
editor = Chef::Util::FileEdit.new(before_rules)
|
||||
editor.search_file_delete_line(/#{cve_marker}/)
|
||||
editor.insert_line_after_match(
|
||||
/^# End required lines$/,
|
||||
"# #{cve_marker}: drop IPv4 TCP segments carrying IP options\n" \
|
||||
"-A ufw-before-input -m comment --comment \"#{cve_marker}\" #{ipopts_match} " \
|
||||
"-m limit --limit 5/min -j LOG --log-prefix \"[#{cve_marker}] ipopts \" --log-level 4\n" \
|
||||
"-A ufw-before-input -m comment --comment \"#{cve_marker}\" #{ipopts_match} -j DROP"
|
||||
)
|
||||
editor.write_file
|
||||
|
||||
raise "#{cve_marker}: failed to add the ip-options drop rule to #{before_rules}" \
|
||||
unless ::File.read(before_rules).include?(cve_marker)
|
||||
end
|
||||
not_if { ::File.exist?(before_rules) && ::File.read(before_rules).include?(cve_marker) }
|
||||
notifies :run, "execute[reload ufw]"
|
||||
end
|
||||
|
||||
execute "reload ufw" do
|
||||
command "ufw reload"
|
||||
action :nothing
|
||||
end
|
||||
|
||||
# --- 2. Disable Multipath TCP where the kernel supports it ------------------
|
||||
#
|
||||
# MPTCP is in-tree since Linux 5.6; the /proc entry only exists when it is
|
||||
# available, so the compile-time guard keeps older (5.4) hosts from writing a
|
||||
# sysctl for a key their kernel does not know. Nothing in our stack needs MPTCP.
|
||||
#
|
||||
# Not using the core `sysctl` resource here: its reload runs `sysctl -p`, which
|
||||
# reads /etc/sysctl.conf - a file that does not exist on Ubuntu 26.04, making
|
||||
# the resource fail. Writing the drop-in and applying the value explicitly works
|
||||
# on every release we run.
|
||||
if ::File.exist?("/proc/sys/net/mptcp/enabled")
|
||||
file "/etc/sysctl.d/99-chef-net.mptcp.enabled.conf" do
|
||||
content "net.mptcp.enabled = 0\n"
|
||||
owner "root"
|
||||
group "root"
|
||||
mode "0644"
|
||||
end
|
||||
|
||||
execute "disable multipath TCP (net.mptcp.enabled=0)" do
|
||||
command "sysctl -w net.mptcp.enabled=0"
|
||||
not_if { ::File.read("/proc/sys/net/mptcp/enabled").strip == "0" }
|
||||
end
|
||||
end
|
||||
@@ -77,6 +77,8 @@ unless node.chef_environment == "development"
|
||||
|
||||
include_recipe "kosmos-base::firewall"
|
||||
|
||||
include_recipe "kosmos-base::cve_2026_98432"
|
||||
|
||||
include_recipe "kosmos-base::tmux"
|
||||
|
||||
include_recipe "kosmos-postfix"
|
||||
|
||||
@@ -59,6 +59,10 @@ systemd_unit "node_exporter.service" do
|
||||
User: "node_exporter",
|
||||
Group: "node_exporter",
|
||||
ExecStart: "/usr/local/bin/node_exporter --web.listen-address=:#{node_exporter_port} --collector.textfile.directory=/var/lib/node_exporter/textfile",
|
||||
# node_exporter 1.11.1 is built with Go >= 1.24, which opens MPTCP
|
||||
# listeners by default on Linux since 5.6. That is the precondition of
|
||||
# the CVE-2026-98432 exploit, and we do not need MPTCP, so opt out.
|
||||
Environment: "GODEBUG=multipathtcp=0",
|
||||
Restart: "on-failure",
|
||||
RestartSec: "5",
|
||||
NoNewPrivileges: "yes",
|
||||
@@ -70,6 +74,7 @@ systemd_unit "node_exporter.service" do
|
||||
},
|
||||
})
|
||||
triggers_reload true
|
||||
notifies :restart, "service[node_exporter]", :delayed
|
||||
action :create
|
||||
end
|
||||
|
||||
|
||||
Reference in new issue
Block a user