Releases: rfxn/rfxn-defense
Release list
v3.0.2 - EL7 silent-failure closure + install-time ptrace fix
EL7 silent-failure closure. Empirically validated against a fresh CentOS 7.9.2009 VM (systemd 219, procps-ng 3.3.10, kernel 3.10).
Critical fixes (EL7-specific)
Four mitigation layers shipped through every prior 3.0.x build as silent no-ops on EL7. Defense-in-depth premise - "if a layer is shipped, it MUST actually work" - was being violated for the ssh-keysign-pwn primary mitigation (CVE-2026-46333) on every EL7 host since v3.0.0.
- systemd
RestrictNamespaces=silently ignored by systemd v219 (added in v235). The15-rfxn-defense-userns.confdrop-in is now gated out of EL7 builds;detect.shremoves stale files from prior installs. - sysctl
-keysilent-skip prefix mis-parsed by EL7 procps-ng 3.3.10 (added in 3.3.12). Both99-rfxn-defense-userns.confand99-rfxn-defense-ptrace.confwere silent no-ops on EL7. userns sysctl is now distro-gated; ptrace.conf dropped the-prefix. %posttrans sysctlloop never loadedptrace.confon any distro (regression introduced by the v3.0.1 ptrace-split). Replaced withsysctl --system. CVE-2026-46333 mitigation now actually applies at install time.- Auditor masking:
systemd_restrict_namespacesreturned silent SKIP "no tenant units found" on EL7 → now emits explicit "predates v235" reason or FAIL with stale drop-in list.userns_sysctl=OKnow distinguishes "by rfxn drop-in" from "by kernel/distro default" so operators don't get false confidence.
What's in the box
- 9 signed RPMs per EL (EL7 / EL8 / EL9 / EL10) = 36 RPMs, all GPG-signed by the Copyfail Project Signing Key (
D5E80F80) RPM-GPG-KEY-rfxnfor offline signature verification
Quick install
sudo curl -sSL https://rfxn.github.io/rfxn-defense/rfxn-defense.repo \
-o /etc/yum.repos.d/rfxn-defense.repo
sudo dnf upgrade -y rfxn-defense # from v3.0.1 → v3.0.2New permanent EL7 live-host test runner
packaging/test-el7-live.sh - 33 assertions across packaging sanity / auditor JSON / empirical mitigation probes / copyfail-defense 2.1.1 → rfxn-defense 3.0.2 upgrade path including stale-drop-in cleanup verification.
Full notes
See CHANGELOG section of the spec, FOLLOWUPS.md, and the live page at https://rfxn.github.io/rfxn-defense/.
v3.0.1 - systemd syntax + ptrace split + rootless FP hotfix
Hotfix release. Validated against two live EL10 hosts (cPanel tenant
fleet + admin host) before tagging.
Why this release
v3.0.0 (and all prior 2.0.2+ releases) had three latent gaps in the
mitigation layer that lived dormant because the auditor and the build
pipeline both failed to surface them. Live-host validation against
production-like systems uncovered:
B0 (CRITICAL): systemd drop-in deny-list syntax broken
Our drop-ins shipped RestrictNamespaces=~user ~net and
RestrictAddressFamilies=~AF_ALG ~AF_KEY ~AF_RDS. systemd parses the
first as: ~ prefix, then tokens user and ~net — the second token
is invalid (~ is only a prefix character), so systemd logs a
parse error and DROPS THE WHOLE DIRECTIVE. Net result on every
shipped host since v2.0.2:
RestrictNamespacesbecameno(default: no restriction)RestrictAddressFamiliescollapsed to just~AF_ALG
Empirical proof on test unit:
- Broken syntax:
unshare(NEWUSER|NEWNET)succeeds (exit 0) - Fixed syntax:
unshare(NEWUSER|NEWNET)fails (exit 1)
cf2 / DF-ESP / Fragnesia / PinTheft systemd-layer coverage was
non-functional. Fixed by using ~A B C (single ~ prefix per
systemd.exec(5)).
B1 (CRITICAL): kernel.yama.ptrace_scope=2 lost to userns suppression
kernel.yama.ptrace_scope=2 (ssh-keysign-pwn primary mitigation,
CVE-2026-46333)
was bundled inside rfxn-sysctl-userns.conf. When auto-detect
suppressed the userns drop-in (rootless containers / Flatpak /
firejail / browser detected), the whole file was removed —
including ptrace_scope. keysign-pwn coverage missing on the bulk of
modern Linux hosts.
Split into independent always-applied
/etc/sysctl.d/99-rfxn-defense-ptrace.conf. ptrace_scope has no
known legitimate suppression criteria (gdb-as-root still works at
ptrace_scope=2); shipping it always-on.
B2 (CRITICAL): rootless detector false-positive on hosts with podman installed-but-unused
detect_rootless_containers Signal 2 fired on the storage-tree
initialization that containers-common / podman creates on
install: tmp/, overlay-containers/containers.lock,
overlay-images/images.lock, libpod/, db.sql. Zero actual
containers, signal still fired. Combined with B1: ptrace_scope was
suppressed on every RHEL host with podman pre-installed.
Tightened: require a non-lockfile entry inside
overlay-containers/, overlay-images/, vfs-containers/, or
vfs-images/ — those directories receive children only when a
real container or image is created.
Auditor cosmetic fixes (rfxn-local-check)
- B4:
audit_rule_af_algregex wasa0=38(decimal);auditctl -l
printsa0=0x26(hex). All 5 loadedrfxn_*rules misreported as
MISSING. Switched to key-name match. Adds explicit OK reporting for
the 4 non-AF_ALGrfxn_*keys. - B5: cf1 applicability now derives from kernel sink reachability
(algif_aead_state) instead of userspace (af_alg_socket). Shim
returning EPERM is a mitigation, not "sink not reachable". cf1 row
now correctly creditsld_preload_shiminstead of misreporting n/a. - B6:
systemd_restrict_namespacesno longer silently drops units
whoseRestrictNamespacesparses-but-doesn't-block (cascade from B0). - B8: Auditor banner reframed to "rfxn-defense host posture
auditor / cf1 / cf2 / DF-ESP / DF-RxRPC / Fragnesia / PinTheft /
DirtyDecrypt / keysign-pwn" (was: "Copy Fail bug-class Checker /
cf1 / cf2 / Dirty Frag", pre-rename branding covering 3 of 7
classes).
Upgrading
sudo dnf upgrade -y rfxn-defense
sudo systemctl daemon-reload
sudo sysctl --system # apply the new ptrace.conf drop-in
Live re-validation: every host that auto-updated from v3.0.0 should
see kernel.yama.ptrace_scope=2 applied (verify:
cat /proc/sys/kernel/yama/ptrace_scope), systemctl show -p RestrictNamespaces sshd.service returning a non-no value, and
auto-detect.json either no longer reporting rootless (if no real
containers exist) or correctly reporting it (if real containers do
exist).
Asset list
EL7/EL8/EL9/EL10 binary RPMs + SRPM, 36 assets, all signed with the
Copyfail Project Signing Key
(6001 1CDC EA2F F52D 975A FDEE 6D30 F32C D5E8 0F80).
rfxn-defense v3.0.0 - responsive defense layer for Linux
Major release: project rename copyfail-defense -> rfxn-defense + reframe as a responsive defense layer for Linux.
rfxn-defense ships kernel-LPE mitigations as 0days land, closing the delta between public CVE disclosure and the kernel/software vendor patch set landing on hosts. A 4-hourly auto-update cron keeps installed hosts current with each new release: install once, stay covered.
Upgrading from copyfail-defense (2.x) or afalg-defense (1.0.x)
sudo dnf upgrade -y rfxn-defense
The double Obsoletes:/Provides: chain on every subpackage handles the rename automatically. State migrates: /var/lib/copyfail-defense/ -> /var/lib/rfxn-defense/, /etc/copyfail/ -> /etc/rfxn-defense/ via %pretrans mv -n (idempotent).
SIEM operator action required
Audit-key rename — update ausearch -k <key> queries:
| Legacy (v2.x) | New (v3.0.0+) |
|---|---|
copyfail_afalg |
rfxn_afalg |
copyfail_afkey |
rfxn_afkey |
copyfail_afrxrpc |
rfxn_afrxrpc |
copyfail_afrds |
rfxn_afrds |
copyfail_pidfd_getfd |
rfxn_pidfd_getfd |
What's new
rfxn-defense-autoupdatesubpackage (NEW, hard-Required by meta). Ships/etc/cron.d/rfxn-defense-update+/usr/libexec/rfxn-defense/update.sh. Every 4 hours (jittered 0-600s, flock-protected, 600s timeout), runs targeteddnf upgrade rfxn-defense*from the rfxn-defense repo only. All output to journald vialogger -t rfxn-defense-update. Opt-out:touch /etc/rfxn-defense/auto-update.disabled.- DirtyDecrypt (CVE-2026-31635) cross-stamp: verified coverage by existing rxrpc cuts. No new primitive needed.
RPM-GPG-KEY-rfxnsibling: byte-identical to retainedRPM-GPG-KEY-copyfail; .repo lists both gpgkey URLs so dnf accepts either.
Coverage (unchanged from v2.1.1)
Copy Fail page-cache family: cf1 (CVE-2026-31431), cf2 / Dirty Frag-ESP (CVE-2026-43284), Dirty Frag-RxRPC (CVE-2026-43500), Fragnesia, PinTheft, DirtyDecrypt (CVE-2026-31635). FD-theft class: ssh-keysign-pwn (CVE-2026-46333).
Build matrix
EL7 / EL8 / EL9 / EL10, x86_64. 32 binary RPMs + 4 SRPMs, all signed.
GPG signing key fingerprint (unchanged through v3.0.0):
6001 1CDC EA2F F52D 975A FDEE 6D30 F32C D5E8 0F80
v2.1.1: io_uring auto-detect
copyfail-defense v2.1.1
Promote kernel.io_uring_disabled = 2 from operator opt-in (v2.1.0, commented) to auto-applied with layered suppression.
What changed
- detect.sh new
detect_io_uring_workload()with three runtime signals:liburing.soin any/proc/*/maps, known-consumer binary list (postgres, scylla, mariadbd, dockerd, redis-server, nginx, envoy, rabbitmq-server), io_uring-named systemd unit. - decide_suppressions() priority chain: env force > env suppress > kernel gate (<6.6) > io_uring workload > rootless containers > userns consumers.
- sysctl split into two drop-in files so io_uring suppresses independently of userns/ptrace_scope. New
/etc/sysctl.d/99-copyfail-defense-iouring.conf(single key) alongside the existing userns drop-in. - JSON state
detected.io_uring_workloadblock,suppressed.sysctl_iouringis now a dict{suppressed, reason}(vs plain bool for the others) so the suppression reason surfaces for operator triage. Suppression reasons:force_full,env_force,env_suppress,kernel_too_old,io_uring_workload,rootless_containers,userns_consumers,none. - Auditor new
check_io_uring_disabled(MITIGATION);check_auto_detect_stateextended to handle dict-shape suppression entries. - gh-pages older RPMs (v2.0.x + v2.1.0) moved to
/repo/{EL}/x86_64/archive/so dnf install resolves only v2.1.1. Archive URLs remain reachable for backward-compatible curl links.
Operator override
- `CFD_FORCE_IOURING_DISABLE=1 copyfail-redetect` — apply regardless of detection
- `CFD_SUPPRESS_IOURING_DISABLE=1 copyfail-redetect` — suppress regardless of detection
- `/etc/copyfail/force-full` sentinel — apply everything (existing global override; beats env-suppress)
Install
```
curl -fL -o /etc/yum.repos.d/copyfail.repo https://rfxn.github.io/copyfail/copyfail.repo
dnf install copyfail-defense
```
Build matrix
EL7 / EL8 / EL9 / EL10. 7 binary RPMs + 1 SRPM per EL (32 artifacts total).
v2.1.0 - PinTheft + ssh-keysign-pwn coverage + EL7
- Add PinTheft (RDS + io_uring) coverage: rds/rds_tcp/rds_rdma modprobe
blacklist, ~AF_RDS in always-on systemd 10-* drop-in, copyfail_afrds
auditd rule on AF_RDS=21 socket creation. - Add ssh-keysign-pwn (CVE-2026-46333) coverage: kernel.yama.ptrace_scope=2
sysctl key, copyfail_pidfd_getfd auditd rule on syscall 438 numeric. - Add commented-out kernel.io_uring_disabled=2 secondary mitigation
(operator opt-in; Linux 6.6+ only). - Bug-class taxonomy gains pintheft (Copy Fail class) and keysign-pwn
(FD-theft class - first member). - detect.sh adds detect_rds_workload() with three signals (Oracle oratab,
crsctl binary, rds.ko already loaded). Suppresses modprobe-rds on
Oracle Grid / HPC hosts; JSON state schema_version stays "2" (backward
compatible). - Auditor adds check_ptrace_scope, check_pidfd_getfd_auditd_rule,
check_rds_modprobe, check_af_rds_restrict. _aggregate_bug_classes()
extended with pintheft + keysign-pwn entries. - Build matrix expands to EL7/8/9/10. EL7 uses custom mock chroot with
vault.centos.org URLs; falls back to native rpmbuild if vault unreachable. - test-repo.sh adds IMAGE[7]=quay.io/centos/centos:7, defaults to (7 8 9 10),
adds new assertions for AF_RDS in 10-* drop-in, ptrace_scope sysctl,
both new audit-rule keys, rds-host suppression scenario.
v2.0.2 — Fragnesia / Dirty Frag-ESP coverage
copyfail-defense 2.0.2 — Fragnesia / Dirty Frag-ESP coverage
Three Fragnesia / Dirty Frag bug-class hardening additions driven by
the 2026-05-08..2026-05-13 advisory wave (Wiz, Sysdig, Red Hat
RHSB-2026-003, CloudLinux, AWS, Microsoft).
CVE map
| Class | CVE | Sink |
|---|---|---|
| cf1 (Copy Fail) | CVE-2026-31431 | algif_aead AEAD scratch-write |
| cf2 / Dirty Frag-ESP | CVE-2026-43284 | esp_input skip_cow path |
| Dirty Frag-RxRPC | CVE-2026-43500 | rxkad_verify_packet_1 in-place pcbc(fcrypt) |
| Fragnesia | (no CVE yet — same surface as CVE-2026-43284) | espintcp ULP after splice |
What's new
copyfail-defense-sysctl (new subpackage, meta-Requires) —
host-wide /etc/sysctl.d/99-copyfail-defense-userns.conf disabling
unprivileged user-namespace creation:
-user.max_user_namespaces = 0
-kernel.unprivileged_userns_clone = 0
-kernel.apparmor_restrict_unprivileged_userns = 1
The - prefix on each key silently skips unknown keys on a given
kernel (sysctl.d(5)). Closes the CLONE_NEWUSER prerequisite of
the cf2 / DF-ESP / Fragnesia chain host-wide, complementing the
per-tenant-unit RestrictNamespaces=~user.
Auto-suppressed when any userns consumer is detected:
| Workload | Detection signal |
|---|---|
| Rootless containers | /home/*/.local/share/containers/storage/, /var/lib/containers/storage/ (mtime<90d), /run/user/<UID>/containers/, podman.socket enabled |
| Flatpak | non-empty /var/lib/flatpak/{app,runtime}, per-user ~/.local/share/flatpak/app |
| firejail | /usr/bin/firejail present |
| Desktop browsers | /usr/bin/{chromium,chromium-browser,google-chrome,firefox,firefox-esr} present |
copyfail-defense-audit (new subpackage, meta-Recommends) —
auditd tripwire rules at /etc/audit/rules.d/99-copyfail-defense.rules
catching socket(AF_ALG/AF_KEY/AF_RXRPC) syscalls from
auid≥1000. Real value on hosts where modprobe blacklists are
auto-suppressed (IPsec/AFS workloads) and the kernel sink stays
reachable.
ausearch -k copyfail_afalg # cf1 socket prep
ausearch -k copyfail_afkey # cf2 / DF-ESP / Fragnesia SA-config via legacy AF_KEY
ausearch -k copyfail_afrxrpc # DF-RxRPC socket prep~AF_KEY added to RestrictAddressFamilies on the always-on
10-* systemd drop-in and the containers example. Closes the legacy
PF_KEYv2 SA-config path used by Dirty Frag / Fragnesia (the other
SA-config path — XFRM netlink — still requires CAP_NET_ADMIN).
Upgrading
sudo dnf upgrade copyfail-defenseThe auto-detect re-runs on upgrade and applies/suppresses the new
sysctl drop-in based on the host's userns-consumer signal. Use
copyfail-redetect to refresh detection after fleet changes
(post-Flatpak install, post-firejail install, etc.).
To force a full install (skip detection):
sudo mkdir -p /etc/copyfail
sudo touch /etc/copyfail/force-full
sudo dnf install -y copyfail-defenseValidation
Validated end-to-end via packaging/test-repo.sh against this exact
build tree before publish — 39/39 OK across EL8/EL9/EL10
including the new userns_consumer scenario (Flatpak signal trips
suppression; per-unit cuts retained). Sentinel review of source
commits closed pre-publish: 2 Majors + 5 Minors fixed inline,
4 deferred with rationale in FOLLOWUPS.md.
Signed artifacts
All RPMs + repository metadata (repomd.xml.asc) detach-signed by
the Copyfail Project Signing Key.
- Fingerprint:
6001 1CDC EA2F F52D 975A FDEE 6D30 F32C D5E8 0F80 - Key URL: https://rfxn.github.io/copyfail/RPM-GPG-KEY-copyfail
Source
- Repo: https://github.com/rfxn/copyfail
- DNF repo: https://rfxn.github.io/copyfail/
- Spec changelog: see
packaging/copyfail-defense.spec%changelog
v2.0.1-2 - scriptlet hotfix (EL8 install regression)
Hotfix release - same Version (2.0.1), Release bump (1 -> 2)
No functional change beyond the packaging interpreter fix. Operators on EL/Alma 8 should dnf upgrade copyfail-defense to land 2.0.1-2 - the prior 2.0.1-1 RPMs have been removed from the gh-pages dnf repo and replaced with 2.0.1-2.
Why
Reported by Jamie Sexton on AlmaLinux 8 stage hosts: 2.0.1-1's %posttrans and %postun scriptlets for -modprobe and -systemd aborted with:
```
/var/tmp/rpm-tmp.iIkADz: line 7: syntax error near unexpected token `>'
/var/tmp/rpm-tmp.iIkADz: line 7: `/usr/libexec/copyfail-defense/detect.sh apply modprobe 2> >(tee /dev/stderr \
```
Cause: the proc-sub idiom 2> >(tee /dev/stderr | logger ...) added in 2.0.1-1 per D-55 (surface detect.sh stderr to dnf output) requires bash; RPM scriptlets default to /bin/sh, which on EL/Alma 8 is bash invoked in POSIX mode where process substitution is a syntax error.
Impact on hosts that hit this: the install/remove appeared to succeed but the conditional drop-ins (cf2-xfrm, rxrpc, userns) were never suppressed and auto-detect.json was never written. Always-on cuts (cf1, the 10-* systemd drops) DID land via RPM's normal %config install path, so the cf1 mitigation was active even on broken hosts.
What changed
- spec: declare
-p /bin/bashon%posttrans modprobe,%posttrans systemd,%postun modprobe,%postun systemd. - spec: also escape
%{version}->%%{version}in%pretrans modprobeand%pretrans systemd. Pre-existing 2.0.1-1 bug: RPM expanded the unescaped macro at build time to the literal new-package version, so the v2.0.0 -> v2.0.1 monolithic-file rename guard never fired and99-copyfail-defense.confwas deleted by RPM cleanup with no.rpmsave-v2.0.1preserved. Surfaced by this hotfix's gh-pages-staging canary. - packaging/test-repo.sh: new
assert_no_scriptlet_failregression guard at every dnf install/upgrade/remove site - greps captured output forscriptlet failed,Error in (POST|PRE)*, andsyntax error nearand fails loudly. Closes the canary gap that let 2.0.1-1 ship without catching this class of bug. Also fixed two pre-existing test bugs (grep -chper-file count instead of summed integer; the systemd-only scenario asserted -modprobe was NOT pulled, but the meta package's umbrella Requires forces it).
Validation
End-to-end against this exact build's RPMs (REPO_URL pointed at a local httpd serving the staging tree before push): all 33 scenarios green on EL8/9/10 (run_test_in + upgrade + 9 detection scenarios, including subuid_no_storage, force_full, redetect, split_upgrade, systemd_only).
Assets
18 signed RPMs (5 binary subpackages + SRPM, x EL8/9/10). All signed with GPG key 6001 1CDC EA2F F52D 975A FDEE 6D30 F32C D5E8 0F80.
dnf operators on the copyfail repo will get 2.0.1-2 automatically on next refresh; manual operators can grab the per-EL RPMs below.
v2.0.1 — auto-detect IPsec / AFS / rootless workloads
v2.0.1 — auto-detect IPsec / AFS / rootless workloads
A focused hotfix that replaces the v2.0.0 README's operator-driven
Override paths section with package-driven workload detection. On
hosts running IPsec, AFS, or rootless containers, %posttrans now
auto-suppresses the conflicting drop-in/blacklist; safe mitigations
remain active everywhere.
What it detects
| Class | Signals |
|---|---|
| IPsec | systemctl is-enabled for strongswan / strongswan-starter / strongswan-swanctl / ipsec / libreswan / openswan / pluto, OR /etc/ipsec.conf with conn stanza, OR non-empty /etc/swanctl/conf.d/, /etc/ipsec.d/, /etc/strongswan/conf.d/, /etc/strongswan.d/ |
| AFS | systemctl is-enabled openafs-client / openafs-server / kafs / afsd, OR /etc/openafs/CellServDB, OR /etc/openafs/ThisCell, OR /etc/krb5.conf.d/openafs*, OR /proc/fs/afs |
| Rootless containers | ~/.local/share/containers/storage/overlay-containers (recent mtime), OR /var/lib/containers/storage (recent mtime), OR /run/user/<UID>/containers/, OR podman.socket enabled |
NOT detected: /etc/subuid (shadow-utils populates it for every useradd, ~100% FP rate on cPanel/hosting fleets), docker group membership (signals rootful daemon access, not rootless mode), FRR (BGP-only deployments dominate; FP > FN on cf2 protection).
What it suppresses, when
| Rung | Suppressed when |
|---|---|
99-copyfail-defense-cf2-xfrm.conf (esp4/esp6/xfrm_user/xfrm_algo modprobe) |
IPsec detected |
99-copyfail-defense-rxrpc.conf (rxrpc modprobe) |
AFS detected |
12-copyfail-defense-rxrpc-af.conf (~AF_RXRPC systemd, all 5 tenant units) |
AFS detected (preserves aklog / kinit -A / userspace AFS tooling) |
15-copyfail-defense-userns.conf (~user ~net, only user@.service.d/) |
rootless containers detected |
The cf1 modprobe blacklist, ~AF_ALG restriction, and base systemd hardening (~mount, ~swap, etc.) remain unconditional.
Operator levers
/usr/sbin/copyfail-redetect— re-runs detection (e.g., after enabling IPsec post-install)/etc/copyfail/force-fullsentinel — bypasses detection, applies all mitigations regardless/var/lib/copyfail-defense/auto-detect.json— versioned detection state for SIEM ingest (schema v2)- Auditor
posture.auto_detectexposesdetected,suppressed,signals[], andforce_full_active
Upgrade safety
%pretrans renames v2.0.0 monolithic conf files to <path>.rpmsave-v2.0.1 (NOT rm). Operator hand-edits to v2.0.0 files survive on disk for inspection.
cmp_and_install preserves operator hand-edits to conditional drop-ins: detect.sh skips overwrite with a logged WARN if the deployed file diverges from the template (no chattr +i workaround needed).
Architecture
/usr/libexec/copyfail-defense/detect.sh (meta package, single shared helper) takes apply <scope> arguments where scope is modprobe / systemd / both. Each subpackage's %posttrans passes its own scope. JSON emission via python3 (NUL-delimited stdin pipe to survive bash command-substitution stripping).
Documentation
- README "Override paths" → "Auto-detection of conflicting workloads"
- gh-pages
index.htmlupdated with v2.0.1 lede + download tables - SPEC §12 D-27..D-58 captures all design decisions for the hotfix
Verification
sudo dnf upgrade copyfail-defense
journalctl -t copyfail-defense | tail # see what was detected and suppressed
sudo copyfail-local-check --json | jq .posture.auto_detectReleased from commit e16f739, built across centos-stream+epel-{8,9,10}-x86_64 mock chroots; 18 RPMs signed by the Copyfail Project Signing Key (fingerprint 6001 1CDC EA2F F52D 975A FDEE 6D30 F32C D5E8 0F80).
copyfail-defense v2.0.0 - cf1 + cf2 + Dirty Frag
copyfail-defense v2.0.0 — cf1 + cf2 + Dirty Frag
Pivot from afalg-defense (CVE-2026-31431-only) to a copyfail-defense umbrella covering the full Copy Fail bug class:
| CVE | Sink | Privilege | |
|---|---|---|---|
| cf1 | CVE-2026-31431 | algif_aead AEAD scratch-write |
none |
| cf2 | (no CVE yet) | esp_input skip_cow path |
CAP_NET_ADMIN via unshare(NEWUSER|NEWNET) |
| Dirty Frag-ESP | (embargo broken) | same as cf2 | same as cf2 |
| Dirty Frag-RxRPC | (no CVE, no upstream patch) | rxkad_verify_packet_1 in-place pcbc(fcrypt) |
none |
Install
sudo curl -sSL https://rfxn.github.io/copyfail/copyfail.repo \
-o /etc/yum.repos.d/copyfail.repo
sudo dnf install -y copyfail-defense
sudo /usr/sbin/copyfail-shim-enableUpgrading from afalg-defense v1.0.x
sudo dnf upgrade -y copyfail-defenseThe Obsoletes: / Provides: chain performs the rename swap automatically and pulls in the new subpackages. Epoch: 1 is introduced in 2.0.0 and persists in all future releases. Compat metadata (Provides: afalg-defense*) retained through the 2.0.x release line.
Subpackages
| Subpackage | Coverage |
|---|---|
copyfail-defense-shim |
LD_PRELOAD AF_ALG block (cf1 primary) |
copyfail-defense-modprobe |
/etc/modprobe.d/99-copyfail-defense.conf cuts algif_aead/authenc/authencesn/af_alg/esp4/esp6/xfrm_user/xfrm_algo/rxrpc |
copyfail-defense-systemd |
drop-ins for user@/sshd/cron/crond/atd: RestrictAddressFamilies=~AF_ALG ~AF_RXRPC + RestrictNamespaces=~user ~net + SystemCallFilter=~@swap; container-runtime drop-ins ship as opt-in examples |
copyfail-defense-auditor |
read-only host posture auditor, 26 checks across 5 categories, JSON output gains posture.bug_classes_covered (SIEM array) + posture.bug_classes (per-class map with kernel_sink + per-layer booleans) |
Override paths
The default install applies RestrictNamespaces=~user ~net to user@.service — this breaks rootless podman/buildah. Override per-unit with a 20-override.conf drop-in (see README). IPsec / AFS workloads similarly require operator review of the modprobe drop file.
Signing
All RPMs and repodata are GPG-signed with the Copyfail Project Signing Key:
fingerprint: 6001 1CDC EA2F F52D 975A FDEE 6D30 F32C D5E8 0F80
key id: 6D30F32CD5E80F80
algo: RSA-4096
expires: 2028-04-29
uid: Copyfail Project Signing Key <proj@rfxn.com>
gpgcheck=1 (per-RPM) + repo_gpgcheck=1 (detached repomd.xml.asc over the metadata) — both enforced in the published .repo.
Two adversarial review cycles
v2.0.0 went through /r-vpe pipeline orchestration (intake → fresh /r-spec → fresh /r-plan → /r-build) plus two adversarial sentinel review cycles. Every fix from cycle 1 was verified clean in cycle 2; cycle 2 surfaced 2 must-fix regressions which were also addressed before sign. See SPEC.md §10 for the full decision evolution and PLAN.md self-review sections for the issues addressed.
afalg-defense 1.0.1
First tagged release of the copyfail / afalg-defense package family.
Userspace defense-in-depth primitives for CVE-2026-31431 ("Copy Fail")
and the broader AF_ALG attack surface, packaged as signed RPMs for
EL8 / EL9 / EL10.
🔬 Deep-dive writeup: Copy Fail (CVE-2026-31431) — rfxn.com/research.
Kernel-level mechanics, exploit primitives, and why the userspace
shim closes the practical attacker windows.
Signing key
fingerprint: 6001 1CDC EA2F F52D 975A FDEE 6D30 F32C D5E8 0F80
uid: Copyfail Project Signing Key <proj@rfxn.com>
algorithm: RSA-4096, expires 2028-04-29
key file: https://rfxn.github.io/copyfail/RPM-GPG-KEY-copyfail
Install via DNF repository
A single `.repo` works on EL8/EL9/EL10 and performs full signature
checking automatically:
```sh
sudo curl -sSL https://rfxn.github.io/copyfail/copyfail.repo
-o /etc/yum.repos.d/copyfail.repo
sudo dnf install -y afalg-defense
```
dnf pulls the public key from `gpgkey=` on first use and prompts to
trust it. Cross-check the fingerprint against the value above before
accepting.
Activation is an explicit operator action
The shim is installed but not auto-enabled by `%post`. After
`dnf install`:
```sh
sudo /usr/sbin/copyfail-shim-enable # smoke-tests, then writes /etc/ld.so.preload
sudo /usr/sbin/copyfail-shim-disable # reverses it
```
Out-of-band verification
```sh
curl -sSL https://rfxn.github.io/copyfail/RPM-GPG-KEY-copyfail
| sudo rpm --import /dev/stdin
rpm -K afalg-defense-1.0.1-1.el9.x86_64.rpm
expect: digests signatures OK
```
Verify the block is live
```sh
python3 -c 'import socket; socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0)'
expect: PermissionError [Errno 1] Operation not permitted
```
Packages
| Package | Arch | Contents |
|---|---|---|
| `afalg-defense` | x86_64 | meta - pulls shim + auditor |
| `afalg-defense-shim` | x86_64 | `/usr/lib64/no-afalg.so` + `copyfail-shim-{enable,disable}` |
| `afalg-defense-auditor` | noarch | `/usr/sbin/copyfail-local-check` |
Per-EL binary RPMs are independently compiled against each
distribution's glibc (EL8: glibc 2.28 with split libdl; EL9/EL10:
glibc 2.34+ with merged libdl). Do not cross-install across ELs.
See README.md
for the defense-in-depth ladder this package fits into.
rfxn.com | forged in prod | Ryan MacDonald