feat: add vhost mapping and automatic status management

main
flop 5 days ago
parent 6796f8daa6
commit 48c1dec6b6
  1. 45
      flake.nix
  2. 11
      machines/auth.domain.com/configuration.nix
  3. 10
      machines/status.domain.com/configuration.nix
  4. 37
      modules/gatus_service.nix
  5. 7
      modules/keycloak.nix
  6. 27
      modules/nginx.nix

@ -37,16 +37,51 @@
sharedModules = [ sharedModules = [
./modules/users.nix ./modules/users.nix
./modules/auto-update.nix ./modules/auto-update.nix
./modules/nginx.nix
]; ];
sharedArgs = { # Central subdomain -> service mapping.
inherit domain git_url key_mappings; # Each entry can set:
# description human-readable label (used as the gatus monitor name)
# ports.http local port nginx proxies to
# websockets enable WebSocket proxying
# gatus_health health-check path monitored by gatus (e.g. "/health").
# Leave empty/unset to hide this host from the status page.
vhosts = {
"auth.${domain}" = {
description = "Keycloak";
ports = {"http" = 8080; };
gatus_health = "/health";
};
"status.${domain}" = {
description = "Gatus status page";
ports = {"http" = 8081; };
gatus_health = "/";
};
"pad.${domain}" = {
# TODO: not implemented (hidden from gatus until gatus_health is set)
description = "HedgeDoc";
ports = {"http" = 3000; "httpws" = 3001; };
websockets = true;
};
"git.${domain}" = {
# TODO: not implemented (hidden from gatus until gatus_health is set)
description = "HedgeDoc";
ports = {"http" = 3001; "ssh" = 22; };
websockets = true;
};
};
baseArgs = {
inherit domain git_url key_mappings vhosts;
}; };
in { in {
nixosConfigurations = { nixosConfigurations = {
auth.domain.com = nixpkgs.lib.nixosSystem { auth.domain.com = nixpkgs.lib.nixosSystem {
system = "x86_64-linux"; system = "x86_64-linux";
specialArgs = sharedArgs; specialArgs = baseArgs // {
domains = [ "auth.${domain}" "status.${domain}" ];
};
modules = [ modules = [
./machines/auth.domain.com/configuration.nix ./machines/auth.domain.com/configuration.nix
./machines/auth.domain.com/hardware-configuration.nix ./machines/auth.domain.com/hardware-configuration.nix
@ -57,7 +92,9 @@
status.domain.com = nixpkgs.lib.nixosSystem { status.domain.com = nixpkgs.lib.nixosSystem {
system = "x86_64-linux"; system = "x86_64-linux";
specialArgs = sharedArgs; specialArgs = baseArgs // {
domains = [ "pad.${domain}" "gitea.${domain}" ];
};
modules = [ modules = [
./machines/status.domain.com/configuration.nix ./machines/status.domain.com/configuration.nix
./machines/status.domain.com/hardware-configuration.nix ./machines/status.domain.com/hardware-configuration.nix

@ -43,17 +43,6 @@
enable = true; enable = true;
hostname = "status.domain.com"; hostname = "status.domain.com";
title = "Status"; title = "Status";
endpoints = [
{
name = "Keycloak";
url = "https://auth.domain.com/health";
interval = "1m";
conditions = [
"[STATUS] == 200"
"[RESPONSE_TIME] < 500"
];
}
];
}; };
infra.autoUpdate = { infra.autoUpdate = {

@ -28,6 +28,16 @@
bash bash
]; ];
# Central nginx subdomain mapping example (enable when ready):
# infra.nginx = {
# enable = true;
# vhosts."pad.${config.networking.domain}" = {
# description = "HedgeDoc";
# internal_proxy = "http://localhost:3000";
# websockets = true;
# };
# };
infra.autoUpdate = { infra.autoUpdate = {
enable = true; enable = true;
# selects the nixosConfigurations.status.domain.com output from the flake # selects the nixosConfigurations.status.domain.com output from the flake

@ -1,9 +1,26 @@
{ config, lib, pkgs, domain, ... }: { config, lib, pkgs, domain, vhosts ? { }, ... }:
with lib; with lib;
let let
cfg = config.infra.gatus; cfg = config.infra.gatus;
vhost = vhosts.${cfg.hostname} or { };
port = vhost.ports.http or 8081;
# Auto-infer monitored hosts from the central vhosts map.
# Only entries with a non-empty `gatus_health` path are monitored.
monitored = lib.filterAttrs (_: v: (v.gatus_health or "") != "") vhosts;
autoEndpoints = lib.mapAttrsToList (name: v:
{
name = v.description or name;
url = "https://${name}${v.gatus_health}";
interval = "1m";
conditions = [
"[STATUS] == 200"
"[RESPONSE_TIME] < 1000"
];
}
) monitored;
in { in {
options.infra.gatus = { options.infra.gatus = {
enable = mkEnableOption "Gatus status page behind nginx"; enable = mkEnableOption "Gatus status page behind nginx";
@ -14,12 +31,6 @@ in {
description = "Virtual host name serving the Gatus status page"; description = "Virtual host name serving the Gatus status page";
}; };
port = mkOption {
type = types.port;
default = 8081;
description = "Port Gatus listens on locally";
};
title = mkOption { title = mkOption {
type = types.str; type = types.str;
default = "Status"; default = "Status";
@ -47,24 +58,16 @@ in {
enable = true; enable = true;
settings = mkMerge [ settings = mkMerge [
{ {
web.port = cfg.port; web.port = port;
storage = { storage = {
type = "sqlite"; type = "sqlite";
path = "/var/lib/gatus/data.db"; path = "/var/lib/gatus/data.db";
}; };
ui.title = cfg.title; ui.title = cfg.title;
endpoints = cfg.endpoints; endpoints = autoEndpoints ++ cfg.endpoints;
} }
cfg.extraSettings cfg.extraSettings
]; ];
}; };
services.nginx = {
enable = true;
virtualHosts.${cfg.hostname}.locations."/".proxyPass =
"http://127.0.0.1:${toString cfg.port}";
};
networking.firewall.allowedTCPPorts = [ 80 443 ];
}; };
} }

@ -1,9 +1,11 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, vhosts ? { }, ... }:
with lib; with lib;
let let
cfg = config.services.keycloak; cfg = config.services.keycloak;
vhost = vhosts.${cfg.hostname} or { };
port = vhost.ports.http or 8080;
in { in {
options.services.keycloak = { options.services.keycloak = {
enable = mkEnableOption "Keycloak identity provider"; enable = mkEnableOption "Keycloak identity provider";
@ -61,6 +63,7 @@ in {
enable = true; enable = true;
settings = { settings = {
hostname = cfg.hostname; hostname = cfg.hostname;
http-port = port;
hostname-strict-backchannel = true; hostname-strict-backchannel = true;
proxy = "edge"; proxy = "edge";
db = mkDefault "postgres"; db = mkDefault "postgres";
@ -71,7 +74,5 @@ in {
admin-password-file = cfg.adminPasswordFile; admin-password-file = cfg.adminPasswordFile;
} // cfg.settings; } // cfg.settings;
}; };
networking.firewall.allowedTCPPorts = [ 80 443 ];
}; };
} }

@ -0,0 +1,27 @@
{ config, lib, pkgs, vhosts ? { }, domains ? [ ], ... }:
let
served = lib.filterAttrs (k: _: builtins.elem k domains) vhosts;
enabled = served != { };
in {
config = lib.mkIf enabled {
services.nginx = {
enable = true;
virtualHosts = lib.mapAttrs (name: vhost:
let
port = vhost.ports.http or 80;
in {
enableACME = vhost.enableACME or false;
forceSSL = vhost.enableACME or false;
extraConfig = vhost.extraConfig or "";
locations."/" = {
proxyPass = "http://localhost:${toString port}";
proxyWebsockets = vhost.websockets or false;
};
}
) served;
};
networking.firewall.allowedTCPPorts = [ 80 443 ];
};
}
Loading…
Cancel
Save