captive: allow mutliple instances

Signed-off-by: John Crispin <john@phrozen.org>
This commit is contained in:
John Crispin
2023-04-13 07:59:21 +02:00
parent f2a7137c3a
commit 99cd625111
9 changed files with 1204 additions and 1195 deletions

View File

@@ -990,6 +990,8 @@ let routing_table = {
/** @lends uCentral.captive.prototype */
let captive = {
interfaces: {},
next: 0,
/**
@@ -998,9 +1000,19 @@ let captive = {
* @param {string} id The ID to lookup or reserve
* @returns {number} The table number allocated for the given ID
*/
get: function() {
return this.next++;
}
get: function(name) {
let iface = this.next++;
push(this.interfaces[name].iface, iface);
return iface;
},
/**
* Add an interface
*/
interface: function(name, config) {
this.interfaces[name] = config;
this.interfaces[name].iface = [];
},
};
/**

View File

@@ -1,25 +1,119 @@
{%
if (config.radius_gw_proxy)
services.set_enabled("radius-gw-proxy", true);
# Captive Portal Configuration
add opennds opennds
set opennds.@opennds[-1].enabled='1'
set opennds.@opennds[-1].fwhook_enabled='1'
set opennds.@opennds[-1].debuglevel='1'
add_list opennds.@opennds[-1].users_to_router='allow tcp port 53'
add_list opennds.@opennds[-1].users_to_router='allow udp port 53'
add_list opennds.@opennds[-1].users_to_router='allow udp port 67'
add_list opennds.@opennds[-1].users_to_router='allow tcp port 22'
add_list opennds.@opennds[-1].users_to_router='allow tcp port 80'
add_list opennds.@opennds[-1].users_to_router='allow tcp port 443'
set opennds.@opennds[-1].login_option_enabled='1'
set opennds.@opennds[-1].gatewayinterface='br-{{ name }}'
set opennds.@opennds[-1].gatewayname={{ s(interface.captive.gateway_name) }}
set opennds.@opennds[-1].maxclients='{{ interface.captive.max_clients }}'
set opennds.@opennds[-1].gatewayfqdn={{ s(interface.captive.gateway_fqdn) }}
set opennds.@opennds[-1].uploadrate='{{ interface.captive.upload_rate }}'
set opennds.@opennds[-1].downloadrate='{{ interface.captive.download_rate }}'
set opennds.@opennds[-1].uploadquota='{{ interface.captive.upload_quota }}'
set opennds.@opennds[-1].downloadquota='{{ interface.captive.download_quota }}'
add_list opennds.@opennds[-1].authenticated_users='block to 192.168.0.0/16'
add_list opennds.@opennds[-1].authenticated_users='block to 172.16.0.0/24'
add_list opennds.@opennds[-1].authenticated_users='block to 10.0.0.0/24'
add_list opennds.@opennds[-1].authenticated_users='allow all'
function radius_proxy_tlv(server, port, name) {
let tlv = replace(serial, /^(..)(..)(..)(..)(..)(..)$/, "$1$2$3$4$5$6") + sprintf(":%s:%s:%s", server, port, name);
return tlv;
}
captive.interface(section, config);
let name = split(section, '_')[0];
if (!captive.web_root)
system('cp -r /www-uspot /tmp/ucentral/');
else {
let fs = require('fs');
fs.mkdir('/tmp/ucentral/www-uspot');
let web_root = fs.open('/tmp/ucentral/web-root.tar', 'w');
web_root.write(b64dec(captive.web_root));
web_root.close();
system('tar x -C /tmp/ucentral/www-uspot -f /tmp/ucentral/web-root.tar');
}
if (captive.radius_gw_proxy)
services.set_enabled("radius-gw-proxy", true);
%}
# Captive Portal service configuration
set uspot.{{ section }}=uspot
set uspot.{{ section }}.auth_mode={{ s(config.auth_mode) }}
set uspot.{{ section }}.web_root={{ b(config.web_root) }}
set uspot.{{ section }}.idle_timeout={{ config.idle_timeout }}
set uspot.{{ section }}.session_timeout={{ config.session_timeout }}
{% if (config.auth_mode in [ 'radius', 'uam']): %}
{% if (config.radius_gw_proxy): %}
set uspot.{{ section }}.auth_server='127.0.0.1'
set uspot.{{ section }}.auth_port='1812'
set uspot.{{ section }}.auth_proxy={{ s(radius_proxy_tlv(config.auth_server, config.auth_port, 'captive')) }}
{% if (config.acct_server): %}
set uspot.{{ section }}.acct_server='127.0.0.1'
set uspot.{{ section }}.acct_port='1813'
set uspot.{{ section }}.acct_proxy={{ s(radius_proxy_tlv(config.acct_server, config.acct_port, 'captive')) }}
{% endif %}
{% else %}
set uspot.{{ section }}.auth_server={{ s(config.auth_server) }}
set uspot.{{ section }}.auth_port={{ s(config.auth_port) }}
set uspot.{{ section }}.acct_server={{ s(config.acct_server) }}
set uspot.{{ section }}.acct_port={{ s(config.acct_port) }}
{% endif %}
set uspot.{{ section }}.auth_secret={{ s(config.auth_secret) }}
set uspot.{{ section }}.acct_secret={{ s(config.acct_secret) }}
set uspot.{{ section }}.acct_interval={{ config.acct_interval }}
{% endif %}
{% if (captive.auth_mode == 'credentials'): %}
{% for (let cred in captive.credentials): %}
add uspot credentials
set uspot.@credentials[-1].username={{ s(cred.username) }}
set uspot.@credentials[-1].password={{ s(cred.password) }}
set uspot.@credentials[-1].interface={{ s(section) }}
{% endfor %}
{% endif %}
{% if (config.auth_mode == 'uam'): %}
{%
let math = require('math');
let challenge = "";
for (let i = 0; i < 16; i++)
challenge += sprintf('%02x', math.rand() % 255);
%}
set uspot.{{ section }}.challenge={{ s(challenge) }}
set uspot.{{ section }}.uam_port={{ s(config.uam_port) }}
set uspot.{{ section }}.uam_secret={{ s(config.uam_secret) }}
set uspot.{{ section }}.uam_server={{ s(config.uam_server) }}
set uspot.{{ section }}.nasid={{ s(config.nasid) }}
set uspot.{{ section }}.nasmac={{ s(config.nasmac || serial) }}
set uspot.{{ section }}.ssid={{ s(config.ssid) }}
set uspot.{{ section }}.mac_format={{ s(config.mac_format) }}
set uspot.{{ section }}.final_redirect_url={{ s(config.final_redirect_url) }}
set uspot.{{ section }}.mac_auth={{ b(config.mac_auth) }}
set uhttpd.uam{{ config.uam_port }}=uhttpd
set uhttpd.@uhttpd[-1].redirect_https='0'
set uhttpd.@uhttpd[-1].rfc1918_filter='1'
set uhttpd.@uhttpd[-1].max_requests='5'
set uhttpd.@uhttpd[-1].max_connections='100'
set uhttpd.@uhttpd[-1].cert='/etc/uhttpd.crt'
set uhttpd.@uhttpd[-1].key='/etc/uhttpd.key'
set uhttpd.@uhttpd[-1].script_timeout='60'
set uhttpd.@uhttpd[-1].network_timeout='30'
set uhttpd.@uhttpd[-1].http_keepalive='20'
set uhttpd.@uhttpd[-1].tcp_keepalive='1'
add_list uhttpd.@uhttpd[-1].listen_http='0.0.0.0:{{ config.uam_port }}'
add_list uhttpd.@uhttpd[-1].listen_http='[::]:{{ config.uam_port }}'
set uhttpd.@uhttpd[-1].home=/tmp/ucentral/www-uspot
add_list uhttpd.@uhttpd[-1].ucode_prefix='/logon=/usr/share/uspot/handler-uam.uc'
add_list uhttpd.@uhttpd[-1].ucode_prefix='/logoff=/usr/share/uspot/handler-uam.uc'
add_list uhttpd.@uhttpd[-1].ucode_prefix='/logout=/usr/share/uspot/handler-uam.uc'
set firewall.{{ name + config.uam_port}}_1=rule
set firewall.@rule[-1].name='Allow-UAM-{{ name }}'
set firewall.@rule[-1].src='{{ name }}'
set firewall.@rule[-1].dest_port='{{ config.uam_port }}'
set firewall.@rule[-1].proto='tcp'
set firewall.@rule[-1].target='ACCEPT'
set firewall.@rule[-1].mark='1/127'
set firewall.{{ name + config.uam_port}}_2=rule
set firewall.@rule[-1].name='Allow-UAM-{{ name }}'
set firewall.@rule[-1].src='{{ name }}'
set firewall.@rule[-1].dest_port='{{ config.uam_port }}'
set firewall.@rule[-1].proto='tcp'
set firewall.@rule[-1].target='ACCEPT'
set firewall.@rule[-1].mark='2/127'
{% endif %}

View File

@@ -249,28 +249,41 @@
return '';
}
function calculate_ifname() {
function calculate_ifname(name) {
if ('captive' in ssid.services)
return 'wlanc' + captive.get();
return 'wlanc' + captive.get(name);
return '';
}
let radius_gw_proxy = ssid.services && (index(ssid.services, "radius-gw-proxy") >= 0);
if ('captive' in ssid.services && !ssid.captive)
ssid.captive = state?.services?.captive || {};
if (ssid.captive)
include("captive.uc", {
section: name + '_' + count,
config: ssid.captive
});
%}
# Wireless configuration
{% for (let n, phy in phys): %}
{% let basename = name + '_' + n + '_' + count; %}
{% let section = (owe ? 'o' : '' ) + basename; %}
{% let basename = name + '_' + count; %}
{% let ssidname = basename + '_' + n + '_' + count; %}
{% let section = (owe ? 'o' : '' ) + ssidname; %}
{% let id = wiphy.allocate_ssid_section_id(phy) %}
{% let crypto = validate_encryption(phy); %}
{% let ifname = calculate_ifname(n) %}
{% let ifname = calculate_ifname(basename) %}
{% if (!crypto) continue; %}
set wireless.{{ section }}=wifi-iface
set wireless.{{ section }}.ucentral_path={{ s(location) }}
set wireless.{{ section }}.uci_section={{ s(section) }}
set wireless.{{ section }}.device={{ phy.section }}
{% if ('captive' in ssid.services): %}
set wireless.{{ section }}.ifname={{ s(ifname) }}
set uspot.devices.{{ ifname }}={{ basename }}
{% endif %}
{% if (ssid?.encryption?.proto == 'owe-transition'): %}
{% ssid.hidden_ssid = 1 %}
{% ssid.name += '-OWE' %}
@@ -279,7 +292,7 @@ set wireless.{{ section }}.owe_transition_ifname={{ s('o' + section) }}
{% endif %}
{% if (owe): %}
set wireless.{{ section }}.ifname={{ s(section) }}
set wireless.{{ section }}.owe_transition_ifname={{ s(basename) }}
set wireless.{{ section }}.owe_transition_ifname={{ s(ssidname) }}
{% endif %}
{% if (bss_mode == 'mesh'): %}
set wireless.{{ section }}.mode={{ bss_mode }}

View File

@@ -12,86 +12,9 @@ services.set_enabled("spotfilter", enable);
services.set_enabled("uspot", enable);
if (!enable)
return;
if (!captive.web_root)
system('cp -r /www-uspot /tmp/ucentral/');
else {
let fs = require('fs');
fs.mkdir('/tmp/ucentral/www-uspot');
let web_root = fs.open('/tmp/ucentral/web-root.tar', 'w');
web_root.write(b64dec(captive.web_root));
web_root.close();
system('tar x -C /tmp/ucentral/www-uspot -f /tmp/ucentral/web-root.tar');
}
if (captive.radius_gw_proxy)
services.set_enabled("radius-gw-proxy", true);
function radius_proxy_tlv(server, port, name) {
let tlv = replace(serial, /^(..)(..)(..)(..)(..)(..)$/, "$1$2$3$4$5$6") + sprintf(":%s:%s:%s", server, port, name);
return tlv;
}
%}
# Captive Portal service configuration
set uspot.config.auth_mode={{ s(captive.auth_mode) }}
set uspot.config.web_root={{ b(captive.web_root) }}
set uspot.config.idle_timeout={{ captive.idle_timeout }}
set uspot.config.session_timeout={{ captive.session_timeout }}
{% if (captive.auth_mode in [ 'radius', 'uam']): %}
{% if (captive.radius_gw_proxy): %}
set uspot.radius.auth_server='127.0.0.1'
set uspot.radius.auth_port='1812'
set uspot.radius.auth_proxy={{ s(radius_proxy_tlv(captive.auth_server, captive.auth_port, 'captive')) }}
{% if (captive.acct_server): %}
set uspot.radius.acct_server='127.0.0.1'
set uspot.radius.acct_port='1813'
set uspot.radius.acct_proxy={{ s(radius_proxy_tlv(captive.acct_server, captive.acct_port, 'captive')) }}
{% endif %}
{% else %}
set uspot.radius.auth_server={{ s(captive.auth_server) }}
set uspot.radius.auth_port={{ s(captive.auth_port) }}
set uspot.radius.acct_server={{ s(captive.acct_server) }}
set uspot.radius.acct_port={{ s(captive.acct_port) }}
{% endif %}
set uspot.radius.auth_secret={{ s(captive.auth_secret) }}
set uspot.radius.acct_secret={{ s(captive.acct_secret) }}
set uspot.radius.acct_interval={{ captive.acct_interval }}
{% endif %}
{% if (captive.auth_mode == 'uam'): %}
set uspot.uam.uam_port={{ s(captive.uam_port) }}
set uspot.uam.uam_secret={{ s(captive.uam_secret) }}
set uspot.uam.uam_server={{ s(captive.uam_server) }}
set uspot.uam.nasid={{ s(captive.nasid) }}
set uspot.uam.nasmac={{ s(captive.nasmac || serial) }}
set uspot.uam.ssid={{ s(captive.ssid) }}
set uspot.uam.mac_format={{ s(captive.mac_format) }}
set uspot.uam.final_redirect_url={{ s(captive.final_redirect_url) }}
set uspot.uam.mac_auth={{ b(captive.mac_auth) }}
{%
let math = require('math');
let challenge = "";
for (let i = 0; i < 16; i++)
challenge += sprintf('%02x', math.rand() % 255);
%}
set uspot.uam.challenge={{ s(challenge) }}
{% endif %}
{% if (captive.auth_mode == 'credentials'): %}
{% for (let cred in captive.credentials): %}
add uspot credentials
set uspot.@credentials[-1].username={{ s(cred.username) }}
set uspot.@credentials[-1].password={{ s(cred.password) }}
{% endfor %}
{% endif %}
{% for (let interface in interfaces): %}
{% for (let interface in uniq(interfaces)): %}
{% let name = ethernet.calculate_name(interface) %}
add firewall redirect
set firewall.@redirect[-1].name='Redirect-captive-{{ name }}'
@@ -117,24 +40,6 @@ set firewall.@rule[-1].proto='tcp'
set firewall.@rule[-1].target='ACCEPT'
set firewall.@rule[-1].mark='2/127'
{% if (captive.auth_mode == 'uam'): %}
add firewall rule
set firewall.@rule[-1].name='Allow-UAM-{{ name }}'
set firewall.@rule[-1].src='{{ name }}'
set firewall.@rule[-1].dest_port='{{ captive.uam_port }}'
set firewall.@rule[-1].proto='tcp'
set firewall.@rule[-1].target='ACCEPT'
set firewall.@rule[-1].mark='1/127'
add firewall rule
set firewall.@rule[-1].name='Allow-UAM-{{ name }}'
set firewall.@rule[-1].src='{{ name }}'
set firewall.@rule[-1].dest_port='{{ captive.uam_port }}'
set firewall.@rule[-1].proto='tcp'
set firewall.@rule[-1].target='ACCEPT'
set firewall.@rule[-1].mark='2/127'
{% endif %}
{% if (interface.role == 'downstream'): %}
add firewall rule
set firewall.@rule[-1].name='Allow-pre-captive-{{ name }}'
@@ -169,32 +74,11 @@ set uhttpd.@uhttpd[-1].script_timeout='60'
set uhttpd.@uhttpd[-1].network_timeout='30'
set uhttpd.@uhttpd[-1].http_keepalive='20'
set uhttpd.@uhttpd[-1].tcp_keepalive='1'
set uhttpd.@uhttpd[-1].no_dirlists='1'
add_list uhttpd.@uhttpd[-1].listen_http='0.0.0.0:80'
add_list uhttpd.@uhttpd[-1].listen_http='[::]:80'
set uhttpd.@uhttpd[-1].home=/tmp/ucentral/www-uspot
add_list uhttpd.@uhttpd[-1].ucode_prefix='/hotspot=/usr/share/uspot/handler.uc'
add_list uhttpd.@uhttpd[-1].ucode_prefix='/=/usr/share/uspot/handler-cpd.uc'
add_list uhttpd.@uhttpd[-1].ucode_prefix='/cpd=/usr/share/uspot/handler-cpd.uc'
add_list uhttpd.@uhttpd[-1].ucode_prefix='/env=/usr/share/uspot/handler-env.uc'
set uhttpd.@uhttpd[-1].error_page='/cpd'
{% if (captive.auth_mode == 'uam'): %}
add uhttpd uhttpd
set uhttpd.@uhttpd[-1].redirect_https='0'
set uhttpd.@uhttpd[-1].rfc1918_filter='1'
set uhttpd.@uhttpd[-1].max_requests='5'
set uhttpd.@uhttpd[-1].max_connections='100'
set uhttpd.@uhttpd[-1].cert='/etc/uhttpd.crt'
set uhttpd.@uhttpd[-1].key='/etc/uhttpd.key'
set uhttpd.@uhttpd[-1].script_timeout='60'
set uhttpd.@uhttpd[-1].network_timeout='30'
set uhttpd.@uhttpd[-1].http_keepalive='20'
set uhttpd.@uhttpd[-1].tcp_keepalive='1'
add_list uhttpd.@uhttpd[-1].listen_http='0.0.0.0:{{ captive.uam_port }}'
add_list uhttpd.@uhttpd[-1].listen_http='[::]:{{ captive.uam_port }}'
set uhttpd.@uhttpd[-1].home=/tmp/ucentral/www-uspot
add_list uhttpd.@uhttpd[-1].ucode_prefix='/logon=/usr/share/uspot/handler-uam.uc'
add_list uhttpd.@uhttpd[-1].ucode_prefix='/logoff=/usr/share/uspot/handler-uam.uc'
add_list uhttpd.@uhttpd[-1].ucode_prefix='/logout=/usr/share/uspot/handler-uam.uc'
{% endif %}

View File

@@ -3,52 +3,50 @@ let interfaces = services.lookup_interfaces_by_ssids("captive");
let enable = length(interfaces);
if (enable != 1)
return;
let name;
for (let interface in interfaces)
name = ethernet.calculate_name(interface);
for (let name, data in captive.interfaces) {
let config = {
name,
devices: [],
config: {
default_class: 0,
default_dns_class: 1,
client_autoremove: false,
class: [
{
index: 0,
device_macaddr: split(name, '_')[0],
fwmark: 1,
fwmark_mask: 127
}, {
index: 1,
fwmark: 2,
fwmark_mask: 127
}
],
whitelist: [
{
"class": 1,
"hosts": [ ],
"address": [],
}
]
}
};
let config = {
name: "hotspot",
devices: [],
config: {
default_class: 0,
default_dns_class: 1,
client_autoremove: false,
class: [
{
index: 0,
device_macaddr: name,
fwmark: 1,
fwmark_mask: 127
}, {
index: 1,
fwmark: 2,
fwmark_mask: 127
}
],
whitelist: [
{
"class": 1,
"hosts": [ ],
"address": [],
}
]
}
};
for (let iface in data.iface)
push(config.devices, 'wlanc' + iface);
for (let id = 0; id < captive.next; id++)
push(config.devices, 'wlanc' + id);
for (let fqdn in data.walled_garden_fqdn)
push(config.config.whitelist[0].hosts, fqdn);
for (let fqdn in state.services.captive.walled_garden_fqdn)
push(config.config.whitelist[0].hosts, fqdn);
for (let ipaddr in data.walled_garden_ipaddr)
push(config.config.whitelist[0].address, ipaddr);
for (let ipaddr in state.services.captive.walled_garden_ipaddr)
push(config.config.whitelist[0].address, ipaddr);
let fs = require('fs');
let file = fs.open('/tmp/spotfilter.json', 'w');
file.write(config);
file.close();
services.set_enabled("uhttpd", true)
let fs = require('fs');
let file = fs.open('/tmp/spotfilter-' + name + '.json', 'w');
file.write(config);
file.close();
services.set_enabled("uhttpd", true)
}
%}

View File

@@ -117,8 +117,7 @@
}
services.set_enabled("usteer2", true);
if (state?.services?.captive)
include('spotfilter.uc');
include('spotfilter.uc');
if (state.config_raw)
include("config_raw.uc", { location: '/config_raw', config_raw: state.config_raw });

View File

@@ -152,6 +152,8 @@ properties:
$ref: "https://ucentral.io/schema/v1/interface/ssid/quality-thresholds/"
access-control-list:
$ref: "https://ucentral.io/schema/v1/interface/ssid/acl/"
captive:
$ref: 'https://ucentral.io/schema/v1/service/captive/'
hostapd-bss-raw:
description:
This array allows passing raw hostapd.conf lines.

File diff suppressed because it is too large Load Diff

View File

@@ -1672,6 +1672,236 @@
}
}
},
"service.captive.click": {
"type": "object",
"properties": {
"auth-mode": {
"type": "string",
"const": "click-to-continue"
}
}
},
"service.captive.radius": {
"type": "object",
"properties": {
"auth-mode": {
"type": "string",
"const": "radius"
},
"auth-server": {
"type": "string",
"format": "uc-host",
"examples": [
"192.168.1.10"
]
},
"auth-port": {
"type": "integer",
"maximum": 65535,
"minimum": 1024,
"default": 1812
},
"auth-secret": {
"type": "string",
"examples": [
"secret"
]
},
"acct-server": {
"type": "string",
"format": "uc-host",
"examples": [
"192.168.1.10"
]
},
"acct-port": {
"type": "integer",
"maximum": 65535,
"minimum": 1024,
"default": 1812
},
"acct-secret": {
"type": "string",
"examples": [
"secret"
]
},
"acct-interval": {
"type": "integer",
"default": 600
}
}
},
"service.captive.credentials": {
"type": "object",
"properties": {
"auth-mode": {
"type": "string",
"const": "credentials"
},
"credentials": {
"type": "array",
"items": {
"type": "object",
"properties": {
"username": {
"type": "string"
},
"password": {
"type": "string"
}
}
}
}
}
},
"service.captive.uam": {
"type": "object",
"properties": {
"auth-mode": {
"type": "string",
"const": "uam"
},
"uam-port": {
"type": "integer",
"maximum": 65535,
"minimum": 1024,
"default": 3990
},
"uam-secret": {
"type": "string"
},
"uam-server": {
"type": "string"
},
"nasid": {
"type": "string"
},
"nasmac": {
"type": "string"
},
"auth-server": {
"type": "string",
"format": "uc-host",
"examples": [
"192.168.1.10"
]
},
"auth-port": {
"type": "integer",
"maximum": 65535,
"minimum": 1024,
"default": 1812
},
"auth-secret": {
"type": "string",
"examples": [
"secret"
]
},
"acct-server": {
"type": "string",
"format": "uc-host",
"examples": [
"192.168.1.10"
]
},
"acct-port": {
"type": "integer",
"maximum": 65535,
"minimum": 1024,
"default": 1812
},
"acct-secret": {
"type": "string",
"examples": [
"secret"
]
},
"acct-interval": {
"type": "integer",
"default": 600
},
"ssid": {
"type": "string"
},
"mac-format": {
"type": "string",
"enum": [
"aabbccddeeff",
"aa-bb-cc-dd-ee-ff",
"aa:bb:cc:dd:ee:ff",
"AABBCCDDEEFF",
"AA:BB:CC:DD:EE:FF",
"AA-BB-CC-DD-EE-FF"
]
},
"final-redirect-url": {
"type": "string",
"enum": [
"default",
"uam"
]
},
"mac-auth": {
"type": "boolean",
"default": "default"
},
"radius-gw-proxy": {
"type": "boolean",
"default": false
}
}
},
"service.captive": {
"allOf": [
{
"oneOf": [
{
"$ref": "#/$defs/service.captive.click"
},
{
"$ref": "#/$defs/service.captive.radius"
},
{
"$ref": "#/$defs/service.captive.credentials"
},
{
"$ref": "#/$defs/service.captive.uam"
}
]
},
{
"type": "object",
"properties": {
"walled-garden-fqdn": {
"type": "array",
"items": {
"type": "string"
}
},
"walled-garden-ipaddr": {
"type": "array",
"items": {
"type": "string",
"format": "uc-ip"
}
},
"web-root": {
"type": "string",
"format": "uc-base64"
},
"idle-timeout": {
"type": "integer",
"default": 600
},
"session-timeout": {
"type": "integer"
}
}
}
]
},
"interface.ssid": {
"type": "object",
"properties": {
@@ -1816,6 +2046,9 @@
"access-control-list": {
"$ref": "#/$defs/interface.ssid.acl"
},
"captive": {
"$ref": "#/$defs/service.captive"
},
"hostapd-bss-raw": {
"type": "array",
"items": {
@@ -2714,236 +2947,6 @@
}
}
},
"service.captive.click": {
"type": "object",
"properties": {
"auth-mode": {
"type": "string",
"const": "click-to-continue"
}
}
},
"service.captive.radius": {
"type": "object",
"properties": {
"auth-mode": {
"type": "string",
"const": "radius"
},
"auth-server": {
"type": "string",
"format": "uc-host",
"examples": [
"192.168.1.10"
]
},
"auth-port": {
"type": "integer",
"maximum": 65535,
"minimum": 1024,
"default": 1812
},
"auth-secret": {
"type": "string",
"examples": [
"secret"
]
},
"acct-server": {
"type": "string",
"format": "uc-host",
"examples": [
"192.168.1.10"
]
},
"acct-port": {
"type": "integer",
"maximum": 65535,
"minimum": 1024,
"default": 1812
},
"acct-secret": {
"type": "string",
"examples": [
"secret"
]
},
"acct-interval": {
"type": "integer",
"default": 600
}
}
},
"service.captive.credentials": {
"type": "object",
"properties": {
"auth-mode": {
"type": "string",
"const": "credentials"
},
"credentials": {
"type": "array",
"items": {
"type": "object",
"properties": {
"username": {
"type": "string"
},
"password": {
"type": "string"
}
}
}
}
}
},
"service.captive.uam": {
"type": "object",
"properties": {
"auth-mode": {
"type": "string",
"const": "uam"
},
"uam-port": {
"type": "integer",
"maximum": 65535,
"minimum": 1024,
"default": 3990
},
"uam-secret": {
"type": "string"
},
"uam-server": {
"type": "string"
},
"nasid": {
"type": "string"
},
"nasmac": {
"type": "string"
},
"auth-server": {
"type": "string",
"format": "uc-host",
"examples": [
"192.168.1.10"
]
},
"auth-port": {
"type": "integer",
"maximum": 65535,
"minimum": 1024,
"default": 1812
},
"auth-secret": {
"type": "string",
"examples": [
"secret"
]
},
"acct-server": {
"type": "string",
"format": "uc-host",
"examples": [
"192.168.1.10"
]
},
"acct-port": {
"type": "integer",
"maximum": 65535,
"minimum": 1024,
"default": 1812
},
"acct-secret": {
"type": "string",
"examples": [
"secret"
]
},
"acct-interval": {
"type": "integer",
"default": 600
},
"ssid": {
"type": "string"
},
"mac-format": {
"type": "string",
"enum": [
"aabbccddeeff",
"aa-bb-cc-dd-ee-ff",
"aa:bb:cc:dd:ee:ff",
"AABBCCDDEEFF",
"AA:BB:CC:DD:EE:FF",
"AA-BB-CC-DD-EE-FF"
]
},
"final-redirect-url": {
"type": "string",
"enum": [
"default",
"uam"
]
},
"mac-auth": {
"type": "boolean",
"default": "default"
},
"radius-gw-proxy": {
"type": "boolean",
"default": false
}
}
},
"service.captive": {
"allOf": [
{
"oneOf": [
{
"$ref": "#/$defs/service.captive.click"
},
{
"$ref": "#/$defs/service.captive.radius"
},
{
"$ref": "#/$defs/service.captive.credentials"
},
{
"$ref": "#/$defs/service.captive.uam"
}
]
},
{
"type": "object",
"properties": {
"walled-garden-fqdn": {
"type": "array",
"items": {
"type": "string"
}
},
"walled-garden-ipaddr": {
"type": "array",
"items": {
"type": "string",
"format": "uc-ip"
}
},
"web-root": {
"type": "string",
"format": "uc-base64"
},
"idle-timeout": {
"type": "integer",
"default": 600
},
"session-timeout": {
"type": "integer"
}
}
}
]
},
"service.gps": {
"type": "object",
"properties": {