feat(monitoring): more status endpoints

This commit is contained in:
arcnmx 2024-06-01 11:22:28 -07:00
parent ebfd1f5a9a
commit 23b746191f
27 changed files with 344 additions and 138 deletions

View file

@ -1,7 +1,7 @@
{ config, lib, pkgs, ... }:
let
inherit (lib) types mkIf mkOption mkEnableOption mkPackageOption mkDefault;
inherit (lib) types mkIf mkOption mkEnableOption mkPackageOption mkOptionDefault;
cfg = config.services.gatus;
@ -206,6 +206,8 @@ in {
'';
};
ui = {
hide-conditions =
mkEnableOption "hiding the condition results on the UI";
hide-hostname =
mkEnableOption "hiding the hostname in the result";
hide-url = mkEnableOption "hiding the URL in the results";
@ -221,7 +223,7 @@ in {
};
};
};
config = { name = mkDefault name; };
config = { name = mkOptionDefault name; };
}));
default = { };
};
@ -316,9 +318,9 @@ in {
StateDirectory = "gatus";
LogsDirectory = "gatus";
EnvironmentFile =
mkIf (cfg.environmentFile != null) cfg.environmentFile;
mkIf (cfg.environmentFile != null) [ cfg.environmentFile ];
AmbientCapabilities = "CAP_NET_RAW"; # needed for ICMP probes
AmbientCapabilities = [ "CAP_NET_RAW" ]; # needed for ICMP probes
DevicePolicy = "closed";
LockPersonality = true;
MemoryDenyWriteExecute = true;
@ -337,13 +339,15 @@ in {
ProtectProc = "invisible";
ProtectSystem = "strict";
RemoveIPC = true;
RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
UMask = "0077";
ExecStart = "${cfg.package}/bin/gatus";
ExecStart = [
(lib.getExe cfg.package)
];
};
};
@ -359,4 +363,4 @@ in {
};
meta.maintainers = with lib.maintainers; [ christoph-heiss ];
}
}

View file

@ -12,7 +12,7 @@
nodeExporterSystems =
filter (
system:
system.config.access.online.enable
system.config.exports.prometheus.exporter.enable
&& system.config.exports.prometheus.exporter.services != []
)
(attrValues systems);

View file

@ -162,20 +162,22 @@
service ? system.exports.services.${serviceName},
portName ? "default",
port ? service.ports.${portName},
host ? access.${getAddressFor} system.name network,
defaultPort ? null,
network ? "lan",
scheme ? null,
getAddressFor ? "getAddressFor",
}: let
scheme' =
if scheme == null
then port.protocol
else scheme;
if scheme == null then "${port.protocol}://"
else if scheme == "" then ""
else "${scheme}://";
port' =
if !port.enable
then throw "${system.name}.exports.services.${service.name}.ports.${portName} isn't enabled"
else if port.port == defaultPort then ""
else ":${toString port.port}";
host = access.${getAddressFor} system.name network;
url = "${scheme'}://${mkAddress6 host}${port'}";
url = "${scheme'}${mkAddress6 host}${port'}";
in
assert service.enable; url;
};

View file

