Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Overview

Introduction

This solution employs a router/server computer with two network interfaces, one is a network uplink, the other connects to the wifi routers with their respective supplicants.

...

A package containing the mentiond scripts, as well as the empty MySQL schema for the database used by some of them, is attached to this wiki page.

Scripts

Common MySQL database schema

A MySQL schema is contained in the shwl_add_shwl_del_pmu.sql in the attached archive. It is used by the shwl_add.sh, shwl_del.sh and pam_to_mysql_update.sh scripts to log events and errors. It contains a single table named 'event_log', it's format is as follows:

log_iddevice_usernamedevice_ipdevice_macrad_attr_NAS-IP-Addressrad_attr_NAS-Portrad_attr_Called-Station-Idrad_attr_NAS-Identifierrad_attr_Framed-MTUrad_attr_NAS-Port-Typerad_attr_EAP-Typerad_attr_Event-Timestamplog_timeevent
A unique identifier for the entry, auto-generated by MySQLSee the below sections on each script for all possible valuesSee the below sections on each script for all possible valuesSee the below sections on each script for all possible valuesThe value of the NAS-IP-Address RADIUS attribute, when applicableThe value of the NAS-Port RADIUS attribute, when applicableThe value of the Called-Station-Id RADIUS attribute, when applicableThe value of the NAS-Identifier RADIUS attribute, when applicableThe value of the Framed-MTU RADIUS attribute, when applicableThe value of the NAS-Port-Type RADIUS attribute, when applicableThe value of the EAP-Type RADIUS attribute, when applicableThe value of the Event-Timestamp RADIUS attribute, when applicableThe time when the entry is loggedDescription of the event, see the below sections on each script for all possible values

NOTE: The 'log_time' column indicates the time when the MySQL entry was logged (i.e. all the scripts use the MySQL NOW() function in the field for this column in all their queries), whereas the rad_attr_Event-Timestamp column contains the content of the Event-Timestamp RADIUS attribute, when applicable, which is expected to contain the timestamp of when the RADIUS server received the request.

NOTE: This database is used only by the above mentioned scripts. One more MySQL database is expected to be running on the server for FreeRADIUS, which will also be accessed by the pam_to_mysql_update.sh script to update user passwords stored in there.

shwl_add.sh

This script is intended to be run by script_launcher.py (below mentioned).

It reads 10 lines on stdin, as follows:

1Username (RADIUS attribute: User-Name)
2Supplicant MAC address (RADIUS attribute: Calling-Station-Id)
3RADIUS attribute: NAS-IP-Address
4

RADIUS attribute: NAS-Port

5RADIUS attribute: Called-Station-Id
6RADIUS attribute: NAS-Identifier
7RADIUS attribute: Framed-MTU
8RADIUS attribute: NAS-Port-Type
9RADIUS attribute: EAP-Type
10RADIUS attribute: Event-Timestamp

The supplicant MAC address is used in the script, the remaining values are simply logged into the MySQL database. Some sanity check is performed on the username before the script continues. The supplicant MAC address is processed so as to obtain it in both of the following formats regardless of the format the NAS specified it in: filename friendly version: 0123456789ab, normal version: 01:23:45:67:89:ab. The script then checks if a file named as the filename friendly version of the supplicant MAC address already exists (referred to by the script as an IP file). If it does not, the script runs an ARP scan on the configured network interface and in the resulting table, looks for the IP address matching the specified supplicant MAC address. In case no such device is found, it repeats the scan a configurable amount of times at a configurable interval before giving up. Once a matching IP is found some amount of sanity check is performed on the scan results (e.g. to detect multiple MACs being used by the found IP), and then the event is logged into MySQL with "connect" mentioned in the 'event' column, the found IP is added to the configured shorewall dynamic zone, and a file named as the filename friendly version of the supplicant MAC address is created containing the device's IP, and a file named as the device's IP (referred to by the script as a timestamp file) is created containing the present timestamp. In case adding the IP address to the shorewall dynamic zone is not successful, the script waits a configurable amount of time, and then attempts a second time, after which it gives up (it was observed that it might happen that FreeRADIUS starts earlier in the boot process than Shorewall, and if this script is started during that timeframe the shorewall add command fails). In case the IP file is already present, the script simply logs the event to MySQL with "re-auth" mentioned in the 'event' column and re-writes the timestamp file with the present timestamp. In case an error is encountered, the script logs the error to MySQL mentioning "err-add-N" (where N is the error number) in the 'event' column, populating the remaining columns with their respective information as may be available at the time of the error, and exits immediately, returning the error number as exit code. In case access is available to the script's stdout and stderr, a description of the error message is also printed (and the script is quite verbose about what's happening), in case not, it is possible to look in the script's code for calls to the shwl_add_error_message_close() function, identify the call where the error number in question is passed to the function, and the error description can be found in the same function call. The configurable options can be found at the top of the script. The username is logged into the 'device_username' column, the IP address found during the scan is logged in the 'device_ip' column, the above mentioned normal version of the supplicant MAC address is logged in the 'device_mac' column, and the remaining RADIUS attributes are logged in the columns with the respective names.

