138 lines
5.4 KiB
Nix
138 lines
5.4 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
namespace,
|
|
pkgs,
|
|
...
|
|
}:
|
|
with lib;
|
|
let
|
|
name = "nebula-ui";
|
|
cfg = config.${namespace}.services.${name};
|
|
|
|
statsListenAddr = "${cfg.statsListenAddress}:${toString cfg.statsPort}";
|
|
|
|
nebulaUiConfig = lib.${namespace}.mkModule {
|
|
inherit config name;
|
|
description = "Nebula network web UI (stats + cert signing)";
|
|
options = {
|
|
# Override mkModule defaults: bind to localhost only; firewall closed by
|
|
# default since this service sits behind a Caddy reverse proxy.
|
|
listenAddress = lib.${namespace}.mkOpt types.str "127.0.0.1" "Address nebula-ui listens on";
|
|
openFirewall =
|
|
lib.${namespace}.mkBoolOpt false
|
|
"Open firewall for nebula-ui (not needed behind a reverse proxy)";
|
|
|
|
# ── Stats endpoint ───────────────────────────────────────────────────────
|
|
statsListenAddress =
|
|
lib.${namespace}.mkOpt types.str "127.0.0.1"
|
|
"Address nebula's stats HTTP endpoint listens on";
|
|
|
|
statsPort = lib.${namespace}.mkOpt types.port 8474 "Port nebula's stats HTTP endpoint listens on";
|
|
|
|
# ── CA secrets ───────────────────────────────────────────────────────────
|
|
# The CA cert/key are already decrypted by the nebula sops.nix.
|
|
# We need a *separate* sops secret for the CA key exposed to nebula-ui
|
|
# because the nebula module only exposes it to nebula-<network>.
|
|
caCertSecretKey =
|
|
lib.${namespace}.mkOpt types.str ""
|
|
"SOPS secret key for the CA certificate (e.g. \"pi5/nebula/ca-cert\")";
|
|
|
|
caKeySecretKey =
|
|
lib.${namespace}.mkOpt types.str ""
|
|
"SOPS secret key for the CA private key (e.g. \"pi5/nebula/ca-key\")";
|
|
|
|
secretsFile =
|
|
lib.${namespace}.mkOpt types.str ""
|
|
"Path to the SOPS secrets YAML that holds the CA cert + key";
|
|
|
|
# ── Network identity ─────────────────────────────────────────────────────
|
|
networkName =
|
|
lib.${namespace}.mkOpt types.str "jallen-nebula"
|
|
"Nebula network name (must match services.nebula.networkName)";
|
|
};
|
|
|
|
moduleConfig = {
|
|
assertions = [
|
|
{
|
|
assertion = cfg.caCertSecretKey != "";
|
|
message = "mjallen.services.nebula-ui.caCertSecretKey must be set";
|
|
}
|
|
{
|
|
assertion = cfg.caKeySecretKey != "";
|
|
message = "mjallen.services.nebula-ui.caKeySecretKey must be set";
|
|
}
|
|
{
|
|
assertion = cfg.secretsFile != "";
|
|
message = "mjallen.services.nebula-ui.secretsFile must be set";
|
|
}
|
|
];
|
|
|
|
# ── SOPS secrets ─────────────────────────────────────────────────────────
|
|
# ca-cert: already declared by the nebula module (owned by nebula-<network>,
|
|
# mode 0440). We only append restartUnits here; access is via group membership.
|
|
sops.secrets."${cfg.caCertSecretKey}" = {
|
|
restartUnits = [ "nebula-ui.service" ];
|
|
};
|
|
|
|
# ca-key: only used by nebula-ui, so we own it outright.
|
|
sops.secrets."${cfg.caKeySecretKey}" = {
|
|
sopsFile = cfg.secretsFile;
|
|
owner = name;
|
|
group = name;
|
|
restartUnits = [ "nebula-ui.service" ];
|
|
};
|
|
|
|
# ── User / group ────────────────────────────────────────────────────────
|
|
users.users.${name} = {
|
|
isSystemUser = true;
|
|
group = name;
|
|
# Grant read access to the nebula CA secrets (owned by nebula-<network>)
|
|
extraGroups = [ "nebula-${cfg.networkName}" ];
|
|
description = "Nebula UI service user";
|
|
};
|
|
users.groups.${name} = { };
|
|
|
|
# ── Systemd service ─────────────────────────────────────────────────────
|
|
systemd.services.${name} = {
|
|
description = "Nebula network web UI";
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [
|
|
"network.target"
|
|
"sops-nix.service"
|
|
];
|
|
|
|
environment = {
|
|
NEBULA_UI_CA_CERT_PATH = config.sops.secrets."${cfg.caCertSecretKey}".path;
|
|
NEBULA_UI_CA_KEY_PATH = config.sops.secrets."${cfg.caKeySecretKey}".path;
|
|
NEBULA_UI_STATS_URL = "http://${statsListenAddr}";
|
|
NEBULA_UI_NETWORK_NAME = cfg.networkName;
|
|
NEBULA_UI_LISTEN_HOST = cfg.listenAddress;
|
|
NEBULA_UI_LISTEN_PORT = toString cfg.port;
|
|
};
|
|
|
|
serviceConfig = {
|
|
ExecStart = "${pkgs.${namespace}.nebula-ui}/bin/nebula-ui";
|
|
User = name;
|
|
Group = name;
|
|
Restart = "on-failure";
|
|
RestartSec = "5s";
|
|
|
|
# Hardening
|
|
NoNewPrivileges = true;
|
|
PrivateTmp = true;
|
|
ProtectSystem = "strict";
|
|
ProtectHome = true;
|
|
ReadOnlyPaths = [
|
|
config.sops.secrets."${cfg.caCertSecretKey}".path
|
|
config.sops.secrets."${cfg.caKeySecretKey}".path
|
|
];
|
|
};
|
|
};
|
|
};
|
|
};
|
|
in
|
|
{
|
|
imports = [ nebulaUiConfig ];
|
|
}
|