From 843e77e4fb087bde2698341f5e60498e435f685c Mon Sep 17 00:00:00 2001 From: Ryan Cook Date: Sat, 4 Apr 2026 18:55:15 -0400 Subject: [PATCH 1/3] Added NAS-IP-Address attribute to radius access server calls Changed NAS-Port-Type to virtual for radius access server calls --- .../mvc/app/library/OPNsense/Auth/Radius.php | 13 +++++++++++- src/www/system_authservers.php | 21 ++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/opnsense/mvc/app/library/OPNsense/Auth/Radius.php b/src/opnsense/mvc/app/library/OPNsense/Auth/Radius.php index 25f5edebd6f..f4008297a29 100644 --- a/src/opnsense/mvc/app/library/OPNsense/Auth/Radius.php +++ b/src/opnsense/mvc/app/library/OPNsense/Auth/Radius.php @@ -69,6 +69,11 @@ class Radius extends Base implements IAuthConnector */ private $callingStationId = null; + /** + * @var string ip addess to use for NAS-IP-Address attribute + */ + private $nasIpAddress = null; + /** * @var int timeout to use */ @@ -109,6 +114,8 @@ class Radius extends Base implements IAuthConnector */ private $syncDefaultGroups = []; + private $nasPortType = RADIUS_ETHERNET; + private function mapTerminateCause($cause) { switch ($cause) { @@ -165,6 +172,8 @@ public function setProperties($config) 'radius_acct_port' => 'acctPort', 'radius_protocol' => 'protocol', 'radius_stationid' => 'calledStationId', + 'radius_nasipaddress' => 'nasIpAddress', + 'radius_nasporttype' => 'nasPortType', 'refid' => 'nasIdentifier' ); @@ -507,12 +516,14 @@ public function authenticate($username, $password) $error = radius_strerror($radius); } elseif (!radius_put_int($radius, RADIUS_NAS_PORT, 0)) { $error = radius_strerror($radius); - } elseif (!radius_put_int($radius, RADIUS_NAS_PORT_TYPE, RADIUS_ETHERNET)) { + } elseif (!radius_put_int($radius, RADIUS_NAS_PORT_TYPE, $this->nasPortType)) { $error = radius_strerror($radius); } elseif (!empty($this->calledStationId) && !radius_put_string($radius, RADIUS_CALLED_STATION_ID, $this->calledStationId)) { $error = radius_strerror($radius); } elseif (!empty($this->callingStationId) && !radius_put_string($radius, RADIUS_CALLING_STATION_ID, $this->callingStationId)) { $error = radius_strerror($radius); + } elseif (!empty($this->nasIpAddress) && !radius_put_addr($radius, RADIUS_NAS_IP_ADDRESS, $this->nasIpAddress)) { + $error = radius_stderror($radius); } else { // Implement extra protocols in this section. switch ($this->protocol) { diff --git a/src/www/system_authservers.php b/src/www/system_authservers.php index 383a8dd7c89..8c76cb31d16 100644 --- a/src/www/system_authservers.php +++ b/src/www/system_authservers.php @@ -100,6 +100,7 @@ $pconfig['radius_secret'] = $a_server[$id]['radius_secret'] ?? ''; $pconfig['radius_timeout'] = $a_server[$id]['radius_timeout'] ?? ''; $pconfig['radius_stationid'] = $a_server[$id]['radius_stationid'] ?? ''; + $pconfig['radius_nasipaddress'] = $a_server[$id]['radius_nasipaddress'] ?? ''; if (!empty($pconfig['radius_auth_port']) && !empty($pconfig['radius_acct_port'])) { @@ -284,6 +285,12 @@ unset($server['radius_stationid']); } + if (!empty($pconfig['radius_nasipaddress'])) { + $server['radius_nasipaddress'] = $pconfig['radius_nasipaddress']; + } else { + unset($server['radius_nasipaddress']); + } + if ($pconfig['radius_srvcs'] == "both") { $server['radius_auth_port'] = $pconfig['radius_auth_port']; $server['radius_acct_port'] = $pconfig['radius_acct_port']; @@ -296,7 +303,8 @@ $server['sync_memberof'] = !empty($pconfig['sync_memberof']); $server['sync_memberof_groups'] = !empty($pconfig['sync_memberof_groups']) ? implode(",", $pconfig['sync_memberof_groups']) : []; $server['sync_default_groups'] = !empty($pconfig['sync_default_groups']) ? implode(",", $pconfig['sync_default_groups']) : []; - $server['sync_create_local_users'] = !empty($pconfig['sync_create_local_users']); + $server['sync_create_local_users'] = !empty($pconfig['sync_create_local_users']); + $server['radius_nasporttype'] = RADIUS_VIRTUAL; } elseif ($server['type'] == 'local') { foreach (['password_policy_duration', 'enable_password_policy_constraints', 'password_policy_complexity', 'password_policy_compliance', @@ -369,6 +377,8 @@ 'radius_srvcs', 'radius_timeout', 'radius_stationid', + 'radius_nasipaddress', + 'radius_nasporttype', 'sync_create_local_users', 'sync_memberof', 'ldap_attr_memberof', @@ -875,6 +885,15 @@ + + + + + + + From 624060475d2763ea9842b10b1376c03b09959367 Mon Sep 17 00:00:00 2001 From: Ryan Cook Date: Sun, 5 Apr 2026 18:17:32 -0400 Subject: [PATCH 2/3] Fixed formatting --- src/opnsense/mvc/app/library/OPNsense/Auth/Radius.php | 4 ++-- src/www/system_authservers.php | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/opnsense/mvc/app/library/OPNsense/Auth/Radius.php b/src/opnsense/mvc/app/library/OPNsense/Auth/Radius.php index f4008297a29..e50688d4022 100644 --- a/src/opnsense/mvc/app/library/OPNsense/Auth/Radius.php +++ b/src/opnsense/mvc/app/library/OPNsense/Auth/Radius.php @@ -172,8 +172,8 @@ public function setProperties($config) 'radius_acct_port' => 'acctPort', 'radius_protocol' => 'protocol', 'radius_stationid' => 'calledStationId', - 'radius_nasipaddress' => 'nasIpAddress', - 'radius_nasporttype' => 'nasPortType', + 'radius_nasipaddress' => 'nasIpAddress', + 'radius_nasporttype' => 'nasPortType', 'refid' => 'nasIdentifier' ); diff --git a/src/www/system_authservers.php b/src/www/system_authservers.php index 8c76cb31d16..ea9c65b82f1 100644 --- a/src/www/system_authservers.php +++ b/src/www/system_authservers.php @@ -288,7 +288,7 @@ if (!empty($pconfig['radius_nasipaddress'])) { $server['radius_nasipaddress'] = $pconfig['radius_nasipaddress']; } else { - unset($server['radius_nasipaddress']); + unset($server['radius_nasipaddress']); } if ($pconfig['radius_srvcs'] == "both") { @@ -303,8 +303,8 @@ $server['sync_memberof'] = !empty($pconfig['sync_memberof']); $server['sync_memberof_groups'] = !empty($pconfig['sync_memberof_groups']) ? implode(",", $pconfig['sync_memberof_groups']) : []; $server['sync_default_groups'] = !empty($pconfig['sync_default_groups']) ? implode(",", $pconfig['sync_default_groups']) : []; - $server['sync_create_local_users'] = !empty($pconfig['sync_create_local_users']); - $server['radius_nasporttype'] = RADIUS_VIRTUAL; + $server['sync_create_local_users'] = !empty($pconfig['sync_create_local_users']); + $server['radius_nasporttype'] = RADIUS_VIRTUAL; } elseif ($server['type'] == 'local') { foreach (['password_policy_duration', 'enable_password_policy_constraints', 'password_policy_complexity', 'password_policy_compliance', From 82b502c3ece0aacd06dcdbba02fd355f70cd0362 Mon Sep 17 00:00:00 2001 From: Ryan Cook Date: Sun, 5 Apr 2026 20:49:05 -0400 Subject: [PATCH 3/3] Moved NAS-IP-Address setting to getConfigurationOptions as suggested in PR comment Made RADIUS_NAS_PORT_TYPE hard-coded to RADIUS_VIRTUAL as suggested in PR comment Added dropdown of available interface IPs for NAS-IP-Address --- .../mvc/app/library/OPNsense/Auth/Radius.php | 33 +++++++++++++++---- src/www/system_authservers.php | 19 ----------- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/opnsense/mvc/app/library/OPNsense/Auth/Radius.php b/src/opnsense/mvc/app/library/OPNsense/Auth/Radius.php index e50688d4022..041d8bcfb71 100644 --- a/src/opnsense/mvc/app/library/OPNsense/Auth/Radius.php +++ b/src/opnsense/mvc/app/library/OPNsense/Auth/Radius.php @@ -27,6 +27,7 @@ */ namespace OPNsense\Auth; +require_once("interfaces.inc"); /** * Class Radius connector @@ -114,8 +115,6 @@ class Radius extends Base implements IAuthConnector */ private $syncDefaultGroups = []; - private $nasPortType = RADIUS_ETHERNET; - private function mapTerminateCause($cause) { switch ($cause) { @@ -173,7 +172,6 @@ public function setProperties($config) 'radius_protocol' => 'protocol', 'radius_stationid' => 'calledStationId', 'radius_nasipaddress' => 'nasIpAddress', - 'radius_nasporttype' => 'nasPortType', 'refid' => 'nasIdentifier' ); @@ -219,6 +217,31 @@ public function getConfigurationOptions() return []; } }; + + $options['radius_nasipaddress'] = []; + $options['radius_nasipaddress']['name'] = gettext('NAS IP address'); + $options['radius_nasipaddress']['type'] = 'dropdown'; + $options['radius_nasipaddress']['default'] = ''; + $options['radius_nasipaddress']['options'] = [null => '']; + $interfaces = get_configured_interface_with_descr(); + + // Create options list using interface IPs + foreach($interfaces as $if => $descr) { + $ip = get_interface_ip($if); + $options['radius_nasipaddress']['options'] += [$ip => "$descr - $ip"]; + } + + $options['radius_nasipaddress']['validate'] = function ($value) { + $interfaces = get_configured_interface_with_descr(); + $ips = []; + foreach($interfaces as $if => $descr) + $ips[] = get_interface_ip($if); + if(!empty($value) && !in_array($value, $ips)) { + return [gettext('Invalid address specified')]; + } else { + return []; + } + }; return $options; } @@ -510,13 +533,11 @@ public function authenticate($username, $password) $error = radius_strerror($radius); } elseif (!radius_put_int($radius, RADIUS_SERVICE_TYPE, RADIUS_LOGIN)) { $error = radius_strerror($radius); - } elseif (!radius_put_int($radius, RADIUS_FRAMED_PROTOCOL, RADIUS_ETHERNET)) { - $error = radius_strerror($radius); } elseif (!radius_put_string($radius, RADIUS_NAS_IDENTIFIER, $this->nasIdentifier)) { $error = radius_strerror($radius); } elseif (!radius_put_int($radius, RADIUS_NAS_PORT, 0)) { $error = radius_strerror($radius); - } elseif (!radius_put_int($radius, RADIUS_NAS_PORT_TYPE, $this->nasPortType)) { + } elseif (!radius_put_int($radius, RADIUS_NAS_PORT_TYPE, RADIUS_VIRTUAL)) { $error = radius_strerror($radius); } elseif (!empty($this->calledStationId) && !radius_put_string($radius, RADIUS_CALLED_STATION_ID, $this->calledStationId)) { $error = radius_strerror($radius); diff --git a/src/www/system_authservers.php b/src/www/system_authservers.php index ea9c65b82f1..383a8dd7c89 100644 --- a/src/www/system_authservers.php +++ b/src/www/system_authservers.php @@ -100,7 +100,6 @@ $pconfig['radius_secret'] = $a_server[$id]['radius_secret'] ?? ''; $pconfig['radius_timeout'] = $a_server[$id]['radius_timeout'] ?? ''; $pconfig['radius_stationid'] = $a_server[$id]['radius_stationid'] ?? ''; - $pconfig['radius_nasipaddress'] = $a_server[$id]['radius_nasipaddress'] ?? ''; if (!empty($pconfig['radius_auth_port']) && !empty($pconfig['radius_acct_port'])) { @@ -285,12 +284,6 @@ unset($server['radius_stationid']); } - if (!empty($pconfig['radius_nasipaddress'])) { - $server['radius_nasipaddress'] = $pconfig['radius_nasipaddress']; - } else { - unset($server['radius_nasipaddress']); - } - if ($pconfig['radius_srvcs'] == "both") { $server['radius_auth_port'] = $pconfig['radius_auth_port']; $server['radius_acct_port'] = $pconfig['radius_acct_port']; @@ -304,7 +297,6 @@ $server['sync_memberof_groups'] = !empty($pconfig['sync_memberof_groups']) ? implode(",", $pconfig['sync_memberof_groups']) : []; $server['sync_default_groups'] = !empty($pconfig['sync_default_groups']) ? implode(",", $pconfig['sync_default_groups']) : []; $server['sync_create_local_users'] = !empty($pconfig['sync_create_local_users']); - $server['radius_nasporttype'] = RADIUS_VIRTUAL; } elseif ($server['type'] == 'local') { foreach (['password_policy_duration', 'enable_password_policy_constraints', 'password_policy_complexity', 'password_policy_compliance', @@ -377,8 +369,6 @@ 'radius_srvcs', 'radius_timeout', 'radius_stationid', - 'radius_nasipaddress', - 'radius_nasporttype', 'sync_create_local_users', 'sync_memberof', 'ldap_attr_memberof', @@ -885,15 +875,6 @@ - - - - - - -