General
-------
-This module allows you to store radius accounting data directly into Couchbase and authorize users from documents already stored in Couchbase. It was tested to handle thousands of radius requests per second from several thousand Aerohive access points using a FreeRADIUS installation with this module for accounting and authorization. You should list the ```couchbase``` module in both the ```accounting``` and ```authorization``` sections of your site configuration if you are planning to use it for both purposes. You should also have ```pap``` enabled for authenticating users based on cleartext or hashed password attributes. As always YMMV.
+This module allows you to write accounting data directly to Couchbase as JSON documents and authorize users from JSON documents stored within Couchbase. It was tested to handle thousands of radius requests per second from several thousand Aerohive access points using a FreeRADIUS installation with this module for accounting and authorization. You should list the ```couchbase``` module in both the ```accounting``` and ```authorization``` sections of your site configuration if you are planning to use it for both purposes. You should also have ```pap``` enabled for authenticating users based on cleartext or hashed password attributes. As always YMMV.
Accounting
----------
This exmaple is from an Aerohive wireless access point.
- {
- "docType": "radacct",
- "startTimestamp": "Jul 15 2013 13:22:07 CDT",
- "stopTimestamp": "null",
- "sessionId": "51D241D3-0000047A",
- "lastStatus": 3,
- "authentic": 1,
- "userName": "mruser@blargs.com",
- "nasIpAddress": "172.28.4.150",
- "nasIdentifier": "air4.corp.blargs.com",
- "nasPort": 0,
- "calledStationId": "40-18-b1-01-3c-54",
- "framedIpAddress": "172.27.2.87",
- "callingStationId": "8C-2D-AA-72-36-BA",
- "nasPortType": 19,
- "connectInfo": "11ng",
- "sessionTime": 5821,
- "inputPackets": 5591,
- "inputOctets": 681742,
- "inputGigawords": 0,
- "outputOctets": 536306,
- "outputGigawords": 0,
- "outputPackets": 1087,
- "lastUpdated": "Jul 15 2013 14:59:08 CDT",
- "uniqueId": "029d975fc48ecb41444da52a65e62a55",
- "calledStationSSID": "BLARGS-WIFI",
- "strippedUserName": "mruser",
- "strippedUserDomain": "blargs.com"
- }
+```
+{
+ "docType": "radacct",
+ "startTimestamp": "Jul 15 2013 13:22:07 CDT",
+ "stopTimestamp": "null",
+ "sessionId": "51D241D3-0000047A",
+ "lastStatus": 3,
+ "authentic": 1,
+ "userName": "mruser@blargs.com",
+ "nasIpAddress": "172.28.4.150",
+ "nasIdentifier": "air4.corp.blargs.com",
+ "nasPort": 0,
+ "calledStationId": "40-18-b1-01-3c-54",
+ "framedIpAddress": "172.27.2.87",
+ "callingStationId": "8C-2D-AA-72-36-BA",
+ "nasPortType": 19,
+ "connectInfo": "11ng",
+ "sessionTime": 5821,
+ "inputPackets": 5591,
+ "inputOctets": 681742,
+ "inputGigawords": 0,
+ "outputOctets": 536306,
+ "outputGigawords": 0,
+ "outputPackets": 1087,
+ "lastUpdated": "Jul 15 2013 14:59:08 CDT",
+ "uniqueId": "029d975fc48ecb41444da52a65e62a55",
+ "calledStationSSID": "BLARGS-WIFI",
+ "strippedUserName": "mruser",
+ "strippedUserDomain": "blargs.com"
+}
+```
To generate the 'calledStationSSID' fields you will need to use the ```rewrite_called_station_id``` policy in the ```preacct``` section of your config. Similarly to get the 'Stripped-User-Name' and 'Stripped-User-Domain' attributes you can create a file in ```raddb/policy.d/``` with the following content:
- ## nt domain regex
- simple_nt_regexp = "^([^\\\\\\\\]*)(\\\\\\\\(.*))$"
+```
+## simple nt domain regex
+simple_nt_regexp = "^([^\\\\\\\\]*)(\\\\\\\\(.*))$"
- ## simple nai regex
- simple_nai_regexp = "^([^@]*)(@(.*))$"
+## simple nai regex
+simple_nai_regexp = "^([^@]*)(@(.*))$"
- ## split user@domain and domain\user formats
- strip_user_domain {
- if(User-Name && (User-Name =~ /${policy.simple_nt_regexp}/)){
- update request {
- Stripped-User-Domain = "%{1}"
- Stripped-User-Name = "%{3}"
+## split user@domain and domain\user formats
+strip_user_domain {
+ if(User-Name && (User-Name =~ /${policy.simple_nt_regexp}/)){
+ update request {
+ Stripped-User-Domain = "%{1}"
+ Stripped-User-Name = "%{3}"
+ }
}
- }
- elsif(User-Name && (User-Name =~ /${policy.simple_nai_regexp}/)){
- update request {
- Stripped-User-Name = "%{1}"
- Stripped-User-Domain = "%{3}"
+ elsif(User-Name && (User-Name =~ /${policy.simple_nai_regexp}/)){
+ update request {
+ Stripped-User-Name = "%{1}"
+ Stripped-User-Domain = "%{3}"
+ }
}
- }
- else {
- noop
- }
- }
+ else {
+ noop
+ }
+}
+```
You can then reference this policy in both the ```preacct``` and ```authorization``` sections of your configuration before calling this module.
The authorization funcionality relies on the user documents being stored with deterministic keys based on information available in the authorization request. The format of those keys may be specified in unlang like the example below:
- user_key = "raduser_%{md5:%{tolower:%{%{Stripped-User-Name}:-%{User-Name}}}}"
+```user_key = "raduser_%{md5:%{tolower:%{%{Stripped-User-Name}:-%{User-Name}}}}"```
This will create an md5 hash of the lowercase 'Stripped-User-Name' attribute or the 'User-Name' attribute if 'Stripped-User-Name' doesn't exist. The module will then attempt to fetch the resulting key from the configured couchbase bucket.
The document structure is straight forward and flexible:
- {
- "docType": "raduser",
- "userName": "test",
- "config": {
- "SHA-Password": {
- "value": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
- "op": ":="
- }
- },
- "reply": {
- "Reply-Message": {
- "value": "Hidey Ho!",
- "op": "="
+```json
+{
+ "docType": "raduser",
+ "userName": "test",
+ "config": {
+ "SHA-Password": {
+ "value": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
+ "op": ":="
+ }
+ },
+ "reply": {
+ "Reply-Message": {
+ "value": "Hidey Ho!",
+ "op": "="
+ }
}
- }
- }
+}
+```
You may specify any valid combination of attributes and operations in the JSON document.
Configuration
-------------
- couchbase {
+```
+couchbase {
#
# List of Couchbase hosts (hosts may be space, tab, comma or semi-colon separated).
# Ports are optional if servers are listening on the standard port.
# Note: Atrributes not in this map will not be recorded.
#
map {
- sessionId = 'Acct-Session-Id'
- uniqueId = 'Acct-Unique-Session-Id'
- lastStatus = 'Acct-Status-Type'
- authentic = 'Acct-Authentic'
- userName = 'User-Name'
- strippedUserName = 'Stripped-User-Name'
- strippedUserDomain = 'Stripped-User-Domain'
- realm = 'Realm'
- nasIpAddress = 'NAS-IP-Address'
- nasIdentifier = 'NAS-Identifier'
- nasPort = 'NAS-Port'
- calledStationId = 'Called-Station-Id'
- calledStationSSID = 'Called-Station-SSID'
- callingStationId = 'Calling-Station-Id'
- framedIpAddress = 'Framed-IP-Address'
- nasPortType = 'NAS-Port-Type'
- connectInfo = 'Connect-Info'
- sessionTime = 'Acct-Session-Time'
- inputPackets = 'Acct-Input-Packets'
- outputPackets = 'Acct-Output-Packets'
- inputOctets = 'Acct-Input-Octets'
- outputOctets = 'Acct-Output-Octets'
- inputGigawords = 'Acct-Input-Gigawords'
- outputGigawords = 'Acct-Output-Gigawords'
- lastUpdated = 'Event-Timestamp'
+ sessionId = 'Acct-Session-Id'
+ uniqueId = 'Acct-Unique-Session-Id'
+ lastStatus = 'Acct-Status-Type'
+ authentic = 'Acct-Authentic'
+ userName = 'User-Name'
+ strippedUserName = 'Stripped-User-Name'
+ strippedUserDomain = 'Stripped-User-Domain'
+ realm = 'Realm'
+ nasIpAddress = 'NAS-IP-Address'
+ nasIdentifier = 'NAS-Identifier'
+ nasPort = 'NAS-Port'
+ calledStationId = 'Called-Station-Id'
+ calledStationSSID = 'Called-Station-SSID'
+ callingStationId = 'Calling-Station-Id'
+ framedIpAddress = 'Framed-IP-Address'
+ nasPortType = 'NAS-Port-Type'
+ connectInfo = 'Connect-Info'
+ sessionTime = 'Acct-Session-Time'
+ inputPackets = 'Acct-Input-Packets'
+ outputPackets = 'Acct-Output-Packets'
+ inputOctets = 'Acct-Input-Octets'
+ outputOctets = 'Acct-Output-Octets'
+ inputGigawords = 'Acct-Input-Gigawords'
+ outputGigawords = 'Acct-Output-Gigawords'
+ lastUpdated = 'Event-Timestamp'
}
# Couchbase document key for user documents (unlang supported)
# modules, for all kinds of connection-related activity.
#
pool {
- # Number of connections to start
- start = 5
-
- # Minimum number of connections to keep open
- min = 5
-
- # Maximum number of connections
- #
- # If these connections are all in use and a new one
- # is requested, the request will NOT get a connection.
- #
- # NOTE: This should be greater than or equal to "min" above.
- max = 20
-
- # Spare connections to be left idle
- #
- # NOTE: Idle connections WILL be closed if "idle_timeout"
- # is set. This should be less than or equal to "max" above.
- spare = 15
-
- # Number of uses before the connection is closed
- #
- # NOTE: A setting of 0 means infinite (no limit).
- uses = 0
-
- # The lifetime (in seconds) of the connection
- #
- # NOTE: A setting of 0 means infinite (no limit).
- lifetime = 0
-
- # The idle timeout (in seconds). A connection which is
- # unused for this length of time will be closed.
- #
- # NOTE: A setting of 0 means infinite (no timeout).
- idle_timeout = 1200
-
- # NOTE: All configuration settings are enforced. If a
- # connection is closed because of "idle_timeout",
- # "uses", or "lifetime", then the total number of
- # connections MAY fall below "min". When that
- # happens, it will open a new connection. It will
- # also log a WARNING message.
- #
- # The solution is to either lower the "min" connections,
- # or increase lifetime/idle_timeout.
+ # Number of connections to start
+ start = 5
+
+ # Minimum number of connections to keep open
+ min = 5
+
+ # Maximum number of connections
+ #
+ # If these connections are all in use and a new one
+ # is requested, the request will NOT get a connection.
+ #
+ # NOTE: This should be greater than or equal to "min" above.
+ max = 20
+
+ # Spare connections to be left idle
+ #
+ # NOTE: Idle connections WILL be closed if "idle_timeout"
+ # is set. This should be less than or equal to "max" above.
+ spare = 15
+
+ # Number of uses before the connection is closed
+ #
+ # NOTE: A setting of 0 means infinite (no limit).
+ uses = 0
+
+ # The lifetime (in seconds) of the connection
+ #
+ # NOTE: A setting of 0 means infinite (no limit).
+ lifetime = 0
+
+ # The idle timeout (in seconds). A connection which is
+ # unused for this length of time will be closed.
+ #
+ # NOTE: A setting of 0 means infinite (no timeout).
+ idle_timeout = 1200
+
+ # NOTE: All configuration settings are enforced. If a
+ # connection is closed because of "idle_timeout",
+ # "uses", or "lifetime", then the total number of
+ # connections MAY fall below "min". When that
+ # happens, it will open a new connection. It will
+ # also log a WARNING message.
+ #
+ # The solution is to either lower the "min" connections,
+ # or increase lifetime/idle_timeout.
}
- }
+}
+```