dump
This commit is contained in:
parent
6ba25b90a9
commit
0f49c6c37c
35 changed files with 747 additions and 120 deletions
|
|
@ -24,7 +24,7 @@ with lib; let
|
|||
enhancer = entry:
|
||||
entry
|
||||
// {
|
||||
_upstream = "http://${entry.host}:${toString entry.port}${entry.path or ""}";
|
||||
_upstream = "http://${entry.host}:${toString entry.port}";
|
||||
_fqdn = "${entry.subdomain}.${entry._nodeConfig.config.homelab.externalDomain or homelabCfg.externalDomain}";
|
||||
_internal = "${entry.host}:${toString entry.port}";
|
||||
};
|
||||
|
|
|
|||
162
modules/homelab/services/alertmanager.nix
Normal file
162
modules/homelab/services/alertmanager.nix
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
with lib; let
|
||||
serviceName = "alertmanager";
|
||||
cfg = config.homelab.services.${serviceName};
|
||||
homelabCfg = config.homelab;
|
||||
in {
|
||||
imports = [
|
||||
(import ../lib/features/monitoring.nix serviceName)
|
||||
(import ../lib/features/logging.nix serviceName)
|
||||
(import ../lib/features/proxy.nix serviceName)
|
||||
];
|
||||
|
||||
# Core service options
|
||||
options.homelab.services.${serviceName} = {
|
||||
enable = mkEnableOption "Vault Warden";
|
||||
|
||||
description = mkOption {
|
||||
type = types.str;
|
||||
default = "Vault Warden";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 9093;
|
||||
};
|
||||
|
||||
openFirewall = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether to open the ports specified in `port` and `webPort` in the firewall.
|
||||
'';
|
||||
};
|
||||
|
||||
environmentFile = lib.mkOption {
|
||||
type = with lib.types; nullOr path;
|
||||
default = null;
|
||||
example = "/var/lib/vaultwarden.env";
|
||||
description = ''
|
||||
Additional environment file as defined in {manpage}`systemd.exec(5)`.
|
||||
|
||||
Secrets like {env}`ADMIN_TOKEN` and {env}`SMTP_PASSWORD`
|
||||
should be passed to the service without adding them to the world-readable Nix store.
|
||||
|
||||
Note that this file needs to be available on the host on which `vaultwarden` is running.
|
||||
|
||||
As a concrete example, to make the Admin UI available (from which new users can be invited initially),
|
||||
the secret {env}`ADMIN_TOKEN` needs to be defined as described
|
||||
[here](https://github.com/dani-garcia/vaultwarden/wiki/Enabling-admin-page):
|
||||
|
||||
```
|
||||
# Admin secret token, see
|
||||
# https://github.com/dani-garcia/vaultwarden/wiki/Enabling-admin-page
|
||||
ADMIN_TOKEN=...copy-paste a unique generated secret token here...
|
||||
```
|
||||
'';
|
||||
};
|
||||
|
||||
systemdServices = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [
|
||||
"vaultwarden.service"
|
||||
"vaultwarden"
|
||||
];
|
||||
description = "Systemd services to monitor";
|
||||
};
|
||||
};
|
||||
|
||||
# Service configuration with smart defaults
|
||||
config = mkIf cfg.enable (mkMerge [
|
||||
{
|
||||
services.prometheus.alertmanager = {
|
||||
enable = true;
|
||||
openFirewall = cfg.openFirewall;
|
||||
|
||||
environmentFile = alertmanagerEnv;
|
||||
|
||||
webExternalUrl = "http://monitor.lab:9093"; # optional but helpful
|
||||
configuration = {
|
||||
route = {
|
||||
receiver = "null";
|
||||
group_by = ["alertname"];
|
||||
group_wait = "10s";
|
||||
group_interval = "5m";
|
||||
repeat_interval = "4h";
|
||||
|
||||
routes = [
|
||||
{
|
||||
receiver = "telegram";
|
||||
matchers = [
|
||||
"severity =~ \"warning|critical\""
|
||||
];
|
||||
group_wait = "10s";
|
||||
continue = true;
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
receivers = [
|
||||
{name = "null";}
|
||||
{
|
||||
name = "telegram";
|
||||
telegram_configs = [
|
||||
{
|
||||
api_url = "https://api.telegram.org";
|
||||
bot_token = "$TELEGRAM_BOT_TOKEN";
|
||||
chat_id = -1002642560007;
|
||||
message_thread_id = 4;
|
||||
parse_mode = "HTML";
|
||||
send_resolved = true;
|
||||
message = "{{ template \"telegram.message\". }}";
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
|
||||
templates = [
|
||||
(pkgs.writeText "telegram.tmpl" (builtins.readFile ./provisioning/templates/telegram.tmpl))
|
||||
# (pkgs.writeText "telegram.markdown.v2.tmpl" (builtins.readFile ./provisioning/templates/telegram.markdown.v2.tmpl))
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [cfg.port];
|
||||
}
|
||||
{
|
||||
homelab.services.${serviceName}.monitoring = {
|
||||
metrics.path = "/metrics";
|
||||
|
||||
healthCheck.path = "/healthz";
|
||||
healthCheck.conditions = ["[STATUS] == 200" "[RESPONSE_TIME] < 1000"];
|
||||
|
||||
extraLabels = {
|
||||
component = "example";
|
||||
};
|
||||
};
|
||||
}
|
||||
{
|
||||
# homelab.services.${serviceName}.logging = {
|
||||
# files = ["/var/log/example/log.log"];
|
||||
# # parsing = {
|
||||
# # regex = "^ts=(?P<timestamp>[^ ]+) caller=(?P<caller>[^ ]+) level=(?P<level>\\w+) msg=\"(?P<message>[^\"]*)\"";
|
||||
# # extractFields = ["level" "caller"];
|
||||
# # };
|
||||
# extraLabels = {
|
||||
# component = "example";
|
||||
# application = "example";
|
||||
# };
|
||||
# };
|
||||
}
|
||||
{
|
||||
homelab.services.${serviceName}.proxy = {
|
||||
enableAuth = true;
|
||||
};
|
||||
}
|
||||
]);
|
||||
}
|
||||
96
modules/homelab/services/caddy.nix
Normal file
96
modules/homelab/services/caddy.nix
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
with lib; let
|
||||
serviceName = "caddy";
|
||||
cfg = config.homelab.services.${serviceName};
|
||||
homelabCfg = config.homelab;
|
||||
|
||||
allProxyEntries = homelabCfg.reverseProxy.global.allEntries;
|
||||
generateVirtualHosts = entries:
|
||||
listToAttrs (map (entry: {
|
||||
name = entry._fqdn;
|
||||
value = {
|
||||
extraConfig = ''
|
||||
reverse_proxy ${entry._upstream}
|
||||
'';
|
||||
};
|
||||
})
|
||||
entries);
|
||||
in {
|
||||
imports = [
|
||||
(import ../lib/features/monitoring.nix serviceName)
|
||||
(import ../lib/features/logging.nix serviceName)
|
||||
];
|
||||
|
||||
# Core service options
|
||||
options.homelab.services.${serviceName} = {
|
||||
enable = mkEnableOption "Caddy web server";
|
||||
|
||||
description = mkOption {
|
||||
type = types.str;
|
||||
default = "Caddy web server";
|
||||
};
|
||||
|
||||
openFirewall = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether to open the ports specified in `port` and `webPort` in the firewall.
|
||||
'';
|
||||
};
|
||||
|
||||
systemdServices = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [
|
||||
"caddy.service"
|
||||
"caddy"
|
||||
];
|
||||
description = "Systemd services to monitor";
|
||||
};
|
||||
};
|
||||
|
||||
# Service configuration with smart defaults
|
||||
config = mkIf cfg.enable (mkMerge [
|
||||
{
|
||||
services.caddy = {
|
||||
enable = true;
|
||||
|
||||
virtualHosts = generateVirtualHosts allProxyEntries;
|
||||
};
|
||||
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [80 443];
|
||||
}
|
||||
{
|
||||
# homelab.services.${serviceName}.monitoring = {
|
||||
# metrics.path = "/metrics";
|
||||
|
||||
# healthCheck.path = "/healthz";
|
||||
# healthCheck.conditions = ["[STATUS] == 200" "[RESPONSE_TIME] < 1000"];
|
||||
|
||||
# extraLabels = {
|
||||
# component = "example";
|
||||
# };
|
||||
# };
|
||||
}
|
||||
{
|
||||
# homelab.services.${serviceName}.logging = {
|
||||
# files = ["/var/log/example/log.log"];
|
||||
# # parsing = {
|
||||
# # regex = "^ts=(?P<timestamp>[^ ]+) caller=(?P<caller>[^ ]+) level=(?P<level>\\w+) msg=\"(?P<message>[^\"]*)\"";
|
||||
# # extractFields = ["level" "caller"];
|
||||
# # };
|
||||
# extraLabels = {
|
||||
# component = "example";
|
||||
# application = "example";
|
||||
# };
|
||||
# };
|
||||
}
|
||||
{
|
||||
# homelab.services.${serviceName}.proxy = {
|
||||
# enableAuth = true;
|
||||
# };
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
|
@ -4,6 +4,26 @@
|
|||
./gatus.nix
|
||||
./prometheus.nix
|
||||
./grafana.nix
|
||||
./example.nix
|
||||
./vaultwarden.nix
|
||||
# ./monitoring/loki.nix
|
||||
#
|
||||
#
|
||||
# TODO
|
||||
#
|
||||
# ./alertmanager.nix
|
||||
# ./dnsmasq.nix
|
||||
# ./authelia.nix
|
||||
# ./lldap.nix
|
||||
# ./roundcube.nix
|
||||
# ./mailserver.nix
|
||||
./caddy.nix
|
||||
# ./traefik.nix
|
||||
# ./ente-photos.nix
|
||||
# ./forgejo.nix
|
||||
# ./forgejo-runner.nix
|
||||
# ./jellyfin.nix
|
||||
# ./arr.nix
|
||||
#
|
||||
];
|
||||
}
|
||||
|
|
|
|||
86
modules/homelab/services/example.nix
Normal file
86
modules/homelab/services/example.nix
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
with lib; let
|
||||
serviceName = "example";
|
||||
cfg = config.homelab.services.${serviceName};
|
||||
homelabCfg = config.homelab;
|
||||
in {
|
||||
imports = [
|
||||
(import ../lib/features/monitoring.nix serviceName)
|
||||
(import ../lib/features/logging.nix serviceName)
|
||||
(import ../lib/features/proxy.nix serviceName)
|
||||
];
|
||||
|
||||
# Core service options
|
||||
options.homelab.services.${serviceName} = {
|
||||
enable = mkEnableOption "Example Homelab Service";
|
||||
|
||||
description = mkOption {
|
||||
type = types.str;
|
||||
default = "Example Homelab Service";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 1234;
|
||||
};
|
||||
|
||||
openFirewall = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether to open the ports specified in `port` and `webPort` in the firewall.
|
||||
'';
|
||||
};
|
||||
|
||||
systemdServices = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [
|
||||
"example.service"
|
||||
"example"
|
||||
];
|
||||
description = "Systemd services to monitor";
|
||||
};
|
||||
};
|
||||
|
||||
# Service configuration with smart defaults
|
||||
config = mkIf cfg.enable (mkMerge [
|
||||
{
|
||||
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [cfg.port];
|
||||
}
|
||||
{
|
||||
homelab.services.${serviceName}.monitoring = {
|
||||
metrics.path = "/metrics";
|
||||
|
||||
healthCheck.path = "/healthz";
|
||||
healthCheck.conditions = ["[STATUS] == 200" "[RESPONSE_TIME] < 1000"];
|
||||
|
||||
extraLabels = {
|
||||
component = "example";
|
||||
};
|
||||
};
|
||||
}
|
||||
{
|
||||
homelab.services.${serviceName}.logging = {
|
||||
files = ["/var/log/example/log.log"];
|
||||
# parsing = {
|
||||
# regex = "^ts=(?P<timestamp>[^ ]+) caller=(?P<caller>[^ ]+) level=(?P<level>\\w+) msg=\"(?P<message>[^\"]*)\"";
|
||||
# extractFields = ["level" "caller"];
|
||||
# };
|
||||
extraLabels = {
|
||||
component = "example";
|
||||
application = "example";
|
||||
};
|
||||
};
|
||||
}
|
||||
{
|
||||
homelab.services.${serviceName}.proxy = {
|
||||
enableAuth = true;
|
||||
};
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
with lib; let
|
||||
cfg = config.homelab.services.monitoring-stack;
|
||||
in {
|
||||
imports = [
|
||||
./prometheus.nix
|
||||
./alertmanager.nix
|
||||
./grafana.nix
|
||||
];
|
||||
|
||||
options.homelab.services.monitoring-stack = {
|
||||
enable = mkEnableOption "Complete monitoring stack (Prometheus + Alertmanager + Grafana)";
|
||||
|
||||
prometheus = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Enable Prometheus";
|
||||
};
|
||||
};
|
||||
|
||||
alertmanager = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Enable Alertmanager";
|
||||
};
|
||||
};
|
||||
|
||||
grafana = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Enable Grafana";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
# Enable services based on configuration
|
||||
homelab.services.prometheus.enable = mkDefault cfg.prometheus.enable;
|
||||
homelab.services.alertmanager.enable = mkDefault cfg.alertmanager.enable;
|
||||
homelab.services.grafana.enable = mkDefault cfg.grafana.enable;
|
||||
|
||||
# Configure Prometheus to use Alertmanager if both are enabled
|
||||
homelab.services.prometheus.alertmanager = mkIf (cfg.prometheus.enable && cfg.alertmanager.enable) {
|
||||
enable = true;
|
||||
url = "http://localhost:${toString config.homelab.services.alertmanager.port}";
|
||||
};
|
||||
|
||||
# Configure Grafana to use Prometheus if both are enabled
|
||||
homelab.services.grafana.datasources.prometheus = mkIf (cfg.prometheus.enable && cfg.grafana.enable) {
|
||||
url = "http://localhost:${toString config.homelab.services.prometheus.port}";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -11,7 +11,6 @@ with lib; let
|
|||
|
||||
# Generate Prometheus scrape configs from global monitoring data
|
||||
prometheusScrapeConfigs = let
|
||||
# Get all metrics - try global first, fallback to local
|
||||
allMetrics = homelabCfg.monitoring.global.allMetrics;
|
||||
|
||||
jobGroups = groupBy (m: m.jobName) allMetrics;
|
||||
|
|
|
|||
137
modules/homelab/services/vaultwarden.nix
Normal file
137
modules/homelab/services/vaultwarden.nix
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
with lib; let
|
||||
serviceName = "vaultwarden";
|
||||
cfg = config.homelab.services.${serviceName};
|
||||
homelabCfg = config.homelab;
|
||||
in {
|
||||
imports = [
|
||||
(import ../lib/features/monitoring.nix serviceName)
|
||||
(import ../lib/features/logging.nix serviceName)
|
||||
(import ../lib/features/proxy.nix serviceName)
|
||||
];
|
||||
|
||||
# Core service options
|
||||
options.homelab.services.${serviceName} = {
|
||||
enable = mkEnableOption "Vault Warden";
|
||||
|
||||
description = mkOption {
|
||||
type = types.str;
|
||||
default = "Vault Warden";
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 8222;
|
||||
};
|
||||
|
||||
openFirewall = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether to open the ports specified in `port` and `webPort` in the firewall.
|
||||
'';
|
||||
};
|
||||
|
||||
environmentFile = lib.mkOption {
|
||||
type = with lib.types; nullOr path;
|
||||
default = null;
|
||||
example = "/var/lib/vaultwarden.env";
|
||||
description = ''
|
||||
Additional environment file as defined in {manpage}`systemd.exec(5)`.
|
||||
|
||||
Secrets like {env}`ADMIN_TOKEN` and {env}`SMTP_PASSWORD`
|
||||
should be passed to the service without adding them to the world-readable Nix store.
|
||||
|
||||
Note that this file needs to be available on the host on which `vaultwarden` is running.
|
||||
|
||||
As a concrete example, to make the Admin UI available (from which new users can be invited initially),
|
||||
the secret {env}`ADMIN_TOKEN` needs to be defined as described
|
||||
[here](https://github.com/dani-garcia/vaultwarden/wiki/Enabling-admin-page):
|
||||
|
||||
```
|
||||
# Admin secret token, see
|
||||
# https://github.com/dani-garcia/vaultwarden/wiki/Enabling-admin-page
|
||||
ADMIN_TOKEN=...copy-paste a unique generated secret token here...
|
||||
```
|
||||
'';
|
||||
};
|
||||
|
||||
systemdServices = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [
|
||||
"vaultwarden.service"
|
||||
"vaultwarden"
|
||||
];
|
||||
description = "Systemd services to monitor";
|
||||
};
|
||||
};
|
||||
|
||||
# Service configuration with smart defaults
|
||||
config = mkIf cfg.enable (mkMerge [
|
||||
{
|
||||
services.vaultwarden = {
|
||||
enable = true;
|
||||
config = {
|
||||
DOMAIN = "https://bitwarden.example.com";
|
||||
SIGNUPS_ALLOWED = false;
|
||||
|
||||
ROCKET_ADDRESS = "0.0.0.0";
|
||||
ROCKET_PORT = cfg.port;
|
||||
|
||||
ROCKET_LOG = "critical";
|
||||
|
||||
# This example assumes a mailserver running on localhost,
|
||||
# thus without transport encryption.
|
||||
# If you use an external mail server, follow:
|
||||
# https://github.com/dani-garcia/vaultwarden/wiki/SMTP-configuration
|
||||
# SMTP_HOST = "127.0.0.1";
|
||||
# SMTP_PORT = 25;
|
||||
# SMTP_SSL = false;
|
||||
|
||||
# SMTP_FROM = "admin@bitwarden.example.com";
|
||||
# SMTP_FROM_NAME = "example.com Bitwarden server";
|
||||
|
||||
ADMIN_TOKEN = "1234";
|
||||
};
|
||||
environmentFile = cfg.environmentFile;
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [cfg.port];
|
||||
}
|
||||
{
|
||||
# homelab.services.${serviceName}.monitoring = {
|
||||
# metrics.path = "/metrics";
|
||||
|
||||
# healthCheck.path = "/healthz";
|
||||
# healthCheck.conditions = ["[STATUS] == 200" "[RESPONSE_TIME] < 1000"];
|
||||
|
||||
# extraLabels = {
|
||||
# component = "example";
|
||||
# };
|
||||
# };
|
||||
}
|
||||
{
|
||||
# homelab.services.${serviceName}.logging = {
|
||||
# files = ["/var/log/example/log.log"];
|
||||
# # parsing = {
|
||||
# # regex = "^ts=(?P<timestamp>[^ ]+) caller=(?P<caller>[^ ]+) level=(?P<level>\\w+) msg=\"(?P<message>[^\"]*)\"";
|
||||
# # extractFields = ["level" "caller"];
|
||||
# # };
|
||||
# extraLabels = {
|
||||
# component = "example";
|
||||
# application = "example";
|
||||
# };
|
||||
# };
|
||||
}
|
||||
{
|
||||
homelab.services.${serviceName}.proxy = {
|
||||
enableAuth = true;
|
||||
};
|
||||
}
|
||||
]);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue