net/freeradius: Proxy Configuration Page (#3142)

This commit is contained in:
clanto007
2022-11-15 08:34:13 +01:00
committed by GitHub
parent ebcdb5eef6
commit 943924c0ff
17 changed files with 1261 additions and 5 deletions
+2 -3
View File
@@ -1,8 +1,7 @@
PLUGIN_NAME= freeradius
PLUGIN_VERSION= 1.9.21
PLUGIN_REVISION= 1
PLUGIN_VERSION= 1.9.22
PLUGIN_COMMENT= RADIUS Authentication, Authorization and Accounting Server
PLUGIN_DEPENDS= freeradius3
PLUGIN_MAINTAINER= m.muenz@gmail.com
.include "../../Mk/plugins.mk"
.include "../../Mk/plugins.mk"
+3
View File
@@ -14,6 +14,9 @@ The server is fast, feature-rich, modular, and scalable.
Plugin Changelog
================
1.9.22
* Add Proxy configuration page: HomeServer, HomeServerPool, Realm
1.9.21
@@ -0,0 +1,409 @@
<?php
/*
* Copyright (C) 2015-2017 Deciso B.V.
* Copyright (C) 2017 Michael Muenz <m.muenz@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace OPNsense\Freeradius\Api;
use OPNsense\Freeradius\Proxy;
use OPNsense\Core\Config;
use OPNsense\Base\ApiMutableModelControllerBase;
use OPNsense\Base\UIModelGrid;
class ProxyController extends ApiMutableModelControllerBase
{
protected static $internalModelName = 'Proxy';
protected static $internalModelClass = '\OPNsense\Freeradius\Proxy';
public function getAction()
{
// define list of configurable settings
$result = array();
if ($this->request->isGet()) {
$mdlProxy = new Proxy();
$result['proxy'] = $mdlProxy->getNodes();
}
return $result;
}
public function setAction()
{
$result = array("result" => "failed");
if ($this->request->isPost()) {
// load model and update with provided data
$mdlProxy = new Proxy();
$mdlProxy->setNodes($this->request->getPost("proxy"));
// perform validation
$valMsgs = $mdlProxy->performValidation();
foreach ($valMsgs as $field => $msg) {
if (!array_key_exists("validations", $result)) {
$result["validations"] = array();
}
$result["validations"]["proxy." . $msg->getField()] = $msg->getMessage();
}
// serialize model to config and save
if ($valMsgs->count() == 0) {
$mdlProxy->serializeToConfig();
Config::getInstance()->save();
$result["result"] = "saved";
}
}
return $result;
}
public function searchRealmAction()
{
$this->sessionClose();
$mdlRealm = $this->getModel();
$grid = new UIModelGrid($mdlRealm->realms->realm);
return $grid->fetchBindRequest(
$this->request,
array("enabled", "name", "auth_pool", "acct_pool", "nostrip" )
);
}
public function getRealmAction($uuid = null)
{
$mdlRealm = $this->getModel();
if ($uuid != null) {
$node = $mdlRealm->getNodeByReference('realms.realm.' . $uuid);
if ($node != null) {
// return node
return array("realm" => $node->getNodes());
}
} else {
$node = $mdlRealm->realms->realm->add();
return array("realm" => $node->getNodes());
}
return array();
}
public function addRealmAction()
{
$result = array("result" => "failed");
if ($this->request->isPost() && $this->request->hasPost("realm")) {
$result = array("result" => "failed", "validations" => array());
$mdlRealm = $this->getModel();
$node = $mdlRealm->realms->realm->Add();
$node->setNodes($this->request->getPost("realm"));
$valMsgs = $mdlRealm->performValidation();
foreach ($valMsgs as $field => $msg) {
$fieldnm = str_replace($node->__reference, "realm", $msg->getField());
$result["validations"][$fieldnm] = $msg->getMessage();
}
if (count($result['validations']) == 0) {
unset($result['validations']);
// save config if validated correctly
$mdlRealm->serializeToConfig();
Config::getInstance()->save();
unset($result['validations']);
$result["result"] = "saved";
}
}
return $result;
}
public function delRealmAction($uuid)
{
$result = array("result" => "failed");
if ($this->request->isPost()) {
$mdlRealm = $this->getModel();
if ($uuid != null) {
if ($mdlRealm->realms->realm->del($uuid)) {
$mdlRealm->serializeToConfig();
Config::getInstance()->save();
$result['result'] = 'deleted';
} else {
$result['result'] = 'not found';
}
}
}
return $result;
}
public function setRealmAction($uuid)
{
if ($this->request->isPost() && $this->request->hasPost("realm")) {
$mdlSetting = $this->getModel();
if ($uuid != null) {
$node = $mdlSetting->getNodeByReference('realms.realm.' . $uuid);
if ($node != null) {
$result = array("result" => "failed", "validations" => array());
$clientInfo = $this->request->getPost("realm");
$node->setNodes($clientInfo);
$valMsgs = $mdlSetting->performValidation();
foreach ($valMsgs as $field => $msg) {
$fieldnm = str_replace($node->__reference, "realm", $msg->getField());
$result["validations"][$fieldnm] = $msg->getMessage();
}
if (count($result['validations']) == 0) {
// save config if validated correctly
$mdlSetting->serializeToConfig();
Config::getInstance()->save();
$result = array("result" => "saved");
}
return $result;
}
}
}
return array("result" => "failed");
}
public function toggle_handler($uuid, $elements, $element)
{
$result = array("result" => "failed");
if ($this->request->isPost()) {
$mdlSetting = $this->getModel();
if ($uuid != null) {
$node = $mdlSetting->getNodeByReference($elements . '.' . $element . '.' . $uuid);
if ($node != null) {
if ($node->enabled->__toString() == "1") {
$result['result'] = "Disabled";
$node->enabled = "0";
} else {
$result['result'] = "Enabled";
$node->enabled = "1";
}
// if item has toggled, serialize to config and save
$mdlSetting->serializeToConfig();
Config::getInstance()->save();
}
}
}
return $result;
}
public function toggleRealmAction($uuid)
{
return $this->toggle_handler($uuid, 'realms', 'realm');
}
public function searchHomeserverAction()
{
$this->sessionClose();
$mdlHomeserver = $this->getModel();
$grid = new UIModelGrid($mdlHomeserver->homeservers->homeserver);
return $grid->fetchBindRequest(
$this->request,
array("enabled", "name", "type", "addresstype", "ipaddr", "ipaddr6", "ipaddr6", "virtualserver", "port", "proto", "secret", "sourceip", "response_window", "no_response_fail", "zombieperiod", "reviveinterval", "statuscheck", "checkinterval", "numanswersalive", "max_outstanding", "limit_maxconnections", "limit_maxrequests", "limit_lifetime", "limit_idletimeout" )
);
}
public function getHomeserverAction($uuid = null)
{
$mdlHomeserver = $this->getModel();
if ($uuid != null) {
$node = $mdlHomeserver->getNodeByReference('homeservers.homeserver.' . $uuid);
if ($node != null) {
// return node
return array("homeserver" => $node->getNodes());
}
} else {
$node = $mdlHomeserver->homeservers->homeserver->add();
return array("homeserver" => $node->getNodes());
}
return array();
}
public function addHomeserverAction()
{
$result = array("result" => "failed");
if ($this->request->isPost() && $this->request->hasPost("homeserver")) {
$result = array("result" => "failed", "validations" => array());
$mdlHomeserver = $this->getModel();
$node = $mdlHomeserver->homeservers->homeserver->Add();
$node->setNodes($this->request->getPost("homeserver"));
$valMsgs = $mdlHomeserver->performValidation();
foreach ($valMsgs as $field => $msg) {
$fieldnm = str_replace($node->__reference, "homeserver", $msg->getField());
$result["validations"][$fieldnm] = $msg->getMessage();
}
if (count($result['validations']) == 0) {
unset($result['validations']);
// save config if validated correctly
$mdlHomeserver->serializeToConfig();
Config::getInstance()->save();
unset($result['validations']);
$result["result"] = "saved";
}
}
return $result;
}
public function delHomeserverAction($uuid)
{
$result = array("result" => "failed");
if ($this->request->isPost()) {
$mdlHomeserver = $this->getModel();
if ($uuid != null) {
if ($mdlHomeserver->homeservers->homeserver->del($uuid)) {
$mdlHomeserver->serializeToConfig();
Config::getInstance()->save();
$result['result'] = 'deleted';
} else {
$result['result'] = 'not found';
}
}
}
return $result;
}
public function setHomeserverAction($uuid)
{
if ($this->request->isPost() && $this->request->hasPost("homeserver")) {
$mdlSetting = $this->getModel();
if ($uuid != null) {
$node = $mdlSetting->getNodeByReference('homeservers.homeserver.' . $uuid);
if ($node != null) {
$result = array("result" => "failed", "validations" => array());
$clientInfo = $this->request->getPost("homeserver");
$node->setNodes($clientInfo);
$valMsgs = $mdlSetting->performValidation();
foreach ($valMsgs as $field => $msg) {
$fieldnm = str_replace($node->__reference, "homeserver", $msg->getField());
$result["validations"][$fieldnm] = $msg->getMessage();
}
if (count($result['validations']) == 0) {
// save config if validated correctly
$mdlSetting->serializeToConfig();
Config::getInstance()->save();
$result = array("result" => "saved");
}
return $result;
}
}
}
return array("result" => "failed");
}
public function toggleHomeserverAction($uuid)
{
return $this->toggle_handler($uuid, 'homeservers', 'homeserver');
}
public function searchHomeserverpoolAction()
{
$this->sessionClose();
$mdlHomeserverpool = $this->getModel();
$grid = new UIModelGrid($mdlHomeserverpool->homeserverpools->homeserverpool);
return $grid->fetchBindRequest(
$this->request,
array("enabled", "name", "type", "virtualserver", "virtualserver", "homeservers", "fallback" )
);
}
public function getHomeserverpoolAction($uuid = null)
{
$mdlHomeserverpool = $this->getModel();
if ($uuid != null) {
$node = $mdlHomeserverpool->getNodeByReference('homeserverpools.homeserverpool.' . $uuid);
if ($node != null) {
// return node
return array("homeserverpool" => $node->getNodes());
}
} else {
$node = $mdlHomeserverpool->homeserverpools->homeserverpool->add();
return array("homeserverpool" => $node->getNodes());
}
return array();
}
public function addHomeserverpoolAction()
{
$result = array("result" => "failed");
if ($this->request->isPost() && $this->request->hasPost("homeserverpool")) {
$result = array("result" => "failed", "validations" => array());
$mdlHomeserverpool = $this->getModel();
$node = $mdlHomeserverpool->homeserverpools->homeserverpool->Add();
$node->setNodes($this->request->getPost("homeserverpool"));
$valMsgs = $mdlHomeserverpool->performValidation();
foreach ($valMsgs as $field => $msg) {
$fieldnm = str_replace($node->__reference, "homeserverpool", $msg->getField());
$result["validations"][$fieldnm] = $msg->getMessage();
}
if (count($result['validations']) == 0) {
unset($result['validations']);
// save config if validated correctly
$mdlHomeserverpool->serializeToConfig();
Config::getInstance()->save();
unset($result['validations']);
$result["result"] = "saved";
}
}
return $result;
}
public function delHomeserverpoolAction($uuid)
{
$result = array("result" => "failed");
if ($this->request->isPost()) {
$mdlHomeserverpool = $this->getModel();
if ($uuid != null) {
if ($mdlHomeserverpool->homeserverpools->homeserverpool->del($uuid)) {
$mdlHomeserverpool->serializeToConfig();
Config::getInstance()->save();
$result['result'] = 'deleted';
} else {
$result['result'] = 'not found';
}
}
}
return $result;
}
public function setHomeserverpoolAction($uuid)
{
if ($this->request->isPost() && $this->request->hasPost("homeserverpool")) {
$mdlSetting = $this->getModel();
if ($uuid != null) {
$node = $mdlSetting->getNodeByReference('homeserverpools.homeserverpool.' . $uuid);
if ($node != null) {
$result = array("result" => "failed", "validations" => array());
$clientInfo = $this->request->getPost("homeserverpool");
$node->setNodes($clientInfo);
$valMsgs = $mdlSetting->performValidation();
foreach ($valMsgs as $field => $msg) {
$fieldnm = str_replace($node->__reference, "homeserverpool", $msg->getField());
$result["validations"][$fieldnm] = $msg->getMessage();
}
if (count($result['validations']) == 0) {
// save config if validated correctly
$mdlSetting->serializeToConfig();
Config::getInstance()->save();
$result = array("result" => "saved");
}
return $result;
}
}
}
return array("result" => "failed");
}
public function toggleHomeserverpoolAction($uuid)
{
return $this->toggle_handler($uuid, 'homeserverpools', 'homeserverpool');
}
}
@@ -0,0 +1,40 @@
<?php
/*
* Copyright (C) 2017 Michael Muenz <m.muenz@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
namespace OPNsense\Freeradius;
class ProxyController extends \OPNsense\Base\IndexController
{
public function indexAction()
{
$this->view->formDialogEditFreeRADIUSHomeserver = $this->getForm("dialogEditFreeRADIUSHomeserver");
$this->view->formDialogEditFreeRADIUSHomeserverpool = $this->getForm("dialogEditFreeRADIUSHomeserverpool");
$this->view->formDialogEditFreeRADIUSRealm = $this->getForm("dialogEditFreeRADIUSRealm");
$this->view->pick('OPNsense/Freeradius/proxy');
}
}
@@ -0,0 +1,140 @@
<form>
<field>
<id>homeserver.enabled</id>
<label>Enabled</label>
<type>checkbox</type>
<help>This will enable or disable the homeserver setting.</help>
</field>
<field>
<id>homeserver.name</id>
<label>Name</label>
<type>text</type>
<help>Set the name of Home Server.</help>
</field>
<field>
<id>homeserver.type</id>
<label>Type</label>
<type>dropdown</type>
<help>Set type of auth.</help>
</field>
<field>
<id>homeserver.addresstype</id>
<label>Address Type</label>
<type>dropdown</type>
<help>Set type of address</help>
</field>
<field>
<id>homeserver.ipaddr</id>
<label>IP Address</label>
<type>text</type>
<help>Set the ip address.</help>
</field>
<field>
<id>homeserver.ipaddr6</id>
<label>IP Address v6</label>
<type>text</type>
<help>Set the ip v6 address.</help>
</field>
<field>
<id>homeserver.virtualserver</id>
<label>Virtual Server</label>
<type>text</type>
<help>Set the virtual server address.</help>
</field>
<field>
<id>homeserver.port</id>
<label>Port</label>
<type>text</type>
<help>Set the port</help>
</field>
<field>
<id>homeserver.proto</id>
<label>Protocol</label>
<type>dropdown</type>
<help>Set the protocol</help>
</field>
<field>
<id>homeserver.secret</id>
<label>Secret</label>
<type>password</type>
<help>Set the secret</help>
</field>
<field>
<id>homeserver.sourceip</id>
<label>Source IP Address</label>
<type>text</type>
<help>Source IP Address</help>
</field>
<field>
<id>homeserver.response_window</id>
<label>Response Window</label>
<type>text</type>
<help>Response Window from 5 to 60</help>
</field>
<field>
<id>homeserver.no_response_fail</id>
<label>No Response Fail</label>
<type>checkbox</type>
<help>No Response Fail</help>
</field>
<field>
<id>homeserver.zombieperiod</id>
<label>Zombie Period</label>
<type>text</type>
<help>Zombie Period from 20 to 120</help>
</field>
<field>
<id>homeserver.reviveinterval</id>
<label>Revive Interval</label>
<type>text</type>
<help>Revive interval from 60 to 3600</help>
</field>
<field>
<id>homeserver.statuscheck</id>
<label>Status Check</label>
<type>dropdown</type>
<help>Status Check</help>
</field>
<field>
<id>homeserver.checkinterval</id>
<label>Check Interval</label>
<type>text</type>
<help>Check Interval from 6 to 120</help>
</field>
<field>
<id>homeserver.numanswersalive</id>
<label>Num answers to alive</label>
<type>text</type>
<help>number of status checks in a row that the home server needs to respond to before it is marked alive. From 3 to 10</help>
</field>
<field>
<id>homeserver.max_outstanding</id>
<label>Max Outstanding</label>
<type>text</type>
<help>Limit the total number of outstanding packets to the home server.</help>
</field>
<field>
<id>homeserver.limit_maxconnections</id>
<label>Limit Max Connections</label>
<type>text</type>
<help>Limit the number of TCP connections to the home server. Setting this to 0 means "no limit".</help>
</field>
<field>
<id>homeserver.limit_maxrequests</id>
<label>Limit Max Requests</label>
<type>text</type>
<help>Limit the total number of requests sent over one TCP connection. Setting this to 0 means "no limit".</help>
</field>
<field>
<id>homeserver.limit_lifetime</id>
<label>Limit lifetime</label>
<type>text</type>
<help>The lifetime, in seconds, of a TCP connection. Setting this to 0 means "forever".</help>
</field>
<field>
<id>homeserver.limit_idletimeout</id>
<label>Limit Idle Timeout</label>
<type>text</type>
<help>The idle timeout, in seconds, of a TCP connection. Setting this to 0 means "no timeout".</help>
</field>
</form>
@@ -0,0 +1,40 @@
<form>
<field>
<id>homeserverpool.enabled</id>
<label>Enabled</label>
<type>checkbox</type>
<help>This will enable or disable the home server pool.</help>
</field>
<field>
<id>homeserverpool.name</id>
<label>Name</label>
<type>text</type>
<help>Set the name of Home Server Pool</help>
</field>
<field>
<id>homeserverpool.type</id>
<label>Type</label>
<type>dropdown</type>
<help>Set the type of pool</help>
</field>
<field>
<id>homeserverpool.virtualserver</id>
<label>Virtual Server</label>
<type>text</type>
<help>A virtual_server may be specified here.</help>
</field>
<field>
<id>homeserverpool.homeservers</id>
<label>Home Servers</label>
<style>tokenize</style>
<type>select_multiple</type>
<allownew>true</allownew>
<help>The home servers.</help>
</field>
<field>
<id>homeserverpool.fallback</id>
<label>Fallback</label>
<type>text</type>
<help>If ALL home servers are dead, then this "fallback" home server is used.</help>
</field>
</form>
@@ -0,0 +1,32 @@
<form>
<field>
<id>realm.enabled</id>
<label>Enabled</label>
<type>checkbox</type>
<help>This will enable or disable the proxy config.</help>
</field>
<field>
<id>realm.name</id>
<label>Name</label>
<type>text</type>
<help>Set the name of Realm</help>
</field>
<field>
<id>realm.auth_pool</id>
<label>Authentication Pool</label>
<type>text</type>
<help>Should point to a "home_server_pool" that was defined.</help>
</field>
<field>
<id>realm.acct_pool</id>
<label>Accounting Pool</label>
<type>text</type>
<help>If used, should point to a "home_server_pool" that was defined.</help>
</field>
<field>
<id>realm.nostrip</id>
<label>No Strip</label>
<type>checkbox</type>
<help>Normally, when an incoming User-Name is matched against the realm, the realm name is "stripped" off, and the "stripped" user name is used to perform matches.</help>
</field>
</form>
@@ -145,4 +145,11 @@
<advanced>true</advanced>
<help>Set database name of remote MySQL server.</help>
</field>
<field>
<id>general.fallbackproxy</id>
<label>Default Fallback Proxy</label>
<type>checkbox</type>
<advanced>true</advanced>
<help>Fallback for proxy server. Default disabled.</help>
</field>
</form>
@@ -65,4 +65,4 @@
<type>text</type>
<help>Filter for group objects, should match all available group objects a user might be a member of.</help>
</field>
</form>
</form>
@@ -148,5 +148,9 @@
<default>radius</default>
<Required>Y</Required>
</mysqldb>
<fallbackproxy type="BooleanField">
<default>0</default>
<Required>Y</Required>
</fallbackproxy>
</items>
</model>
@@ -48,4 +48,4 @@
<Required>N</Required>
</group_filter>
</items>
</model>
</model>
@@ -8,6 +8,11 @@
<Lease VisibleName="DHCP Leases" url="/ui/freeradius/lease/index" order="36"/>
<EAP url="/ui/freeradius/eap/index" order="40"/>
<LDAP url="/ui/freeradius/ldap/index" order="50"/>
<Proxy url="/ui/freeradius/proxy/index" order="60">
<Homeservers url="/ui/freeradius#homeservers"/>
<Homeserverpools url="/ui/freeradius#homeserverpools"/>
<Realms url="/ui/freeradius#realms"/>
</Proxy>
<LogFile VisibleName="Log File" order="80" url="/ui/diagnostics/log/core/radius"/>
</FreeRADIUS>
</Services>
@@ -0,0 +1,22 @@
<?php
namespace OPNsense\Freeradius;
use OPNsense\Base\BaseModel;
use OPNsense\Core\Backend;
class Proxy extends BaseModel
{
/**
* check if module is enabled
* @return bool is the ZabbixAgent service enabled
*/
public function isEnabled()
{
if ((string)$this->settings->main->enabled === "1") {
return true;
}
return false;
}
}
@@ -0,0 +1,191 @@
<model>
<mount>//OPNsense/freeradius/proxy</mount>
<description>Proxy configuration</description>
<version>0.1</version>
<items>
<homeservers>
<homeserver type="ArrayField">
<enabled type="BooleanField">
<default>1</default>
<Required>Y</Required>
</enabled>
<name type="TextField">
<Required>Y</Required>
</name>
<type type="OptionField">
<default>1</default>
<Required>Y</Required>
<OptionValues>
<auth>auth</auth>
<acct>acct</acct>
<authacct>auth+acct</authacct>
<coa>coa</coa>
</OptionValues>
</type>
<addresstype type="OptionField">
<default>ipv4</default>
<Required>Y</Required>
<OptionValues>
<ipv4>ipv4</ipv4>
<ipv6>ipv6</ipv6>
<virtual_server>virtual_server</virtual_server>
</OptionValues>
</addresstype>
<ipaddr type="TextField">
<default>172.0.0.1</default>
<Required>N</Required>
</ipaddr>
<ipaddr6 type="TextField">
<default>::1</default>
<Required>N</Required>
</ipaddr6>
<virtualserver type="TextField">
<default>foo</default>
<Required>N</Required>
</virtualserver>
<port type="IntegerField">
<default>1812</default>
<Required>Y</Required>
<MinimumValue>1</MinimumValue>
<MaximumValue>65535</MaximumValue>
</port>
<proto type="OptionField">
<default>udp</default>
<Required>Y</Required>
<OptionValues>
<udp>udp</udp>
<tcp>tcp</tcp>
</OptionValues>
</proto>
<secret type="TextField">
<default>testing123</default>
<Required>N</Required>
</secret>
<sourceip type="TextField">
<Required>N</Required>
</sourceip>
<response_window type="IntegerField">
<default>20</default>
<Required>Y</Required>
<MinimumValue>5</MinimumValue>
<MaximumValue>60</MaximumValue>
</response_window>
<no_response_fail type="BooleanField">
<Required>N</Required>
</no_response_fail>
<zombieperiod type="IntegerField">
<default>40</default>
<Required>Y</Required>
<MinimumValue>20</MinimumValue>
<MaximumValue>120</MaximumValue>
</zombieperiod>
<reviveinterval type="IntegerField">
<default>120</default>
<Required>Y</Required>
<MinimumValue>60</MinimumValue>
<MaximumValue>3600</MaximumValue>
</reviveinterval>
<statuscheck type="OptionField">
<default>status-server</default>
<Required>Y</Required>
<OptionValues>
<none>none</none>
<status-server>status-server</status-server>
<request>request</request>
</OptionValues>
</statuscheck>
<checkinterval type="IntegerField">
<default>30</default>
<Required>Y</Required>
<MinimumValue>6</MinimumValue>
<MaximumValue>120</MaximumValue>
</checkinterval>
<numanswersalive type="IntegerField">
<default>3</default>
<Required>Y</Required>
<MinimumValue>3</MinimumValue>
<MaximumValue>10</MaximumValue>
</numanswersalive>
<max_outstanding type="IntegerField">
<default>65536</default>
<Required>Y</Required>
</max_outstanding>
<limit_maxconnections type="IntegerField">
<default>16</default>
<Required>Y</Required>
</limit_maxconnections>
<limit_maxrequests type="IntegerField">
<default>0</default>
<Required>Y</Required>
</limit_maxrequests>
<limit_lifetime type="IntegerField">
<default>0</default>
<Required>Y</Required>
</limit_lifetime>
<limit_idletimeout type="IntegerField">
<default>0</default>
<Required>Y</Required>
</limit_idletimeout>
</homeserver>
</homeservers>
<homeserverpools>
<homeserverpool type="ArrayField">
<enabled type="BooleanField">
<default>1</default>
<Required>Y</Required>
</enabled>
<name type="TextField">
<Required>Y</Required>
</name>
<type type="OptionField">
<default>fail-over</default>
<Required>Y</Required>
<OptionValues>
<fail-over>fail-over</fail-over>
<load-balance>load-balance</load-balance>
<client-balance>client-balance</client-balance>
<client-port-balance>client-port-balance</client-port-balance>
<keyed-balance>keyed-balance</keyed-balance>
</OptionValues>
</type>
<virtualserver type="TextField">
<Required>N</Required>
</virtualserver>
<homeservers type="CSVListField">
<default>localhost</default>
<Required>Y</Required>
<multiple>Y</multiple>
<mask>/^([a-zA-Z0-9\.:\[\]\-\/]*?,)*([a-zA-Z0-9\.:\[\]\-\/]*)$/</mask>
<ChangeCase>lower</ChangeCase>
<ValidationMessage>Please provide valid server addresses, i.e. radius.example.com, 10.0.0.2 or 10.0.0.0/24.</ValidationMessage>
</homeservers>
<fallback type="TextField">
<default></default>
<Required>N</Required>
</fallback>
</homeserverpool>
</homeserverpools>
<realms>
<realm type="ArrayField">
<enabled type="BooleanField">
<default>1</default>
<Required>Y</Required>
</enabled>
<name type="TextField">
<Required>Y</Required>
</name>
<auth_pool type="TextField">
<default></default>
<Required>N</Required>
</auth_pool>
<acct_pool type="TextField">
<default></default>
<Required>N</Required>
</acct_pool>
<nostrip type="BooleanField">
<Required>N</Required>
</nostrip>
</realm>
</realms>
</items>
</model>
@@ -0,0 +1,237 @@
{#
OPNsense® is Copyright © 2014 2016 by Deciso B.V.
Copyright (C) 2017 Michael Muenz <m.muenz@gmail.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
#}
<script>
$( document ).ready(function() {
var data_get_map = {'frm_zabbixagent':"/api/freeradius/proxy/get"};
// load initial data
mapDataToFormUI(data_get_map).done(function(){
formatTokenizersUI();
$('.selectpicker').selectpicker('refresh');
updateServiceControlUI('freeradius');
});
/*************************************************************************************************************
* link grid actions
*************************************************************************************************************/
$("#grid-homeservers").UIBootgrid(
{ 'search':'/api/freeradius/proxy/searchHomeserver',
'get':'/api/freeradius/proxy/getHomeserver/',
'set':'/api/freeradius/proxy/setHomeserver/',
'add':'/api/freeradius/proxy/addHomeserver/',
'del':'/api/freeradius/proxy/delHomeserver/',
'toggle':'/api/freeradius/proxy/toggleHomeserver/',
options: {
rowCount:[10,25,50,100,500,1000]
}
}
);
$("#grid-homeserverpools").UIBootgrid(
{ 'search':'/api/freeradius/proxy/searchHomeserverpool',
'get':'/api/freeradius/proxy/getHomeserverpool/',
'set':'/api/freeradius/proxy/setHomeserverpool/',
'add':'/api/freeradius/proxy/addHomeserverpool/',
'del':'/api/freeradius/proxy/delHomeserverpool/',
'toggle':'/api/freeradius/proxy/toggleHomeserverpool/',
options: {
rowCount:[10,25,50,100,500,1000]
}
}
);
$("#grid-realms").UIBootgrid(
{ 'search':'/api/freeradius/proxy/searchRealm',
'get':'/api/freeradius/proxy/getRealm/',
'set':'/api/freeradius/proxy/setRealm/',
'add':'/api/freeradius/proxy/addRealm/',
'del':'/api/freeradius/proxy/delRealm/',
'toggle':'/api/freeradius/proxy/toggleRealm/',
options: {
rowCount:[10,25,50,100,500,1000]
}
}
);
/*************************************************************************************************************
* Commands
*************************************************************************************************************/
// form save event handlers for all defined forms
$('[id*="save_"]').each(function(){
$(this).click(function(){
var frm_id = $(this).closest("form").attr("id");
var frm_title = $(this).closest("form").attr("data-title");
// save data for tab
saveFormToEndpoint(url="/api/freeradius/proxy/set",formid=frm_id,callback_ok=function(){
// set progress animation when reloading
$("#"+frm_id+"_progress").addClass("fa fa-spinner fa-pulse");
// on correct save, perform restart
ajaxCall(url="/api/freeradius/proxy/reconfigure", sendData={}, callback=function(data,status){
// when done, disable progress animation.
$("#"+frm_id+"_progress").removeClass("fa fa-spinner fa-pulse");
if (status != "success" || data['status'] != 'ok' ) {
// fix error handling
BootstrapDialog.show({
type:BootstrapDialog.TYPE_WARNING,
title: frm_title,
message: JSON.stringify(data),
draggable: true
});
} else {
updateServiceControlUI('freeradius');
}
// when done, disable progress animation.
$("#"+frm_id+"_progress").removeClass("fa fa-spinner fa-pulse");
});
});
});
});
/**
* Reconfigure
*/
$("#reconfigureAct").click(function(){
$("#reconfigureAct_progress").addClass("fa fa-spinner fa-pulse");
ajaxCall(url="/api/freeradius/service/reconfigure", sendData={}, callback=function(data,status) {
// when done, disable progress animation.
$("#reconfigureAct_progress").removeClass("fa fa-spinner fa-pulse");
updateServiceControlUI('freeradius');
if (status != "success" || data['status'] != 'ok') {
BootstrapDialog.show({
type: BootstrapDialog.TYPE_WARNING,
title: "{{ lang._('Error reconfiguring FreeRADIUS') }}",
message: data['status'],
draggable: true
});
} else {
ajaxCall(url="/api/freeradius/service/reconfigure", sendData={});
}
});
});
});
</script>
<ul class="nav nav-tabs" data-tabs="tabs" id="maintabs">
<li class= "active"><a data-toggle="tab" id="homeservers-tab" href="#homeservers">{{ lang._('Home Servers') }}</a></li>
<li><a data-toggle="tab" id="homeserverpools-tab" href="#homeserverpools">{{ lang._('Home Servers Pool') }}</a></li>
<li><a data-toggle="tab" id="realms-tab" href="#realms">{{ lang._('Realms') }}</a></li>
</ul>
<div class="content-box tab-content col-xs-12 __mb">
{# manually add tab content #}
<div id="homeservers" class="tab-pane fade in active">
<!-- tab page "homeservers" -->
<table id="grid-homeservers" class="table table-condensed table-hover table-striped" data-editDialog="dialogEditFreeRADIUSHomeserver" data-editAlert="OverrideChangeMessage">
<thead>
<tr>
<th data-column-id="enabled" data-width="6em" data-type="string" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
<th data-column-id="name" data-type="string">{{ lang._('Name') }}</th>
<th data-column-id="type" data-type="string">{{ lang._('Type') }}</th>
<th data-column-id="commands" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button data-action="add" type="button" class="btn btn-xs btn-default"><span class="fa fa-plus"></span></button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span class="fa fa-trash-o"></span></button>
</td>
</tr>
</tfoot>
</table>
</div>
<div id="homeserverpools" class="tab-pane fade in">
<!-- tab page "homeserverpools" -->
<table id="grid-homeserverpools" class="table table-condensed table-hover table-striped" data-editDialog="dialogEditFreeRADIUSHomeserverpool" data-editAlert="OverrideChangeMessage">
<thead>
<tr>
<th data-column-id="enabled" data-width="6em" data-type="string" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
<th data-column-id="name" data-type="string">{{ lang._('Name') }}</th>
<th data-column-id="type" data-type="dropdown">{{ lang._('Type') }}</th>
<th data-column-id="virtualserver" data-type="string">{{ lang._('Virtual Server') }}</th>
<th data-column-id="commands" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button data-action="add" type="button" class="btn btn-xs btn-default"><span class="fa fa-plus"></span></button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span class="fa fa-trash-o"></span></button>
</td>
</tr>
</tfoot>
</table>
</div>
<div id="realms" class="tab-pane fade in">
<!-- tab page "realms" -->
<table id="grid-realms" class="table table-condensed table-hover table-striped table-responsive" data-editDialog="dialogEditFreeRADIUSRealm" data-editAlert="OverrideChangeMessage">
<thead>
<tr>
<th data-column-id="enabled" data-width="6em" data-type="string" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
<th data-column-id="name" data-type="string" data-visible="true">{{ lang._('Name') }}</th>
<th data-column-id="auth_pool" data-type="string" data-visible="true">{{ lang._('Authentication Pool') }}</th>
<th data-column-id="commands" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button data-action="add" type="button" class="btn btn-xs btn-default"><span class="fa fa-plus"></span></button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span class="fa fa-trash-o"></span></button>
</td>
</tr>
</tfoot>
</table>
</div>
</div>
<div class="col-md-12">
<hr/>
<button class="btn btn-primary" id="reconfigureAct" type="button"><b>{{ lang._('Apply') }}</b> <i id="reconfigureAct_progress" class=""></i></button>
<br/><br/>
</div>
{# include dialogs #}
{{ partial("layout_partials/base_dialog",['fields':formDialogEditFreeRADIUSHomeserver,'id':'dialogEditFreeRADIUSHomeserver','label':lang._('Edit Homeservers')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogEditFreeRADIUSHomeserverpool,'id':'dialogEditFreeRADIUSHomeserverpool','label':lang._('Edit Homeserverpools')])}}
{{ partial("layout_partials/base_dialog",['fields':formDialogEditFreeRADIUSRealm,'id':'dialogEditFreeRADIUSRealm','label':lang._('Edit Realms')])}}
@@ -1,4 +1,5 @@
clients.conf:/usr/local/etc/raddb/clients.conf
proxy.conf:/usr/local/etc/raddb/proxy.conf
dictionary:/usr/local/etc/raddb/dictionary
mac2ip:/usr/local/etc/raddb/mods-config/passwd/mac2ip
mods-enabled-counter:/usr/local/etc/raddb/mods-enabled/counter
@@ -0,0 +1,126 @@
proxy server {
{% if helpers.exists('OPNsense.freeradius.general.fallbackproxy') and OPNsense.freeradius.general.fallbackproxy == '1' %}
default_fallback = yes
{% else %}
default_fallback = no
{% endif %}
}
{% if helpers.exists('OPNsense.freeradius.proxy.homeservers.homeserver') %}
{% for homeserver_list in helpers.toList('OPNsense.freeradius.proxy.homeservers.homeserver') %}
{% if homeserver_list.enabled == '1' %}
home_server {{ homeserver_list.name }} {
type = {{ homeserver_list.type }}
{% if homeserver_list.addresstype == 'ipv4' %}
ipaddr = {{ homeserver_list.ipaddr }}
{% endif %}
{% if homeserver_list.addresstype == 'ipv6' %}
ipv6addr = {{ homeserver_list.ipaddr6 }}
{% endif %}
{% if homeserver_list.addresstype == 'virtual_server' %}
virtual_server = {{ homeserver_list.virtualserver }}
{% endif %}
port = {{ homeserver_list.port }}
proto = {{ homeserver_list.proto }}
secret = {{ homeserver_list.secret }}
{% if homeserver_list.sourceip != '' %}
src_ipaddr = {{ homeserver_list.sourceip }}
{% endif %}
response_window = {{ homeserver_list.response_window }}
{% if homeserver_list.no_response_fail == '1' %}
no_response_fail = yes
{% else %}
no_response_fail = no
{% endif %}
zombie_period = {{ homeserver_list.zombieperiod }}
revive_interval = {{ homeserver_list.reviveinterval }}
status_check = {{ homeserver_list.statuscheck }}
check_interval = {{ homeserver_list.checkinterval }}
num_answers_to_alive = {{ homeserver_list.numanswersalive }}
max_outstanding = {{ homeserver_list.max_outstanding }}
coa {
irt = 2
mrt = 16
mrc = 5
mrd = 30
}
limit {
max_connections = {{ homeserver_list.limit_maxconnections }}
max_requests = {{ homeserver_list.limit_maxrequests }}
lifetime = {{ homeserver_list.limit_lifetime }}
idle_timeout = {{ homeserver_list.limit_idletimeout }}
}
}
{% endif %}
{% endfor %}
{% else %}
home_server localhost {
type = auth
ipaddr = 127.0.0.1
port = 1812
secret = testing123
response_window = 20
zombie_period = 40
revive_interval = 120
status_check = status-server
check_interval = 30
num_answers_to_alive = 3
max_outstanding = 65536
coa {
irt = 2
mrt = 16
mrc = 5
mrd = 30
}
limit {
max_connections = 16
max_requests = 0
lifetime = 0
idle_timeout = 0
}
}
{% endif %}
{% if helpers.exists('OPNsense.freeradius.proxy.homeserverpools.homeserverpool') %}
{% for homeserverpool_list in helpers.toList('OPNsense.freeradius.proxy.homeserverpools.homeserverpool') %}
{% if homeserverpool_list.enabled == '1' %}
home_server_pool {{ homeserverpool_list.name }} {
type = {{ homeserverpool_list.type }}
{% if homeserverpool_list.virtualserver != '' %}
virtual_server = {{ homeserverpool_list.virtualserver }}
{% endif %}
{% if homeserverpool_list.homeservers is defined %}
{% for network in homeserverpool_list.homeservers.split(',') %}
home_server = {{ network }}
{% endfor %}
{% endif %}
{% if homeserverpool_list.fallback is defined %}
fallback = {{ homeserverpool_list.fallback }}
{% endif %}
}
{% endif %}
{% endfor %}
{% else %}
home_server_pool my_auth_failover {
type = fail-over
home_server = localhost
}
{% endif %}
{% if helpers.exists('OPNsense.freeradius.proxy.realms.realm') %}
{% for realm_list in helpers.toList('OPNsense.freeradius.proxy.realms.realm') %}
{% if realm_list.enabled == '1' %}
realm {{ realm_list.name }} {
{% if realm_list.auth_pool is defined %}
auth_pool = {{ realm_list.auth_pool }}
{% endif %}
{% if realm_list.acct_pool is defined %}
acct_pool = {{ realm_list.acct_pool }}
{% endif %}
{% if realm_list.nostrip == '1' %}
nostrip
{% endif %}
}
{% endif %}
{% endfor %}
{% else %}
realm LOCAL {
}
{% endif %}