Skip to content

Instantly share code, notes, and snippets.

@m3nu
Last active May 3, 2026 18:39
Show Gist options
  • Select an option

  • Save m3nu/c19269ef4fd6fa53b03eb388f77464da to your computer and use it in GitHub Desktop.

Select an option

Save m3nu/c19269ef4fd6fa53b03eb388f77464da to your computer and use it in GitHub Desktop.
Ansible playbook for CVE-2026-31431 mitigation
# Mitigation for CVE-2026-31431 ("Copy Fail") — algif_aead LPE
# https://xint.io/blog/copy-fail-linux-distributions
# Apply: ansible-playbook playbooks/cve-2026-31431.yml
#
# One mitigation per OS family, plus a cleanup pass for hosts that received
# the (now-abandoned) systemd seccomp drop-ins in earlier runs.
#
# Tags:
# cve-kernel RHEL/Alma 9, 10: add initcall_blacklist=algif_aead_init
# to GRUB. No reboot — the arg becomes active on the
# next boot.
# cve-kernel-reboot Superset of cve-kernel: writes the arg AND reboots
# (idempotent — skipped if already in /proc/cmdline).
# cve-rmmod Debian family: install /etc/modprobe.d/disable-algif.conf
# and rmmod algif_aead so the module is gone now and
# cannot be reloaded.
# cve-systemd-remove All hosts: remove sshd.service.d/ and user@.service.d/
# cve-2026-31431.conf drop-ins, daemon-reload, restart
# sshd. user@.service is intentionally not restarted
# (would kill rootless pods).
#
# Default (no --tags): every block runs, gated by ansible_os_family /
# ansible_distribution_major_version. RHEL hosts get kernel + reboot +
# systemd-remove; Debian hosts get rmmod + systemd-remove.
#
# Cleanup once the vendor kernel is patched:
# RHEL/Alma: grubby --update-kernel=ALL --remove-args=initcall_blacklist=algif_aead_init && reboot
# Debian: rm /etc/modprobe.d/disable-algif.conf
---
- hosts: all
gather_facts: true
become: yes
serial: 1
tasks:
# --- kernel initcall blacklist (RHEL/Alma 9 and 10 only) ---
# Prevents algif_aead_init from running at boot so the AEAD handler is
# never registered with af_alg; bind(AF_ALG, ..., "aead") returns ENOENT
# system-wide. algif_aead is built into vmlinux on these releases, so a
# modprobe blacklist would do nothing.
- name: Apply kernel initcall blacklist mitigation (RHEL/Alma 9 and 10)
tags: [cve-kernel-reboot]
when:
- ansible_os_family == 'RedHat'
- ansible_distribution_major_version in ['9', '10']
block:
- name: Read current GRUB kernel args
ansible.builtin.command:
cmd: grubby --info=ALL
register: grubby_info
changed_when: false
tags: [cve-kernel, cve-kernel-reboot]
- name: Add initcall_blacklist=algif_aead_init to all kernels
ansible.builtin.command:
cmd: grubby --update-kernel=ALL --args=initcall_blacklist=algif_aead_init
when: "'initcall_blacklist=algif_aead_init' not in grubby_info.stdout"
tags: [cve-kernel, cve-kernel-reboot]
- name: Check whether running kernel has the mitigation active
ansible.builtin.command:
cmd: grep -qw initcall_blacklist=algif_aead_init /proc/cmdline
register: cmdline_check
changed_when: false
failed_when: false
- name: Reboot to activate the kernel mitigation
when: cmdline_check.rc != 0
block:
- name: Trigger reboot
ansible.builtin.shell: sleep 3; reboot
ignore_errors: true
changed_when: false
async: 1
poll: 0
- name: Wait for server to come back after reboot
ansible.builtin.wait_for_connection:
timeout: 900
delay: 40
register: reboot_result
- name: Reboot time
ansible.builtin.debug:
msg: "The system rebooted in {{ reboot_result.elapsed }} seconds."
- name: Verify running kernel now includes the mitigation
ansible.builtin.command:
cmd: grep -qw initcall_blacklist=algif_aead_init /proc/cmdline
changed_when: false
# --- Debian family modprobe blacklist + unload ---
# algif_aead is a loadable module on Debian/Ubuntu, so an install rule
# plus rmmod removes the attack surface immediately and prevents
# reloads (including auto-load via AF_ALG bind requests).
- name: Apply algif_aead modprobe blacklist + unload (Debian family)
tags: [cve-rmmod]
when: ansible_os_family == 'Debian'
block:
- name: Write modprobe blacklist for algif_aead
ansible.builtin.copy:
dest: /etc/modprobe.d/disable-algif.conf
owner: root
group: root
mode: '0644'
content: |
# CVE-2026-31431 mitigation. Prevents algif_aead from being loaded.
install algif_aead /bin/false
- name: Unload algif_aead if currently loaded
ansible.builtin.command: rmmod algif_aead
register: rmmod_result
failed_when:
- rmmod_result.rc != 0
- "'not currently loaded' not in (rmmod_result.stderr | default(''))"
- "'is not currently loaded' not in (rmmod_result.stderr | default(''))"
changed_when: rmmod_result.rc == 0
- name: Verify algif_aead is not loaded
ansible.builtin.shell: "! lsmod | awk '{print $1}' | grep -qx algif_aead"
changed_when: false
# --- remove systemd AF_ALG drop-ins ---
# Earlier iterations of this playbook installed RestrictAddressFamilies=~AF_ALG
# drop-ins on sshd.service and user@.service. The seccomp filter caused
# issues for legitimate workloads and has been abandoned. Removing the
# drop-ins is safe to run on hosts that never had them — file: state=absent
# is a no-op there.
- name: Remove systemd AF_ALG drop-ins
tags: [cve-systemd-remove]
block:
- name: Remove sshd.service AF_ALG drop-in
ansible.builtin.file:
path: /etc/systemd/system/sshd.service.d/cve-2026-31431.conf
state: absent
register: sshd_dropin_removed
- name: Remove user@.service AF_ALG drop-in
ansible.builtin.file:
path: /etc/systemd/system/user@.service.d/cve-2026-31431.conf
state: absent
register: user_dropin_removed
- name: Reload systemd if drop-ins were removed
ansible.builtin.systemd:
daemon_reload: yes
when: sshd_dropin_removed.changed or user_dropin_removed.changed
# Restart sshd so the running daemon drops the now-removed
# seccomp filter. Existing SSH sessions are not killed — only
# the listener restarts. user@<uid>.service is intentionally
# NOT restarted: that would kill rootless pods. The redundant
# filter on running per-user instances clears naturally on the
# next user-instance restart or host reboot.
- name: Restart sshd to drop the seccomp filter
ansible.builtin.systemd:
name: sshd
state: restarted
when: sshd_dropin_removed.changed
@m3nu
Copy link
Copy Markdown
Author

m3nu commented Apr 30, 2026

Updated to remove the systemd workaround, since it was causing issues with containers and the initcall_blacklist is cleaner. Also added Debian. For Debian no reboot is needed. For RHEL it needs a reboot because the module is built in.

@antoine-mesobfc
Copy link
Copy Markdown

It seems that RHEL 8 also use built-in module, at least v8.10.

@aapjeisbaas
Copy link
Copy Markdown

I can confirm that rhel 8.10 works with the playbook above. @antoine-mesobfc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment