From fab441b4386f3144f370430bb8a3a693b16aaf03 Mon Sep 17 00:00:00 2001 From: arcnmx Date: Thu, 23 May 2024 13:37:19 -0700 Subject: [PATCH] feat(hass): vouch auth disabled for now, nginx config needs more tweaking --- modules/nixos/home-assistant.nix | 50 ++++++++++++++++++++++++------ modules/nixos/nginx/vouch.nix | 10 ++++-- modules/nixos/vouch.nix | 4 ++- nixos/access/home-assistant.nix | 51 ++++++++++++++++++++++++++++-- nixos/home-assistant.nix | 53 +++++++++++++++++++++++++++++++- nixos/vouch.nix | 11 ++++++- systems/hakurei/nixos.nix | 4 +-- systems/tei/cloudflared.nix | 13 +++++--- systems/tei/nixos.nix | 17 ++++++++-- 9 files changed, 185 insertions(+), 28 deletions(-) diff --git a/modules/nixos/home-assistant.nix b/modules/nixos/home-assistant.nix index 70185a7c..5fc312de 100644 --- a/modules/nixos/home-assistant.nix +++ b/modules/nixos/home-assistant.nix @@ -1,11 +1,12 @@ { pkgs, config, + access, lib, ... }: let cfg = config.services.home-assistant; - inherit (lib.modules) mkIf mkMerge mkBefore mkDefault mkOptionDefault; + inherit (lib.modules) mkIf mkMerge mkBefore mkAfter mkDefault mkOptionDefault; inherit (lib.options) mkOption mkEnableOption; inherit (lib.lists) optional elem unique; inherit (lib.strings) toLower; @@ -16,10 +17,27 @@ in { type = str; default = config.networking.domain; }; + localDomain = mkOption { + type = nullOr str; + default = null; + }; secretsFile = mkOption { type = nullOr path; default = null; }; + reverseProxy = { + enable = mkEnableOption "use_x_forwarded_for"; + trustedAddresses = mkOption { + type = listOf str; + }; + auth = { + enable = mkEnableOption "auth-header"; + debug = mkEnableOption "debug logging"; + userHeader = mkOption { + type = str; + }; + }; + }; homekit = { enable = mkEnableOption "homekit" @@ -114,27 +132,34 @@ in { }; config.services.home-assistant = { + reverseProxy = { + trustedAddresses = access.cidrForNetwork.loopback.all; + }; config = mkMerge [ { homeassistant = { external_url = "https://${cfg.domain}"; + internal_url = mkIf (cfg.localDomain != null) "https://${cfg.localDomain}"; }; logger = { default = mkDefault "info"; + logs = { + "custom_components.auth_header" = mkIf (cfg.reverseProxy.enable && cfg.reverseProxy.auth.enable && cfg.reverseProxy.auth.debug) "debug"; + }; }; http = { + use_x_forwarded_for = cfg.reverseProxy.enable; + trusted_proxies = mkIf cfg.reverseProxy.enable cfg.reverseProxy.trustedAddresses; cors_allowed_origins = [ - "https://google.com" + (mkIf cfg.googleAssistant.enable "https://google.com") + (mkIf (cfg.localDomain != null) "https://${cfg.localDomain}") + # TODO: (mkIf (cfg.reverseProxy.enable && cfg.reverseProxy.auth.enable) vouch cors idk) "https://www.home-assistant.io" ]; - use_x_forwarded_for = "true"; - trusted_proxies = let - inherit (config.networking.access) cidrForNetwork; - in - cidrForNetwork.allLocal.all - ++ [ - "200::/7" - ]; + }; + auth_header = mkIf (cfg.reverseProxy.enable && cfg.reverseProxy.auth.enable) { + username_header = cfg.reverseProxy.auth.userHeader; + debug = mkIf cfg.reverseProxy.auth.debug true; }; recorder = { db_url = mkIf config.services.postgresql.enable (mkDefault "postgresql://@/hass"); @@ -280,5 +305,10 @@ in { (map ({platform, ...}: platform) cfg.config.media_player or []) (map ({platform, ...}: platform) cfg.config.tts or []) ]; + customComponents = [ + (mkIf (cfg.reverseProxy.enable && cfg.reverseProxy.auth.enable) + pkgs.home-assistant-custom-components.auth-header + ) + ]; }; } diff --git a/modules/nixos/nginx/vouch.nix b/modules/nixos/nginx/vouch.nix index 5490775d..126bf45a 100644 --- a/modules/nixos/nginx/vouch.nix +++ b/modules/nixos/nginx/vouch.nix @@ -18,7 +18,9 @@ virtualHost, xvars, ... - }: { + }: let + cfg = config.vouch; + in { options.vouch = with lib.types; { requireAuth = mkEnableOption "require auth to access this location"; setProxyHeader = mkOption { @@ -32,7 +34,7 @@ enableVouchTail = enableVouchLocal && tailscale.enable && false; allowOrigin = url: "add_header Access-Control-Allow-Origin ${url};"; in - mkIf config.vouch.requireAuth { + mkIf cfg.requireAuth { lua = mkIf virtualHost.vouch.auth.lua.enable { access.block = mkMerge [ (mkBefore virtualHost.vouch.auth.lua.accessRequest) @@ -41,7 +43,9 @@ ]; }; xvars.enable = mkIf (enableVouchTail || virtualHost.vouch.auth.lua.enable) true; - proxy.headers.set.X-Vouch-User = mkOptionDefault "$auth_resp_x_vouch_user"; + proxy.headers.set = mkIf cfg.setProxyHeader { + X-Vouch-User = mkOptionDefault "$auth_resp_x_vouch_user"; + }; extraConfig = assert virtualHost.vouch.enable; mkMerge [ (mkIf (!virtualHost.vouch.requireAuth) virtualHost.vouch.auth.requestDirective) diff --git a/modules/nixos/vouch.nix b/modules/nixos/vouch.nix index 8f269ba4..776e293e 100644 --- a/modules/nixos/vouch.nix +++ b/modules/nixos/vouch.nix @@ -12,6 +12,7 @@ mkDefault mkOptionDefault mkOption + mkPackageOption mkEnableOption types getExe @@ -22,6 +23,7 @@ in { options.services.vouch-proxy = with types; { enable = mkEnableOption "vouch"; + package = mkPackageOption pkgs "vouch-proxy" { }; user = mkOption { type = str; default = "vouch-proxy"; @@ -157,7 +159,7 @@ in { "${preprocess}" ]; ExecStart = [ - "${getExe pkgs.vouch-proxy} -config ${cfg.settingsPath}" + "${getExe cfg.package} -config ${cfg.settingsPath}" ]; Restart = "on-failure"; RestartSec = mkDefault 5; diff --git a/nixos/access/home-assistant.nix b/nixos/access/home-assistant.nix index e9876a48..dbd55ce9 100644 --- a/nixos/access/home-assistant.nix +++ b/nixos/access/home-assistant.nix @@ -32,24 +32,60 @@ in { }; }; virtualHosts = let + vouchHost = { config, ... }: { + vouch = { + requireAuth = mkDefault false; + auth.lua = { + enable = mkDefault true; + accessRequest = '' + ngx.ctx.auth_res = ngx.location.capture("${config.vouch.auth.requestLocation}") + if ngx.ctx.auth_res.status == ngx.HTTP_OK then + local vouch_user = ngx.re.match(ngx.ctx.auth_res.header["X-Vouch-User"], [[^([^@]+)@.*$]]) + ngx.var["hass_user"] = vouch_user[1] + end + ''; + }; + }; + extraConfig = '' + set $hass_user ""; + ''; + }; + headers.set.X-Hass-User = mkDefault "$hass_user"; copyFromVhost = mkDefault "home-assistant"; locations = { "/" = { - proxy.enable = true; + proxy = { + inherit headers; + enable = true; + }; + }; + # TODO: restrict to "/auth/authorize" and "/auth/login_flow" only..? + "/auth/" = { virtualHost, config, ... }: { + proxy = { + inherit headers; + enable = true; + }; + vouch = mkIf virtualHost.vouch.enable { + requireAuth = true; + }; }; "/api/websocket" = { proxy = { + inherit headers; enable = true; websocket.enable = true; }; }; }; in { - home-assistant = { + home-assistant = { ... }: { + imports = [ vouchHost ]; inherit name locations; proxy.upstream = mkDefault upstreamName; }; - home-assistant'local = { + home-assistant'local = { ... }: { + imports = [ vouchHost ]; + vouch.enable = mkDefault nginx.virtualHosts.home-assistant.vouch.enable; inherit name listen' locations; ssl.cert = { inherit copyFromVhost; @@ -61,6 +97,15 @@ in { }; }; }; + config.services.home-assistant = { + reverseProxy = { + enable = mkDefault true; + auth = { + enable = mkIf (nginx.virtualHosts.home-assistant.enable && nginx.virtualHosts.home-assistant.vouch.enable) true; + userHeader = "X-Hass-User"; + }; + }; + }; config.networking.firewall.allowedTCPPorts = let inherit (nginx.virtualHosts.home-assistant'local) listen'; in diff --git a/nixos/home-assistant.nix b/nixos/home-assistant.nix index 808c940d..ebd69a74 100644 --- a/nixos/home-assistant.nix +++ b/nixos/home-assistant.nix @@ -1,10 +1,12 @@ { config, + access, + gensokyo-zone, lib, ... }: let cfg = config.services.home-assistant; - inherit (lib.modules) mkIf mkDefault; + inherit (lib.modules) mkIf mkMerge mkDefault; sopsFile = mkDefault ./secrets/home-assistant.yaml; in { sops.secrets = mkIf cfg.enable { @@ -22,7 +24,15 @@ in { enable = mkDefault true; mutableUiConfig = mkDefault true; domain = mkDefault "home.${config.networking.domain}"; + localDomain = mkDefault "home.local.${config.networking.domain}"; secretsFile = mkDefault config.sops.secrets.ha-secrets.path; + reverseProxy = { + enable = mkDefault true; + trustedAddresses = mkMerge [ + access.cidrForNetwork.int.all + # [ "200::/7" ] + ]; + }; config = { homeassistant = { name = "Gensokyo"; @@ -33,9 +43,50 @@ in { currency = "CAD"; country = "CA"; time_zone = "America/Vancouver"; + # media_dirs, allowlist_external_urls, allowlist_external_dirs? packages = { manual = "!include manual.yaml"; }; + auth_providers = let + inherit (lib.attrsets) genAttrs; + shanghai = with gensokyo-zone.systems.shanghai.config.network.networks.local; [ + address4 + address6 + ]; + nue = with gensokyo-zone.systems.nue.config.network.networks.local; [ + address4 + address6 + ]; + logistics = with gensokyo-zone.systems.logistics.config.network.networks.local; [ + address4 + address6 + ]; + koishi = with gensokyo-zone.systems.koishi.config.network.networks.local; [ + address4 + #address6 + ]; + guest = logistics ++ [ + # bedroom tv + "10.1.1.67" + ]; + kat = koishi; + arc = shanghai ++ nue; + enableTrustedAuth = false; + in mkIf enableTrustedAuth [ + { + type = "trusted_networks"; + #allow_bypass_login = true; + trusted_networks = guest; + trusted_users = + genAttrs guest (_: "4051fcce77564010a836fd6b108bbb4b") + #genAttrs arc (_: "0c9c9382890746c2b246b76557f22953") + #genAttrs kat (_: "a6e96c523d334aabaea71743839ef584") + ; + } + { + type = "homeassistant"; + } + ]; }; frontend = { themes = "!include_dir_merge_named themes"; diff --git a/nixos/vouch.nix b/nixos/vouch.nix index 80f04228..0d1b890a 100644 --- a/nixos/vouch.nix +++ b/nixos/vouch.nix @@ -1,15 +1,24 @@ { - lib, config, + pkgs, + lib, ... }: let inherit (lib.modules) mkIf mkMerge mkDefault; cfg = config.services.vouch-proxy; sopsFile = mkDefault ./secrets/vouch.yaml; enableKeycloak = true; + hassVouch = false; in { services.vouch-proxy = { enable = mkDefault true; + package = mkIf hassVouch (pkgs.vouch-proxy.overrideAttrs (old: { + postPatch = '' + sed -i handlers/login.go \ + -e 's/badStrings *=.*$/badStrings = []string{}/' + '' + old.postPatch or ""; + doCheck = false; + })); domain = mkDefault "login.${config.networking.domain}"; authUrl = mkIf enableKeycloak ( mkDefault "https://sso.${config.networking.domain}/realms/${config.networking.domain}" diff --git a/systems/hakurei/nixos.nix b/systems/hakurei/nixos.nix index cdb6e29a..1a330b1b 100644 --- a/systems/hakurei/nixos.nix +++ b/systems/hakurei/nixos.nix @@ -3,13 +3,12 @@ meta, lib, access, - gensokyo-zone, ... }: let - inherit (gensokyo-zone.lib) mkAddress6; inherit (lib.modules) mkIf mkMerge; inherit (config.services) nginx; inherit (nginx) virtualHosts; + hassVouch = false; in { imports = let inherit (meta) nixos; @@ -283,6 +282,7 @@ in { # not the real hass record-holder, so don't respond globally.. local.denyGlobal = true; ssl.cert.enable = true; + vouch.enable = mkIf hassVouch true; }; zigbee2mqtt = { # not the real z2m record-holder, so don't respond globally.. diff --git a/systems/tei/cloudflared.nix b/systems/tei/cloudflared.nix index 7cf02a3f..2f5ae625 100644 --- a/systems/tei/cloudflared.nix +++ b/systems/tei/cloudflared.nix @@ -19,11 +19,14 @@ in { (nginx.virtualHosts.zigbee2mqtt.proxied.cloudflared.getIngress {}) (nginx.virtualHosts.grocy.proxied.cloudflared.getIngress {}) (nginx.virtualHosts.barcodebuddy.proxied.cloudflared.getIngress {}) - { - ${home-assistant.domain} = assert home-assistant.enable; { - service = access.proxyUrlFor {serviceName = "home-assistant";}; - }; - } + (if home-assistant.reverseProxy.auth.enable + then (nginx.virtualHosts.home-assistant.proxied.cloudflared.getIngress {}) + else { + ${home-assistant.domain} = assert home-assistant.enable && home-assistant.reverseProxy.enable; { + service = access.proxyUrlFor {serviceName = "home-assistant";}; + }; + } + ) ]; }; }; diff --git a/systems/tei/nixos.nix b/systems/tei/nixos.nix index e06850d4..e4bf6b45 100644 --- a/systems/tei/nixos.nix +++ b/systems/tei/nixos.nix @@ -1,8 +1,14 @@ { config, meta, + lib, ... -}: { +}: let + inherit (lib.modules) mkIf; + inherit (lib.lists) optional; + hassVouchAuth = false; + hassVouch = false; +in { imports = let inherit (meta) nixos; in [ @@ -21,7 +27,7 @@ nixos.grocy nixos.barcodebuddy ./cloudflared.nix - ]; + ] ++ optional hassVouchAuth nixos.access.home-assistant; services.nginx = { proxied.enable = true; @@ -29,8 +35,15 @@ zigbee2mqtt.proxied.enable = "cloudflared"; grocy.proxied.enable = "cloudflared"; barcodebuddy.proxied.enable = "cloudflared"; + home-assistant = mkIf hassVouchAuth { + proxied.enable = "cloudflared"; + vouch.enable = mkIf hassVouch true; + }; }; }; + services.home-assistant = mkIf hassVouchAuth { + reverseProxy.auth.enable = true; + }; sops.defaultSopsFile = ./secrets.yaml;