Releases: pyradius/pyrad2
3.1
What's Changed
- Update links to reflect pyradius domain by @nicholasamorim in #40
- FreeRadius Conformance Tests and Fixes by @nicholasamorim in #41
- Support for ipv4prefix encoding, fixes for Proxy, retry for Sync client by @nicholasamorim in #42
- Refactor EAP code by @nicholasamorim in #43
- Add CHAP, EAP-GTC, MS-CHAPv2, EAP-MSCHAPv2 by @nicholasamorim in #44
- 3.1 Release by @nicholasamorim in #45
Full Changelog: 3.0...3.1
3.0
If you upgrade from 2.5 without code changes, expect:
- UDP servers will drop
Access-Requestpackets that don't carryMessage-Authenticator. Passrequire_message_authenticator=Falseto restore the old default. - UDP clients will refuse
Access-Accept/Reject/Challengereplies missingMessage-Authenticator. Passenforce_ma=False. Serverwill reject packets whose Request Authenticator fails MD5 verification. Passenable_pkt_verify=False.RadSecServer/RadSecClientwon't accept TLS 1.2 connections unless you passminimum_tls_version=ssl.TLSVersion.TLSv1_2.- Existing log pipelines watching for
[pyrad2 trace]lines onPYRAD2_TRACE=1will go silent untilPYRAD2_TRACE_UNSAFE=1is also set.
This is a security and defaults overhaul. Every UDP server / client in pyrad2 now ships with BlastRADIUS-safe defaults out of the box, RadSec deployments default to TLS 1.3, and the sync server now verifies request authenticators before invoking your handlers , matching the long-standing ServerAsync behaviour. All of these are observable behaviour breaks for existing deployments; the section below documents each opt-out.
Security defaults (BREAKING)
- BlastRADIUS (CVE-2024-3596) mitigated by default.
Server,ServerAsync,Client, andClientAsyncnow default to enforcingMessage-Authenticatoron everyAccess-Requestand on the matchingAccess-Accept/Reject/Challengereply. To bridge a legacy NAS that doesn't emit the attribute, passrequire_message_authenticator=False(servers) orenforce_ma=False(clients). - Sync
Servernow verifies request authenticators by default. MirrorsServerAsync.enable_pkt_verifywhich previously was the only side that ran the check. Passenable_pkt_verify=Falseto opt out for legacy NASes that emit malformed authenticators. - RadSec defaults to TLS 1.3.
RadSecServer.DEFAULT_MINIMUM_TLS_VERSIONandRadSecClient.DEFAULT_MINIMUM_TLS_VERSIONare nowssl.TLSVersion.TLSv1_3(RFC 9325 deprecates TLS 1.1-, treats 1.2 as legacy; RFC 9750 mandates 1.3 for RADIUS/1.1). Passminimum_tls_version=ssl.TLSVersion.TLSv1_2to bridge a legacy peer that can't yet negotiate 1.3. The floor is auto-promoted to 1.3 whenradius_versionsincludesV1_1, regardless. - Constant-time MAC and MD5 comparisons across the verify path. All switched from
==tohmac.compare_digest, closing a timing-side-channel that let an off-path attacker probe valid MACs one byte at a time. - Zero-authenticator Access-Request rejected.
AuthPacket.verify_auth_requestnow rejects v1.0 Access-Requests whose Request Authenticator is all-zero.Packet.salt_cryptno longer falls back to a zero authenticator when one is missing , it callscreate_authenticator()so salt-encrypted attributes (Tunnel-Password, MS-MPPE keys) can't be recovered without the shared secret. PYRAD2_TRACEnow requires a second-step acknowledgement. SettingPYRAD2_TRACE=1no longer activates the wire trace on its own. The trace dumps the Request Authenticator and obfuscatedUser-Passwordbytes; with the shared secret known (commonly to anyone reading the log archive), the plaintext is fully recoverable. SetPYRAD2_TRACE_UNSAFE=1alongside to acknowledge and enable. A warning is logged on activation.
Security hardening
$INCLUDEsandboxing.Dictionary/DictFilenow confine$INCLUDEresolution to a base directory (defaults to the entry-point file's parent; configurable viainclude_base_dir=). A dictionary with$INCLUDE /etc/passwdor$INCLUDE ../../fooraisesParseError. The entry-point file is exempt , it defines the base.ParseErrorsignature tightened. The old**dataswallowsilently droppedname=,path=, and similar typos. The new signature explicitly acceptsfile=andline=; the filename now consistently surfaces in error messages.- Vendor-id range check.
DictionaryrejectsVENDORdeclarations outside0..0xFFFFFF(RFC 2865 §5.26 SMI PEN range) instead of silently passing through values that later corrupt the VSA encoder. eap.password_from_packetno longer falls back to User-Name. ReturningUser-Nameas the EAP-MD5 secret silently mis-keyed the challenge; any observer who saw the request could reproduce the digest. RaisesPacketErrorwhenUser-Passwordis absent.pw_decryptwarns on shared-secret mismatch. Non-UTF-8 bytes after de-obfuscation almost always indicate the receiver's secret doesn't match the sender's. The function now logs aWARNINGonce per call and continues with a lossy decode so legacy handlers don't crash.- Dedup cache failed-handler behaviour documented. A handler that exits without sending a reply still drops its in-flight marker, so a retransmission within the TTL window is processed fresh rather than suppressed , bug acknowledged.
Architecture
- New
pyrad2.router.RequestRouterunifies the dispatch state machine thatServerandServerAsyncpreviously each owned separately. - Packet subclass deduplication.
- Cross-client factory deduplication.
Per-transport identifier management
DatagramProtocolClient.create_idscans for a free slot instead of blindly returning(packet_id + 1) % 256. Raises a new typedIdentifierExhausted(inpyrad2.exceptions) when all 256 slots on a single (source IP, source port) flow are in flight. The bare-Exceptioncollision error insend_packetis now also typed.- Module-level
CURRENT_IDis thread-safe. Athreading.Lockserialises the increment so concurrentPacket()construction across threads can't read+write the same counter and end up with colliding ids.
Performance
- RFC 2865 keystream loops rewritten for
_salt_en_decrypt,pw_crypt, andpw_decrypt. The byte-at-a-timebytes((hash[i] ^ buf[i],))concat chain is replaced by an int-XOR +bytearrayaccumulator. ~3× faster on User-Password and Tunnel-Password sized inputs. - Asyncio deprecation cleanup.
DX / quality
- Sync
Client._send_packetno longer mutates the caller'sAcct-Delay-Time. The retry loop still bumps the value while a request is in flight, but it now snapshots the caller's original list (or absence) and restores it on exit , so reusing the sameAcctPacketacross multiplesend_packetcalls no longer compounds the delay. getaddrinfofixes.- Test suite migrated to pytest-style.
- Packaging metadata consolidated.
setup.pyandsetup.cfg
deleted.pyproject.tomlcarries the full PEP 639license = "BSD-3-Clause"+license-files = ["LICENSE.txt"], full
classifiers,dynamic = ["version"]reading from
pyrad2/__init__.py, and[build-system]with hatchling. - Documentation tooling no longer ships to runtime users.
2.5
Breaking Changes
- BlastRADIUS measures are now on by default.
What's Changed
- Harden against BlastRADIUS and Access-Request forgery by @nicholasamorim in https://github.com/nicholasamorim/pyrad2/pull/26
Full Changelog: nicholasamorim/pyrad2@2.4...2.5
2.4
What's Changed
- Add RFC 9765 RADIUS/1.1 support over RadSec by @nicholasamorim in https://github.com/nicholasamorim/pyrad2/pull/25
Full Changelog: nicholasamorim/pyrad2@2.3...2.4
2.3
What's Changed
- Add RFC 5080 duplicate detection and response cache by @nicholasamorim in https://github.com/nicholasamorim/pyrad2/pull/24
Full Changelog: nicholasamorim/pyrad2@2.2...2.3
2.2
What's Changed
- Add tests by @nicholasamorim in https://github.com/nicholasamorim/pyrad2/pull/21
- Dictionary improvements by @nicholasamorim in https://github.com/nicholasamorim/pyrad2/pull/22
- Add scenarios/ end-to-end demos and PYRAD2_TRACE wire-level visibility by @nicholasamorim in https://github.com/nicholasamorim/pyrad2/pull/23
Full Changelog: nicholasamorim/pyrad2@2.1...2.2
2.1
Breaking Changes
RadSec
- Default flips: deployments on TLS 1.1 or with non-matching cert SANs now fail. Escape hatch: pass custom config.
- Stricter MA + connection reuse default. Peers sending malformed MAs are now rejected. Escape hatch: reuse_connection=False.
What's Changed
- RadSec improvements by @nicholasamorim in https://github.com/nicholasamorim/pyrad2/pull/16
- Harden RadSec TLS, streams and Message-Authenticator policy by @nicholasamorim in https://github.com/nicholasamorim/pyrad2/pull/17
- Add Status-Server support by @nicholasamorim in https://github.com/nicholasamorim/pyrad2/pull/18
- Improvements to COA and disconnect packets better by @nicholasamorim in https://github.com/nicholasamorim/pyrad2/pull/19
- Fix async client retry/timeout correctness and add EAP-MD5 parity by @nicholasamorim in https://github.com/nicholasamorim/pyrad2/pull/20
Full Changelog: nicholasamorim/pyrad2@2.0...2.1
2.0
What's Changed
- Breaking Changes: The entire codebase has been converted from CamaleCase to use Python's snake case.
- Enforce Message-Authenticator if present
- Ascend-Data-Filter now supports
deletekeyword - Several fixes, more typing
- Updated documentation and examples
1.2.0
What's Changed
- Use selectors in place of select on Windows by @ndptech in https://github.com/nicholasamorim/pyrad2/pull/9
New Contributors
- @ndptech made their first contribution in https://github.com/nicholasamorim/pyrad2/pull/9
Full Changelog: nicholasamorim/pyrad2@1.1.1...1.2.0
1.1.1
Fixes
ssl.CERT_REQUIREDis enabled by default.