@ -3,11 +3,11 @@
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.modules) mkIf;
inherit (lib.attrsets) mapAttrs;
in {
config.exports.services.deluge = {config, ...}: {
displayName = mkAlmostOptionDefault "Deluge";
nixos = {
serviceAttr = "deluge";
assertions = let
@ -32,14 +32,23 @@ in {
];
};
defaults.port.listen = mkAlmostOptionDefault "lan";
ports = mapAttrs (_: mapAlmostOptionDefaults) {
ports = let
gatus.client.network = mkAlmostOptionDefault "ip4";
in {
default = {
port = 58846;
port = mkAlmostOptionDefault 58846;
transport = "tcp";
status = {
inherit gatus;
};
};
web = {
port = 8112;
port = mkAlmostOptionDefault 8112;
protocol = "http";
status = {
inherit gatus;
enable = mkAlmostOptionDefault true;
};
};
};
};

View file

@ -3,22 +3,38 @@
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs;
inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.modules) mkOptionDefault;
in {
config.exports.services.dnsmasq = {config, ...}: {
config.exports.services.dnsmasq = {system, config, ...}: {
displayName = mkAlmostOptionDefault "Dnsmasq";
id = mkAlmostOptionDefault "dns";
nixos = {
serviceAttr = "dnsmasq";
};
defaults.port.listen = mkAlmostOptionDefault "lan";
ports = mapAttrs (_: mapAlmostOptionDefaults) {
ports = {
default = {
port = 53;
port = mkAlmostOptionDefault 53;
transport = "udp";
status = {
enable = mkAlmostOptionDefault true;
gatus = {
protocol = "dns";
settings = {
dns = {
query-type = mkOptionDefault "A";
query-name = mkOptionDefault system.access.fqdn;
};
conditions = mkOptionDefault [
"[BODY] == ${system.network.networks.local.address4}"
];
};
};
};
};
tcp = {
port = config.ports.default.port;
port = mkAlmostOptionDefault config.ports.default.port;
transport = "tcp";
};
};

View file

@ -3,18 +3,19 @@
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs;
inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
in {
config.exports.services.freeipa = {
displayName = mkAlmostOptionDefault "FreeIPA";
id = mkAlmostOptionDefault "ipa";
ports = mapAttrs (_: mapAlmostOptionDefaults) {
ports = {
default = {
port = 443;
port = mkAlmostOptionDefault 443;
protocol = "https";
status.enable = mkAlmostOptionDefault true;
};
redirect = {
port = 80;
port = mkAlmostOptionDefault 80;
protocol = "http";
};
};

View file

@ -3,34 +3,41 @@
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs;
inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
in {
config.exports.services.freepbx = {
config.exports.services.freepbx = {config, ...}: {
displayName = mkAlmostOptionDefault "FreePBX";
id = mkAlmostOptionDefault "pbx";
ports = mapAttrs (_: mapAlmostOptionDefaults) {
ports = {
http = {
port = 80;
displayName = mkAlmostOptionDefault null;
port = mkAlmostOptionDefault 80;
protocol = "http";
status.enable = mkAlmostOptionDefault true;
};
https = {
port = 443;
port = mkAlmostOptionDefault 443;
protocol = "https";
};
ucp = {
port = 8001;
port = mkAlmostOptionDefault 8001;
protocol = "http";
displayName = mkAlmostOptionDefault "UCP";
status = {
enable = mkAlmostOptionDefault config.ports.http.status.enable;
gatus.client.network = mkAlmostOptionDefault "ip4";
};
};
ucp-ssl = {
port = 8003;
port = mkAlmostOptionDefault 8003;
protocol = "https";
};
asterisk = {
port = 8088;
port = mkAlmostOptionDefault 8088;
protocol = "http";
};
asterisk-ssl = {
port = 8089;
port = mkAlmostOptionDefault 8089;
protocol = "https";
};
};

View file

@ -3,10 +3,11 @@
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.modules) mkIf;
in {
config.exports.services.invidious = {config, ...}: {
displayName = mkAlmostOptionDefault "Invidious";
id = mkAlmostOptionDefault "yt";
nixos = {
serviceAttr = "invidious";
@ -17,9 +18,10 @@ in {
})
];
};
ports.default = mapAlmostOptionDefaults {
port = 3000;
ports.default = {
port = mkAlmostOptionDefault 3000;
protocol = "http";
status.enable = mkAlmostOptionDefault true;
};
};
}

View file

@ -3,11 +3,11 @@
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.modules) mkIf;
inherit (lib.attrsets) mapAttrs;
in {
config.exports.services.keycloak = {config, ...}: {
displayName = mkAlmostOptionDefault "Keycloak";
id = mkAlmostOptionDefault "sso";
nixos = {
serviceAttr = "keycloak";
@ -28,15 +28,17 @@ in {
}))
];
};
ports = mapAttrs (_: mapAlmostOptionDefaults) {
ports = {
http = {
enable = !config.ports.https.enable;
port = 8080;
enable = mkAlmostOptionDefault (!config.ports.https.enable);
port = mkAlmostOptionDefault 8080;
protocol = "http";
status.enable = mkAlmostOptionDefault true;
};
https = {
port = 8443;
port = mkAlmostOptionDefault 8443;
protocol = "https";
status.enable = mkAlmostOptionDefault config.ports.http.status.enable;
};
};
};

View file

@ -3,21 +3,23 @@
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs;
inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
in {
config.exports.services.ldap = {config, ...}: {
displayName = mkAlmostOptionDefault "LDAP";
defaults.port.listen = mkAlmostOptionDefault "lan";
ports = mapAttrs (_: mapAlmostOptionDefaults) {
ports = {
default = {
port = 389;
port = mkAlmostOptionDefault 389;
transport = "tcp";
starttls = true;
starttls = mkAlmostOptionDefault true;
status.enable = mkAlmostOptionDefault true;
};
ssl = {
port = 636;
port = mkAlmostOptionDefault 636;
ssl = true;
listen = "wan";
status.enable = mkAlmostOptionDefault config.ports.default.status.enable;
};
};
};

View file

@ -1,5 +1,5 @@
let
portModule = {config, gensokyo-zone, lib, ...}: let
portModule = {system, config, gensokyo-zone, lib, ...}: let
inherit (gensokyo-zone.lib) unmerged;
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkMerge mkOptionDefault;
@ -12,24 +12,40 @@ let
enable = mkEnableOption "status checks";
alert = {
enable = mkEnableOption "health check alerts" // {
default = true;
default = system.exports.status.alert.enable;
};
};
gatus = {
enable = mkEnableOption "gatus" // {
default = true;
};
settings = mkOption {
type = unmerged.types.attrs;
client = {
network = mkOption {
type = enum [ "ip" "ip4" "ip6" ];
default = "ip";
};
};
http = {
path = mkOption {
type = str;
default = "/";
};
statusCondition = mkOption {
type = nullOr str;
};
};
protocol = mkOption {
type = str;
};
settings = mkOption {
type = unmerged.types.attrs;
};
};
};
};
config = {
status.gatus = let
cfg = config.status.gatus;
defaultProtocol =
if config.protocol != null then mkOptionDefault config.protocol
else if config.starttls then mkOptionDefault "starttls"
@ -38,20 +54,26 @@ let
else mkIf false (throw "unreachable");
in {
protocol = defaultProtocol;
http.statusCondition = mkOptionDefault (
if cfg.protocol == "http" || cfg.protocol == "https" then "[STATUS] == 200"
else null
);
settings = mkMerge [
{
conditions = mkMerge [
(mkIf (config.ssl || config.starttls) (mkOptionDefault [
"[CERTIFICATE_EXPIRATION] > 72h"
]))
(mkOptionDefault [
"[CONNECTED] == true"
])
];
}
(mkIf (config.protocol == "http" || config.protocol == "https") {
(mkIf (cfg.http.statusCondition != null) {
conditions = mkOptionDefault [
"[STATUS] == 200"
cfg.http.statusCondition
];
})
(mkIf (cfg.protocol == "dns") {
conditions = mkOptionDefault [
"[DNS_RCODE] == NOERROR"
];
})
];
@ -121,7 +143,7 @@ in
...
}: let
inherit (gensokyo-zone.lib) mapListToAttrs mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.options) mkOption;
inherit (lib.options) mkOption mkEnableOption;
inherit (lib.modules) mkIf mkOptionDefault;
inherit (lib.attrsets) attrNames filterAttrs nameValuePair;
mkExporter = {
@ -156,11 +178,24 @@ in
in {
options.exports = with lib.types; {
prometheus = {
exporter.services = mkOption {
type = listOf str;
exporter = {
enable = mkEnableOption "prometheus ingress" // {
default = config.access.online.enable;
};
services = mkOption {
type = listOf str;
};
};
};
status = {
enable = mkEnableOption "status checks" // {
default = config.access.online.enable;
};
alert = {
enable = mkEnableOption "health check alerts" // {
default = config.access.online.enable && config.type == "NixOS";
};
};
services = mkOption {
type = listOf str;
};
@ -184,6 +219,7 @@ in
config.exports.services =
{
prometheus = {config, ...}: {
displayName = mkAlmostOptionDefault "Prometheus";
nixos = {
serviceAttr = "prometheus";
assertions = mkIf config.enable [

View file

@ -3,13 +3,13 @@
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.modules) mkIf;
inherit (lib.attrsets) mapAttrs;
inherit (lib.lists) all imap0;
inherit (lib.trivial) id;
in {
config.exports.services.mosquitto = {config, ...}: {
displayName = mkAlmostOptionDefault "Mosquitto";
id = mkAlmostOptionDefault "mqtt";
nixos = {
serviceAttr = "mosquitto";
@ -28,15 +28,17 @@ in {
];
};
defaults.port.listen = mkAlmostOptionDefault "lan";
ports = mapAttrs (_: mapAlmostOptionDefaults) {
ports = {
default = {
port = 1883;
port = mkAlmostOptionDefault 1883;
transport = "tcp";
status.enable = mkAlmostOptionDefault true;
};
ssl = {
enable = false;
port = 8883;
enable = mkAlmostOptionDefault false;
port = mkAlmostOptionDefault 8883;
ssl = true;
status.enable = mkAlmostOptionDefault config.ports.default.status.enable;
};
};
};

View file

@ -3,19 +3,22 @@
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs;
inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
in {
config.exports.services.motion = {config, ...}: {
displayName = mkAlmostOptionDefault "Motion";
defaults.port.listen = mkAlmostOptionDefault "lan";
ports = mapAttrs (_: mapAlmostOptionDefaults) {
ports = {
default = {
port = 8080;
port = mkAlmostOptionDefault 8080;
protocol = "http";
status.enable = mkAlmostOptionDefault true;
};
stream = {
port = 8081;
port = mkAlmostOptionDefault 8081;
protocol = "http";
displayName = mkAlmostOptionDefault "Stream";
status.enable = mkAlmostOptionDefault true;
};
};
};

View file

@ -7,7 +7,7 @@
inherit (lib.modules) mkIf;
inherit (lib.attrsets) mapAttrs;
in {
config.exports.services.nginx = {config, ...}: let
config.exports.services.nginx = {config, system, ...}: let
mkAssertion = f: nixosConfig: let
cfg = nixosConfig.services.nginx;
in
@ -25,6 +25,7 @@ in {
message = "proxied.port mismatch";
};
in {
displayName = mkAlmostOptionDefault "NGINX/${system.name}";
nixos = {
serviceAttr = "nginx";
assertions = mkIf config.enable (map mkAssertion [
@ -34,19 +35,27 @@ in {
]);
};
defaults.port.listen = mkAlmostOptionDefault "lan";
ports = mapAttrs (_: mapAlmostOptionDefaults) {
ports = {
http = {
port = 80;
port = mkAlmostOptionDefault 80;
protocol = "http";
status = {
enable = mkAlmostOptionDefault true;
gatus.http.statusCondition = mkAlmostOptionDefault "[STATUS] == any(200, 404)";
};
};
https = {
enable = false;
port = 443;
enable = mkAlmostOptionDefault false;
port = mkAlmostOptionDefault 443;
protocol = "https";
status = {
enable = mkAlmostOptionDefault config.ports.http.status.enable;
gatus.http.statusCondition = mkAlmostOptionDefault config.ports.http.status.gatus.http.statusCondition;
};
};
proxied = {
enable = false;
port = 9080;
enable = mkAlmostOptionDefault false;
port = mkAlmostOptionDefault 9080;
protocol = "http";
listen = "lan";
};

View file

@ -8,6 +8,7 @@
inherit (lib.attrsets) mapAttrs;
in {
config.exports.services.openwebrx = {config, ...}: {
displayName = mkAlmostOptionDefault "OpenWebRX";
id = mkAlmostOptionDefault "webrx";
nixos = {
serviceAttr = "openwebrx";
@ -25,10 +26,11 @@ in {
];
};
defaults.port.listen = mkAlmostOptionDefault "lan";
ports = mapAttrs (_: mapAlmostOptionDefaults) {
ports = {
default = {
port = 8073;
port = mkAlmostOptionDefault 8073;
protocol = "http";
status.enable = mkAlmostOptionDefault true;
};
};
};

View file

@ -3,43 +3,47 @@
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs;
inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
in {
config.exports.services.plex = {
displayName = mkAlmostOptionDefault "Plex";
nixos.serviceAttr = "plex";
defaults.port.listen = mkAlmostOptionDefault "lan";
ports = mapAttrs (_: mapAlmostOptionDefaults) {
ports = {
default = {
port = 32400;
port = mkAlmostOptionDefault 32400;
protocol = "http";
status = {
enable = mkAlmostOptionDefault true;
gatus.http.statusCondition = mkAlmostOptionDefault "[STATUS] == 401";
};
};
roku = {
port = 8324;
port = mkAlmostOptionDefault 8324;
transport = "tcp";
};
dlna-tcp = {
port = 32469;
port = mkAlmostOptionDefault 32469;
transport = "tcp";
};
dlna-udp = {
port = 1900;
port = mkAlmostOptionDefault 1900;
transport = "udp";
};
gdm0 = {
port = 32410;
port = mkAlmostOptionDefault 32410;
transport = "udp";
};
gdm1 = {
port = 32412;
port = mkAlmostOptionDefault 32412;
transport = "udp";
};
gdm2 = {
port = 32413;
port = mkAlmostOptionDefault 32413;
transport = "udp";
};
gdm3 = {
port = 32414;
port = mkAlmostOptionDefault 32414;
transport = "udp";
};
};

View file

@ -14,6 +14,7 @@
cfg = config.exports;
portModule = {
config,
name,
service,
...
}: {
@ -23,6 +24,17 @@
// {
default = true;
};
name = mkOption {
type = str;
default = name;
};
displayName = mkOption {
type = nullOr str;
default =
if config.name == "default" then null
else if config.ssl && (config.name == "ssl" || config.name == "https") then "SSL"
else config.name;
};
listen = mkOption {
type = enum ["wan" "lan" "int" "localhost"];
};

View file

@ -3,47 +3,47 @@
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs;
inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
in {
config.exports.services.unifi = {config, ...}: {
nixos.serviceAttr = "unifi";
defaults.port.listen = mkAlmostOptionDefault "lan";
ports = mapAttrs (_: mapAlmostOptionDefaults) {
ports = {
management = {
# remote login
port = 8443;
port = mkAlmostOptionDefault 8443;
protocol = "https";
listen = "int";
status.enable = mkAlmostOptionDefault true;
};
uap = {
# UAP to inform controller
port = 8080;
port = mkAlmostOptionDefault 8080;
transport = "tcp";
};
portal = {
# HTTP portal redirect, if guest portal is enabled
port = 8880;
port = mkAlmostOptionDefault 8880;
protocol = "http";
};
portal-secure = {
# HTTPS portal redirect
port = 8843;
port = mkAlmostOptionDefault 8843;
protocol = "https";
};
speedtest = {
# UniFi mobile speed test
port = 6789;
port = mkAlmostOptionDefault 6789;
transport = "tcp";
};
stun = {
port = 3478;
port = mkAlmostOptionDefault 3478;
transport = "udp";
listen = "wan";
};
discovery = {
# device discovery
port = 10001;
port = mkAlmostOptionDefault 10001;
transport = "udp";
};
};

View file

@ -3,9 +3,8 @@
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.modules) mkIf;
inherit (lib.attrsets) mapAttrs;
in {
config.exports.services.vaultwarden = {config, ...}: {
id = mkAlmostOptionDefault "bw";
@ -27,14 +26,20 @@ in {
})
];
};
ports = mapAttrs (_: mapAlmostOptionDefaults) {
ports = {
default = {
port = 8222;
port = mkAlmostOptionDefault 8222;
protocol = "http";
status.enable = mkAlmostOptionDefault true;
};
websocket = {
port = 8223;
port = mkAlmostOptionDefault 8223;
protocol = "http";
displayName = mkAlmostOptionDefault "WebSocket";
status = {
enable = mkAlmostOptionDefault true;
gatus.protocol = "ws";
};
};
};
};

View file

@ -3,10 +3,11 @@
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.modules) mkIf;
in {
config.exports.services.vouch-proxy = {config, ...}: {
displayName = mkAlmostOptionDefault "Vouch Proxy";
id = mkAlmostOptionDefault "login";
defaults.port.listen = mkAlmostOptionDefault "localhost";
nixos = {
@ -18,9 +19,16 @@ in {
})
];
};
ports.default = mapAlmostOptionDefaults {
port = 30746;
ports.default = {
port = mkAlmostOptionDefault 30746;
protocol = "http";
status = {
enable = mkAlmostOptionDefault true;
gatus.http = {
#path = "/validate";
statusCondition = mkAlmostOptionDefault "[STATUS] == 404";
};
};
};
};
}

View file

@ -3,7 +3,7 @@
gensokyo-zone,
...
}: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault;
inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.modules) mkIf;
in {
config.exports.services.zigbee2mqtt = {config, ...}: {

View file

@ -6,31 +6,58 @@
...
}: let
inherit (gensokyo-zone) systems;
inherit (gensokyo-zone.lib) mkAlmostOptionDefault unmerged;
inherit (lib.modules) mkMerge mkOptionDefault;
inherit (gensokyo-zone.lib) mkAddress6 mkAlmostOptionDefault unmerged;
inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault;
inherit (lib.attrsets) attrValues nameValuePair listToAttrs;
inherit (lib.lists) filter length optional concatMap;
inherit (lib.strings) hasPrefix hasInfix optionalString concatStringsSep match;
cfg = config.services.gatus;
statusSystems = filter (system: system.config.access.online.enable) (attrValues systems);
statusSystems = filter (system: system.config.exports.status.enable) (attrValues systems);
mapSystem = system: let
statusServices = map (serviceName: system.config.exports.services.${serviceName}) system.config.exports.status.services;
in concatMap (mkServiceEndpoint system) statusServices;
serviceEndpoints = concatMap (mkServiceEndpoint system) statusServices;
systemEndpoint = mkSystemEndpoint system;
in serviceEndpoints ++ [ systemEndpoint ];
mkPortEndpoint = { system, service, port, unique }: let
inherit (port.status) gatus;
name = if unique
then service.displayName
else "${service.displayName}: ${port.name}";
conf = {
url = mkOptionDefault (access.proxyUrlFor {
inherit service port;
system = system.config;
scheme = gatus.protocol;
#network = port.listen;
});
hasId = service.id != service.name;
displayName = service.displayName + optionalString (!unique && port.displayName != null) "/${port.displayName}";
name = concatStringsSep "-" ([
service.name
] ++ optional hasId service.id ++ [
port.name
system.config.name
]);
#network = port.listen;
network = "lan";
protocolOverrides = {
dns = {
# XXX: they're lying when they say "You may optionally prefix said DNS IPs with dns://"
scheme = "";
};
starttls.host = system.config.access.fqdn;
};
in nameValuePair name ({ ... }: {
imports =
optional port.status.alert.enable alertingConfigAlerts
urlConf = {
inherit service port network;
system = system.config;
scheme = gatus.protocol;
${if gatus.client.network != "ip" then "getAddressFor" else null} = {
ip = "getAddressFor";
ip4 = "getAddress4For";
ip6 = "getAddress6For";
}.${gatus.client.network};
} // protocolOverrides.${gatus.protocol} or { };
url = access.proxyUrlFor urlConf + optionalString (gatus.http.path != "/") gatus.http.path;
conf = {
enabled = mkIf (gatus.protocol == "starttls") (mkAlmostOptionDefault false);
name = mkAlmostOptionDefault displayName;
group = mkAlmostOptionDefault groups.services;
url = mkOptionDefault url;
client.network = mkAlmostOptionDefault gatus.client.network;
};
in nameValuePair name (_: {
imports = [ alertingConfig ]
++ optional port.status.alert.enable alertingConfigAlerts
++ optional (gatus.protocol == "http" || gatus.protocol == "https") alertingConfigHttp;
config = mkMerge [
@ -45,13 +72,31 @@
in map (port: mkPortEndpoint {
inherit system service port unique;
}) gatusPorts;
mkSystemEndpoint = system: let
inherit (system.config.exports) status;
network = "lan";
getAddressFor = if system.config.network.networks.local.address4 or null != null then "getAddress4For" else "getAddressFor";
addr = access.${getAddressFor} system.config.name network;
addrIs6 = hasInfix ":" addr;
in nameValuePair "ping-${system.config.name}" (_: {
imports = [ alertingConfig ]
++ optional status.alert.enable alertingConfigAlerts;
config = {
name = mkAlmostOptionDefault system.config.name;
# XXX: it can't seem to ping ipv6 for some reason..? :<
enabled = mkIf addrIs6 (mkAlmostOptionDefault false);
client.network = mkIf addrIs6 (mkAlmostOptionDefault "ip6");
group = mkAlmostOptionDefault (groups.forSystem system);
url = mkOptionDefault "icmp://${mkAddress6 addr}";
};
});
alertingConfigAlerts = {
alerts = [
{
type = "discord";
send-on-resolved = true;
description = "Healthcheck failed.";
failure-threshold = 1;
failure-threshold = 10;
success-threshold = 3;
}
];
@ -60,9 +105,39 @@
# Common interval for refreshing all basic HTTP endpoints
interval = mkAlmostOptionDefault "30s";
};
alertingConfig = { config, ... }: let
isLan = match ''.*(::|10\.|127\.|\.(local|int|tail)\.).*'' config.url != null;
isDns = hasPrefix "dns://" config.url || config.dns.query-name or null != null;
in {
conditions = mkOptionDefault [
"[CONNECTED] == true"
];
ui = mkMerge [
(mkIf isDns {
hide-conditions = mkAlmostOptionDefault true;
})
(mkIf isLan {
hide-hostname = mkAlmostOptionDefault true;
hide-url = mkAlmostOptionDefault true;
})
];
client = {
# XXX: no way to specify SSL hostname/SNI separately from the url :<
insecure = mkAlmostOptionDefault true;
};
};
groups = {
services = "Services";
systems = "Systems";
forSystem = system: let
node = systems.${system.config.proxmox.node.name}.config;
in if system.config.proxmox.enabled then "${groups.systems}/${node.name}"
else groups.systems;
};
in {
sops.secrets.gatus_environment_file = {
sopsFile = ../secrets/gatus.yaml;
sops.secrets.gatus_environment_file = mkIf cfg.enable {
sopsFile = mkDefault ../secrets/gatus.yaml;
owner = cfg.user;
};
services.gatus = {
enable = true;
@ -101,7 +176,7 @@ in {
};
};
networking.firewall.interfaces.lan.allowedTCPPorts = [
networking.firewall.interfaces.lan.allowedTCPPorts = mkIf cfg.enable [
cfg.settings.web.port
];
}

View file

@ -44,7 +44,7 @@ in {
serverName = null;
default = mkDefault true;
reuseport = mkDefault true;
locations."/".extraConfig = mkDefault ''
locations."/".extraConfig = ''
return 404;
'';
};

View file

@ -22,6 +22,7 @@
samba.enable = true;
vouch-proxy = {
enable = true;
displayName = "Vouch Proxy/local";
id = "login.local";
};
nginx = let

View file

@ -28,4 +28,6 @@ _: {
};
};
};
# XXX: currently unplugged :<
access.online.enable = false;
}

View file

@ -5,6 +5,7 @@ _: {
./nixos.nix
];
ci.allowFailure = true;
access.online.enable = false;
proxmox = {
vm = {
id = 201;

View file

@ -5,6 +5,7 @@ _: {
arch = "x86_64";
type = "NixOS";
ci.allowFailure = true;
access.online.enable = false;
modules = [
./nixos.nix
];