shwl_del.sh

This script is intended to be run by crontab at a regular interval.

This script loops through all the IP addresses present in the configured shorewall dynamic zone, reads their corresponding timestamp file (see shwl_add.sh description) and checks whether the configured amount of time has elapsed since. If it has, it logs the event to MySQL specifying "expire" in the 'event' column, removes the IP from the shorewall dynamic zone, deletes the timestamp file and searches for files (although there should be only one) containing the IP address (to find the IP file, see shwl_add.sh description) and deletes them. In the unexpected case that no timestamp file is found for a given IP or it does not contain a valid timestamp, the same actions are taken as when the configured amount of time has elapsed, but "untracked" is mentioned in the 'event' column instead of "expire". In case an error is encountered, the script logs the error to MySQL mentioning "err-del-N" (where N is the error number) in the 'event' column, populating the remaining columns with their respective information as may be available at the time of the error, but continues execution. At the end, it returns the error number as exit code, or, in case there were multiple errors, 127. In case access is available to the script's stdout and stderr, a description of the error message is also printed (and the script is quite verbose about what's happening), in case not, it is possible to look in the script's code for calls to the shwl_del_error_message() function, identify the call where the error number in question is passed to the function, and the error description can be found in the same function call. The configurable options can be found at the top of the script. The IP address being processed is logged in the 'device_ip' column, a comma separated list of the IP files is logged in the 'device_mac' column, or the text "/// no-ip-files ///" in case no IP files were present, or the text "/// err-del-N ///" (where N is the error number) in case an error occurred while searching for the file(s), and the rad_attr_* as well as the 'device_username' columns are left empty. The text "/// n/a ///" may be present in the 'device_ip' and 'device_mac' columns if this information is not applicable.

script_launcher.py

This script is intended to be called by the FreeRADIUS Rlm_python module, which, in the intended configuration, calls the post_auth(attr) function passing a tuple of tuples containing the relevant RADIUS attributes and their values as the 'attr' argument.

This script looks for the values of the following items in the tuple of tuples:

User-NameMandatory
Calling-Station-IdMandatory
NAS-IP-AddressMandatory
NAS-PortOptional
Called-Station-IdMandatory
NAS-IdentifierOptional
Framed-MTUOptional
NAS-Port-TypeOptional
EAP-TypeOptional
Event-TimestampMandatory

The script executes the configured command in the background and then writes to the new process's stdin the values of the above attributes, one attribute on each line, in the order shown in the table. It then exits, leaving the new process running. If one of the RADIUS attributes mentioned above as "Optional" is missing, the script writes "None" in its place. If one of the "Mandatory" ones is missing an error occurs. In case an error is encountered, the script exits immediately, returning radiusd.RLM_MODULE_FAIL. (This means if an error occurs after the new process has been launched, e.g. because a mandatory RADIUS attribute is missing, the script exits leaving the new process running, doing nothing, waiting for its stdin to be populated). If no error occurs, the script returns radiusd.RLM_MODULE_OK at the end. The configurable options can be found at the top of the script.

pam_to_mysql_update.sh

This script is intended to be run by the libpam-script PAM module during the PAM auth and PAM password stacks execution. In the intended configuration, the libpam-script module checks the script's exit code and reports failure back to the PAM stack, causing the PAM operation to fail, in case an error occurred in the script.

It reads the following environment variables:

PAM_USERThe system user for whom the PAM operation is running
PAM_AUTHTOKThe user's password (in case of a password change operation, the new password)
PAM_SERVICEThe service that invoked PAM (e.g. sshd when the user is attempting to log in through SSH)

This script makes use of two MySQL databases, the one with the above mentioned 'event_log' table for logging errors, and FreeRADIUS's database for updating passwords.

...