Skip to content

Releases: pyradius/pyrad2

3.1

04 Jun 12:49
be01741

Choose a tag to compare

What's Changed

Full Changelog: 3.0...3.1

3.0

02 Jun 10:30

Choose a tag to compare

3.0

If you upgrade from 2.5 without code changes, expect:

  1. UDP servers will drop Access-Request packets that don't carry Message-Authenticator. Pass require_message_authenticator=False to restore the old default.
  2. UDP clients will refuse Access-Accept / Reject / Challenge replies missing Message-Authenticator. Pass enforce_ma=False.
  3. Server will reject packets whose Request Authenticator fails MD5 verification. Pass enable_pkt_verify=False.
  4. RadSecServer / RadSecClient won't accept TLS 1.2 connections unless you pass minimum_tls_version=ssl.TLSVersion.TLSv1_2.
  5. Existing log pipelines watching for [pyrad2 trace] lines on PYRAD2_TRACE=1 will go silent until PYRAD2_TRACE_UNSAFE=1 is 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, and ClientAsync now default to enforcing Message-Authenticator on every Access-Request and on the matching Access-Accept / Reject / Challenge reply. To bridge a legacy NAS that doesn't emit the attribute, pass require_message_authenticator=False (servers) or enforce_ma=False (clients).
  • Sync Server now verifies request authenticators by default. Mirrors ServerAsync.enable_pkt_verify which previously was the only side that ran the check. Pass enable_pkt_verify=False to opt out for legacy NASes that emit malformed authenticators.
  • RadSec defaults to TLS 1.3. RadSecServer.DEFAULT_MINIMUM_TLS_VERSION and RadSecClient.DEFAULT_MINIMUM_TLS_VERSION are now ssl.TLSVersion.TLSv1_3 (RFC 9325 deprecates TLS 1.1-, treats 1.2 as legacy; RFC 9750 mandates 1.3 for RADIUS/1.1). Pass minimum_tls_version=ssl.TLSVersion.TLSv1_2 to bridge a legacy peer that can't yet negotiate 1.3. The floor is auto-promoted to 1.3 when radius_versions includes V1_1, regardless.
  • Constant-time MAC and MD5 comparisons across the verify path. All switched from == to hmac.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_request now rejects v1.0 Access-Requests whose Request Authenticator is all-zero. Packet.salt_crypt no longer falls back to a zero authenticator when one is missing , it calls create_authenticator() so salt-encrypted attributes (Tunnel-Password, MS-MPPE keys) can't be recovered without the shared secret.
  • PYRAD2_TRACE now requires a second-step acknowledgement. Setting PYRAD2_TRACE=1 no longer activates the wire trace on its own. The trace dumps the Request Authenticator and obfuscated User-Password bytes; with the shared secret known (commonly to anyone reading the log archive), the plaintext is fully recoverable. Set PYRAD2_TRACE_UNSAFE=1 alongside to acknowledge and enable. A warning is logged on activation.

Security hardening

  • $INCLUDE sandboxing. Dictionary / DictFile now confine $INCLUDE resolution to a base directory (defaults to the entry-point file's parent; configurable via include_base_dir=). A dictionary with $INCLUDE /etc/passwd or $INCLUDE ../../foo raises ParseError. The entry-point file is exempt , it defines the base.
  • ParseError signature tightened. The old **data swallowsilently dropped name=, path=, and similar typos. The new signature explicitly accepts file= and line=; the filename now consistently surfaces in error messages.
  • Vendor-id range check. Dictionary rejects VENDOR declarations outside 0..0xFFFFFF (RFC 2865 §5.26 SMI PEN range) instead of silently passing through values that later corrupt the VSA encoder.
  • eap.password_from_packet no longer falls back to User-Name. Returning User-Name as the EAP-MD5 secret silently mis-keyed the challenge; any observer who saw the request could reproduce the digest. Raises PacketError when User-Password is absent.
  • pw_decrypt warns 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 a WARNING once 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.RequestRouter unifies the dispatch state machine that Server and ServerAsync previously each owned separately.
  • Packet subclass deduplication.
  • Cross-client factory deduplication.

Per-transport identifier management

  • DatagramProtocolClient.create_id scans for a free slot instead of blindly returning (packet_id + 1) % 256. Raises a new typed IdentifierExhausted (in pyrad2.exceptions) when all 256 slots on a single (source IP, source port) flow are in flight. The bare-Exception collision error in send_packet is now also typed.
  • Module-level CURRENT_ID is thread-safe. A threading.Lock serialises the increment so concurrent Packet() 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, and pw_decrypt. The byte-at-a-time bytes((hash[i] ^ buf[i],)) concat chain is replaced by an int-XOR + bytearray accumulator. ~3× faster on User-Password and Tunnel-Password sized inputs.
  • Asyncio deprecation cleanup.

DX / quality

  • Sync Client._send_packet no longer mutates the caller's Acct-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 same AcctPacket across multiple send_packet calls no longer compounds the delay.
  • getaddrinfo fixes.
  • Test suite migrated to pytest-style.
  • Packaging metadata consolidated. setup.py and setup.cfg
    deleted. pyproject.toml carries the full PEP 639 license = "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

01 Jun 20:43

Choose a tag to compare

2.5

Breaking Changes

  • BlastRADIUS measures are now on by default.

What's Changed

Full Changelog: nicholasamorim/pyrad2@2.4...2.5

2.4

17 May 17:50

Choose a tag to compare

2.4

What's Changed

Full Changelog: nicholasamorim/pyrad2@2.3...2.4

2.3

17 May 14:34

Choose a tag to compare

2.3

What's Changed

Full Changelog: nicholasamorim/pyrad2@2.2...2.3

2.2

17 May 12:17

Choose a tag to compare

2.2

What's Changed

Full Changelog: nicholasamorim/pyrad2@2.1...2.2

2.1

17 May 10:20

Choose a tag to compare

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

Full Changelog: nicholasamorim/pyrad2@2.0...2.1

2.0

06 Apr 14:48
273fd6e

Choose a tag to compare

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 delete keyword
  • Several fixes, more typing
  • Updated documentation and examples

1.2.0

22 Jul 11:54

Choose a tag to compare

What's Changed

New Contributors

Full Changelog: nicholasamorim/pyrad2@1.1.1...1.2.0

1.1.1

09 Jul 16:11

Choose a tag to compare

Fixes

  • ssl.CERT_REQUIRED is enabled by default.