Skip to content

[defect]: RadSec/TCP listener with lifetime = 0 and idle_timeout = 0 closes idle connections after 30 seconds #5926

Description

@je4yfc

What type of defect/bug is this?

Unexpected behaviour (obvious or verified by project member)

How can the issue be reproduced?

Environment

  • FreeRADIUS 3.2.10
  • radsecproxy 1.11.2
  • FreeRADIUS running in a Linux container
  • Incoming RadSec over TCP/2083
  • Mutual TLS
  • TLS 1.3 observed on the connection

I observed a deterministic 30-second server-side disconnect when an incoming RadSec listener is configured with both lifetime = 0 and idle_timeout = 0. The issue is reproducible independently of RADIUS authentication traffic.

The incoming TLS listener is configured as:

listen {
    ipaddr = *
    port = 2083
    type = auth+acct
    proto = tcp
    virtual_server = default
    clients = radsec

    limit {
        max_connections = 128
        lifetime = 0
        idle_timeout = 0
    }

    tls {
        # certificates omitted
        require_client_cert = yes
    }
}

clients radsec {
    client radsec-peer {
        ipaddr = 10.x.x.x
        proto = tls
        secret = <redacted>

        limit {
            max_connections = 128
            lifetime = 0
            idle_timeout = 0
        }
    }
}

We verified that the configuration file actually consumed by the running container contains these values. The enabled TLS site is a symlink to the expected sites-available/tls; there is no alternate/stale configuration tree involved.

freeradius -XC reports the listener as:

listen {
        type = "auth+acct"
        virtual_server = "default"
        ipaddr = *
        port = 2083
        proto = "tcp"

        ...

  limit {
        max_connections = 128
        lifetime = 0
        idle_timeout = 0
  }
}

Configuration appears to be ok

### Reproduction

1. Configure the incoming RadSec/TCP listener and matching TLS client as
   shown above.
2. Start FreeRADIUS 3.2.10.
3. Connect using radsecproxy 1.11.2.
4. Disable or minimise radsecproxy Status-Server polling so that the TLS
   connection becomes genuinely idle.
5. Allow the connection to sit idle.
6. After approximately 30 seconds, FreeRADIUS closes the TCP connection.
7. radsecproxy immediately reconnects.
8. The sequence repeats every ~30 seconds.

The client reports:

```text
tlsconnect: TLS connection to internal-radsec (... port 2083), TLSv1.3 ... up
sslreadtimeout: SSL: error:0A000126:SSL routines::unexpected eof while reading
closeh: connection to server internal-radsec lost
tlsconnect: TLS connection to internal-radsec (... port 2083), TLSv1.3 ... up

Packet-level verification

I captured the traffic on the FreeRADIUS host to determine which endpoint was actually terminating the session. For one representative connection:

22:24:02.52  TLS handshake / final connection setup traffic
22:24:32.50  <freeradius-container>:2083 > <radsec-peer>:41503 Flags [F.]

The first FIN originates from the FreeRADIUS container. The same sequence was observed repeatedly on separate TCP connections:

~22:23:02  FreeRADIUS FIN
~22:23:32  FreeRADIUS FIN
~22:24:02  FreeRADIUS FIN
~22:24:32  FreeRADIUS FIN
~22:25:02  FreeRADIUS FIN
~22:25:32  FreeRADIUS FIN
~22:26:02  FreeRADIUS FIN
~22:26:32  FreeRADIUS FIN
~22:27:02  FreeRADIUS FIN
~22:27:32  FreeRADIUS FIN

This ruled out radsecproxy initiating the disconnect, and also ruled out a firewall/NAT silently expiring the connectioon since FreeRADIUS itself sends the initial TCP FIN.

A/B test

We then changed only the two relevant values from:

idle_timeout = 0

to:

idle_timeout = 900

for both the listener and matching RadSec client, and restarted FreeRADIUS.

With that change, the same radsecproxy peer remained connected for more than 17 minutes without the previous EOF / close / reconnect cycle.

Changing the explicit timeout therefore changes the behaviour from:

idle_timeout = 0
    -> server closes connection after ~30 seconds

to:

idle_timeout = 900
    -> connection remains established beyond the previous 30-second limit

Relevant FreeRADIUS 3.2.10 source

While investigating the behaviour, I found the following logic in src/main/listen.c on the release_3_2_10 branch:

if (sock->limit.lifetime) {
        ...
} else if (!sock->limit.idle_timeout) {
        sock->limit.idle_timeout = 30;
}

This appears to explain the observed behaviour exactly: when both values are zero, the parsed listener state is subsequently normalised to an effective 30-second idle timeout.

However, freeradius -XC continues to display:

lifetime = 0
idle_timeout = 0

so the effective runtime behaviour is not apparent from the configuration check output.

Expected behaviour / question

If idle_timeout = 0 is intended to represent a disabled/unlimited idle timeout, we would expect the RadSec connection to remain open.

If zero is intentionally interpreted as "use the default 30-second timeout", it would be useful for the effective configuration/debug output and the relevant sample configuration/documentation to make that behaviour explicit.

The current combination is particularly confusing because:

  1. the configured value is idle_timeout = 0;
  2. freeradius -XC reports idle_timeout = 0;
  3. the server actually enforces approximately 30 seconds;
  4. setting an explicit non-zero timeout immediately changes the runtime behaviour.

Impact

The practical impact for long-lived RadSec connections is:

  • TLS connections are unnecessarily torn down and re-established every
    30 seconds when idle;
  • repeated TLS handshake overhead;
  • repeated disconnect/reconnect logging on the RadSec peer;
  • transport appears unstable despite otherwise healthy connectivity;
  • the churn can obscure genuine authentication/RADIUS transport failures.

An explicit non-zero idle_timeout is an effective workaround.

Log output from the FreeRADIUS daemon

Info: Ready to process requests

... adding new socket auth+acct from client (10.208.104.66, 34485) -> (*, 2083, virtual-server=default)
... shutting down socket auth+acct from client (10.208.104.66, 34485) -> (*, 2083, virtual-server=default)

... adding new socket auth+acct from client (10.208.104.66, 42651) -> (*, 2083, virtual-server=default)
... shutting down socket auth+acct from client (10.208.104.66, 42651) -> (*, 2083, virtual-server=default)

# The add/shutdown sequence repeats as the RadSec peer reconnects.

Relevant log output from client utilities

radsecproxy 1.11.2 starting
createlistener: listening for udp on 127.0.0.1:1812
tlsconnect: TLS connection to internal-radsec (... port 2083), TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 up

# approximately 30 seconds later:
sslreadtimeout: SSL: error:0A000126:SSL routines::unexpected eof while reading
closeh: connection to server internal-radsec lost
tlsconnect: TLS connection to internal-radsec (... port 2083), TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 up

# repeats approximately every 30 seconds while idle

The full capture confirms that the first FIN comes from FreeRADIUS, not radsecproxy.

Backtrace from LLDB or GDB

Not applicable; no crash occurs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    defectcategory: a defect or misbehaviour

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions