Radiator Server Documentation — v10.34.2

Chargeable-User-Identity

Use Chargeable-User-Identity (CUI) to correlate authentication and accounting with an opaque user identifier. The hmac_sha256 filter generates a stable CUI from the authenticated identity and a secret key.

This example reads the key from an environment variable named CUI_HMAC_KEY; replace env.CUI_HMAC_KEY with your key expression. Add the assignment at the end of the RADIUS authentication handler's @execute block, after successful authentication, with aaa.identity set to the authenticated user's identity.

@execute {
    # Existing authentication actions go here.

    if all {
        # Handle both initial requests and reauthentication with an existing CUI.
        radius.request.attr.Chargeable-User-Identity != none;
    } then {
        radius.reply.attr.Chargeable-User-Identity = aaa.identity | hmac_sha256(env.CUI_HMAC_KEY) | hex;
    }
}

The result is a 64-character hexadecimal CUI. Matching identities and keys produce matching CUIs across servers.

With this configuration, Radiator Server supports the home-server CUI behavior in RFC 4372, section 2.1: return a CUI after successful authentication when requested, including during reauthentication. Configure the NAS to include it in Start, Interim and Stop accounting requests. Proxies must forward it unchanged.

Daily Chargeable-User-Identity (CUI) rotation

A stable CUI allows requests for the same user to be correlated indefinitely. You can reduce this long-term linkability by rotating the CUI at a defined interval.

The following example generates one stable CUI per day. Authentication, reauthentication, accounting, diagnostics, and support systems can correlate the user's activity during that day. The same inputs produce a different CUI after the day changes.

In this example, each day follows Coordinated Universal Time (UTC) and runs from 00:00:00 through 23:59:59 UTC.

Daily rotation limits correlation through the CUI across different days. It does not make the user anonymous when other attributes, logs, or network identifiers can identify or correlate the user.

The validity timestamps form part of the HMAC input. They do not create an expiry time that Radiator enforces, and they cannot be recovered from the generated CUI. Their purpose is to change the HMAC input at each day boundary.

Configure the daily CUI pipeline

Define the reusable pipeline in a separate configuration file, such as 15_pipeline-daily-cui.radconf:

aaa {
    @pipeline "ASSIGN_DAILY_CUI" {
        if all {
            # Generate a CUI only when the client requests one.
            radius.request.attr.Chargeable-User-Identity != none;

            # The authentication configuration provides this identity.
            vars.cui_identity != none;
            vars.cui_identity != "";

            # The shared HMAC key must be configured.
            env.CUI_HMAC_KEY != none;
            env.CUI_HMAC_KEY != "";
        } then {
            # Read the clock once so both boundaries use the same day.
            vars.cui_now = datetime.timestamp.utc;

            vars.cui_valid_from = vars.cui_now | timestamp_format("[year]-[month]-[day]T00:00:00Z") | timestamp_format("[unix_timestamp]");
            vars.cui_valid_to = vars.cui_now | timestamp_format("[year]-[month]-[day]T23:59:59Z") | timestamp_format("[unix_timestamp]");

            # Operator-Name is optional.
            vars.cui_operator_name = radius.request.attr.Operator-Name | default("UNKNOWN");

            # The validity interval rotates the CUI each day.
            vars.cui_plain_text = "cui|v1|%{vars.cui_valid_from}|%{vars.cui_valid_to}|%{vars.cui_identity}|%{vars.cui_operator_name}";

            vars.cui = vars.cui_plain_text | hmac_sha256(env.CUI_HMAC_KEY) | base64_encode;
            radius.reply.attr.Chargeable-User-Identity = vars.cui;
        }
    }
}

Set vars.cui_identity to a stable authenticated identity before invoking the pipeline.

Set CUI_HMAC_KEY to a shared secret key. Radiator instances that must generate matching CUIs must use the same key. Keep the key stable while previous and current CUIs must remain reproducible.

The HMAC input contains:

  • The format identifier cui
  • The format version v1
  • The start of the current day as Unix timestamp seconds
  • The end of the current day as Unix timestamp seconds
  • vars.cui_identity
  • The request Operator-Name, or UNKNOWN when the attribute is absent

Operator-Name scopes the generated value by operator. Matching users produce different CUIs when their Operator-Name values differ. Requests without the attribute use the UNKNOWN scope.

The hmac_sha256 filter prevents deriving the CUI without the shared key. The base64_encode filter only converts the raw HMAC result to text; it does not provide additional confidentiality.

Invoke the daily CUI pipeline

Invoke ASSIGN_DAILY_CUI after successful processing:

@post_execute {
    if all {
        "%{aaa.result}" == "accept";
    } then {
        invoke "ASSIGN_DAILY_CUI";
    }
}

Use this @post_execute example when the corresponding @execute block contains the complete authentication flow.

The pipeline returns a CUI only when:

  • Authentication succeeded.
  • The request contains Chargeable-User-Identity.
  • vars.cui_identity contains a non-empty value.
  • CUI_HMAC_KEY contains a non-empty value.

Initial authentication and reauthentication use the same generation process. Within one day, matching input values produce the same reply CUI.

A session that crosses the day boundary continues using its previous CUI until authentication or reauthentication supplies a newly rotated CUI.