From 5a661e88090474e44e758159dcb7a0a459606c1d Mon Sep 17 00:00:00 2001 From: arcnmx Date: Tue, 9 Jan 2024 13:12:55 -0800 Subject: [PATCH] refactor: move services out of systems/tewi/ --- modules/nixos/cloudflared.nix | 63 +++++ modules/nixos/home-assistant.nix | 193 +++++++++++++ modules/nixos/kanidm.nix | 91 ++++++ modules/nixos/mosquitto.nix | 25 ++ modules/nixos/nginx-vouch.nix | 75 +++++ modules/nixos/vouch.nix | 163 +++++++++++ modules/nixos/zigbee2mqtt.nix | 22 ++ nixos/access/gensokyo.nix | 12 + nixos/access/nextcloud.nix | 13 + nixos/access/plex.nix | 33 +++ nixos/access/zigbee2mqtt.nix | 22 ++ nixos/cloudflared.nix | 5 + nixos/deluge.nix | 32 +++ nixos/home-assistant.nix | 133 +++++++++ nixos/kanidm.nix | 33 +++ {systems/tewi => nixos}/mosquitto.nix | 15 +- {systems/tewi => nixos}/nginx.nix | 2 - {systems/tewi => nixos}/postgres.nix | 0 nixos/reisen-ct/filesystem.nix | 8 + {systems/tewi => nixos}/syncplay.nix | 0 nixos/vouch.nix | 26 ++ {systems/tewi => nixos}/zigbee2mqtt.nix | 19 +- .../tewi => packages}/androidtvremote2.nix | 0 systems/tewi/access.nix | 99 ------- systems/tewi/cloudflared.nix | 72 +---- systems/tewi/deluge.nix | 23 -- systems/tewi/home-assistant.nix | 260 ------------------ systems/tewi/kanidm.nix | 48 ---- systems/tewi/nixos.nix | 22 +- systems/tewi/vouch.nix | 121 -------- 30 files changed, 992 insertions(+), 638 deletions(-) create mode 100644 modules/nixos/cloudflared.nix create mode 100644 modules/nixos/home-assistant.nix create mode 100644 modules/nixos/kanidm.nix create mode 100644 modules/nixos/mosquitto.nix create mode 100644 modules/nixos/nginx-vouch.nix create mode 100644 modules/nixos/vouch.nix create mode 100644 modules/nixos/zigbee2mqtt.nix create mode 100644 nixos/access/gensokyo.nix create mode 100644 nixos/access/nextcloud.nix create mode 100644 nixos/access/plex.nix create mode 100644 nixos/access/zigbee2mqtt.nix create mode 100644 nixos/cloudflared.nix create mode 100644 nixos/deluge.nix create mode 100644 nixos/home-assistant.nix create mode 100644 nixos/kanidm.nix rename {systems/tewi => nixos}/mosquitto.nix (84%) rename {systems/tewi => nixos}/nginx.nix (97%) rename {systems/tewi => nixos}/postgres.nix (100%) create mode 100644 nixos/reisen-ct/filesystem.nix rename {systems/tewi => nixos}/syncplay.nix (100%) create mode 100644 nixos/vouch.nix rename {systems/tewi => nixos}/zigbee2mqtt.nix (67%) rename {systems/tewi => packages}/androidtvremote2.nix (100%) delete mode 100644 systems/tewi/access.nix delete mode 100644 systems/tewi/home-assistant.nix delete mode 100644 systems/tewi/kanidm.nix delete mode 100644 systems/tewi/vouch.nix diff --git a/modules/nixos/cloudflared.nix b/modules/nixos/cloudflared.nix new file mode 100644 index 00000000..378ce453 --- /dev/null +++ b/modules/nixos/cloudflared.nix @@ -0,0 +1,63 @@ +{ pkgs, config, utils, lib, ... }: let + inherit (lib.attrsets) mapAttrsToList mapAttrs' nameValuePair filterAttrsRecursive; + inherit (lib.lists) singleton; + inherit (lib.modules) mkIf mkMerge mkForce; + inherit (lib.options) mkOption mkEnableOption; + cfg = config.services.cloudflared; +in { + options.services.cloudflared = with lib.types; { + tunnels = let + tunnelModule = { config, ... }: { + options = { + extraTunnel = { + enable = mkEnableOption "extra tunnels" // { + default = config.extraTunnel.ingress != { }; + }; + ingress = mkOption { + type = attrs; + default = { }; + }; + }; + }; + }; + in mkOption { + type = attrsOf (submodule tunnelModule); + }; + }; + config.systemd.services = let + filterConfig = filterAttrsRecursive (_: v: ! builtins.elem v [ null [ ] { } ]); + mapIngress = hostname: ingress: { + inherit hostname; + } // filterConfig (filterConfig ingress); + in mkIf cfg.enable (mapAttrs' (uuid: tunnel: let + RuntimeDirectory = "cloudflared-tunnel-${uuid}"; + configPath = "/run/${RuntimeDirectory}/config.yml"; + settings = { + tunnel = uuid; + credentials-file = tunnel.credentialsFile; + ingress = mapAttrsToList mapIngress tunnel.ingress + ++ mapAttrsToList mapIngress tunnel.extraTunnel.ingress + ++ singleton { service = tunnel.default; }; + }; + in nameValuePair "cloudflared-tunnel-${uuid}" (mkMerge [ + { + after = mkIf config.services.tailscale.enable [ "tailscale-autoconnect.service" ]; + serviceConfig = { + RestartSec = 10; + }; + } + (mkIf tunnel.extraTunnel.enable { + serviceConfig = { + inherit RuntimeDirectory; + ExecStart = mkForce [ + "${cfg.package}/bin/cloudflared tunnel --config=${configPath} --no-autoupdate run" + ]; + ExecStartPre = [ + (pkgs.writeShellScript "cloudflared-tunnel-${uuid}-prepare" '' + ${utils.genJqSecretsReplacementSnippet settings configPath} + '') + ]; + }; + }) + ])) cfg.tunnels); +} diff --git a/modules/nixos/home-assistant.nix b/modules/nixos/home-assistant.nix new file mode 100644 index 00000000..95bc98ec --- /dev/null +++ b/modules/nixos/home-assistant.nix @@ -0,0 +1,193 @@ +{ + pkgs, + config, + lib, + ... +}: let + cfg = config.services.home-assistant; + inherit (lib.modules) mkIf mkMerge mkBefore mkDefault; + inherit (lib.options) mkOption mkEnableOption; + inherit (lib.lists) optional elem; +in { + options.services.home-assistant = with lib.types; { + mutableUiConfig = mkEnableOption "UI-editable config files"; + domain = mkOption { + type = str; + default = config.networking.domain; + }; + homekit.enable = mkEnableOption "homekit" // { + default = cfg.config.homekit or [ ] != [ ]; + }; + googleAssistant.enable = mkEnableOption "Google Assistant" // { + default = cfg.config.google_assistant or { } != { }; + }; + androidTv.enable = mkEnableOption "Android TV" // { + default = elem "androidtv" cfg.extraComponents; + }; + cast = { + enable = mkEnableOption "Chromecast" // { + default = elem "cast" cfg.extraComponents; + }; + openFirewall = mkEnableOption "Chromecast ports" // { + default = cfg.openFirewall; + }; + }; + }; + + config = { + networking.firewall = mkIf cfg.enable { + allowedTCPPorts = mkIf (cfg.openFirewall && cfg.homekit.enable) ( + map ({ port, ... }: port) cfg.config.homekit or [ ] + ); + + allowedUDPPortRanges = [ + (mkIf (cfg.cast.enable && cfg.cast.openFirewall) { + from = 32768; + to = 60999; + }) + ]; + }; + + # MDNS + services.avahi = mkIf (cfg.enable && cfg.homekit.enable) { + enable = mkDefault true; + publish.enable = false; + }; + + systemd.services.home-assistant = mkIf (cfg.enable && cfg.mutableUiConfig) { + # UI-editable config files + preStart = mkBefore '' + touch ${cfg.configDir}/{automations,scenes,scripts,manual,homekit_entity_config,homekit_include_entities}.yaml + ''; + }; + }; + + config.services.home-assistant = { + config = mkMerge [ + { + homeassistant = { + external_url = "https://${cfg.domain}"; + }; + logger = { + default = mkDefault "info"; + }; + http = { + cors_allowed_origins = [ + "https://google.com" + "https://www.home-assistant.io" + ]; + use_x_forwarded_for = "true"; + trusted_proxies = [ + "127.0.0.0/24" + "200::/7" + "100.64.0.0/10" + "fd7a:115c:a1e0:ab12::/64" + "::1" + ]; + }; + recorder = { + db_url = mkIf config.services.postgresql.enable (mkDefault "postgresql://@/hass"); + }; + counter = {}; + device_tracker = {}; + energy = {}; + group = {}; + history = {}; + input_boolean = {}; + input_button = {}; + input_datetime = {}; + input_number = {}; + input_select = {}; + input_text = {}; + logbook = {}; + schedule = {}; + map = {}; + media_source = {}; + media_player = []; + mobile_app = {}; + my = {}; + person = {}; + ssdp = {}; + switch = {}; + stream = {}; + sun = {}; + system_health = {}; + tag = {}; + template = {}; + timer = {}; + webhook = {}; + zeroconf = {}; + zone = {}; + sensor = {}; + } + (mkIf cfg.mutableUiConfig { + # https://nixos.wiki/wiki/Home_Assistant#Combine_declarative_and_UI_defined_automations + "automation manual" = []; + "automation ui" = "!include automations.yaml"; + # https://nixos.wiki/wiki/Home_Assistant#Combine_declarative_and_UI_defined_scenes + "scene manual" = []; + "scene ui" = "!include scenes.yaml"; + "script manual" = []; + "script ui" = "!include scripts.yaml"; + }) + ]; + package = let + inherit (cfg.package) python; + hasBrother = elem "brother" cfg.extraComponents; + # https://github.com/pysnmp/pysnmp/issues/51 + needsPyasn1pin = if lib.versionOlder python.pkgs.pysnmplib.version "6.0" + then true + else lib.warn "pyasn1 pin likely no longer needed" false; + pyasn1prefix = "${python.pkgs.pysnmp-pyasn1}/${python.sitePackages}"; + home-assistant = pkgs.home-assistant.override { + packageOverrides = self: super: { + }; + }; + in home-assistant.overrideAttrs (old: { + makeWrapperArgs = old.makeWrapperArgs ++ optional (hasBrother && needsPyasn1pin) "--prefix PYTHONPATH : ${pyasn1prefix}"; + disabledTests = old.disabledTests or [ ] ++ [ + "test_check_config" + ]; + }); + extraPackages = python3Packages: with python3Packages; mkMerge [ + [ + psycopg2 + securetar + getmac # for upnp integration + python-otbr-api + protobuf3 + (aiogithubapi.overrideAttrs (_: {doInstallCheck = false;})) + ] + (mkIf cfg.homekit.enable [ + aiohomekit + ]) + (mkIf cfg.androidTv.enable [ + adb-shell + (callPackage ../../packages/androidtvremote2.nix { }) + ]) + ]; + extraComponents = mkMerge [ + [ + "automation" + "scene" + "script" + "default_config" + "environment_canada" + "met" + "generic_thermostat" + "mqtt" + "zeroconf" + ] + (mkIf cfg.homekit.enable [ + "homekit" + ]) + (mkIf cfg.googleAssistant.enable [ + "google" + "google_assistant" + "google_cloud" + ]) + (map ({ platform, ... }: platform) cfg.config.media_player or [ ]) + (map ({ platform, ... }: platform) cfg.config.tts or [ ]) + ]; + }; +} diff --git a/modules/nixos/kanidm.nix b/modules/nixos/kanidm.nix new file mode 100644 index 00000000..28dc9514 --- /dev/null +++ b/modules/nixos/kanidm.nix @@ -0,0 +1,91 @@ +{ + lib, + pkgs, + config, + ... +}: let + inherit (lib) mkIf mkMerge mkDefault mkOptionDefault mkEnableOption mkOption; + cfg = config.services.kanidm; +in { + options.services.kanidm = with lib.types; { + server = { + openFirewall = mkEnableOption "firewall ports"; + unencrypted = { + enable = mkEnableOption "snake oil certificate"; + domain = mkOption { + type = str; + default = cfg.server.frontend.domain; + }; + package = mkOption { + type = package; + }; + }; + frontend = { + domain = mkOption { + type = nullOr str; + default = cfg.serverSettings.domain; + }; + address = mkOption { + type = str; + default = "127.0.0.1"; + }; + port = mkOption { + type = port; + default = 8081; + }; + }; + ldap = { + enable = mkEnableOption "LDAP interface"; + address = mkOption { + type = str; + default = "127.0.0.1"; + }; + port = mkOption { + type = port; + default = 636; + }; + }; + }; + }; + + config = { + networking.firewall.allowedTCPPorts = mkIf (cfg.enableServer && cfg.server.openFirewall) [ + cfg.server.frontend.port + cfg.server.ldap.port + ]; + + services.kanidm = { + server.unencrypted.package = let + cert = pkgs.runCommand "kanidm-cert" { + inherit (cfg.server.unencrypted) domain; + nativeBuildInputs = [ pkgs.buildPackages.minica ]; + } '' + install -d $out + cd $out + minica \ + --ca-key ca.key.pem \ + --ca-cert ca.cert.pem \ + --domains $domain + cat $domain/cert.pem ca.cert.pem > $domain.pem + ''; + in mkOptionDefault cert; + clientSettings = mkIf cfg.enableServer { + uri = mkDefault cfg.serverSettings.origin; + }; + serverSettings = mkMerge [ + { + domain = mkDefault config.networking.domain; + origin = mkDefault "https://${cfg.server.frontend.domain}"; + bindaddress = mkDefault "${cfg.server.frontend.address}:${toString cfg.server.frontend.port}"; + ldapbindaddress = mkIf cfg.server.ldap.enable ( + mkDefault "${cfg.server.ldap.address}:${toString cfg.server.ldap.port}" + ); + } + (mkIf cfg.server.unencrypted.enable { + tls_chain = "${cfg.server.unencrypted.package}/${cfg.server.unencrypted.domain}.pem"; + tls_key = "${cfg.server.unencrypted.package}/${cfg.server.unencrypted.domain}/key.pem"; + }) + ]; + }; + }; +} diff --git a/modules/nixos/mosquitto.nix b/modules/nixos/mosquitto.nix new file mode 100644 index 00000000..ccc86cc3 --- /dev/null +++ b/modules/nixos/mosquitto.nix @@ -0,0 +1,25 @@ +{ + config, + lib, + ... +}: let + inherit (lib) mkIf mkMerge mkEnableOption mkOption; + cfg = config.services.mosquitto; +in { + options.services.mosquitto = with lib.types; { + listeners = let + listenerModule = { ... }: { + options = { + openFirewall = mkEnableOption "firewall"; + }; + }; + in mkOption { + type = listOf (submodule listenerModule); + }; + }; + config = { + networking.firewall.allowedTCPPorts = mkIf cfg.enable (mkMerge ( + map (listener: mkIf listener.openFirewall [ listener.port ]) cfg.listeners + )); + }; +} diff --git a/modules/nixos/nginx-vouch.nix b/modules/nixos/nginx-vouch.nix new file mode 100644 index 00000000..40be5d13 --- /dev/null +++ b/modules/nixos/nginx-vouch.nix @@ -0,0 +1,75 @@ +{ + config, + lib, + ... +}: +with lib; let + inherit (config.services) vouch-proxy; +in { + options = with types; { + services.nginx.virtualHosts = let + vouchModule = { config, ... }: { + options = { + vouch = { + enable = mkEnableOption "vouch auth proxy"; + proxyOrigin = mkOption { + type = str; + }; + authUrl = mkOption { + type = str; + }; + url = mkOption { + type = str; + }; + }; + }; + config = mkMerge [ + { + vouch = mkIf vouch-proxy.enable { + proxyOrigin = let + inherit (vouch-proxy.settings.vouch) listen port; + in mkOptionDefault "http://${listen}:${toString port}"; + authUrl = mkOptionDefault vouch-proxy.authUrl; + url = mkOptionDefault vouch-proxy.url; + }; + } + (mkIf config.vouch.enable { + extraConfig = '' + auth_request /validate; + error_page 401 = @error401; + ''; + locations = { + "/" = { + extraConfig = '' + add_header Access-Control-Allow-Origin ${config.vouch.url}; + add_header Access-Control-Allow-Origin ${config.vouch.authUrl}; + proxy_set_header X-Vouch-User $auth_resp_x_vouch_user; + ''; + }; + "@error401" = { + extraConfig = '' + return 302 ${config.vouch.url}/login?url=$scheme://$http_host$request_uri&vouch-failcount=$auth_resp_failcount&X-Vouch-Token=$auth_resp_jwt&error=$auth_resp_err; + ''; + }; + "/validate" = { + recommendedProxySettings = false; + proxyPass = "${config.vouch.proxyOrigin}/validate"; + extraConfig = '' + proxy_set_header Host $host; + proxy_pass_request_body off; + proxy_set_header Content-Length ""; + auth_request_set $auth_resp_x_vouch_user $upstream_http_x_vouch_user; + auth_request_set $auth_resp_jwt $upstream_http_x_vouch_jwt; + auth_request_set $auth_resp_err $upstream_http_x_vouch_err; + auth_request_set $auth_resp_failcount $upstream_http_x_vouch_failcount; + ''; + }; + }; + }) + ]; + }; + in mkOption { + type = attrsOf (submodule vouchModule); + }; + }; +} diff --git a/modules/nixos/vouch.nix b/modules/nixos/vouch.nix new file mode 100644 index 00000000..d4c78424 --- /dev/null +++ b/modules/nixos/vouch.nix @@ -0,0 +1,163 @@ +{ + config, + utils, + pkgs, + lib, + ... +}: let + inherit (lib) mkIf mkMerge mkDefault mkOptionDefault mkOption mkEnableOption types + getExe; + nixosConfig = config; + cfg = config.services.vouch-proxy; + settingsFormat = pkgs.formats.json { }; +in { + options.services.vouch-proxy = with types; { + enable = mkEnableOption "vouch"; + user = mkOption { + type = str; + default = "vouch-proxy"; + }; + group = mkOption { + type = str; + default = "vouch-proxy"; + }; + authUrl = mkOption { + type = str; + default = config.services.kanidm.serverSettings.origin; + }; + domain = mkOption { + type = str; + default = config.networking.domain; + }; + url = mkOption { + type = str; + default = "https://${cfg.domain}"; + }; + enableSettingsSecrets = mkEnableOption "genJqSecretsReplacementSnippet"; + settings = let + settingsModule = { ... }: { + freeformType = settingsFormat.type; + options = { + vouch = { + cookie = { + domain = mkOption { + type = nullOr str; + default = nixosConfig.networking.domain; + }; + secure = mkOption { + type = bool; + default = true; + }; + }; + port = mkOption { + type = port; + default = 30746; + }; + listen = mkOption { + type = nullOr str; + default = "127.0.0.1"; + }; + allowAllUsers = mkOption { + type = bool; + default = true; + }; + }; + oauth = { + auth_url = mkOption { + type = str; + default = "${cfg.authUrl}/ui/oauth2"; + }; + token_url = mkOption { + type = str; + default = "${cfg.authUrl}/oauth2/token"; + }; + user_info_url = mkOption { + type = str; + default = "${cfg.authUrl}/oauth2/openid/vouch/userinfo"; + }; + scopes = mkOption { + type = listOf str; + default = ["openid" "email" "profile"]; + }; + callback_url = mkOption { + type = str; + default = "${cfg.url}/auth"; + }; + provider = mkOption { + type = nullOr str; + default = "oidc"; + }; + code_challenge_method = mkOption { + type = str; + default = "S256"; + }; + client_id = mkOption { + type = str; + default = "vouch"; + }; + }; + }; + }; + in mkOption { + type = submodule settingsModule; + default = { }; + }; + extraSettings = mkOption { + inherit (settingsFormat) type; + default = { }; + }; + settingsPath = mkOption { + type = path; + }; + }; + config = let + recursiveMergeAttrs = listOfAttrsets: lib.fold (attrset: acc: lib.recursiveUpdate attrset acc) {} listOfAttrsets; + settings = recursiveMergeAttrs [ + cfg.settings + cfg.extraSettings + ]; + settingsPath = if cfg.enableSettingsSecrets + then "/run/vouch-proxy/vouch-config.json" + else settingsFormat.generate "vouch-config.json" settings; + in mkMerge [ + { + services.vouch-proxy = { + settingsPath = mkOptionDefault settingsPath; + }; + } + (mkIf cfg.enable { + systemd.services.vouch-proxy = { + description = "Vouch-proxy"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + ExecStartPre = let + preprocess = pkgs.writeShellScript "vouch-proxy-prestart" ( + utils.genJqSecretsReplacementSnippet settings cfg.settingsPath + ); + in mkIf cfg.enableSettingsSecrets [ + "${preprocess}" + ]; + ExecStart = [ + "${getExe pkgs.vouch-proxy} -config ${cfg.settingsPath}" + ]; + Restart = "on-failure"; + RestartSec = mkDefault 5; + WorkingDirectory = "/var/lib/vouch-proxy"; + StateDirectory = "vouch-proxy"; + RuntimeDirectory = "vouch-proxy"; + User = cfg.user; + Group = cfg.group; + StartLimitBurst = mkDefault 3; + }; + }; + + users.users.${cfg.user} = { + inherit (cfg) group; + isSystemUser = true; + }; + + users.groups.${cfg.group} = {}; + }) + ]; +} diff --git a/modules/nixos/zigbee2mqtt.nix b/modules/nixos/zigbee2mqtt.nix new file mode 100644 index 00000000..d9aba666 --- /dev/null +++ b/modules/nixos/zigbee2mqtt.nix @@ -0,0 +1,22 @@ +{ + config, + lib, + ... +}: let + inherit (lib) mkIf mkEnableOption mkOption; + cfg = config.services.zigbee2mqtt; +in { + options.services.zigbee2mqtt = with lib.types; { + openFirewall = mkEnableOption "firewall port"; + domain = mkOption { + type = str; + default = config.networking.domain; + }; + }; + + config = { + networking.firewall.allowedTCPPorts = mkIf (cfg.enable && cfg.openFirewall) [ + cfg.settings.frontend.port + ]; + }; +} diff --git a/nixos/access/gensokyo.nix b/nixos/access/gensokyo.nix new file mode 100644 index 00000000..221f5d71 --- /dev/null +++ b/nixos/access/gensokyo.nix @@ -0,0 +1,12 @@ +{ + config, + lib, + pkgs, + ... +}: { + services.nginx.virtualHosts.${config.networking.domain} = { + locations."/" = { + root = pkgs.gensokyoZone; + }; + }; +} diff --git a/nixos/access/nextcloud.nix b/nixos/access/nextcloud.nix new file mode 100644 index 00000000..c868bd1b --- /dev/null +++ b/nixos/access/nextcloud.nix @@ -0,0 +1,13 @@ +{ + config, + lib, + meta, + ... +}: +with lib; { + services.nginx.virtualHosts."cloud.${config.networking.domain}" = { + locations = { + "/".proxyPass = meta.tailnet.yukari.ppp 4 80 "nextcloud/"; + }; + }; +} diff --git a/nixos/access/plex.nix b/nixos/access/plex.nix new file mode 100644 index 00000000..43f694da --- /dev/null +++ b/nixos/access/plex.nix @@ -0,0 +1,33 @@ +{ + config, + lib, + meta, + ... +}: +with lib; { + services.nginx.virtualHosts."plex.${config.networking.domain}" = { + locations = { + "/" = { + proxyPass = meta.tailnet.yukari.pp 4 32400; + extraConfig = '' + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_redirect off; + proxy_buffering off; + proxy_set_header X-Plex-Client-Identifier $http_x_plex_client_identifier; + proxy_set_header X-Plex-Device $http_x_plex_device; + proxy_set_header X-Plex-Device-Name $http_x_plex_device_name; + proxy_set_header X-Plex-Platform $http_x_plex_platform; + proxy_set_header X-Plex-Platform-Version $http_x_plex_platform_version; + proxy_set_header X-Plex-Product $http_x_plex_product; + proxy_set_header X-Plex-Token $http_x_plex_token; + proxy_set_header X-Plex-Version $http_x_plex_version; + proxy_set_header X-Plex-Nocache $http_x_plex_nocache; + proxy_set_header X-Plex-Provides $http_x_plex_provides; + proxy_set_header X-Plex-Device-Vendor $http_x_plex_device_vendor; + proxy_set_header X-Plex-Model $http_x_plex_model; + ''; + }; + }; + }; +} diff --git a/nixos/access/zigbee2mqtt.nix b/nixos/access/zigbee2mqtt.nix new file mode 100644 index 00000000..50f4c2e9 --- /dev/null +++ b/nixos/access/zigbee2mqtt.nix @@ -0,0 +1,22 @@ +{ + config, + lib, + ... +}: +with lib; let + cfg = config.services.zigbee2mqtt; +in { + services.nginx.virtualHosts.${cfg.domain} = { + vouch.enable = true; + locations = { + "/" = { + proxyPass = "http://127.0.0.1:${toString cfg.settings.frontend.port}"; + extraConfig = '' + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_http_version 1.1; + ''; + }; + }; + }; +} diff --git a/nixos/cloudflared.nix b/nixos/cloudflared.nix new file mode 100644 index 00000000..daf5e06a --- /dev/null +++ b/nixos/cloudflared.nix @@ -0,0 +1,5 @@ +{ config, lib, ... }: let + inherit (lib.modules) mkDefault; +in { + config.services.cloudflared.enable = mkDefault true; +} diff --git a/nixos/deluge.nix b/nixos/deluge.nix new file mode 100644 index 00000000..dc2bf554 --- /dev/null +++ b/nixos/deluge.nix @@ -0,0 +1,32 @@ +{ config, lib, ... }: let + inherit (lib) mkDefault; + cfg = config.services.deluge; +in { + sops.secrets.deluge-auth = { + inherit (cfg) group; + owner = cfg.user; + }; + services.deluge = { + enable = mkDefault true; + declarative = mkDefault true; + openFirewall = mkDefault true; + web = { + enable = true; + }; + config = { + max_upload_speed = 10.0; + #share_ratio_limit = 2.0; + max_connections_global = 1024; + max_connections_per_second = 50; + max_active_limit = 100; + max_active_downloading = 75; + max_upload_slots_global = 25; + max_active_seeding = 1; + allow_remote = true; + daemon_port = 58846; + listen_ports = [ 6881 6889 ]; + random_port = false; + }; + authFile = config.sops.secrets.deluge-auth.path; + }; +} diff --git a/nixos/home-assistant.nix b/nixos/home-assistant.nix new file mode 100644 index 00000000..37eb9161 --- /dev/null +++ b/nixos/home-assistant.nix @@ -0,0 +1,133 @@ +{ + pkgs, + config, + lib, + ... +}: let + cfg = config.services.home-assistant; + inherit (lib.modules) mkDefault; + inherit (lib.lists) optional; +in { + sops.secrets = { + ha-integration = { + owner = "hass"; + path = "${cfg.configDir}/integration.yaml"; + }; + ha-secrets = { + owner = "hass"; + path = "${cfg.configDir}/secrets.yaml"; + }; + }; + + services.home-assistant = { + enable = mkDefault true; + openFirewall = mkDefault true; + mutableUiConfig = mkDefault true; + domain = mkDefault "home.${config.networking.domain}"; + config = { + homeassistant = { + name = "Gensokyo"; + unit_system = "metric"; + latitude = "!secret home_lat"; + longitude = "!secret home_long"; + elevation = "!secret home_asl"; + currency = "CAD"; + country = "CA"; + time_zone = "America/Vancouver"; + packages = { + manual = "!include manual.yaml"; + }; + }; + frontend = { + themes = "!include_dir_merge_named themes"; + }; + powercalc = { + }; + utility_meter = { + }; + withings = { + use_webhook = true; + }; + recorder = { + auto_purge = true; + purge_keep_days = 14; + commit_interval = 1; + exclude = { + domains = [ + "automation" + "updater" + ]; + entity_globs = [ + "sensor.weather_*" + "sensor.date_*" + ]; + entities = [ + "sun.sun" + "sensor.last_boot" + "sensor.date" + "sensor.time" + ]; + event_types = [ + "call_service" + ]; + }; + }; + google_assistant = { + project_id = "gensokyo-5cfaf"; + service_account = "!include integration.yaml"; + report_state = true; + exposed_domains = [ + "scene" + "script" + "climate" + #"sensor" + ]; + entity_config = {}; + }; + homekit = [ { + name = "Tewi"; + port = 21063; + ip_address = "10.1.1.38"; + filter = let + inherit (cfg.config) google_assistant; + in { + include_domains = google_assistant.exposed_domains; + include_entities = "!include homekit_include_entities.yaml"; + }; + entity_config = "!include homekit_entity_config.yaml"; + } ]; + tts = [ + { + platform = "google_translate"; + service_name = "google_say"; + } + ]; + media_player = [ + { + platform = "mpd"; + name = "Shanghai MPD"; + host = "shanghai.local.cutie.moe"; + password = "!secret mpd-shanghai-password"; + } + ]; + prometheus = {}; + wake_on_lan = {}; + }; + extraComponents = [ + "zha" + "esphome" + "apple_tv" + "spotify" + "brother" + "ipp" + "androidtv" + "cast" + "plex" + "shopping_list" + "tile" + "wake_on_lan" + "withings" + "wled" + ]; + }; +} diff --git a/nixos/kanidm.nix b/nixos/kanidm.nix new file mode 100644 index 00000000..bcaa11a7 --- /dev/null +++ b/nixos/kanidm.nix @@ -0,0 +1,33 @@ +{ + lib, + config, + ... +}: let + inherit (lib) mkDefault; + cfg = config.services.kanidm; +in { + services.kanidm = { + enableServer = true; + enableClient = true; + server = { + unencrypted.enable = mkDefault true; + openFirewall = mkDefault true; + frontend = { + domain = mkDefault "id.${cfg.serverSettings.domain}"; + address = mkDefault "0.0.0.0"; + }; + ldap = { + enable = mkDefault true; + address = mkDefault "0.0.0.0"; + }; + }; + clientSettings = { + verify_ca = mkDefault true; + verify_hostnames = mkDefault true; + }; + serverSettings = { + role = mkDefault "WriteReplica"; + log_level = mkDefault "info"; + }; + }; +} diff --git a/systems/tewi/mosquitto.nix b/nixos/mosquitto.nix similarity index 84% rename from systems/tewi/mosquitto.nix rename to nixos/mosquitto.nix index 0ad3a1db..6e5d9a43 100644 --- a/systems/tewi/mosquitto.nix +++ b/nixos/mosquitto.nix @@ -2,11 +2,9 @@ config, lib, ... -}: { - networking.firewall.allowedTCPPorts = [ - 1883 - ]; - +}: let + inherit (lib) mkDefault; +in { sops.secrets = { z2m-pass.owner = "mosquitto"; systemd-pass.owner = "mosquitto"; @@ -15,10 +13,11 @@ }; services.mosquitto = { - enable = true; - persistence = true; + enable = mkDefault true; + persistence = mkDefault true; listeners = [ { + openFirewall = mkDefault true; acl = [ "pattern readwrite #" ]; @@ -49,7 +48,7 @@ }; }; settings = { - allow_anonymous = false; + allow_anonymous = mkDefault false; }; } ]; diff --git a/systems/tewi/nginx.nix b/nixos/nginx.nix similarity index 97% rename from systems/tewi/nginx.nix rename to nixos/nginx.nix index 58d49bde..4bf39856 100644 --- a/systems/tewi/nginx.nix +++ b/nixos/nginx.nix @@ -29,7 +29,5 @@ with lib; { #proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict"; ''; clientMaxBodySize = "512m"; - virtualHosts = { - }; }; } diff --git a/systems/tewi/postgres.nix b/nixos/postgres.nix similarity index 100% rename from systems/tewi/postgres.nix rename to nixos/postgres.nix diff --git a/nixos/reisen-ct/filesystem.nix b/nixos/reisen-ct/filesystem.nix new file mode 100644 index 00000000..c26cfa7e --- /dev/null +++ b/nixos/reisen-ct/filesystem.nix @@ -0,0 +1,8 @@ +{ + lib, + ... +}: let + inherit (lib) mkDefault; +in { + services.kanidm.serverSettings.db_fs_type = mkDefault "zfs"; +} diff --git a/systems/tewi/syncplay.nix b/nixos/syncplay.nix similarity index 100% rename from systems/tewi/syncplay.nix rename to nixos/syncplay.nix diff --git a/nixos/vouch.nix b/nixos/vouch.nix new file mode 100644 index 00000000..3a913985 --- /dev/null +++ b/nixos/vouch.nix @@ -0,0 +1,26 @@ +{ + lib, + config, + ... +}: let + inherit (lib) mkDefault; + cfg = config.services.vouch-proxy; +in { + services.vouch-proxy = { + enable = mkDefault true; + domain = mkDefault "login.${config.networking.domain}"; + settings = { + vouch.cookie.secure = mkDefault false; + }; + enableSettingsSecrets = mkDefault true; + extraSettings = { + oauth.client_secret._secret = config.sops.secrets.vouch-client-secret.path; + vouch.jwt.secret._secret = config.sops.secrets.vouch-jwt.path; + }; + }; + + sops.secrets = { + vouch-jwt.owner = cfg.user; + vouch-client-secret.owner = cfg.user; + }; +} diff --git a/systems/tewi/zigbee2mqtt.nix b/nixos/zigbee2mqtt.nix similarity index 67% rename from systems/tewi/zigbee2mqtt.nix rename to nixos/zigbee2mqtt.nix index e679b24f..dea4e7c0 100644 --- a/systems/tewi/zigbee2mqtt.nix +++ b/nixos/zigbee2mqtt.nix @@ -2,28 +2,27 @@ config, lib, ... -}: { - networking.firewall.allowedTCPPorts = [ - # Zigbee2MQTT Frontend - 8072 - ]; - +}: let + cfg = config.services.zigbee2mqtt; + inherit (lib) mkDefault; +in { sops.secrets.z2m-secret = { owner = "zigbee2mqtt"; - path = "${config.services.zigbee2mqtt.dataDir}/secret.yaml"; + path = "${cfg.dataDir}/secret.yaml"; }; - users.groups.input.members = ["zigbee2mqtt"]; + users.groups.input.members = [ "zigbee2mqtt" ]; services.zigbee2mqtt = { - enable = true; + enable = mkDefault true; + openFirewall = mkDefault true; + domain = mkDefault "z2m.${config.networking.domain}"; settings = { advanced = { log_level = "info"; network_key = "!secret network_key"; }; mqtt = { - server = "mqtt://127.0.0.1:1883"; user = "z2m"; password = "!secret z2m_pass"; }; diff --git a/systems/tewi/androidtvremote2.nix b/packages/androidtvremote2.nix similarity index 100% rename from systems/tewi/androidtvremote2.nix rename to packages/androidtvremote2.nix diff --git a/systems/tewi/access.nix b/systems/tewi/access.nix deleted file mode 100644 index 322f7e8e..00000000 --- a/systems/tewi/access.nix +++ /dev/null @@ -1,99 +0,0 @@ -{ - config, - lib, - meta, - pkgs, - ... -}: -with lib; { - services.nginx.virtualHosts = mkMerge [ - (mkIf (config.networking.hostName == "tewi") { - "gensokyo.zone" = { - locations."/" = { - root = pkgs.gensokyoZone; - }; - }; - "z2m.gensokyo.zone" = { - extraConfig = '' - auth_request /validate; - error_page 401 = @error401; - ''; - locations = { - "/" = { - proxyPass = "http://127.0.0.1:8072"; - extraConfig = '' - add_header Access-Control-Allow-Origin https://login.gensokyo.zone; - add_header Access-Control-Allow-Origin https://id.gensokyo.zone; - proxy_set_header X-Vouch-User $auth_resp_x_vouch_user; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_http_version 1.1; - ''; - }; - "@error401" = { - extraConfig = '' - return 302 https://login.gensokyo.zone/login?url=$scheme://$http_host$request_uri&vouch-failcount=$auth_resp_failcount&X-Vouch-Token=$auth_resp_jwt&error=$auth_resp_err; - ''; - }; - "/validate" = { - recommendedProxySettings = false; - proxyPass = "http://127.0.0.1:30746/validate"; - extraConfig = '' - proxy_set_header Host $host; - proxy_pass_request_body off; - proxy_set_header Content-Length ""; - auth_request_set $auth_resp_x_vouch_user $upstream_http_x_vouch_user; - auth_request_set $auth_resp_jwt $upstream_http_x_vouch_jwt; - auth_request_set $auth_resp_err $upstream_http_x_vouch_err; - auth_request_set $auth_resp_failcount $upstream_http_x_vouch_failcount; - ''; - }; - }; - }; - }) - (mkIf (config.networking.hostName != "tewi") { - "home.${config.networking.domain}" = { - locations = { - "/" = { - proxyPass = meta.tailnet.yukari.pp 4 8123; - extraConfig = '' - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_http_version 1.1; - ''; - }; - }; - }; - "cloud.kittywit.ch" = { - locations = { - "/".proxyPass = meta.tailnet.yukari.ppp 4 80 "nextcloud/"; - }; - }; - "plex.kittywit.ch" = { - locations = { - "/" = { - proxyPass = meta.tailnet.yukari.pp 4 32400; - extraConfig = '' - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - proxy_redirect off; - proxy_buffering off; - proxy_set_header X-Plex-Client-Identifier $http_x_plex_client_identifier; - proxy_set_header X-Plex-Device $http_x_plex_device; - proxy_set_header X-Plex-Device-Name $http_x_plex_device_name; - proxy_set_header X-Plex-Platform $http_x_plex_platform; - proxy_set_header X-Plex-Platform-Version $http_x_plex_platform_version; - proxy_set_header X-Plex-Product $http_x_plex_product; - proxy_set_header X-Plex-Token $http_x_plex_token; - proxy_set_header X-Plex-Version $http_x_plex_version; - proxy_set_header X-Plex-Nocache $http_x_plex_nocache; - proxy_set_header X-Plex-Provides $http_x_plex_provides; - proxy_set_header X-Plex-Device-Vendor $http_x_plex_device_vendor; - proxy_set_header X-Plex-Model $http_x_plex_model; - ''; - }; - }; - }; - }) - ]; -} diff --git a/systems/tewi/cloudflared.nix b/systems/tewi/cloudflared.nix index 06b7f848..2951240f 100644 --- a/systems/tewi/cloudflared.nix +++ b/systems/tewi/cloudflared.nix @@ -1,75 +1,31 @@ -{ pkgs, config, utils, lib, ... }: let - inherit (lib) mapAttrsToList mapAttrs' nameValuePair splitString last singleton - mkIf mkMerge mkForce; +{ config, lib, ... }: let inherit (config) services; - inherit (services.kanidm.serverSettings) domain; - cfg = services.cloudflared; apartment = "131222b0-9db0-4168-96f5-7d45ec51c3be"; - shadowTunnel = { - ${apartment}.ingress.deluge = { - hostname._secret = config.sops.secrets.cloudflared-tunnel-apartment-deluge.path; - service = "http://localhost:${toString services.deluge.web.port}"; - }; - }; in { sops.secrets.cloudflared-tunnel-apartment.owner = services.cloudflared.user; sops.secrets.cloudflared-tunnel-apartment-deluge.owner = services.cloudflared.user; services.cloudflared = { - enable = true; tunnels = { ${apartment} = { credentialsFile = config.sops.secrets.cloudflared-tunnel-apartment.path; default = "http_status:404"; - ingress = mapAttrs' (prefix: nameValuePair "${prefix}${domain}") { - "".service = "http://localhost:80"; - "home.".service = "http://localhost:${toString services.home-assistant.config.http.server_port}"; - "z2m.".service = "http://localhost:80"; - "login.".service = "http://localhost:${toString services.vouch-proxy.settings.vouch.port}"; - "id." = let - inherit (services.kanidm.serverSettings) bindaddress; - port = last (splitString ":" bindaddress); - in { - service = "https://127.0.0.1:${port}"; + ingress = { + ${config.networking.domain}.service = "http://localhost:80"; + ${services.home-assistant.domain}.service = "http://localhost:${toString services.home-assistant.config.http.server_port}"; + ${services.zigbee2mqtt.domain}.service = "http://localhost:80"; + ${services.vouch-proxy.domain}.service = "http://localhost:${toString services.vouch-proxy.settings.vouch.port}"; + ${services.kanidm.server.frontend.domain} = { + service = "https://127.0.0.1:${toString services.kanidm.server.frontend.port}"; originRequest.noTLSVerify = true; }; }; + extraTunnel.ingress = { + deluge = { + hostname._secret = config.sops.secrets.cloudflared-tunnel-apartment-deluge.path; + service = "http://localhost:${toString services.deluge.web.port}"; + }; + }; }; }; }; - systemd.services = let - filterConfig = lib.filterAttrsRecursive (_: v: ! builtins.elem v [ null [ ] { } ]); - mapIngress = hostname: ingress: { - inherit hostname; - } // filterConfig (filterConfig ingress); - in mkIf cfg.enable (mapAttrs' (uuid: tunnel: let - RuntimeDirectory = "cloudflared-tunnel-${uuid}"; - configPath = "/run/${RuntimeDirectory}/config.yml"; - settings = { - tunnel = uuid; - credentials-file = tunnel.credentialsFile; - ingress = mapAttrsToList mapIngress tunnel.ingress - ++ mapAttrsToList mapIngress shadowTunnel.${uuid}.ingress or { } - ++ singleton { service = tunnel.default; }; - }; - in nameValuePair "cloudflared-tunnel-${uuid}" (mkMerge [ - { - after = [ "tailscale-autoconnect.service" ]; - serviceConfig = { - RestartSec = 10; - }; - } - (mkIf (shadowTunnel.${uuid} or { } != { }) { - serviceConfig = { - inherit RuntimeDirectory; - ExecStart = mkForce [ - "${cfg.package}/bin/cloudflared tunnel --config=${configPath} --no-autoupdate run" - ]; - ExecStartPre = [ - (pkgs.writeShellScript "cloudflared-tunnel-${uuid}-prepare" '' - ${utils.genJqSecretsReplacementSnippet settings configPath} - '') - ]; - }; - }) - ])) cfg.tunnels); } diff --git a/systems/tewi/deluge.nix b/systems/tewi/deluge.nix index 312072e3..1a94a6f5 100644 --- a/systems/tewi/deluge.nix +++ b/systems/tewi/deluge.nix @@ -4,35 +4,12 @@ shadowDir = "/mnt/shadow"; mediaDir = "${shadowDir}/deluge"; in { - sops.secrets.deluge-auth = { - inherit (cfg) group; - owner = cfg.user; - }; services.deluge = { - enable = true; - declarative = true; - openFirewall = true; - web = { - enable = true; - }; config = { download_location = "${mediaDir}/download"; move_completed_path = "${mediaDir}/complete"; move_completed = true; - max_upload_speed = 10.0; - #share_ratio_limit = 2.0; - max_connections_global = 1024; - max_connections_per_second = 50; - max_active_limit = 100; - max_active_downloading = 75; - max_upload_slots_global = 25; - max_active_seeding = 1; - allow_remote = true; - daemon_port = 58846; - listen_ports = [ 6881 6889 ]; - random_port = false; }; - authFile = config.sops.secrets.deluge-auth.path; }; systemd.services = { deluged = { diff --git a/systems/tewi/home-assistant.nix b/systems/tewi/home-assistant.nix deleted file mode 100644 index 2714a1a2..00000000 --- a/systems/tewi/home-assistant.nix +++ /dev/null @@ -1,260 +0,0 @@ -{ - pkgs, - config, - lib, - ... -}: let - cfg = config.services.home-assistant; - inherit (lib.attrsets) attrNames filterAttrs mapAttrs' nameValuePair; - inherit (lib.strings) hasPrefix; -in { - # MDNS - services.avahi.enable = true; - - networking.firewall.allowedTCPPorts = [ - # Home Assistant - cfg.config.http.server_port - ] ++ map ({ port, ... }: port) cfg.config.homekit; - networking.firewall.allowedUDPPorts = [ - # MDNS - 5353 - ]; - - networking.firewall.allowedUDPPortRanges = [ - # Chromecast - { - from = 32768; - to = 60999; - } - ]; - - sops.secrets = { - ha-integration = { - owner = "hass"; - path = "${cfg.configDir}/integration.yaml"; - }; - ha-secrets = { - owner = "hass"; - path = "${cfg.configDir}/secrets.yaml"; - }; - }; - - systemd.services.home-assistant = { - # UI-editable config files - preStart = lib.mkBefore '' - touch ${cfg.configDir}/{automations,scenes,scripts,manual,homekit_entity_config,homekit_include_entities}.yaml - ''; - }; - - services.home-assistant = { - enable = true; - config = { - homeassistant = { - name = "Gensokyo"; - unit_system = "metric"; - latitude = "!secret home_lat"; - longitude = "!secret home_long"; - elevation = "!secret home_asl"; - currency = "CAD"; - country = "CA"; - time_zone = "America/Vancouver"; - external_url = "https://home.gensokyo.zone"; - packages = { - manual = "!include manual.yaml"; - }; - }; - frontend = { - themes = "!include_dir_merge_named themes"; - }; - powercalc = { - }; - utility_meter = { - }; - withings = { - use_webhook = true; - }; - logger = { - default = "info"; - }; - http = { - cors_allowed_origins = [ - "https://google.com" - "https://www.home-assistant.io" - ]; - use_x_forwarded_for = "true"; - trusted_proxies = [ - "127.0.0.0/24" - "200::/7" - "100.64.0.0/10" - "fd7a:115c:a1e0:ab12::/64" - "::1" - ]; - }; - recorder = { - db_url = "postgresql://@/hass"; - auto_purge = true; - purge_keep_days = 14; - commit_interval = 1; - exclude = { - domains = [ - "automation" - "updater" - ]; - entity_globs = [ - "sensor.weather_*" - "sensor.date_*" - ]; - entities = [ - "sun.sun" - "sensor.last_boot" - "sensor.date" - "sensor.time" - ]; - event_types = [ - "call_service" - ]; - }; - }; - google_assistant = { - project_id = "gensokyo-5cfaf"; - service_account = "!include integration.yaml"; - report_state = true; - exposed_domains = [ - "scene" - "script" - "climate" - #"sensor" - ]; - entity_config = {}; - }; - homekit = [ { - name = "Tewi"; - port = 21063; - ip_address = "10.1.1.38"; - filter = let - inherit (cfg.config) google_assistant; - in { - include_domains = google_assistant.exposed_domains; - include_entities = "!include homekit_include_entities.yaml"; - }; - entity_config = "!include homekit_entity_config.yaml"; - } ]; - tts = [ - { - platform = "google_translate"; - service_name = "google_say"; - } - ]; - # https://nixos.wiki/wiki/Home_Assistant#Combine_declarative_and_UI_defined_automations - "automation manual" = []; - "automation ui" = "!include automations.yaml"; - # https://nixos.wiki/wiki/Home_Assistant#Combine_declarative_and_UI_defined_scenes - "scene manual" = []; - "scene ui" = "!include scenes.yaml"; - "script manual" = []; - "script ui" = "!include scripts.yaml"; - counter = {}; - device_tracker = {}; - energy = {}; - group = {}; - history = {}; - input_boolean = {}; - input_button = {}; - input_datetime = {}; - input_number = {}; - input_select = {}; - input_text = {}; - logbook = {}; - schedule = {}; - map = {}; - media_source = {}; - media_player = [ - { - platform = "mpd"; - name = "Shanghai MPD"; - host = "10.1.1.32"; - password = "!secret mpd-shanghai-password"; - } - ]; - mobile_app = {}; - my = {}; - person = {}; - prometheus = {}; - ssdp = {}; - switch = {}; - stream = {}; - sun = {}; - system_health = {}; - tag = {}; - template = {}; - timer = {}; - webhook = {}; - wake_on_lan = {}; - zeroconf = {}; - zone = {}; - sensor = {}; - }; - package = let - inherit (lib) warn versionOlder elem; - inherit (cfg.package) python; - hasBrother = elem "brother" cfg.extraComponents; - # https://github.com/pysnmp/pysnmp/issues/51 - needsPyasn1pin = if versionOlder python.pkgs.pysnmplib.version "6.0" - then true - else warn "pyasn1 pin likely no longer needed" false; - pyasn1prefix = "${python.pkgs.pysnmp-pyasn1}/${python.sitePackages}"; - home-assistant = pkgs.home-assistant.override { - packageOverrides = self: super: { - }; - }; - in home-assistant.overrideAttrs (old: { - makeWrapperArgs = old.makeWrapperArgs ++ lib.optional (hasBrother && needsPyasn1pin) "--prefix PYTHONPATH : ${pyasn1prefix}"; - disabledTests = old.disabledTests or [ ] ++ [ - "test_check_config" - ]; - }); - extraPackages = python3Packages: - with python3Packages; [ - psycopg2 - aiohomekit - securetar - getmac # for upnp integration - python-otbr-api - protobuf3 - adb-shell - (callPackage ./androidtvremote2.nix { }) - (aiogithubapi.overrideAttrs (_: {doInstallCheck = false;})) - ]; - extraComponents = [ - "automation" - "scene" - "script" - "zha" - "esphome" - "apple_tv" - "spotify" - "default_config" - "brother" - "ipp" - "androidtv" - "cast" - "plex" - "environment_canada" - "met" - "generic_thermostat" - "google" - "google_assistant" - "google_cloud" - "google_translate" - "homekit" - "mpd" - "mqtt" - "shopping_list" - "tile" - "wake_on_lan" - "withings" - "wled" - "zeroconf" - ]; - }; -} diff --git a/systems/tewi/kanidm.nix b/systems/tewi/kanidm.nix deleted file mode 100644 index b162bc8f..00000000 --- a/systems/tewi/kanidm.nix +++ /dev/null @@ -1,48 +0,0 @@ -{ - pkgs, - config, - ... -}: let - conf = import ./snakeoil-certs.nix; - domain = conf.domain; - unencryptedCert = with pkgs; - runCommand "kanidm-cert" { - domain = "id.gensokyo.zone"; - nativeBuildInputs = [minica]; - } '' - install -d $out - cd $out - minica \ - --ca-key ca.key.pem \ - --ca-cert ca.cert.pem \ - --domains $domain - cat $domain/cert.pem ca.cert.pem > $domain.pem - ''; -in { - networking.firewall.allowedTCPPorts = [ - 8081 - 636 - ]; - - services.kanidm = { - enableServer = true; - enablePam = false; - enableClient = true; - clientSettings = { - uri = "https://id.gensokyo.zone"; - verify_ca = true; - verify_hostnames = true; - }; - serverSettings = { - domain = "gensokyo.zone"; - origin = "https://id.gensokyo.zone"; - role = "WriteReplica"; - log_level = "info"; - db_fs_type = "zfs"; - bindaddress = "0.0.0.0:8081"; - ldapbindaddress = "0.0.0.0:636"; - tls_chain = "${unencryptedCert}/${unencryptedCert.domain}.pem"; - tls_key = "${unencryptedCert}/${unencryptedCert.domain}/key.pem"; - }; - }; -} diff --git a/systems/tewi/nixos.nix b/systems/tewi/nixos.nix index 9c81d640..feb87e7e 100644 --- a/systems/tewi/nixos.nix +++ b/systems/tewi/nixos.nix @@ -43,16 +43,19 @@ in { (modulesPath + "/installer/scan/not-detected.nix") nixos.sops nixos.tailscale + nixos.cloudflared + nixos.nginx + nixos.access.gensokyo + nixos.access.zigbee2mqtt + nixos.postgres + nixos.vouch + nixos.kanidm + nixos.mosquitto + nixos.zigbee2mqtt + nixos.deluge + nixos.syncplay + nixos.home-assistant inputs.systemd2mqtt.nixosModules.default - ./access.nix - ./syncplay.nix - ./kanidm.nix - ./vouch.nix - ./home-assistant.nix - ./zigbee2mqtt.nix - ./mosquitto.nix - ./postgres.nix - ./nginx.nix ./mediatomb.nix ./deluge.nix ./cloudflared.nix @@ -65,6 +68,7 @@ in { ''; services.cockroachdb.locality = "provider=local,network=gensokyo,host=${config.networking.hostName}"; + services.kanidm.serverSettings.db_fs_type = "zfs"; sops.defaultSopsFile = ./secrets.yaml; diff --git a/systems/tewi/vouch.nix b/systems/tewi/vouch.nix deleted file mode 100644 index 05f7e54e..00000000 --- a/systems/tewi/vouch.nix +++ /dev/null @@ -1,121 +0,0 @@ -{ - config, - utils, - pkgs, - lib, - ... -}: { - options = with lib; let - origin = "https://id.gensokyo.zone"; - in { - services.vouch-proxy = { - settings = { - vouch = { - cookie = { - domain = mkOption { - type = types.nullOr types.str; - default = "gensokyo.zone"; - }; - secure = mkOption { - type = types.bool; - default = true; - }; - }; - port = mkOption { - type = lib.types.port; - default = 30746; - }; - listen = mkOption { - type = types.nullOr types.str; - default = "127.0.0.1"; - }; - allowAllUsers = mkOption { - type = types.bool; - default = true; - }; - }; - oauth = { - auth_url = mkOption { - type = types.str; - default = "${origin}/ui/oauth2"; - }; - token_url = mkOption { - type = types.str; - default = "${origin}/oauth2/token"; - }; - user_info_url = mkOption { - type = types.str; - default = "${origin}/oauth2/openid/vouch/userinfo"; - }; - scopes = mkOption { - type = types.listOf types.str; - default = ["openid" "email" "profile"]; - }; - callback_url = mkOption { - type = types.str; - default = "https://login.gensokyo.zone/auth"; - }; - provider = mkOption { - type = types.nullOr types.str; - default = "oidc"; - }; - code_challenge_method = mkOption { - type = types.str; - default = "S256"; - }; - client_id = mkOption { - type = types.str; - default = "vouch"; - }; - }; - }; - }; - }; - config = { - services.vouch-proxy.settings = { - vouch.cookie.secure = false; - }; - - sops.secrets = { - vouch-jwt.owner = "vouch-proxy"; - vouch-client-secret.owner = "vouch-proxy"; - }; - - systemd.services.vouch-proxy = { - description = "Vouch-proxy"; - after = ["network.target"]; - wantedBy = ["multi-user.target"]; - serviceConfig = { - ExecStart = let - recursiveMergeAttrs = listOfAttrsets: lib.fold (attrset: acc: lib.recursiveUpdate attrset acc) {} listOfAttrsets; - settings = recursiveMergeAttrs [ - config.services.vouch-proxy.settings - { - oauth.client_secret._secret = config.sops.secrets.vouch-client-secret.path; - vouch.jwt.secret._secret = config.sops.secrets.vouch-jwt.path; - } - ]; - in - pkgs.writeShellScript "vouch-proxy-start" '' - ${utils.genJqSecretsReplacementSnippet settings "/run/vouch-proxy/vouch-config.json"} - ${pkgs.vouch-proxy}/bin/vouch-proxy -config /run/vouch-proxy/vouch-config.json - ''; - Restart = "on-failure"; - RestartSec = 5; - WorkingDirectory = "/var/lib/vouch-proxy"; - StateDirectory = "vouch-proxy"; - RuntimeDirectory = "vouch-proxy"; - User = "vouch-proxy"; - Group = "vouch-proxy"; - StartLimitBurst = 3; - }; - }; - - users.users.vouch-proxy = { - isSystemUser = true; - group = "vouch-proxy"; - }; - - users.groups.vouch-proxy = {}; - }; -}