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, proto: ssid.encryption.proto,
auth: ssid.radius.authentication, auth: ssid.radius.authentication,
acct: ssid.radius.accounting, acct: ssid.radius.accounting,
health: ssid.radius.health || {},
dyn_auth: ssid.radius?.dynamic_authorization, dyn_auth: ssid.radius?.dynamic_authorization,
radius: ssid.radius radius: ssid.radius
}; };
@@ -121,6 +122,7 @@
auth: ssid.radius.authentication, auth: ssid.radius.authentication,
acct: ssid.radius.accounting, acct: ssid.radius.accounting,
dyn_auth: ssid.radius?.dynamic_authorization, dyn_auth: ssid.radius?.dynamic_authorization,
health: ssid.radius.health || {},
radius: ssid.radius radius: ssid.radius
}; };
return { return {
@@ -344,6 +346,11 @@ add_list wireless.{{ section }}.radius_acct_req_attr={{ s(radius_vendor_tlv(cryp
{% endif %} {% endif %}
{% 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): %} {% if (crypto.dyn_auth): %}
set wireless.{{ section }}.dae_client={{ crypto.dyn_auth.host }} set wireless.{{ section }}.dae_client={{ crypto.dyn_auth.host }}
set wireless.{{ section }}.dae_port={{ crypto.dyn_auth.port }} 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 maximum: 600
minimum: 60 minimum: 60
default: 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/" $ref: "https://ucentral.io/schema/v1/service/airtime-fairness/"
wireguard-overlay: wireguard-overlay:
$ref: 'https://ucentral.io/schema/v1/service/wireguard-overlay/' $ref: 'https://ucentral.io/schema/v1/service/wireguard-overlay/'
health-check:
$ref: 'https://ucentral.io/schema/v1/service/health-check/'
captive: captive:
$ref: 'https://ucentral.io/schema/v1/service/captive/' $ref: 'https://ucentral.io/schema/v1/service/captive/'

View File

@@ -3184,6 +3184,41 @@ function instantiateInterfaceSsidRadiusServer(location, value, errors) {
return value; 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) { function instantiateInterfaceSsidRadius(location, value, errors) {
if (type(value) == "object") { if (type(value) == "object") {
let obj = {}; let obj = {};
@@ -3453,6 +3488,10 @@ function instantiateInterfaceSsidRadius(location, value, errors) {
obj.accounting = parseAccounting(location + "/accounting", value["accounting"], errors); obj.accounting = parseAccounting(location + "/accounting", value["accounting"], errors);
} }
if (exists(value, "health")) {
obj.health = instantiateInterfaceSsidRadiusHealth(location + "/health", value["health"], errors);
}
return obj; return obj;
} }
@@ -7181,6 +7220,75 @@ function instantiateServiceWireguardOverlay(location, value, errors) {
return value; 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) { function instantiateServiceCaptiveClick(location, value, errors) {
if (type(value) == "object") { if (type(value) == "object") {
let obj = {}; let obj = {};
@@ -7952,6 +8060,10 @@ function instantiateService(location, value, errors) {
obj.wireguard_overlay = instantiateServiceWireguardOverlay(location + "/wireguard-overlay", value["wireguard-overlay"], 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")) { if (exists(value, "captive")) {
obj.captive = instantiateServiceCaptive(location + "/captive", value["captive"], errors); obj.captive = instantiateServiceCaptive(location + "/captive", value["captive"], errors);
} }

View File

@@ -17,9 +17,11 @@ state = {
let ctx = ubus.connect(); let ctx = ubus.connect();
let interfaces = ctx.call("network.interface", "dump").interface; let interfaces = ctx.call("network.interface", "dump").interface;
let cursor = uci.cursor(); let cursor = uci.cursor();
cursor.load("health");
cursor.load("dhcp"); cursor.load("dhcp");
cursor.load("network"); cursor.load("network");
cursor.load("wireless"); cursor.load("wireless");
let config = cursor.get_all("health", "config");
let dhcp = cursor.get_all("dhcp"); let dhcp = cursor.get_all("dhcp");
let wifi_config = cursor.get_all("wireless"); let wifi_config = cursor.get_all("wireless");
let wifi_state = require('wifi.iface'); let wifi_state = require('wifi.iface');
@@ -32,7 +34,7 @@ function find_ssid(ssid) {
return 1; return 1;
} }
function radius_probe(server, port, secret) function radius_probe(server, port, secret, user, pass)
{ {
let f = fs.open("/tmp/radius.conf", "w"); let f = fs.open("/tmp/radius.conf", "w");
if (f) { if (f) {
@@ -50,7 +52,7 @@ function radius_probe(server, port, secret)
f.write(sprintf("%s %s\n", server, secret)); f.write(sprintf("%s %s\n", server, secret));
f.close(); f.close();
} }
return system(['/usr/sbin/radiusprobe']); return system(['/usr/sbin/radiusprobe', user, pass]);
} }
for (let iface in interfaces) { for (let iface in interfaces) {
@@ -66,7 +68,15 @@ for (let iface in interfaces) {
let device = iface.l3_device || iface.interface; let device = iface.l3_device || iface.interface;
let warnings = []; 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']); let rc = system(['/usr/sbin/dhcpdiscover', '-i', device, '-t', '5']);
if (rc) { if (rc) {
health.dhcp = false; health.dhcp = false;
@@ -74,6 +84,15 @@ for (let iface in interfaces) {
} }
} }
let probe_dns = false;
if (length(iface["dns-server"]) && +config.dns_remote)
probe_dns = true;
if (dhcp[name]?.dns_service && +config.dns_local)
probe_dns = true;
if (probe_dns) {
let dns = iface["dns-server"]; let dns = iface["dns-server"];
if (!length(dns) && iface["ipv4-address"] && iface["ipv4-address"][0]) if (!length(dns) && iface["ipv4-address"] && iface["ipv4-address"][0])
dns = [ iface["ipv4-address"][0]["address"] ]; dns = [ iface["ipv4-address"][0]["address"] ];
@@ -86,15 +105,15 @@ for (let iface in interfaces) {
push(warnings, sprintf("DNS %s is not reachable", ip)); push(warnings, sprintf("DNS %s is not reachable", ip));
} }
} }
}
for (let k, iface in wifi_config) { for (let k, iface in wifi_config) {
if (iface[".type"] != "wifi-iface" || iface.network != name) if (iface[".type"] != "wifi-iface" || iface.network != name)
continue; continue;
if (find_ssid(iface.ssid)) if (find_ssid(iface.ssid))
ssid[iface.ssid] = false; ssid[iface.ssid] = false;
if (iface.auth_server && iface.auth_port && iface.auth_secret && !iface.radius_gw_proxy) 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)) { if (radius_probe(iface.auth_server, iface.auth_port, iface.auth_secret, iface.health_username, iface.health_password)) {
radius[iface.ssid] = false; radius[iface.ssid] = false;
push(warnings, sprintf("Radius %s:%s is not reachable", iface.auth_server, iface.auth_port)); 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": { "interface.ssid.radius": {
"type": "object", "type": "object",
"properties": { "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": { "service.captive.click": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -2878,6 +2913,9 @@
"wireguard-overlay": { "wireguard-overlay": {
"$ref": "#/$defs/service.wireguard-overlay" "$ref": "#/$defs/service.wireguard-overlay"
}, },
"health-check": {
"$ref": "#/$defs/service.health-check"
},
"captive": { "captive": {
"$ref": "#/$defs/service.captive" "$ref": "#/$defs/service.captive"
} }