NebularStack
Website

Introduction to RADIUS itself

Objective

RADIUS provides authentication, authorization and accounting (AAA) for network access. This document is a short primer on the RADIUS protocol itself (the players involved, the shared secret, how requests and responses work, and how attributes are encoded) so the rest of the lunar documentation makes sense. It is not a full reference. The RFCs (starting with RFC 2865) are the authority.

The players: server, client and NAS

RADIUS has a few roles you will see throughout this documentation:

  • RADIUS server is the system that answers requests and makes the AAA decisions (authenticate this user, record this session, change or disconnect this session). Lunar is a RADIUS server.

  • RADIUS client is the device that sends requests to the server. RFC 2865 uses "client" for exactly this: the peer that talks to the server. A client is identified on the server by its source IP address and has its own shared secret.

  • NAS (Network Access Server) is the network device the subscriber actually connects through: a BNG / BRAS in a broadband network, or a Wi-Fi controller / hotspot router on wireless. In the common case the NAS is the RADIUS client. It terminates the subscriber and speaks RADIUS to the server, so "client" and "NAS" usually mean the same box.

Two clarifications worth making up front:

  • A RADIUS proxy is a server that forwards requests on to another server. A proxy is itself a client to the server it forwards to, so from the server's point of view the immediate peer is always "the client", whether that is a NAS directly or a proxy standing in front of the NAS.

  • Do not confuse the client (the NAS) with the subscriber, the end user or their device (home router, phone, laptop). The subscriber connects to the NAS, and the NAS is the RADIUS client that asks the server whether to allow them.

The shared secret

Before any request is sent, the client and the server must agree on a shared secret, a password-like string known only to both of them. It is part of the RADIUS protocol itself and is always required, regardless of the transport (see below). The shared secret is what ties a client and server together and secures the exchange. It is used to:

  • Authenticate the two parties. A packet signed with the wrong secret is rejected, so an unknown or spoofed client cannot get a valid answer.

  • Protect integrity. The Request/Response Authenticator and the Message-Authenticator are keyed with the secret, so a tampered packet is detected.

  • Hide sensitive attributes. The User-Password, for example, is obfuscated on the wire using the secret, so it is never sent in the clear.

The shared secret is configured per client on the server: every client (NAS) has its own entry, identified by its source IP address, with its own secret. When a packet arrives, the server looks the client up by IP and uses that client's secret to verify and process it. Both sides must therefore be configured with the exact same secret for that client. A mismatch means the packet fails verification and gets no reply, which is one of the most common RADIUS misconfigurations. Because the secret is per client, each NAS can (and should) have a strong, unique secret.

Transports and ports

RADIUS most commonly runs over UDP, but it can also run over TCP (RFC 6613) or over TLS/RadSec (RFC 6614). The shared secret above applies to all of them.

Each RADIUS function has its own well-known UDP port, and a request must be sent to the port for its type. The defaults below are standardized, but they are configurable, and several RADIUS servers can run on one host on different ports.

The three functions, the requests each accepts and the responses each returns:

Request Type

Valid Request Types

Valid Response Types

Port

Authentication

Access-Request

Access-Accept, Access-Reject, Access-Challenge

1812

Accounting

Accounting-Request

Accounting-Response

1813

Dynamic Authorization

CoA-Request, Disconnect-Request

CoA-ACK, CoA-NAK, Disconnect-ACK, Disconnect-NAK

3799

Authentication and accounting flow from the client to the server. Dynamic authorization is the exception: it flows the other way. The server sends a CoA-Request (Change of Authorization) or Disconnect-Request to a NAS to change or terminate a live session, and the NAS answers with an ACK or NAK. This is the only case where a request is sent to a NAS.

Requests and responses

Every RADIUS message is a packet with a fixed header followed by a list of attributes:

  • The header carries the packet type (a code, e.g. Access-Request), a one-byte Identifier used to match a reply to its request, the total length, and an Authenticator (16 bytes) used for integrity/hiding.

  • The attributes are the key-value fields carrying the actual data about the user, the session and the NAS.

A response has exactly the same structure as a request (the same header layout and the same kind of attribute list), just a different packet type (and its Authenticator is computed differently, keyed with the shared secret). So once you understand one packet, you understand them all. The difference is the code and which attributes are present.

Attributes (TLVs)

Attributes are essentially TLVs (Type, Length, Value) and can be standard (defined by the RFCs) or vendor specific. You do not need the exact parsing rules to work with lunar, but it helps to know the shape:

Field

Size

Meaning

Type

1 byte

The attribute number, a dictionary lookup code (e.g. 1 = User-Name).

Length

1 byte

Total length of this attribute in bytes, counting the Type and Length octets plus the Value.

Value

Length - 2 bytes

The data, interpreted according to the attribute's type.

As a concrete example, the User-Name attribute carrying "alice" is the 7 bytes:

01 07 61 6C 69 63 65
|  |  \_______________/
|  |          value = "alice" (5 bytes of ASCII)
|  length = 7  (1 type + 1 length + 5 value)
type = 1  (User-Name)

A few standard attributes from RFC 2865 (the Length column is in bytes, including the 2-byte Type and Length header):

Attribute

Type

Length

Value

User-Name

1

3+

The login name, as text, e.g. "alice".

User-Password

2

18-130

The password, hidden using the shared secret (PAP only).

NAS-IP-Address

4

6

The NAS's IPv4 address, e.g. 192.0.2.1.

NAS-Port

5

6

The port number, a 32-bit integer.

Service-Type

6

6

The kind of service, an enum, e.g. Framed-User.

The Type is only a number on the wire. A dictionary maps that number to a human name and a data type. Without the dictionary, neither a server nor a client can know what an attribute means or how to read its value.

RADIUS also defines vendor-specific attributes, extended attributes and long-extended attributes (and attributes can be nested or split across several TLVs to carry large values). These extend RADIUS beyond the standard set. Many vendor BNGs, for example, add their own attributes carrying extra information a server can use for policy decisions. The dictionary is again what makes them readable.

Accounting

Accounting requests are sent by the client over the life of a session: a Start when it begins, periodic Interim-Updates, and a Stop when it ends. They carry attributes describing usage (how much data the session has used and how long it has been active), and the server replies with an Accounting-Response to acknowledge receipt.

Why RADIUS is complex

RADIUS is not a simple request/response protocol like HTTP. It is a binary protocol with many rules, edge cases and security considerations, and it is unforgiving of mistakes. A server has to handle several message types and many clients, all correctly and securely. In particular:

  • Every request carries an Identifier and a Request Authenticator, and the reply must echo the Identifier and carry a Response Authenticator, and the two authenticators are computed differently, both keyed on the shared secret.

  • Attributes can be encoded in complex ways (nested TLVs, and continuation to concatenate large values), all of which must be parsed and built exactly.

Getting any of this subtly wrong breaks interoperability or security.

How lunar helps

Lunar absorbs this complexity. It handles the RADIUS protocol (the wire format, the crypto and authenticators, the dictionaries and all the edge cases) and exposes a simple JSON API to your backend. The backend does not need to know anything about RADIUS: it just answers the JSON, and can be written in any language over any data store, as long as it can serve HTTP/JSON requests.