This commit is contained in:
soraefir
2026-05-19 22:09:36 +02:00
parent 3f57b606a0
commit af36497035
7 changed files with 155 additions and 67 deletions

View File

@@ -4,36 +4,60 @@ let
serverCfg = config.syscfg.server; serverCfg = config.syscfg.server;
in { in {
sops = true; vm = {
db = false; portForward = [ 8123 ];
cfg = {cfg,...}:{
services.home-assistant = {
enable = true;
openFirewall = true;
paths = [{ extraComponents = [
path = "${serverCfg.configPath}/homeassistant/"; "matter" "thread" "cast" "zha"
mode = "0755"; "default_config" "met" "esphome" "radio_browser"
}]; "telegram_bot" "swiss_public_transport" "nextcloud" "jellyfin"
] ++ (if containerCfg.extra ? components then containerCfg.extra.components else []);
extraPackages = pp: with pp; [
python-telegram gtts
];
lovelaceConfig = {};
config = {
homeassistant = {
name = "Home";
latitude = "${if containerCfg.extra ? latitude then toString containerCfg.extra.latitude else toString 0}";
longitude = "${if containerCfg.extra ? longitude then toString containerCfg.extra.longitude else toString 0}";
elevation = "${if containerCfg.extra ? elevation then toString containerCfg.extra.elevation else toString 0}";
unit_system = "metric";
time_zone = config.time.timeZone;
};
lovelace = { mode = "yaml"; };
customLovelaceModules = [];
# default_config = {};
http = {
use_x_forwarded_for = true;
trusted_proxies = [ "10.0.0.0/8" "127.0.0.1" ];
};
};
};
};
};
containers = { containers = {
server = builder.mkContainer { dummy = builder.mkContainer {
subdomain = containerCfg.subdomain; subdomain = containerCfg.subdomain;
image = "ghcr.io/home-assistant/home-assistant:${version}"; image = "alpine:latest";
port = 8123; extraLabels = {
secret = name; "traefik.http.services.${containerCfg.subdomain}.loadbalancer.server.url" = "http://${builder.hostIp}:8123";
extraOptions = [
"--network=host" # Shares host IP: fixes timeouts & MDNS discovery
"--cap-add=NET_ADMIN" # Grants administrative network rights to fix DHCP packets
"--cap-add=NET_RAW" # Allows raw socket parsing needed for network sniffing
];
overrides = {
volumes = [
"${serverCfg.configPath}/homeassistant/:/config"
"/run/dbus:/run/dbus:ro"
];
}; };
overrides = {cmd = [ "sleep" "infinity" ];};
}; };
}; };
setup = { setup = {
trigger = "server"; trigger = "dummy";
envFile = config.sops.secrets."CUSTOM".path; envFile = config.sops.secrets."CUSTOM".path;
script = pkgs.writeShellScript "setup" '' script = pkgs.writeShellScript "setup" ''
@@ -60,6 +84,7 @@ in {
-H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{"time_zone":"${config.time.timeZone}"}' > /dev/null 2>&1 || true -d '{"time_zone":"${config.time.timeZone}"}' > /dev/null 2>&1 || true
# We can configure many more things above !
${pkgs.curl} -s -X POST "$HASS_URL/api/onboarding/analytics" \ ${pkgs.curl} -s -X POST "$HASS_URL/api/onboarding/analytics" \
-H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Authorization: Bearer $ACCESS_TOKEN" \
@@ -69,7 +94,6 @@ in {
-H "Authorization: Bearer $ACCESS_TOKEN" \ -H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{"client_id":"'"$HASS_URL"'","redirect_uri":"'"$HASS_URL"'/?auth_callback=1"}' > /dev/null 2>&1 || true -d '{"client_id":"'"$HASS_URL"'","redirect_uri":"'"$HASS_URL"'/?auth_callback=1"}' > /dev/null 2>&1 || true
fi fi
''; '';
}; };

View File

@@ -0,0 +1,3 @@
{... }:{
}

View File

@@ -13,6 +13,9 @@ let
}; };
}; };
}; };
routerName = if subpath != null
then "${subdomain}-${lib.strings.sanitizeDerivationName subpath}"
else subdomain;
in { in {
paths = [{ paths = [{
path = "${serverCfg.dataPath}/transmission/complete"; path = "${serverCfg.dataPath}/transmission/complete";
@@ -41,8 +44,12 @@ in {
WHITELIST = "";# 127.0.0.1,::1,10.*"; WHITELIST = "";# 127.0.0.1,::1,10.*";
# HOST_WHITELIST = "traefik-server,authentik-server,authentik-worker"; # HOST_WHITELIST = "traefik-server,authentik-server,authentik-worker";
}; };
extraLabels = { } // (if serverCfg.containers ? authentik then { extraLabels = {
"traefik.http.routers.${containerCfg.subdomain}.middlewares" = "authentik"; "traefik.http.routers.${routerName}.middlewares" = "transmission-rewrite";
"traefik.http.middlewares.transmission-rewrite.replacepathregex.regex=^/p2p(.*)"
"traefik.http.middlewares.transmission-rewrite.replacepathregex.replacement=/transmission/web$$1"
} // (if serverCfg.containers ? authentik then {
"traefik.http.routers.${routerName}.middlewares" = "authentik,transmission-rewrite";
} else {}); } else {});
overrides = { overrides = {

View File

@@ -1,9 +1,9 @@
{ config, lib, pkgs, serverCfg }: { config, lib, pkgs, serverCfg }:
let let
builder = contBuilder =
{ image ? null, imageStream ? null, imageFile ? null { image ? null, imageStream ? null, imageFile ? null
, secret ? null , secret ? null
, subdomain ? null, subpath?null, port ? 0 , subdomain ? null, subpath?null, port ? null
, extraEnv ? { }, extraLabels ? { }, extraOptions ? [ ] , extraEnv ? { }, extraLabels ? { }, extraOptions ? [ ]
, overrides ? { } , overrides ? { }
}: }:
@@ -35,13 +35,40 @@ let
"traefik.enable" = "false"; "traefik.enable" = "false";
}) // extraLabels; }) // extraLabels;
extraOptions = extraOptions ++ [ extraOptions = [
"--add-host=host.containers.internal:host-gateway" "--add-host=host.containers.internal:host-gateway"
]; ] ++ extraOptions;
}; };
in lib.recursiveUpdate base overrides; in lib.recursiveUpdate base overrides;
vmBuilder = { name, vm }: (import "${pkgs.path}/nixos/lib/eval-config.nix" {
system = "x86_64-linux";
modules = [ vm.cfg
({ config, lib, modulesPath, ... }: {
imports = [
"${modulesPath}/profiles/qemu-guest.nix"
"${modulesPath}/virtualisation/qemu-vm.nix"
];
networking.hostName = name;
networking.useDHCP = true;
networking.firewall.enable = false;
services.qemuGuest.enable = true;
system.stateVersion = "25.11";
virtualisation = {
memorySize = vm.memory or 2048;
cores = vm.cores or 2;
forwardPorts = let
parsePortString = port: {
from = "host";
host.port = port;
guest.port = port;
};
in if (vm ? portForward && vm.portForward != null) then map parsePortString vm.portForward else [];
};})
];
}.config.system.build.vm);
in { in {
mkContainer = builder; mkContainer = contBuilder;
mkVm = vmBuilder;
mkData = { name, dir, vars?{} }: pkgs.runCommand name vars '' mkData = { name, dir, vars?{} }: pkgs.runCommand name vars ''
mkdir -p $out mkdir -p $out
cp -r ${./data + "/${dir}"}/. $out/ cp -r ${./data + "/${dir}"}/. $out/
@@ -52,4 +79,7 @@ in {
done done
''; '';
host = "host.containers.internal"; host = "host.containers.internal";
hostIp = if (config.virtualisation.podman.defaultNetwork.settings ? subnets)
then (builtins.elemAt config.virtualisation.podman.defaultNetwork.settings.subnets 0).gateway
else "10.88.0.1";
} }

View File

@@ -17,6 +17,7 @@ in{
allPathConfigs = lib.concatMap (app: app.paths) appsList; allPathConfigs = lib.concatMap (app: app.paths) appsList;
allSetupConfigs = lib.concatMap (app: if app.setup?script then [({name = app.name; envFile="";} // app.setup)] else []) appsList; allSetupConfigs = lib.concatMap (app: if app.setup?script then [({name = app.name; envFile="";} // app.setup)] else []) appsList;
allCronsConfigs = lib.concatMap (app: app.cron) appsList; allCronsConfigs = lib.concatMap (app: app.cron) appsList;
allVMConfigs = builtins.filter (app: app.vm != null) appsList;
in{ in{
virtualisation.oci-containers = { virtualisation.oci-containers = {
backend = "podman"; backend = "podman";
@@ -47,7 +48,30 @@ in{
''; '';
startAt = "weekly"; startAt = "weekly";
}; };
} // lib.listToAttrs (lib.concatMap (e: [{ }
// lib.listToAttrs (lib.concatMap (e: [{
name = "${e.name}-vm";
value = {
description = "Isolated NixOS Guest VM for ${e.name}";
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ];
environment = {
QEMU_VM_REG_SND = "0";
NIX_DISK_IMAGE = "/media/data/kvm/${e.name}-guest.qcw2";
};
serviceConfig = {
Type = "simple";
Restart = "always";
RestartSec = "10s";
ExecStartPre = "${pkgs.coreutils}/bin/mkdir -p /media/data/kvm";
ExecStart = ''
${builder.mkVm { name = e.name; vm = e.vm; }}/bin/run-${e.name}-vm -nographic
'';
};
};
}]) allVMConfigs)
// lib.listToAttrs (lib.concatMap (e: [{
name = "${e.name}-setup"; name = "${e.name}-setup";
value = { value = {
description = "Run ${e.name} setup"; description = "Run ${e.name} setup";

View File

@@ -29,6 +29,7 @@ in with lib; {
paths = lib.mkOption {type = lib.types.listOf lib.types.attrs; default = [ ];}; paths = lib.mkOption {type = lib.types.listOf lib.types.attrs; default = [ ];};
containers = lib.mkOption {type = lib.types.attrsOf lib.types.attrs; default = { };}; containers = lib.mkOption {type = lib.types.attrsOf lib.types.attrs; default = { };};
vm = lib.mkOption {type = lib.types.nullOr lib.types.attrs; default = null;};
cron = lib.mkOption {type = lib.types.listOf lib.types.str; default = [ ];}; cron = lib.mkOption {type = lib.types.listOf lib.types.str; default = [ ];};
setup = { setup = {

View File

@@ -53,8 +53,7 @@
# ===== DEV ===== # ===== DEV =====
gitea.subdomain = "git"; gitea.subdomain = "git";
# ===== HOME ===== # ===== HOME =====
# homeassistant.subdomain = "hass"; openhab.subdomain = "hass";
# frigate = { subdomain = "hass"; subpath = "cam"; };
# trmnl = { subdomain = "hass"; subpath = "trmnl"; }; # trmnl = { subdomain = "hass"; subpath = "trmnl"; };
# influx.subdomain = "metrum"; # influx.subdomain = "metrum";
}; };