16 September 2026 · 9 min read · recloud engineering

A REST backend for RADIUS on DynamoDB: auth, authorization VSAs and accounting

Most RADIUS deployments start with rlm_sql and a subscribers table, and for a few thousand services that's fine. The pain arrives with growth: schema migrations against a table your BNG reads on every login, accounting interims hammering the same database your portal queries, business logic accumulating inside SQL queries stuffed into a config file. At some point the honest fix is architectural: put a thin REST service in front of the subscriber store, let FreeRADIUS speak HTTP to it, and choose a datastore built for the access pattern — which, for keyed lookups at wire speed, is exactly what DynamoDB is.

BNG / NAS PPPoE · IPoE · ISG RADIUS FreeRADIUS rlm_rest JSON over mTLS HTTPS AAA REST service stateless · on Kubernetes POST /authorize POST /accounting DynamoDB subscribers · sessions streams Billing · analytics ClickHouse · Splynx Access-Accept + VSAs speed · pool · service policy

The FreeRADIUS side: rlm_rest, two calls

FreeRADIUS's rlm_rest module maps lifecycle stages onto HTTP calls. Two endpoints cover the whole job — the shape of the configuration, trimmed to essentials:

rest {
    connect_uri = "https://aaa-api.internal:8443"
    tls { ... }                       # mTLS: only your RADIUS may call this

    authorize {
        uri    = "${..connect_uri}/authorize"
        method = "post"
        body   = "json"
        data   = '{ "username": "%{User-Name}", "nas": "%{NAS-Identifier}",
                    "circuit_id": "%{Agent-Circuit-Id}", "mac": "%{Calling-Station-Id}" }'
    }
    accounting {
        uri    = "${..connect_uri}/accounting"
        method = "post"
        body   = "json"
    }
}

Authentication and authorization collapse into that one /authorize call. For DHCP/IPoE-style subscriber sessions the "password" is typically a fixed sentinel — identity really lives in the circuit ID or MAC — so the service's decision is the authorization: respond 200 and the subscriber gets a session, respond 401 and they don't. What makes the response powerful is what rides along with the accept.

VSAs: the response is the service definition

rlm_rest understands a JSON attribute map in the response, and this is where vendor-specific attributes earn their keep — the same REST service can drive Cisco, Juniper and Mikrotik gear by returning the right VSA per NAS vendor:

{
  "reply:Framed-Pool":            { "op": ":=", "value": "RESIDENTIAL-V4" },
  "reply:Cisco-AVPair":           { "op": "+=", "value": [
      "subscriber:service-name=RES-100M",
      "ip:sub-qos-policy-in=POLICE-100M",
      "ip:sub-qos-policy-out=SHAPE-20M" ] },
  "reply:Mikrotik-Rate-Limit":    { "op": ":=", "value": "100M/20M" },
  "reply:Session-Timeout":        { "op": ":=", "value": 86400 }
}

The service looks up the subscriber, reads their plan, and translates it into whatever dialect the requesting NAS speaks — one place in the whole stack where "50 Mbps plan" becomes concrete attributes. Plan change? Update one item in DynamoDB and send CoA; no RADIUS config touched.

The DynamoDB model: design for the read you make on every login

The access pattern is gloriously narrow: get one subscriber by key, fast, every time anyone connects. That's a single-table design with the circuit ID (or username/MAC) as partition key:

PK                          SK          attributes
SUB#INH0012345              PROFILE     plan=RES-100M, status=active, pool=RESIDENTIAL-V4
SUB#INH0012345              SESSION     sid=..., nas=bng-01, started=..., ttl=...
  • Single-digit-millisecond lookups at any scale, no connection pools to babysit, no read replicas to fail over — the whole reason to pick DynamoDB for this job.
  • Sessions as items with a TTL. Accounting Start writes the session item; Stop deletes it; the TTL sweeps up sessions whose Stop never arrived. "Who is online" becomes a query, and a stale session can't live forever.
  • Idempotency via conditional writes. RADIUS retransmits; keying accounting writes on Acct-Unique-Session-Id with a conditional put makes duplicates a no-op instead of a billing bug.

Accounting: respect the write rate

Do the arithmetic before you ship: 20,000 subscribers with 5-minute interims is ~67 writes/second sustained, spiking hard when an OLT reboots and everyone reconnects at once. Three habits keep it boring:

  • Answer the NAS first, persist second. Queue the accounting record and return immediately — the BNG only needs an ack, and a slow write must never delay an Access-Request sharing the same worker pool.
  • Interims update, they don't append. Overwrite the session item's counters in place; the historical trail belongs downstream.
  • DynamoDB Streams feed the heavy readers. Billing rating, usage graphs and the rest of your analytics consume the change stream into ClickHouse or your billing platform — the hot table stays hot-path only.

Failure policy is a product decision

Sooner or later the REST call will time out. Decide now what FreeRADIUS does: reject (strict, but a backend blip becomes a mass outage) or accept with a conservative default profile (subscribers stay online at, say, a capped rate while you fix things). For residential access the second is almost always right — and it composes well with a small last-known-good cache inside the service itself. Whatever you choose, make it explicit in the rest module's failure handling rather than discovering the default during an incident.

Why the REST layer instead of pointing RADIUS at the database?

  • One owner for the data model. Portal, provisioning and support tooling call the same API the BNG effectively does — no second path that drifts.
  • Business logic lives in code — testable, reviewed, deployed with zero-downtime rollouts — not in SQL fragments inside a RADIUS config.
  • Security composes. mTLS from FreeRADIUS, and the same service can mint or verify tokens as covered in FreeRADIUS as an identity source.
  • The datastore is swappable. The same contract runs happily on MongoDB — the pattern is the point; DynamoDB is simply the least-operations answer if you're already on AWS.

The result is a subscriber backend that scales with your base instead of against it: FreeRADIUS stays a protocol engine, DynamoDB does the one thing it's world-class at, and everything an operator actually changes — plans, policies, VSA dialects — lives in one small, boring, testable service.

Need a hand with this in production?

recloud is a group of software and network engineers specialising in Cisco Systems and Juniper, working with Australian ISPs, network operators and enterprises. See custom RADIUS backends and subscriber access, backend engineering. Or contact us.