From 61c73fcfaff0a62b2fe79c246039b77ed6c5c31e Mon Sep 17 00:00:00 2001 From: Alexander Heldt Date: Mon, 5 Jan 2026 17:43:48 +0100 Subject: [PATCH] manatee: Add `home-assistant` module --- hosts/manatee/modules/certs/default.nix | 31 ++++++++++ .../modules/home-assistant/default.nix | 62 +++++++++++++++++++ hosts/manatee/modules/network/default.nix | 22 +++++++ 3 files changed, 115 insertions(+) create mode 100644 hosts/manatee/modules/certs/default.nix create mode 100644 hosts/manatee/modules/home-assistant/default.nix diff --git a/hosts/manatee/modules/certs/default.nix b/hosts/manatee/modules/certs/default.nix new file mode 100644 index 0000000..349d091 --- /dev/null +++ b/hosts/manatee/modules/certs/default.nix @@ -0,0 +1,31 @@ +{ config, ... }: +{ + security.acme = { + acceptTerms = true; + + defaults = { + email = "acme@ppp.pm"; + }; + + certs = { + "ha.ppp.pm" = { + dnsProvider = "hetzner"; + environmentFile = config.age.secrets.hetzner-dns.path; + group = "nginx"; + + extraLegoFlags = [ + "--dns.resolvers=1.1.1.1:53,8.8.8.8:53" + "--dns.propagation-wait=60s" # Wait for 60 seconds for DNS propagation + "--dns-timeout=60" + "--http-timeout=60" + ]; + }; + }; + }; + + age = { + secrets = { + "hetzner-dns".file = ../../../../secrets/manatee/hetzner-dns.age; + }; + }; +} diff --git a/hosts/manatee/modules/home-assistant/default.nix b/hosts/manatee/modules/home-assistant/default.nix new file mode 100644 index 0000000..b1161f4 --- /dev/null +++ b/hosts/manatee/modules/home-assistant/default.nix @@ -0,0 +1,62 @@ +{ lib, config, ... }: +let + nginxEnabled = config.mod.nginx.enable; +in +{ + hardware.bluetooth.enable = true; + + virtualisation.oci-containers = { + backend = "podman"; + + containers.homeassistant = { + image = "ghcr.io/home-assistant/home-assistant:stable"; + + volumes = [ + "/home/alex/.config/home-assistant:/config" + # Pass in bluetooth + "/run/dbus:/run/dbus:ro" + ]; + + environment.TZ = "Europe/Stockholm"; + + extraOptions = [ + "--network=host" + + # Allows HA to perform low-level network operations (scan/reset adapter) + "--cap-add=NET_ADMIN" + "--cap-add=NET_RAW" + + # Pass in Zigbee antenna + "--device=/dev/serial/by-id/usb-Nabu_Casa_ZBT-2_9C139EAAD464-if00:/dev/ttyACM0" + ]; + }; + }; + + services = { + blueman.enable = true; + + nginx = lib.mkIf nginxEnabled { + recommendedProxySettings = true; + + virtualHosts."ha.ppp.pm" = { + forceSSL = true; + useACMEHost = "ha.ppp.pm"; + + extraConfig = '' + proxy_buffering off; + ''; + + locations."/" = { + proxyPass = "http://127.0.0.1:8123"; + proxyWebsockets = true; + }; + }; + }; + }; + + age = { + secrets = { + "hetzner-dns".file = ../../../../secrets/manatee/hetzner-dns.age; + }; + }; +} diff --git a/hosts/manatee/modules/network/default.nix b/hosts/manatee/modules/network/default.nix index ac7b31d..0665860 100644 --- a/hosts/manatee/modules/network/default.nix +++ b/hosts/manatee/modules/network/default.nix @@ -3,6 +3,10 @@ networking = { hostName = "manatee"; + # Required for asymmetric routing (sending replies out a different interface + # than the default route). Without this, the kernel drops the return traffic. + firewall.checkReversePath = "loose"; + defaultGateway = "192.168.50.1"; nameservers = [ "1.1.1.1" ]; interfaces = { @@ -16,7 +20,25 @@ } ]; }; + + ipv4.routes = [ + { + address = "0.0.0.0"; + prefixLength = 0; + via = "192.168.50.1"; # Router + options = { + table = "100"; + }; + } + ]; }; }; + + # Route packets from machines local IP back to router + localCommands = '' + # Add the rule only if it doesn't exist yet (idempotent) + ip rule list | grep -q "from 192.168.50.203 lookup 100" || \ + ip rule add from 192.168.50.203 lookup 100 priority 5000 + ''; }; }