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
@gaben0
Copy link
Copy Markdown

gaben0 commented Apr 30, 2026

Thanks a lot for sharing this. I wanted to add a comment for anyone who may be coming here to apply this remediation to HPC clusters running the Slurm job scheduler.

The remediation needs to be applied to slurmd as well on the compute nodes. Apply the second task in this playbook ("Deny AF_ALG for sshd-spawned processes") as a drop-in under /etc/systemd/system/slurmd.service.d/

@emwjacobson
Copy link
Copy Markdown

And don't forget to restart your slurmd service after adding the drop-in.

@m3nu
Copy link
Copy Markdown
Author

m3nu commented Apr 30, 2026

Updated to add a second mitigation for RHEL 9/10 as mentioned here: https://news.ycombinator.com/item?id=47956504

@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