
Роль Ansible и скрипт Python, которые обнаруживают уязвимость ядра Linux к CVE-2026-98432 и применяют правило nftables, отбрасывающее входящие IPv4 SYN-пакеты с IP-опциями.
Use-after-free в коде IPv4 TCP ядра Linux
(CVE-2026-98432, CVSS 9.8):
IP-опции, сохранённые из входящего SYN, могут быть освобождены, пока другой CPU
всё ещё читает их для построения SYNACK, поскольку два пути деструктора освобождают
их с помощью простого kfree() вместо ожидания льготного периода RCU.
Исправление в upstream —
e3f33b0a1d89
("ipv4: free inet_opt and ireq_opt after an RCU grace period").
По состоянию на 2026-10-29 ни для одной серии Ubuntu не выпущено исправленное ядро. Каждый затронутый статус в трекере Ubuntu гласит "work in progress" или просто уязвим, и на странице не указано никаких мер смягчения. Этот пакет и есть мера смягчения.
Оба конкурирующих пути читают ireq->ireq_opt — IP-опции IPv4, которые ядро сохранило
из входящего SYN (tcp_v4_save_options()): при построении SYNACK они читаются,
пока разрушение дочернего сокета может их освободить, а миграция request-сокета
(net.ipv4.tcp_migrate_req=1 или программа
BPF_SK_REUSEPORT_SELECT_OR_MIGRATE) разделяет, а затем освобождает те же
опции. Когда SYN не несёт IP-опций IPv4, ireq_opt равен NULL: освобождать нечего
и читать нечего. Мы изучили коммит с исправлением, прежде чем полагаться на это; обе
части исходят из SYN, который нёс IP-опции IPv4.
Таким образом, пакет устанавливает одно правило nftables, которое отбрасывает входящие IPv4 TCP SYN, несущие IP-опции, в собственной таблице, чтобы оно никогда не конфликтовало с правилами Docker, базовой системы или дистрибутива:
table inet samp_cve_2026_98432 {
chain prerouting {
type filter hook prerouting priority -300; policy accept;
ip hdrlength > 5 tcp flags & (syn | ack) == syn counter drop \
comment "CVE-2026-98432: drop inbound IPv4 SYN carrying IP options"
}
}
Проверено на реальном ядре перед выпуском: IPv4 SYN с опциями попадает в счётчик и отбрасывается; обычный SYN проходит нетронутым. TCP-трафик почти никогда не несёт IP-опции (некоторые типы опций повсеместно отбрасываются многими сетями, согласно RFC 7126), поэтому операционная цена правила близка к нулю.
pve,
которое страница Ubuntu не покрывает) сообщают unknown: читайте их
бюллетени. Само правило защитило бы любой хост Linux; таблицы вердиктов —
это то, что относится только к Ubuntu.Правило живёт в памяти ядра и не сохраняется на диск. Пакет мер
противодействия не должен писать в пути персистентности (юниты, cron), а
include-файл nftables загружается только там, где включена служба nftables
дистрибутива, что в этих парках не повсеместно. Итак: после
перезагрузки детекция видит правило отсутствующим (rule_live=0,
persistent=0 в каждой строке результата), и следующее применение его восстанавливает.
Повторный запуск apply после перезагрузок — это операционная модель, и строка
результата никогда не утверждает обратного.
Только чтение, размещается во временном каталоге, который затем удаляет, и сообщает:
uname -r, сопоставленное с версией его пакета)
против исправленных версий Ubuntu для каждого релиза и варианта. Исправление не выпущено
для релиза машины: no_fix. Исправленное ядро выпущено, но не
работает: vulnerable, с restart_needed=1, когда оно установлено и
ждёт перезагрузки (установленное исправление никогда не сообщается как fixed).fixed с workaround: true в
строке результата, пока правило покрывает неисправленное ядро; просто fixed,
когда работающее ядро исправлено.not_affected для релизов, которые трекер считает чистыми, unknown для релизов
и дистрибутивов, которые таблицы не покрывают.0 чисто (fixed, not_affected), 2 подвержено или
не определено (no_fix, vulnerable, unknown: никогда не чисто), 1 сам запуск
завершился неудачей, 130 прервано.syn_options_mitigation/files/samp_common.py — побайтовая копия
samp-common 1.0.4 (сравнения версий dpkg, rpm и apk,
строка результата, загрузка конфигурации). Его sha256 указан в
samp.yaml, и tests/test_samp_pack.py падает, если копия отличается. Никогда
не редактируйте его здесь; повторно вендорьте его с помощью scripts/vendor.py из samp-common.samp/v<версия> (этот:
samp/v1.0.1), подписанные ключом с аппаратной поддержкой. Субстрат
импортирует подписанный тег, никогда не ветку.Один шаг скрипта, одна строка результата. Пока работающее ядро не исправлено, оно
загружает правило; как только работает исправленное ядро, оно удаляет правило и
его таблицу. Оно не редактирует никаких файлов, кроме скрипта, библиотеки и конфигурации, которые
развёртывает (объявлены в touches файла samp.yaml), поэтому список резервных копий в строке
результата пуст. Удалить меру противодействия вручную — одна команда:
nft delete table inet samp_cve_2026_98432
ansible-playbook -i inventory play.yml # a play that runs the role
python3 syn_options_mitigator.py --config syn_options_config.yaml # detect
sudo python3 syn_options_mitigator.py --config syn_options_config.yaml --apply
Когда Ubuntu выпускает исправленные ядра, вставьте версии из трекера
в syn_options_fixed_versions в значениях по умолчанию роли (версии в работе
уже там в комментарии) и выпустите изменение как следующий
подписанный тег; после этого обнаружение начинает сообщать vulnerable/restart_needed
относительно них, а применение удаляет правило с машин, которые перезагружаются в
исправленное ядро.
python3 -m pytest tests/
Модульные тесты подменяют каждую внешнюю команду; контрактный тест
(tests/test_samp_pack.py, из samp-common) сверяет манифест с
ролью, выполняет реальное обнаружение из временного каталога и проверяет
точку входа apply на соответствие правилам одной строки результата и needrestart.
MIT. См. LICENSE.
PR и улучшения приветствуются -> сделайте форк и отправьте merge request.