lib: add mkCaddyReverseProxy, mkFail2banJail, mkGrafanaAnnotationService, extractArrApiKey
This commit is contained in:
103
modules/lib.nix
103
modules/lib.nix
@@ -176,5 +176,108 @@ inputs.nixpkgs.lib.extend (
|
||||
after = [ "${serviceName}-file-perms.service" ];
|
||||
};
|
||||
};
|
||||
# Creates a Caddy virtualHost with reverse_proxy to a local or VPN-namespaced port.
|
||||
# Use `subdomain` for "<name>.${domain}" or `domain` for a full custom domain.
|
||||
# Exactly one of `subdomain` or `domain` must be provided.
|
||||
mkCaddyReverseProxy =
|
||||
{
|
||||
subdomain ? null,
|
||||
domain ? null,
|
||||
port,
|
||||
auth ? false,
|
||||
vpn ? false,
|
||||
}:
|
||||
assert (subdomain != null) != (domain != null);
|
||||
{ config, ... }:
|
||||
let
|
||||
vhostDomain = if domain != null then domain else "${subdomain}.${service_configs.https.domain}";
|
||||
upstream =
|
||||
if vpn then
|
||||
"${config.vpnNamespaces.wg.namespaceAddress}:${builtins.toString port}"
|
||||
else
|
||||
":${builtins.toString port}";
|
||||
in
|
||||
{
|
||||
services.caddy.virtualHosts."${vhostDomain}".extraConfig = lib.concatStringsSep "\n" (
|
||||
lib.optional auth "import ${config.age.secrets.caddy_auth.path}" ++ [ "reverse_proxy ${upstream}" ]
|
||||
);
|
||||
};
|
||||
|
||||
# Creates a fail2ban jail with systemd journal backend.
|
||||
# Covers the common pattern: journal-based detection, http/https ports, default thresholds.
|
||||
mkFail2banJail =
|
||||
{
|
||||
name,
|
||||
unitName ? "${name}.service",
|
||||
failregex,
|
||||
}:
|
||||
{ ... }:
|
||||
{
|
||||
services.fail2ban.jails.${name} = {
|
||||
enabled = true;
|
||||
settings = {
|
||||
backend = "systemd";
|
||||
port = "http,https";
|
||||
# defaults: maxretry=5, findtime=10m, bantime=10m
|
||||
};
|
||||
filter.Definition = {
|
||||
inherit failregex;
|
||||
ignoreregex = "";
|
||||
journalmatch = "_SYSTEMD_UNIT=${unitName}";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
# Creates a hardened Grafana annotation daemon service.
|
||||
# Provides DynamicUser, sandboxing, state directory, and GRAFANA_URL/STATE_FILE automatically.
|
||||
mkGrafanaAnnotationService =
|
||||
{
|
||||
name,
|
||||
description,
|
||||
script,
|
||||
after ? [ ],
|
||||
environment ? { },
|
||||
loadCredential ? null,
|
||||
}:
|
||||
{
|
||||
systemd.services."${name}-annotations" = {
|
||||
inherit description;
|
||||
after = [
|
||||
"network.target"
|
||||
"grafana.service"
|
||||
]
|
||||
++ after;
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
ExecStart = "${pkgs.python3}/bin/python3 ${script}";
|
||||
Restart = "always";
|
||||
RestartSec = "10s";
|
||||
DynamicUser = true;
|
||||
StateDirectory = "${name}-annotations";
|
||||
NoNewPrivileges = true;
|
||||
ProtectSystem = "strict";
|
||||
ProtectHome = true;
|
||||
PrivateTmp = true;
|
||||
RestrictAddressFamilies = [
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
];
|
||||
MemoryDenyWriteExecute = true;
|
||||
}
|
||||
// lib.optionalAttrs (loadCredential != null) {
|
||||
LoadCredential = loadCredential;
|
||||
};
|
||||
environment = {
|
||||
GRAFANA_URL = "http://127.0.0.1:${toString service_configs.ports.private.grafana.port}";
|
||||
STATE_FILE = "/var/lib/${name}-annotations/state.json";
|
||||
}
|
||||
// environment;
|
||||
};
|
||||
};
|
||||
|
||||
# Shell command to extract an API key from an *arr config.xml file.
|
||||
# Returns a string suitable for $() command substitution in shell scripts.
|
||||
extractArrApiKey =
|
||||
configXmlPath: "${lib.getExe pkgs.gnugrep} -oP '(?<=<ApiKey>)[^<]+' ${configXmlPath}";
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user