From b299910dc6b0cde0610e8144bf3d618b1bc01a56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Sat, 31 Oct 2026 14:33:13 +0400 Subject: [PATCH 1/2] 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. --- .../kosmos-base/recipes/cve_2026_98432.rb | 78 +++++++++++++++++++ site-cookbooks/kosmos-base/recipes/default.rb | 2 + .../recipes/node_exporter.rb | 5 ++ 3 files changed, 85 insertions(+) create mode 100644 site-cookbooks/kosmos-base/recipes/cve_2026_98432.rb diff --git a/site-cookbooks/kosmos-base/recipes/cve_2026_98432.rb b/site-cookbooks/kosmos-base/recipes/cve_2026_98432.rb new file mode 100644 index 0000000..8238c32 --- /dev/null +++ b/site-cookbooks/kosmos-base/recipes/cve_2026_98432.rb @@ -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 diff --git a/site-cookbooks/kosmos-base/recipes/default.rb b/site-cookbooks/kosmos-base/recipes/default.rb index 24cd355..9a77128 100644 --- a/site-cookbooks/kosmos-base/recipes/default.rb +++ b/site-cookbooks/kosmos-base/recipes/default.rb @@ -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" diff --git a/site-cookbooks/kosmos_prometheus/recipes/node_exporter.rb b/site-cookbooks/kosmos_prometheus/recipes/node_exporter.rb index f365b78..2c76f68 100644 --- a/site-cookbooks/kosmos_prometheus/recipes/node_exporter.rb +++ b/site-cookbooks/kosmos_prometheus/recipes/node_exporter.rb @@ -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 From 1e018ac6b4e30c15acf20e2806e220da1aa22087 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Sat, 31 Oct 2026 14:33:13 +0400 Subject: [PATCH 2/2] Update nodes Pick up the refreshed node objects from the CVE-2026-98432 converge. --- nodes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nodes b/nodes index ee04075..5378c7f 160000 --- a/nodes +++ b/nodes @@ -1 +1 @@ -Subproject commit ee04075fbc5efecf0c199d79164b413beb321cfd +Subproject commit 5378c7ffc015bf2255a2ff5acef1c8eb9ef04e42