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

View file

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

View file

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

View file

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

View file

@ -3,22 +3,38 @@
gensokyo-zone, gensokyo-zone,
... ...
}: let }: let
inherit (gensokyo-zone.lib) mapAlmostOptionDefaults mkAlmostOptionDefault; inherit (gensokyo-zone.lib) mkAlmostOptionDefault;
inherit (lib.attrsets) mapAttrs; inherit (lib.modules) mkOptionDefault;
in { in {
config.exports.services.dnsmasq = {config, ...}: { config.exports.services.dnsmasq = {system, config, ...}: {
displayName = mkAlmostOptionDefault "Dnsmasq";
id = mkAlmostOptionDefault "dns"; id = mkAlmostOptionDefault "dns";
nixos = { nixos = {
serviceAttr = "dnsmasq"; serviceAttr = "dnsmasq";
}; };
defaults.port.listen = mkAlmostOptionDefault "lan"; defaults.port.listen = mkAlmostOptionDefault "lan";
ports = mapAttrs (_: mapAlmostOptionDefaults) { ports = {
default = { default = {
port = 53; port = mkAlmostOptionDefault 53;
transport = "udp"; 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 = { tcp = {
port = config.ports.default.port; port = mkAlmostOptionDefault config.ports.default.port;
transport = "tcp"; transport = "tcp";
}; };
}; };

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -14,6 +14,7 @@
cfg = config.exports; cfg = config.exports;
portModule = { portModule = {
config, config,
name,
service, service,
... ...
}: { }: {
@ -23,6 +24,17 @@
// { // {
default = true; 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 { listen = mkOption {
type = enum ["wan" "lan" "int" "localhost"]; type = enum ["wan" "lan" "int" "localhost"];
}; };

View file

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

View file

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

View file

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

View file

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

View file

@ -6,31 +6,58 @@
... ...
}: let }: let
inherit (gensokyo-zone) systems; inherit (gensokyo-zone) systems;
inherit (gensokyo-zone.lib) mkAlmostOptionDefault unmerged; inherit (gensokyo-zone.lib) mkAddress6 mkAlmostOptionDefault unmerged;
inherit (lib.modules) mkMerge mkOptionDefault; inherit (lib.modules) mkIf mkMerge mkDefault mkOptionDefault;
inherit (lib.attrsets) attrValues nameValuePair listToAttrs; inherit (lib.attrsets) attrValues nameValuePair listToAttrs;
inherit (lib.lists) filter length optional concatMap; inherit (lib.lists) filter length optional concatMap;
inherit (lib.strings) hasPrefix hasInfix optionalString concatStringsSep match;
cfg = config.services.gatus; 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 mapSystem = system: let
statusServices = map (serviceName: system.config.exports.services.${serviceName}) system.config.exports.status.services; 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 mkPortEndpoint = { system, service, port, unique }: let
inherit (port.status) gatus; inherit (port.status) gatus;
name = if unique hasId = service.id != service.name;
then service.displayName displayName = service.displayName + optionalString (!unique && port.displayName != null) "/${port.displayName}";
else "${service.displayName}: ${port.name}"; name = concatStringsSep "-" ([
conf = { service.name
url = mkOptionDefault (access.proxyUrlFor { ] ++ optional hasId service.id ++ [
inherit service port; port.name
system = system.config; system.config.name
scheme = gatus.protocol; ]);
#network = port.listen; #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 ({ ... }: { urlConf = {
imports = inherit service port network;
optional port.status.alert.enable alertingConfigAlerts 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; ++ optional (gatus.protocol == "http" || gatus.protocol == "https") alertingConfigHttp;
config = mkMerge [ config = mkMerge [
@ -45,13 +72,31 @@
in map (port: mkPortEndpoint { in map (port: mkPortEndpoint {
inherit system service port unique; inherit system service port unique;
}) gatusPorts; }) 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 = { alertingConfigAlerts = {
alerts = [ alerts = [
{ {
type = "discord"; type = "discord";
send-on-resolved = true; send-on-resolved = true;
description = "Healthcheck failed."; description = "Healthcheck failed.";
failure-threshold = 1; failure-threshold = 10;
success-threshold = 3; success-threshold = 3;
} }
]; ];
@ -60,9 +105,39 @@
# Common interval for refreshing all basic HTTP endpoints # Common interval for refreshing all basic HTTP endpoints
interval = mkAlmostOptionDefault "30s"; 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 { in {
sops.secrets.gatus_environment_file = { sops.secrets.gatus_environment_file = mkIf cfg.enable {
sopsFile = ../secrets/gatus.yaml; sopsFile = mkDefault ../secrets/gatus.yaml;
owner = cfg.user;
}; };
services.gatus = { services.gatus = {
enable = true; enable = true;
@ -101,7 +176,7 @@ in {
}; };
}; };
networking.firewall.interfaces.lan.allowedTCPPorts = [ networking.firewall.interfaces.lan.allowedTCPPorts = mkIf cfg.enable [
cfg.settings.web.port cfg.settings.web.port
]; ];
} }

View file

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

View file

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

View file

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

View file

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

View file

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