make health-check configurable

Signed-off-by: John Crispin <john@phrozen.org>
This commit is contained in:
John Crispin
2022-10-15 12:14:28 +02:00
parent 9877d3014b
commit a27c545eaf
9 changed files with 242 additions and 14 deletions

View File

@@ -87,6 +87,7 @@
proto: ssid.encryption.proto,
auth: ssid.radius.authentication,
acct: ssid.radius.accounting,
health: ssid.radius.health || {},
dyn_auth: ssid.radius?.dynamic_authorization,
radius: ssid.radius
};
@@ -121,6 +122,7 @@
auth: ssid.radius.authentication,
acct: ssid.radius.accounting,
dyn_auth: ssid.radius?.dynamic_authorization,
health: ssid.radius.health || {},
radius: ssid.radius
};
return {
@@ -344,6 +346,11 @@ add_list wireless.{{ section }}.radius_acct_req_attr={{ s(radius_vendor_tlv(cryp
{% endif %}
{% endif %}
{% if (crypto.health): %}
set wireless.{{ section }}.health_username={{ s(crypto.health.username) }}
set wireless.{{ section }}.health_password={{ s(crypto.health.password) }}
{% endif %}
{% if (crypto.dyn_auth): %}
set wireless.{{ section }}.dae_client={{ crypto.dyn_auth.host }}
set wireless.{{ section }}.dae_port={{ crypto.dyn_auth.port }}

View File

@@ -0,0 +1,12 @@
{% if (!length(health_check))
health_check = {
dhcp_local: 1,
dhcp_remote: 0,
dns_local: 1,
dns_remote: 1,
};
%}
set health.config.dhcp_local={{ b(health_check.dhcp_local) }}
set health.config.dhcp_remote={{ b(health_check.dhcp_remote) }}
set health.config.dns_local={{ b(health_check.dns_local) }}
set health.config.dns_remote={{ b(health_check.dns_remote) }}

View File

@@ -0,0 +1,12 @@
description:
The credentials used when health check probes this radius server.
type: object
properties:
username:
description:
The username that gets used when doing a healthcheck on this radius server.
type: string
password:
description:
The password that gets used when doing a healthcheck on this radius server.
type: string

View File

@@ -63,3 +63,5 @@ properties:
maximum: 600
minimum: 60
default: 60
health:
$ref: "https://ucentral.io/schema/v1/interface/ssid/radius/health/"

View File

@@ -0,0 +1,24 @@
type: object
description:
This section can be used to setup the services that health check will probe.
properties:
dhcp-local:
description:
This is makes the AP probe local downstream DHCP servers.
type: boolean
default: true
dhcp-remote:
description:
This is makes the AP probe remote upstream DHCP servers.
type: boolean
default: false
dns-local:
description:
This is makes the AP probe DNS servers.
type: boolean
default: true
dns-remote:
description:
This is makes the AP probe DNS servers.
type: boolean
default: true

View File

@@ -37,5 +37,7 @@ properties:
$ref: "https://ucentral.io/schema/v1/service/airtime-fairness/"
wireguard-overlay:
$ref: 'https://ucentral.io/schema/v1/service/wireguard-overlay/'
health-check:
$ref: 'https://ucentral.io/schema/v1/service/health-check/'
captive:
$ref: 'https://ucentral.io/schema/v1/service/captive/'

View File

@@ -3184,6 +3184,41 @@ function instantiateInterfaceSsidRadiusServer(location, value, errors) {
return value;
}
function instantiateInterfaceSsidRadiusHealth(location, value, errors) {
if (type(value) == "object") {
let obj = {};
function parseUsername(location, value, errors) {
if (type(value) != "string")
push(errors, [ location, "must be of type string" ]);
return value;
}
if (exists(value, "username")) {
obj.username = parseUsername(location + "/username", value["username"], errors);
}
function parsePassword(location, value, errors) {
if (type(value) != "string")
push(errors, [ location, "must be of type string" ]);
return value;
}
if (exists(value, "password")) {
obj.password = parsePassword(location + "/password", value["password"], errors);
}
return obj;
}
if (type(value) != "object")
push(errors, [ location, "must be of type object" ]);
return value;
}
function instantiateInterfaceSsidRadius(location, value, errors) {
if (type(value) == "object") {
let obj = {};
@@ -3453,6 +3488,10 @@ function instantiateInterfaceSsidRadius(location, value, errors) {
obj.accounting = parseAccounting(location + "/accounting", value["accounting"], errors);
}
if (exists(value, "health")) {
obj.health = instantiateInterfaceSsidRadiusHealth(location + "/health", value["health"], errors);
}
return obj;
}
@@ -7181,6 +7220,75 @@ function instantiateServiceWireguardOverlay(location, value, errors) {
return value;
}
function instantiateServiceHealthCheck(location, value, errors) {
if (type(value) == "object") {
let obj = {};
function parseDhcpLocal(location, value, errors) {
if (type(value) != "bool")
push(errors, [ location, "must be of type boolean" ]);
return value;
}
if (exists(value, "dhcp-local")) {
obj.dhcp_local = parseDhcpLocal(location + "/dhcp-local", value["dhcp-local"], errors);
}
else {
obj.dhcp_local = true;
}
function parseDhcpRemote(location, value, errors) {
if (type(value) != "bool")
push(errors, [ location, "must be of type boolean" ]);
return value;
}
if (exists(value, "dhcp-remote")) {
obj.dhcp_remote = parseDhcpRemote(location + "/dhcp-remote", value["dhcp-remote"], errors);
}
else {
obj.dhcp_remote = false;
}
function parseDnsLocal(location, value, errors) {
if (type(value) != "bool")
push(errors, [ location, "must be of type boolean" ]);
return value;
}
if (exists(value, "dns-local")) {
obj.dns_local = parseDnsLocal(location + "/dns-local", value["dns-local"], errors);
}
else {
obj.dns_local = true;
}
function parseDnsRemote(location, value, errors) {
if (type(value) != "bool")
push(errors, [ location, "must be of type boolean" ]);
return value;
}
if (exists(value, "dns-remote")) {
obj.dns_remote = parseDnsRemote(location + "/dns-remote", value["dns-remote"], errors);
}
else {
obj.dns_remote = true;
}
return obj;
}
if (type(value) != "object")
push(errors, [ location, "must be of type object" ]);
return value;
}
function instantiateServiceCaptiveClick(location, value, errors) {
if (type(value) == "object") {
let obj = {};
@@ -7952,6 +8060,10 @@ function instantiateService(location, value, errors) {
obj.wireguard_overlay = instantiateServiceWireguardOverlay(location + "/wireguard-overlay", value["wireguard-overlay"], errors);
}
if (exists(value, "health-check")) {
obj.health_check = instantiateServiceHealthCheck(location + "/health-check", value["health-check"], errors);
}
if (exists(value, "captive")) {
obj.captive = instantiateServiceCaptive(location + "/captive", value["captive"], errors);
}

View File

@@ -17,9 +17,11 @@ state = {
let ctx = ubus.connect();
let interfaces = ctx.call("network.interface", "dump").interface;
let cursor = uci.cursor();
cursor.load("health");
cursor.load("dhcp");
cursor.load("network");
cursor.load("wireless");
let config = cursor.get_all("health", "config");
let dhcp = cursor.get_all("dhcp");
let wifi_config = cursor.get_all("wireless");
let wifi_state = require('wifi.iface');
@@ -32,7 +34,7 @@ function find_ssid(ssid) {
return 1;
}
function radius_probe(server, port, secret)
function radius_probe(server, port, secret, user, pass)
{
let f = fs.open("/tmp/radius.conf", "w");
if (f) {
@@ -50,7 +52,7 @@ function radius_probe(server, port, secret)
f.write(sprintf("%s %s\n", server, secret));
f.close();
}
return system(['/usr/sbin/radiusprobe']);
return system(['/usr/sbin/radiusprobe', user, pass]);
}
for (let iface in interfaces) {
@@ -66,7 +68,15 @@ for (let iface in interfaces) {
let device = iface.l3_device || iface.interface;
let warnings = [];
if (dhcp[name] || (iface.data && iface.data.leasetime) || cursor.get("network", iface.interface, 'dhcp_healthcheck')) {
let probe_dhcp = cursor.get("network", iface.interface, 'dhcp_healthcheck') || false;
if (dhcp[name]?.leasetime && +config.dhcp_local)
probe_dhcp = true;
if (iface?.data.leasetime && +config.dhcp_remote)
probe_dhcp = true;
if (probe_dhcp) {
let rc = system(['/usr/sbin/dhcpdiscover', '-i', device, '-t', '5']);
if (rc) {
health.dhcp = false;
@@ -74,27 +84,36 @@ for (let iface in interfaces) {
}
}
let dns = iface["dns-server"];
if (!length(dns) && iface["ipv4-address"] && iface["ipv4-address"][0])
dns = [ iface["ipv4-address"][0]["address"] ];
let probe_dns = false;
for (let ip in dns) {
let rc = system(['/usr/sbin/dnsprobe', '-s', ip]);
if (length(iface["dns-server"]) && +config.dns_remote)
probe_dns = true;
if (rc) {
health.dns = false;
push(warnings, sprintf("DNS %s is not reachable", ip));
if (dhcp[name]?.dns_service && +config.dns_local)
probe_dns = true;
if (probe_dns) {
let dns = iface["dns-server"];
if (!length(dns) && iface["ipv4-address"] && iface["ipv4-address"][0])
dns = [ iface["ipv4-address"][0]["address"] ];
for (let ip in dns) {
let rc = system(['/usr/sbin/dnsprobe', '-s', ip]);
if (rc) {
health.dns = false;
push(warnings, sprintf("DNS %s is not reachable", ip));
}
}
}
for (let k, iface in wifi_config) {
if (iface[".type"] != "wifi-iface" || iface.network != name)
continue;
if (find_ssid(iface.ssid))
ssid[iface.ssid] = false;
if (iface.auth_server && iface.auth_port && iface.auth_secret && !iface.radius_gw_proxy)
if (radius_probe(iface.auth_server, iface.auth_port, iface.auth_secret)) {
if (iface.auth_server && iface.auth_port && iface.auth_secret && iface.health_username && iface.health_password && !iface.radius_gw_proxy)
if (radius_probe(iface.auth_server, iface.auth_port, iface.auth_secret, iface.health_username, iface.health_password)) {
radius[iface.ssid] = false;
push(warnings, sprintf("Radius %s:%s is not reachable", iface.auth_server, iface.auth_port));
}

View File

@@ -1283,6 +1283,17 @@
}
}
},
"interface.ssid.radius.health": {
"type": "object",
"properties": {
"username": {
"type": "string"
},
"password": {
"type": "string"
}
}
},
"interface.ssid.radius": {
"type": "object",
"properties": {
@@ -1355,6 +1366,9 @@
}
}
]
},
"health": {
"$ref": "#/$defs/interface.ssid.radius.health"
}
}
},
@@ -2616,6 +2630,27 @@
}
}
},
"service.health-check": {
"type": "object",
"properties": {
"dhcp-local": {
"type": "boolean",
"default": true
},
"dhcp-remote": {
"type": "boolean",
"default": false
},
"dns-local": {
"type": "boolean",
"default": true
},
"dns-remote": {
"type": "boolean",
"default": true
}
}
},
"service.captive.click": {
"type": "object",
"properties": {
@@ -2878,6 +2913,9 @@
"wireguard-overlay": {
"$ref": "#/$defs/service.wireguard-overlay"
},
"health-check": {
"$ref": "#/$defs/service.health-check"
},
"captive": {
"$ref": "#/$defs/service.captive"
}