feat: sentinella: some
This commit is contained in:
@@ -11,16 +11,14 @@
|
|||||||
}: let
|
}: let
|
||||||
system = pkgs.stdenv.hostPlatform.system;
|
system = pkgs.stdenv.hostPlatform.system;
|
||||||
cfg = config.hectic.services."sentinèlla";
|
cfg = config.hectic.services."sentinèlla";
|
||||||
|
|
||||||
|
probePort = 5988;
|
||||||
|
peersDns = "peers.sentinella.hectic-lab.com";
|
||||||
in {
|
in {
|
||||||
options = {
|
options = {
|
||||||
hectic.services."sentinèlla" = {
|
hectic.services."sentinèlla" = {
|
||||||
probe = {
|
probe = {
|
||||||
enable = lib.mkEnableOption "sentinèlla probe — HTTP server exposing this node's health";
|
enable = lib.mkEnableOption "sentinèlla probe — HTTP server exposing this node's health";
|
||||||
port = lib.mkOption {
|
|
||||||
type = lib.types.port;
|
|
||||||
default = 5988;
|
|
||||||
description = "TCP port the probe listens on.";
|
|
||||||
};
|
|
||||||
urls = lib.mkOption {
|
urls = lib.mkOption {
|
||||||
type = with lib.types; listOf str;
|
type = with lib.types; listOf str;
|
||||||
default = [];
|
default = [];
|
||||||
@@ -52,16 +50,6 @@ in {
|
|||||||
|
|
||||||
watcher = {
|
watcher = {
|
||||||
enable = lib.mkEnableOption "sentinèlla watcher — polls peers discovered via DNS and sends Telegram alerts";
|
enable = lib.mkEnableOption "sentinèlla watcher — polls peers discovered via DNS and sends Telegram alerts";
|
||||||
peersDns = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
example = "peers.sentinella.com";
|
|
||||||
description = ''
|
|
||||||
DNS name with multiple A records, one per peer node.
|
|
||||||
Configure externally (e.g. Cloudflare) with TTL 60:
|
|
||||||
peers.sentinella.com A 1.2.3.4
|
|
||||||
peers.sentinella.com A 5.6.7.8
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
self = lib.mkOption {
|
self = lib.mkOption {
|
||||||
type = with lib.types; nullOr str;
|
type = with lib.types; nullOr str;
|
||||||
default = null;
|
default = null;
|
||||||
@@ -73,11 +61,6 @@ in {
|
|||||||
has a floating IP that hostname -I does not report correctly.
|
has a floating IP that hostname -I does not report correctly.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
peersPort = lib.mkOption {
|
|
||||||
type = lib.types.port;
|
|
||||||
default = 5988;
|
|
||||||
description = "Port all peer probes listen on.";
|
|
||||||
};
|
|
||||||
peersScheme = lib.mkOption {
|
peersScheme = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
default = "http";
|
default = "http";
|
||||||
@@ -125,6 +108,13 @@ in {
|
|||||||
|
|
||||||
config = lib.mkMerge [
|
config = lib.mkMerge [
|
||||||
(lib.mkIf cfg.probe.enable {
|
(lib.mkIf cfg.probe.enable {
|
||||||
|
networking.firewall = {
|
||||||
|
enable = true;
|
||||||
|
allowedTCPPorts = [
|
||||||
|
probePort
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
systemd.services."sentinella-probe" = {
|
systemd.services."sentinella-probe" = {
|
||||||
description = "sentinèlla probe — node health HTTP server";
|
description = "sentinèlla probe — node health HTTP server";
|
||||||
after = [ "network.target" ];
|
after = [ "network.target" ];
|
||||||
@@ -142,7 +132,7 @@ in {
|
|||||||
StandardOutput = "journal";
|
StandardOutput = "journal";
|
||||||
StandardError = "journal";
|
StandardError = "journal";
|
||||||
Environment = lib.filter (s: s != "") [
|
Environment = lib.filter (s: s != "") [
|
||||||
"PORT=${builtins.toString cfg.probe.port}"
|
"PORT=${builtins.toString probePort}"
|
||||||
(lib.optionalString (cfg.probe.urls != []) "URLS=${lib.concatStringsSep " " cfg.probe.urls}")
|
(lib.optionalString (cfg.probe.urls != []) "URLS=${lib.concatStringsSep " " cfg.probe.urls}")
|
||||||
(lib.optionalString (cfg.probe.volumes != []) "VOLUMES=${lib.concatStringsSep " " cfg.probe.volumes}")
|
(lib.optionalString (cfg.probe.volumes != []) "VOLUMES=${lib.concatStringsSep " " cfg.probe.volumes}")
|
||||||
(lib.optionalString (cfg.probe.authFile != null) "AUTH_FILE=${cfg.probe.authFile}")
|
(lib.optionalString (cfg.probe.authFile != null) "AUTH_FILE=${cfg.probe.authFile}")
|
||||||
@@ -178,9 +168,9 @@ in {
|
|||||||
StandardError = "journal";
|
StandardError = "journal";
|
||||||
StateDirectory = "sentinella";
|
StateDirectory = "sentinella";
|
||||||
Environment = lib.filter (s: s != "") [
|
Environment = lib.filter (s: s != "") [
|
||||||
"PEERS_DNS=${cfg.watcher.peersDns}"
|
"PEERS_DNS=${peersDns}"
|
||||||
(lib.optionalString (cfg.watcher.self != null) "SELF=${cfg.watcher.self}")
|
(lib.optionalString (cfg.watcher.self != null) "SELF=${cfg.watcher.self}")
|
||||||
"PEERS_PORT=${builtins.toString cfg.watcher.peersPort}"
|
"PEERS_PORT=${builtins.toString probePort}"
|
||||||
"PEERS_SCHEME=${cfg.watcher.peersScheme}"
|
"PEERS_SCHEME=${cfg.watcher.peersScheme}"
|
||||||
"POLLING_INTERVAL_SEC=${builtins.toString cfg.watcher.pollingIntervalSec}"
|
"POLLING_INTERVAL_SEC=${builtins.toString cfg.watcher.pollingIntervalSec}"
|
||||||
"STATE_DIR=/var/lib/sentinella"
|
"STATE_DIR=/var/lib/sentinella"
|
||||||
|
|||||||
@@ -5,18 +5,11 @@
|
|||||||
domain,
|
domain,
|
||||||
sslOpts,
|
sslOpts,
|
||||||
...
|
...
|
||||||
}: { ... }: let
|
}: { ... }: {
|
||||||
port = 5869;
|
|
||||||
in {
|
|
||||||
hectic.services."sentinèlla" = {
|
hectic.services."sentinèlla" = {
|
||||||
probe = {
|
probe.enable = true;
|
||||||
enable = true;
|
|
||||||
inherit port;
|
|
||||||
};
|
|
||||||
watcher = {
|
watcher = {
|
||||||
enable = true;
|
enable = true;
|
||||||
peersDns = "peers.${domain}";
|
|
||||||
peersPort = port;
|
|
||||||
pollingIntervalSec = 60;
|
pollingIntervalSec = 60;
|
||||||
# TG_TOKEN= and TG_CHAT_ID= are read from sus/sentinella-default.yaml
|
# TG_TOKEN= and TG_CHAT_ID= are read from sus/sentinella-default.yaml
|
||||||
# (auto-declared by the module as sops.secrets."sentinèlla/watcher/environment")
|
# (auto-declared by the module as sops.secrets."sentinèlla/watcher/environment")
|
||||||
@@ -27,7 +20,7 @@ in {
|
|||||||
virtualHosts."probe.${domain}" = sslOpts // {
|
virtualHosts."probe.${domain}" = sslOpts // {
|
||||||
forceSSL = true;
|
forceSSL = true;
|
||||||
locations."/" = {
|
locations."/" = {
|
||||||
proxyPass = "http://127.0.0.1:${builtins.toString port}";
|
proxyPass = "http://127.0.0.1:5988";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{ symlinkJoin, writeTextFile, socat, dash, hectic, curl, gawk, jq, inetutils }:
|
{ symlinkJoin, writeTextFile, socat, dash, hectic, curl, gawk, jq, inetutils, getent }:
|
||||||
let
|
let
|
||||||
shell = "${dash}/bin/dash";
|
shell = "${dash}/bin/dash";
|
||||||
bashOptions = [
|
bashOptions = [
|
||||||
@@ -34,7 +34,7 @@ let
|
|||||||
watcher = hectic.writeShellApplication {
|
watcher = hectic.writeShellApplication {
|
||||||
inherit shell bashOptions;
|
inherit shell bashOptions;
|
||||||
name = "watcher";
|
name = "watcher";
|
||||||
runtimeInputs = [ curl jq inetutils ];
|
runtimeInputs = [ curl jq gawk inetutils getent ];
|
||||||
text = ''
|
text = ''
|
||||||
${builtins.readFile ./log.sh}
|
${builtins.readFile ./log.sh}
|
||||||
${builtins.readFile ./colors.sh}
|
${builtins.readFile ./colors.sh}
|
||||||
|
|||||||
@@ -136,13 +136,11 @@ while :; do
|
|||||||
printf '%s\n' "$peers" | while IFS= read -r url; do
|
printf '%s\n' "$peers" | while IFS= read -r url; do
|
||||||
[ -n "$url" ] || continue
|
[ -n "$url" ] || continue
|
||||||
|
|
||||||
auth_h=""
|
|
||||||
[ -n "$PEERS_TOKEN" ] && auth_h="-H 'Authorization: Basic $PEERS_TOKEN'"
|
|
||||||
|
|
||||||
tmpb=$(mktemp) || exit 1
|
tmpb=$(mktemp) || exit 1
|
||||||
# shellcheck disable=SC2086
|
set -- curl -sS -m "$TIMEOUT" -w '%{http_code}' -o "$tmpb"
|
||||||
code=$(sh -c "curl -sS -m \"$TIMEOUT\" -w '%{http_code}' -o \"$tmpb\" $auth_h \"$url\"") \
|
[ -n "$PEERS_TOKEN" ] && set -- "$@" -H "Authorization: Basic $PEERS_TOKEN"
|
||||||
|| code="000"
|
set -- "$@" "$url"
|
||||||
|
code=$("$@" 2>/dev/null) || code="000"
|
||||||
body=$(cat "$tmpb"); rm -f "$tmpb"
|
body=$(cat "$tmpb"); rm -f "$tmpb"
|
||||||
|
|
||||||
ok="down"; total=0; good=0
|
ok="down"; total=0; good=0
|
||||||
@@ -166,6 +164,8 @@ while :; do
|
|||||||
if [ "$cur" != "$last" ] || [ "$SPAM" = "1" ]; then
|
if [ "$cur" != "$last" ] || [ "$SPAM" = "1" ]; then
|
||||||
notify "$msg"
|
notify "$msg"
|
||||||
printf '%s' "$cur" >"$sfile"
|
printf '%s' "$cur" >"$sfile"
|
||||||
|
else
|
||||||
|
log info "no change: ${WHITE}${msg}${NC}"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,81 @@
|
|||||||
sentinèlla:
|
sentinèlla:
|
||||||
watcher:
|
watcher:
|
||||||
environment: |
|
environment: ENC[AES256_GCM,data:Nm0t4/mXm7TJfdj42C6r5a8IgU+qNRdmjcF0/WhrYUoHkCYbBg6wqIIBg+v6FPrx4oRFukBNBkDpT5cwTGFmzKEQGpOAiSgZYrweme0+GXv/zA==,iv:fjzzOyfNCJYwT92IpJPgf31hNGczb1L0pSw60+4TTbU=,tag:NrbhaztHa5/a0O1Rol22ew==,type:str]
|
||||||
TG_TOKEN=
|
sops:
|
||||||
TG_CHAT_ID=
|
age:
|
||||||
|
- recipient: age1r25zdeqq8nac6dgca9en28r57ffyz9u9d8z5yc25gc8xqz747vaqmdtk0h
|
||||||
|
enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBMOUpaK2VFZXZZMUFjUm5v
|
||||||
|
d01xbUI5bUJ2WUtick1qdjZpZktsQXF1SEdNCmllTTMrRFI1aVMxZkkxVW40SExo
|
||||||
|
eUh3Z3RWdVpSeG9Ucmx6QWRSQlZMUlEKLS0tIE9rbEUraWRzbTRDcXEvaE9JVFJH
|
||||||
|
NFVUcDVYQmhqTlhERGZnaHNTLyt0cWMKUwh34l0uWkyqALqNmt4tTxjCC2NwDg5A
|
||||||
|
k5p+T1qDSm9T1HnMFz8/kJvxEG6xm2R+tDQlYDFkmUn/tSHU97JMdQ==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
- recipient: age1vv46vn4hsn2lg6jy834cpu40c3mvqklldcm3hjtynrhwtpmlpc8szruz4v
|
||||||
|
enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhY3ZEQmowd3RBOXhkVmdI
|
||||||
|
djF5N1QxRENib0hCTUh0RkF5aG9adHVRcHpjCmk5cVR5RlRSUWoxd1NqdXozZVpM
|
||||||
|
S0JUZWI5eEFaR0JzZ2N3RnVycWNsam8KLS0tIFdndEN0V2IrV0xUQjRGZ09lbWMy
|
||||||
|
RlhveTAyN3FrSTYwY3kwdlIrcG9wbzgKVnWZ/TO72XLslo+mAQDOefhfv5FyEk6K
|
||||||
|
0kyernRAnFKkOu/KrLL2DCRScmz0WUdazhTu0tNmP6kk5FFWIWbkzw==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
- recipient: age1x04u7ftjgx8de2gq596e7frauze764cmn7jjwqnx8szthvfft5qq0tezx6
|
||||||
|
enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjMG1OWGFWUVZsaTN2SWR5
|
||||||
|
U2ZMdmIvY0E3Rk1nc3NqUXFUT2VoazVTN1hNCmJGeG13ZlBRd2Z4YkpWYjdJblVu
|
||||||
|
L2NXZk9vL3B0Rmc5V2o5V2pNOGtNRDgKLS0tIFNkZHRUd3ZZSlROSDk2dlhsU3ZJ
|
||||||
|
c3VYc2RFbmtlbzJnalFOR0lyNTFFcjgKd3iD9NOIqY/LU+nrHZnmJ6Ain91M3731
|
||||||
|
4QqelqyYPj8EcdnhxMCh9rhS4zadXz219bGM3D0T3rjSNpF7B3Xh7w==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
- recipient: age15yzgmsvl3ku2w863h6gw2vpmw37m9aruv6xrj4fue6n2jpm7pyuqk9xjmj
|
||||||
|
enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5cnJrUTVIWHpITFZ4ZDJi
|
||||||
|
M0piM3FuUkU0eWhOVEZIQ2lSUFdmbFBMbEdFCmZSRDQ2MlY2SUFza0kvWlFBNnZn
|
||||||
|
Tjk4QVRXRjR2NDdkakhqM0FLU25CdjAKLS0tIGR1Y3dBWlY5a1ZoMTQzTy9iRE1E
|
||||||
|
aHNxTmhuWVQ2SmR5bkNySVVSZ2tFQXMKpiY4+Q0etpunQ563bCwOOZ5aF9Uax4AO
|
||||||
|
fAAu5YDtth/ZlDb0V4sAcrJ1OHWu03HfzabETPBQgWr9oo9zk1P04A==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
- recipient: age1fpytf05sg9n6ywpwkmn09lhpfvgtud9h75h76jhxha475zpnasqq952rpu
|
||||||
|
enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGSFRzcnJvVnlYaDNsSjZP
|
||||||
|
TDRhdmNVRmtSZUNFd1JKL3ZjVW44c0VqRm1NCnJkTlpyQno4ODhWYlhsVkpGbERr
|
||||||
|
R0VheXhOTmdkMWhKWWp1bVpxZUlucHcKLS0tIHBSRVVkaXZMQXpkemRNNzIxQld4
|
||||||
|
RTlsQ3RLOWtJbVNNZ3hZaU1naitwYU0KKi0IL1lUayBsqr/yW74UmMbfsWKDbHMr
|
||||||
|
lY7K++/IhnE0AvZTIwrjeN2+mwb/0UtqcUGv5chCzOHPBPclkE/KVA==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
- recipient: age17yx98qk9gzgcf2q6zhhp05p6mmtrkgz66dvyk9gqclypvlr8rersxjy5v7
|
||||||
|
enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBYKytQZGVCbTBpVTFkakN4
|
||||||
|
ekFUWFk1UzlXUEhNaFcrcWJQbjJHaGVpR0hvCllmSWw0dmJmNnNHK3BKL2wrMThF
|
||||||
|
bWNRTGEwanVpVVZiakVYUm4wKzV3WjQKLS0tIGpBMU5haDl4RW5xSHkrU3FWU294
|
||||||
|
OEtnQ1haNytQQjZycFQrNWQvQ3NaK2sK61NpwiCcbfe5i+V25jErSRUiUDcBx5SJ
|
||||||
|
/AAVOHc1OZzTPZTC1Qh3LEmqhXM2Mwb8arfA3PJERmWEnw2TFoeIDA==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
- recipient: age13h8twnwvgxn04l5ywtru89a6psw5d0uckr2eghxsjp88a5augvsstq5ard
|
||||||
|
enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVTGVvWkIwZWdFME5CeU1F
|
||||||
|
QnBkdlBRMGoxQlpEVmg2OWk3eFJQU0o4UHlzCnFYRGFqZ2JHVWUzZk5wc1VnbXM0
|
||||||
|
YnZuOUQrekNIa3BNMjZoNE5JYVI5aWsKLS0tIHZpRTg3Z29wQnlyb3MxQm9mdkE2
|
||||||
|
c3crSjlZQ05wbEVlREM2WVRaZkJoU3cK97wpdGSdVhJPTa7qZJPCD/bbufQZllLt
|
||||||
|
hPiA4WTP0/MGWlRhIuiwozc2sGndZPWullGANFOK/9QFkb0Zi+PsOw==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
- recipient: age1jxntjca8q2vxvf2jaal4xyvm2ae6sh62fhv897694kuzawfrk5asj00zdt
|
||||||
|
enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBqMkVZSFk3dTVxWlJpUjRO
|
||||||
|
eVF1UFdVQUxIUVIvTHVsdHdVM1V2ZDVLOEhVCkx3SmV3aHZtVFFUQVJOT2tHQjBF
|
||||||
|
U3ZVNmtTeHdjemNQVFpXa3lMbzVJTG8KLS0tIDFkZ1oreFdsekRpTVVMdTZyMWoy
|
||||||
|
cnVyVHB5TmkxdkNleGVFSkJwemR5bGMKoEtPziwcX7DV+RJezLBAeDTbJkYj0isc
|
||||||
|
FBzlI3QoqwQJM5jn1aIjyl8jeE/gEeuyfHJ7Y5pSrAW1poz5fz5mwQ==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
lastmodified: "2026-04-26T23:54:52Z"
|
||||||
|
mac: ENC[AES256_GCM,data:oqOH7exYj8XNAprU1VhMxVTcGqPsHmiEcHFseeNZ6Q6xd+6c+gOEw7iHfdXwON6Dv7ZZbX5eThhrXcg/3Wo+YlGwH0E9ju4PDo9V6L1ajys4HvIDaAL9XVjAuKvGALXIFeboUnAYhlcMQu6+wdDwn+wPlA0J3LXk9XcqciyMx3M=,iv:RXnTGgx3kG22YwmQha1+AeLvg5l97ye3NPr4EGsTzHs=,tag:Du8w4psXyjqMHNCbmrrLCw==,type:str]
|
||||||
|
unencrypted_suffix: _unencrypted
|
||||||
|
version: 3.10.2
|
||||||
|
|||||||
Reference in New Issue
Block a user