diff --git a/hardware/oracle/common.nix b/hardware/oracle/common.nix index aaf16373..f5c1bdb0 100644 --- a/hardware/oracle/common.nix +++ b/hardware/oracle/common.nix @@ -116,6 +116,7 @@ in connection = tf.resources."${config.networking.hostName}".connection.set; }; connection = { + port = lib.head config.services.openssh.ports; }; }; diff --git a/home/gui/nextcloud.nix b/home/gui/nextcloud.nix new file mode 100644 index 00000000..25779ef6 --- /dev/null +++ b/home/gui/nextcloud.nix @@ -0,0 +1,6 @@ +{ config, ... }: { + services = { + nextcloud-client.enable = true; + gnome-keyring.enable = true; + }; +} diff --git a/meta.nix b/meta.nix index f4a50db3..064a0585 100644 --- a/meta.nix +++ b/meta.nix @@ -1,4 +1,45 @@ -{ config, pkgs, lib, root, ... }: { +{ config, pkgs, lib, root, ... }: with lib; let + home = config.deploy.targets.home.tf; +in { + options = { + tailnet_uri = mkOption { + type = types.str; + }; + tailnet = mkOption { + type = types.attrsOf (types.submodule ({ name, ... }: { + options = { + addresses = { + ipv4 = mkOption { + type = types.str; + }; + ipv6 = mkOption { + type = types.str; + }; + }; + tags = mkOption { + type = types.listOf types.str; + }; + }; + })); + }; + }; + config = { + + tailnet_uri = "inskip.me"; + tailnet = let + raw = home.resources.tailnet_devices.importAttr "devices"; + devices = mapListToAttrs (elet: nameValuePair (removeSuffix ".${config.tailnet_uri}" elet.name) { + tags = elet.tags; + addresses = let + addresses = elet.addresses; + ipv4 = head (filter (e: hasInfix "." e) addresses); + ipv6 = head (filter (e: hasInfix ":" e) addresses); + in { + inherit ipv4 ipv6; + }; + }) raw; + in devices; + runners = { lazy = { file = ./default.nix; @@ -14,4 +55,5 @@ deploy.targets.dummy.enable = false; _module.args.pkgs = lib.mkDefault pkgs; +}; } diff --git a/nixos/gui/nextcloud.nix b/nixos/gui/nextcloud.nix new file mode 100644 index 00000000..a8ff9653 --- /dev/null +++ b/nixos/gui/nextcloud.nix @@ -0,0 +1,7 @@ +{ config, ... }: { + services.gnome = { + gnome-keyring.enable = true; + }; + security.pam.services.lightdm.enableGnomeKeyring = true; + programs.seahorse.enable = true; +} diff --git a/nixos/network.nix b/nixos/network.nix index 5806b922..1e90dc08 100644 --- a/nixos/network.nix +++ b/nixos/network.nix @@ -1,10 +1,11 @@ -{ config, lib, tf, pkgs, meta, ... }: with lib; - -{ - options.network = with lib; { - routeDefault = mkOption { - default = true; - type = types.bool; +{ config, lib, tf, pkgs, meta, ... }: with lib; let +in { + options = with lib; { + network = { + routeDefault = mkOption { + default = true; + type = types.bool; + }; }; }; @@ -31,12 +32,29 @@ }; }; - kw.secrets.variables.tailscale-authkey = { - path = "secrets/tailscale"; - field = "password"; + deploy.tf = { + variables.tailscale-apikey = { + value.shellCommand = "${meta.kw.secrets.command} secrets/tailscale -f api_key"; + sensitive = true; + export = true; + }; + providers.tailscale = { + inputs = { + api_key = tf.variables.tailscale-apikey.ref; + tailnet = "inskip.me"; + }; + }; + variables.tailscale-authkey.export = true; + resources.tailnet_key = { + provider = "tailscale"; + type = "tailnet_key"; + inputs = { + reusable = false; + ephemeral = false; + preauthorized = true; + }; + }; }; - - deploy.tf.variables.tailscale-authkey.export = true; networking.firewall = { trustedInterfaces = [ "tailscale0" ]; @@ -71,7 +89,7 @@ fi # otherwise authenticate with tailscale - ${tailscale}/bin/tailscale up -authkey ${tf.variables.tailscale-authkey.get} + ${tailscale}/bin/tailscale up -authkey ${tf.resources.tailnet_key.getAttr "key"} ''; }; }; diff --git a/nixos/systems/tewi/home-assistant.nix b/nixos/systems/tewi/home-assistant.nix index 97af3ecf..edbdd679 100644 --- a/nixos/systems/tewi/home-assistant.nix +++ b/nixos/systems/tewi/home-assistant.nix @@ -1,107 +1,83 @@ -{ config, lib, ... }: { +{ config, lib, tf, ... }: { + kw.secrets.variables.ha-integration = { + path = "secrets/home-assistant"; + field = "notes"; + }; + + secrets.files.ha-integration = { + text = tf.variables.ha-integration.ref; + owner = "hass"; + group = "hass"; + }; + + systemd.services.home-assistant = { + preStart = lib.mkBefore '' + rm ${config.services.home-assistant.configDir}/integration.json + cp --no-preserve=mode ${config.secrets.files.ha-integration.path} ${config.services.home-assistant.configDir}/integration.json + ''; + }; + services.home-assistant = { enable = true; config = { - automation = "automations.yaml"; - config = null; - counter = null; - device_tracker = null; - dhcp = null; - energy = null; - frontend = { themes = "themes"; }; - google_assistant = null; - group = "groups.yaml"; - history = null; - homeassistant = { - external_url = "https://home.gensokyo.zone"; - packages = "packages"; + default_config = {}; + google_assistant = { + project_id = "gensokyo-5cfaf"; + service_account = "!include integration.json"; }; 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" ]; - use_x_forwarded_for = true; }; - image = null; - input_boolean = null; - input_datetime = null; - input_number = null; - input_select = null; - input_text = null; - logbook = null; + + homeassistant = { + name = "Gensokyo"; + unit_system = "metric"; + external_url = "https://home.gensokyo.zone"; + }; logger = { default = "info"; }; - device_tracker = null; - map = null; - media_source = null; - mobile_app = null; - my = null; - person = null; recorder = { - auto_purge = true; - commit_interval = 1; - exclude = { - domains = [ - "automation" - "updater" - ]; - entities = [ - "sun.sun" - "sensor.last_boot" - "sensor.date" - "sensor.time" - ]; - entity_globs = [ - "sensor.weather_*" - "sensor.date_*" - ]; - event_types = [ - "call_service" - ]; - }; - purge_keep_days = 14; + db_url = "postgresql://@/hass"; + + }; + homekit = { + name = "Tewi"; + port = 21063; + ip_address = "10.1.1.38"; }; - scene = "scenes.yaml"; - script = "scripts.yaml"; - ssdp = null; - stream = null; - sun = null; - switch = null; - system_health = null; - tag = null; - template = null; - timer = null; - tts = [{ - platform = "google_translate"; - service_name = "google_say"; - }]; - wake_on_lan = null; - webhook = null; - zeroconf = null; - zone = null; }; + extraPackages = python3Packages: with python3Packages; [ + psycopg2 + securetar + ]; extraComponents = [ "zha" - "esphome" - "apple_tv" - "spotify" - "default_config" - "cast" - "plex" - "google" - "google_assistant" - "google_cloud" - "google_translate" - "homekit" - "mqtt" - "wake_on_lan" - "zeroconf" + "esphome" + "apple_tv" + "spotify" + "default_config" + "cast" + "plex" + "met" + "google" + "google_assistant" + "google_cloud" + "google_translate" + "homekit" + "mqtt" + "wake_on_lan" + "zeroconf" ]; }; -} + } diff --git a/nixos/systems/tewi/mosquitto.nix b/nixos/systems/tewi/mosquitto.nix index 440198b6..207b1d7a 100644 --- a/nixos/systems/tewi/mosquitto.nix +++ b/nixos/systems/tewi/mosquitto.nix @@ -4,12 +4,23 @@ field = "z2m"; }; + kw.secrets.variables.hass-pass = { + path = "secrets/mosquitto"; + field = "hass"; + }; + secrets.files.z2m-pass = { text = tf.variables.z2m-pass.ref; owner = "mosquitto"; group = "mosquitto"; }; + secrets.files.hass-pass = { + text = tf.variables.hass-pass.ref; + owner = "mosquitto"; + group = "mosquitto"; + }; + services.mosquitto = { enable = true; persistence = true; @@ -18,10 +29,16 @@ "pattern readwrite #" ]; users = { + hass = { + passwordFile = config.secrets.files.hass-pass.path; + acl = [ + "readwrite #" + ]; + }; z2m = { passwordFile = config.secrets.files.z2m-pass.path; acl = [ - "topic readwrite zigbee2mqtt/#" + "readwrite #" ]; }; }; diff --git a/nixos/systems/tewi/nginx.nix b/nixos/systems/tewi/nginx.nix new file mode 100644 index 00000000..cf5532cd --- /dev/null +++ b/nixos/systems/tewi/nginx.nix @@ -0,0 +1,69 @@ +{ config, lib, pkgs, tf, ... }: + +with lib; + +{ + secrets.files.dns_creds = { + text = '' + RFC2136_NAMESERVER='${tf.variables.katdns-address.ref}' + RFC2136_TSIG_ALGORITHM='hmac-sha512.' + RFC2136_TSIG_KEY='${tf.variables.katdns-name.ref}' + RFC2136_TSIG_SECRET='${tf.variables.katdns-key.ref}' + ''; + }; + + network.firewall = { + public.tcp.ports = [ 443 80 ]; + private.tcp.ports = [ 443 80 ]; + }; + + services.nginx = { + enable = true; + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + commonHttpConfig = '' + map $scheme $hsts_header { + https "max-age=31536000; includeSubdomains; preload"; + } + add_header Strict-Transport-Security $hsts_header; + #add_header Content-Security-Policy "script-src 'self'; object-src 'none'; base-uri 'none';" always; + add_header 'Referrer-Policy' 'origin-when-cross-origin'; + #add_header X-Frame-Options DENY; + #add_header X-Content-Type-Options nosniff; + #add_header X-XSS-Protection "1; mode=block"; + #proxy_cookie_path / "/; secure; HttpOnly; SameSite=strict"; + ''; + clientMaxBodySize = "512m"; + virtualHosts = { + "gensokyo.zone" = { + forceSSL = true; + enableACME = true; + locations."/" = { + root = pkgs.gensokyoZone; + }; + }; + "home.${config.network.dns.domain}" = { + forceSSL = true; + enableACME = true; + locations = { + "/" = { + proxyPass = "http://127.0.0.1:8123"; + extraConfig = '' + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_http_version 1.1; + ''; + }; + }; + }; + }; + }; + + security.acme = { + defaults.email = config.network.dns.email; + #email = config.network.dns.email; + acceptTerms = true; + }; +} diff --git a/nixos/systems/tewi/nixos.nix b/nixos/systems/tewi/nixos.nix index 99b660c2..b9f5c733 100644 --- a/nixos/systems/tewi/nixos.nix +++ b/nixos/systems/tewi/nixos.nix @@ -1,11 +1,13 @@ { meta, config, lib, pkgs, modulesPath, ... }: { - imports = [ + imports = with meta; [ (modulesPath + "/installer/scan/not-detected.nix") + nixos.network ./home-assistant.nix ./zigbee2mqtt.nix ./mosquitto.nix + ./postgres.nix ]; deploy.tf = { @@ -29,6 +31,9 @@ }; network = { + firewall = { + public.interfaces = lib.singleton "eno1"; + }; addresses = { private = { enable = true; diff --git a/nixos/systems/tewi/postgres.nix b/nixos/systems/tewi/postgres.nix new file mode 100644 index 00000000..6e6da262 --- /dev/null +++ b/nixos/systems/tewi/postgres.nix @@ -0,0 +1,13 @@ +{ config, pkgs, ... }: { + services.postgresql = { + enable = true; + package = pkgs.postgresql_14; + ensureDatabases = [ "hass" ]; + ensureUsers = [{ + name = "hass"; + ensurePermissions = { + "DATABASE hass" = "ALL PRIVILEGES"; + }; + }]; + }; +} diff --git a/nixos/systems/tewi/zigbee2mqtt.nix b/nixos/systems/tewi/zigbee2mqtt.nix index ae539567..a13af876 100644 --- a/nixos/systems/tewi/zigbee2mqtt.nix +++ b/nixos/systems/tewi/zigbee2mqtt.nix @@ -6,8 +6,13 @@ log_level = "info"; network_key = "!secret network_key"; }; + mqtt = { + server = "mqtt://127.0.0.1:1883"; + user = "z2m"; + password = tf.variables.z2m-mqtt-password.ref; + }; homeassistant = true; - permit_join = true; + permit_join = false; frontend = { port = 8072; }; @@ -18,6 +23,11 @@ }; }; +kw.secrets.variables.z2m-mqtt-password = { + path = "secrets/mosquitto"; + field = "z2m"; +}; + kw.secrets.variables.z2m-network-key = { path = "secrets/zigbee2mqtt"; field = "password"; @@ -40,6 +50,6 @@ cp --no-preserve=mode ${config.secrets.files.zigbee2mqtt-secret.path} "${cfg.dataDir}/secret.yaml" ''; - network.firewall.public.tcp.ports = [ 8123 8072 1883 ]; + network.firewall.public.tcp.ports = [ 8123 8072 1883 21064 21063 ]; network.firewall.private.tcp.ports = [ 8123 ]; } diff --git a/overlays/local/default.nix b/overlays/local/default.nix index e22ac4b3..84b73b4d 100644 --- a/overlays/local/default.nix +++ b/overlays/local/default.nix @@ -14,4 +14,15 @@ final: prev: { wezterm = final.callPackage ./wezterm { inherit (final.darwin.apple_sdk.frameworks) Cocoa CoreGraphics Foundation UserNotifications; }; + terraform-providers = prev.terraform-providers // { + tailscale = final.terraform-providers.mkProvider rec { + owner = "tailscale"; + provider-source-address = "registry.terraform.io/${owner}/${owner}"; + repo = "terraform-provider-tailscale"; + rev = "v${version}"; + sha256 = "sha256-/qC8TOtoVoBTWeAFpt2TYE8tlYBCCcn/mzVQ/DN51YQ="; + vendorSha256 = "sha256-8EIxqKkVO706oejlvN79K8aEZAF5H2vZRdr5vbQa0l4="; + version = "0.13.5"; + }; +}; } diff --git a/services/access.nix b/services/access.nix index b3b06d4a..f22fbd24 100644 --- a/services/access.nix +++ b/services/access.nix @@ -5,7 +5,6 @@ cname = { inherit (config.network.addresses.public) target; }; }; - deploy.tf.dns.records.services_cloud = { inherit (config.network.dns) zone; domain = "cloud"; @@ -18,6 +17,12 @@ cname = { inherit (config.network.addresses.public) target; }; }; + deploy.tf.dns.records.gensokyo_home = { + zone = "gensokyo.zone."; + domain = "home"; + cname = { inherit (config.network.addresses.public) target; }; + }; + deploy.tf.dns.records.gensokyo_root_v4 = { zone = "gensokyo.zone."; a = { inherit (config.network.addresses.public.tf.ipv4) address; }; @@ -37,6 +42,20 @@ root = pkgs.gensokyoZone; }; }; + "home.gensokyo.zone" = { + forceSSL = true; + enableACME = true; + locations = { + "/" = { + proxyPass = "http://${meta.tailnet.tewi.addresses.ipv4}:8123"; + extraConfig = '' + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_http_version 1.1; + ''; + }; + }; + }; "home.${config.network.dns.domain}" = { forceSSL = true; enableACME = true; diff --git a/targets/home.nix b/targets/home.nix index 3f4a7efc..19872eee 100644 --- a/targets/home.nix +++ b/targets/home.nix @@ -1,21 +1,36 @@ { config, lib, ... }: with lib; { - deploy.targets.home = { + deploy.targets.home = let meta = config; in { tf = { config, ... }: { imports = optional (builtins.pathExists ../services/irlmail.nix) ../services/irlmail.nix; - dns.records.ygg_grimoire = { - zone = "kittywit.ch."; - domain = "grimoire.ygg"; - aaaa.address = "200:c87d:7960:916:bf0e:a0e1:3da7:4fc6"; - }; + variables.tailscale-apikey = { + value.shellCommand = "${meta.kw.secrets.command} secrets/tailscale -f api_key"; + sensitive = true; + export = true; + }; - dns.records.ygg_boline = { - zone = "kittywit.ch."; - domain = "boline.ygg"; - aaaa.address = "200:474d:14f7:1d21:f171:4e85:a3fa:9393"; + providers.tailscale = { + inputs = { + api_key = config.variables.tailscale-apikey.ref; + tailnet = "inskip.me"; + }; + }; + resources = { + tailnet_devices = { + type = "devices"; + provider = "tailscale"; + dataSource = true; + }; + tailnet_nr = { + provider = "null"; + type = "resource"; + inputs.triggers = { + mew = config.resources.tailnet_devices.refAttr "id"; + }; }; }; }; +}; } diff --git a/tf b/tf index 856827e2..58d25138 160000 --- a/tf +++ b/tf @@ -1 +1 @@ -Subproject commit 856827e23fd7f1ef1d07dea9c5be26c0a0f7dee8 +Subproject commit 58d25138fdd294512a311a0fe3c92007f8d55f7a