Skip to content

Releases: rfxn/rfxn-defense

v3.0.2 - EL7 silent-failure closure + install-time ptrace fix

Choose a tag to compare

@rfxn rfxn released this 24 May 18:46

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). The 15-rfxn-defense-userns.conf drop-in is now gated out of EL7 builds; detect.sh removes stale files from prior installs.
  • sysctl -key silent-skip prefix mis-parsed by EL7 procps-ng 3.3.10 (added in 3.3.12). Both 99-rfxn-defense-userns.conf and 99-rfxn-defense-ptrace.conf were silent no-ops on EL7. userns sysctl is now distro-gated; ptrace.conf dropped the - prefix.
  • %posttrans sysctl loop never loaded ptrace.conf on any distro (regression introduced by the v3.0.1 ptrace-split). Replaced with sysctl --system. CVE-2026-46333 mitigation now actually applies at install time.
  • Auditor masking: systemd_restrict_namespaces returned silent SKIP "no tenant units found" on EL7 → now emits explicit "predates v235" reason or FAIL with stale drop-in list. userns_sysctl=OK now 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-rfxn for 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.2

New 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

Choose a tag to compare

@rfxn rfxn released this 24 May 14:29

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:

  • RestrictNamespaces became no (default: no restriction)
  • RestrictAddressFamilies collapsed 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_alg regex was a0=38 (decimal); auditctl -l
    prints a0=0x26 (hex). All 5 loaded rfxn_* rules misreported as
    MISSING. Switched to key-name match. Adds explicit OK reporting for
    the 4 non-AF_ALG rfxn_* 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 credits ld_preload_shim instead of misreporting n/a.
  • B6: systemd_restrict_namespaces no longer silently drops units
    whose RestrictNamespaces parses-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

Choose a tag to compare

@rfxn rfxn released this 23 May 17:13

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-autoupdate subpackage (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 targeted dnf upgrade rfxn-defense* from the rfxn-defense repo only. All output to journald via logger -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-rfxn sibling: byte-identical to retained RPM-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

Choose a tag to compare

@rfxn rfxn released this 23 May 06:54

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.so in 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_workload block, suppressed.sysctl_iouring is 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_state extended 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

Choose a tag to compare

@rfxn rfxn released this 22 May 21:10
  • 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

Choose a tag to compare

@rfxn rfxn released this 13 May 20:13

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-defense

The 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-defense

Validation

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.

Source

v2.0.1-2 - scriptlet hotfix (EL8 install regression)

Choose a tag to compare

@rfxn rfxn released this 08 May 23:11

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/bash on %posttrans modprobe, %posttrans systemd, %postun modprobe, %postun systemd.
  • spec: also escape %{version} -> %%{version} in %pretrans modprobe and %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 and 99-copyfail-defense.conf was deleted by RPM cleanup with no .rpmsave-v2.0.1 preserved. Surfaced by this hotfix's gh-pages-staging canary.
  • packaging/test-repo.sh: new assert_no_scriptlet_fail regression guard at every dnf install/upgrade/remove site - greps captured output for scriptlet failed, Error in (POST|PRE)*, and syntax error near and 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 -ch per-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

Choose a tag to compare

@rfxn rfxn released this 08 May 18:21

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-full sentinel — bypasses detection, applies all mitigations regardless
  • /var/lib/copyfail-defense/auto-detect.json — versioned detection state for SIEM ingest (schema v2)
  • Auditor posture.auto_detect exposes detected, suppressed, signals[], and force_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.html updated 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_detect

Released 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

Choose a tag to compare

@rfxn rfxn released this 08 May 07:21

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-enable

Upgrading from afalg-defense v1.0.x

sudo dnf upgrade -y copyfail-defense

The 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

Choose a tag to compare

@rfxn rfxn released this 30 Apr 15:32

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