diff --git a/containers/home-assistant.nix b/containers/home-assistant.nix new file mode 100644 index 0000000..e2d3813 --- /dev/null +++ b/containers/home-assistant.nix @@ -0,0 +1,78 @@ +{ nglib, nixpkgs }: +nglib.makeSystem { + system = "x86_64-linux"; + name = "nixng-home-assistant"; + inherit nixpkgs; + config = ({ pkgs, config, nglib, ... }: + let + ids = config.ids; + in + { + config = { + dumb-init = { + enable = true; + type.services = { }; + }; + + init.services.home-assistant = { + shutdownOnExit = true; + }; + + services.home-assistant = { + enable = true; + envsubst = true; + customComponents = { + xiaomi_gateway3 = pkgs.fetchFromGitHub { + owner = "AlexxIT"; + repo = "XiaomiGateway3"; + rev = "v1.6.5"; + sha256 = "sha256-RSIJqsbgnktl7zNaxAKUoMjbkrJ1aJTej0vjlCgstJ8="; + } + "/custom_components/xiaomi_gateway3"; + }; + config = with { inherit (builtins) toString; }; + { default_config = {}; + http.server_port = "8123"; + logger.default = "info"; + homeassistant = + { name = "Home"; + latitude = "\${LATITUDE}"; + longitude = "\${LONGTITUDE}"; + elevation = "\${ELEVATION}"; + # currency = "EUR"; + unit_system = "metric"; + time_zone = "\${TIME_ZONE}"; + internal_url = "http://localhost:8123/"; + }; + frontend.themes = + { }; + }; + package = + (pkgs.home-assistant.override + { extraComponents = + [ "http" + "homeassistant" + "image" + "person" + "cloud" + "onboarding" + "frontend" + "safe_mode" + "met" + "zha" + "mobile_app" + "dhcp" + "logbook" + "history" + "ssdp" + "mqtt" + ]; + extraPackages = ps: with ps; + [ xmodem + ]; + }).overridePythonAttrs (old: + { doCheck = false; + }); + }; + }; + }); +} diff --git a/containers/mosquitto.nix b/containers/mosquitto.nix new file mode 100644 index 0000000..9691267 --- /dev/null +++ b/containers/mosquitto.nix @@ -0,0 +1,33 @@ +{ nglib, nixpkgs }: +nglib.makeSystem { + system = "x86_64-linux"; + name = "nixng-mosquitto"; + inherit nixpkgs; + config = ({ pkgs, config, nglib, ... }: + let + ids = config.ids; + in + { + config = { + dumb-init = { + enable = true; + type.services = { }; + }; + + init.services.mosquitto = { + shutdownOnExit = true; + }; + + services.mosquitto = { + enable = true; + config = { + listener = + ["1883 0.0.0.0" + ({ password_file = "/secrets/mqtt_password"; + }) + ]; + }; + }; + }; + }); + } diff --git a/containers/postfix.nix b/containers/postfix.nix new file mode 100644 index 0000000..f993e6d --- /dev/null +++ b/containers/postfix.nix @@ -0,0 +1,169 @@ +{ nglib, nixpkgs }: +nglib.makeSystem { + system = "x86_64-linux"; + name = "nixng-postfix"; + inherit nixpkgs; + config = ({ pkgs, config, ... }: + { + config = { + dumb-init = { + enable = true; + type.services = {}; + }; + init.services.postfix = { + shutdownOnExit = true; + }; + services.postfix = { + enable = true; + + masterConfig = { + pickup = { + type = "unix"; + private = "n"; + chroot = "n"; + wakeup = "60"; + maxproc = "1"; + command = "pickup"; + }; + cleanup = { type = "unix"; private = "n"; chroot = "n"; maxproc = "0"; + command = "cleanup"; }; + qmgr = { type = "unix"; private = "n"; chroot = "n"; wakeup = "300"; + maxproc = "1"; command = "qmgr"; }; + tlsmgr = { type = "unix"; wakeup = "1000?"; maxproc = 1; command = "tlsmgr"; }; + rewrite = { type = "unix"; chroot = "n"; command = "trivial-rewrite"; }; + bounce = { type = "unix"; chroot = "n"; maxproc = 0; command = "bounce"; }; + defer = { type = "unix"; chroot = "n"; maxproc = 0; command = "bounce"; }; + trace = { type = "unix"; chroot = "n"; maxproc = 0; command = "bounce"; }; + verify = { type = "unix"; chroot = "n"; maxproc = 1; command = "verify"; }; + flush = { type = "unix"; chroot = "n"; wakeup = "1000?"; maxproc = "0"; + command = "flush"; }; + proxymap = { type = "unix"; chroot = "n"; command = "proxymap"; }; + proxywrite = { type = "unix"; chroot = "n"; maxproc = "1"; + command = "proxymap"; }; + smtp = [ { type = "unix"; chroot = "n"; command = "smtp"; } + { type = "inet"; private = "n"; chroot = "n"; command = "smtpd"; } + ]; + relay = { type = "unix"; chroot = "n"; command = '' + smtp + -o syslog_name=postfix/$service_name + # -o smtp_helo_timeout=5 -o smtp_connect_timeout=5 + ''; }; + showq = { type = "unix"; private = "n"; chroot = "n"; command = "showq"; }; + error = { type = "unix"; chroot = "n"; command = "error"; }; + retry = { type = "unix"; chroot = "n"; command = "error"; }; + discard = { type = "unix"; chroot = "n"; command = "discard"; }; + local = { type = "unix"; unpriv = "n"; chroot = "n"; command = "local"; }; + virtual = { type = "unix"; unpriv = "n"; chroot = "n"; command = "virtual"; }; + lmtp = { type = "unix"; chroot = "n"; command = "lmtp"; }; + anvil = { type = "unix"; chroot = "n"; maxproc = 1; command = "anvil"; }; + scache = { type = "unix"; chroot = "n"; maxproc = 1; command = "scache"; }; + postlog = + { type = "unix-dgram"; private = "n"; chroot = "n"; maxproc = "1"; + command = "postlogd"; }; + }; + + mainConfig = { + smptd_banner = [ "$myhostname" "ESMTP" "$mail_name" "(Ubuntu)" ]; + biff = "no"; + + # delay_warning_time = "4h"; + + append_dot_mydomain = "no"; + + readme_directory = "no"; + + compatibility_level = 2; + + smtpd_tls_cert_file = "/etc/letsencrypt/live/example.com/fullchain.pem"; + smtpd_tls_key_file = "/etc/letsencrypt/live/example.com/privkey.pem"; + smtpd_use_tls = "yes"; + smtpd_tls_auth_only = "yes"; + smtp_tls_security_level = "may"; + smtpd_tls_security_level = "may"; + smtpd_sasl_security_options = [ "noanonymous" "noplaintext" ]; + smtpd_sasl_tls_security_options = "noanonymous"; + + smtpd_sasl_type = "dovecot"; + smtpd_sasl_path = "private/auth"; + smtpd_sals_auth_enable = "yes"; + + smtpd_helo_restrictions = + [ "permit_mynetworks" + "permit_sals_authenticated" + "reject_invalid_helo_hostname" + "reject_non_fqdn_helo_hostname" + ]; + smtpd_recipient_restrictions = + [ "permit_mynetworks" + "permit_sasl_authenticated" + "reject_non_fqdn_recipient" + "reject_unknown_recipient_domain" + "reject_unlisted_recipient" + "reject_unauth_destination" + ]; + smtpd_sender_restrictions = + [ "permit_mynetworks" + "permit_sasl_authenticated" + "reject_non_fqdn_sender" + "reject_unknown_sender_domain" + ]; + smtpd_relay_restrictions = + [ "permit_mynetworks" + "permit_sasl_authenticated" + "defer_unauth_destination" + ]; + + myhostname = "example.org"; + alias_maps = "hash:/etc/aliases"; + alias_database = "hash:/etc/aliases"; + mydoamin = "example.org"; + myorigin = "$mydomain"; + mydestination = "localhost"; + relayhost = ""; + mynetworks = + [ "127.0.0.0/8" + "[::ffff:127.0.0.0]/104" + "[::1]/128" + ]; + mailbox_size_limit = 0; + recipient_delimiter = "+"; + inet_interfaces = "all"; + inet_protocols = "all"; + + virtual_transport = "lmtp:unix:private/dovecot-lmtp"; + + virtual_mailbox_domains = "mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf"; + virtual_mailbox_maps = "mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf"; + virtual_alias_maps = + [ "mysql:/etc/postfix/mysql-virtual-alias-maps.cf" + "mysql:/etc/postfix/mysql-virtual-email2email.cf" + ]; + + disable_vrfy_command = "yes"; + strict_rfc821_envelopes = "yes"; + # smtpd_etrn_restrictions = "yes"; + # smtpd_reject_unlisted_sender = "yes"; + # smtpd_reject_unlisted_recipient = "yes"; + smtpd_delay_reject = "yes"; + smtpd_helo_required = "yes"; + smtp_always_send_ehlo = "yes"; + # smtpd_hard_error_limit = 1; + smtpd_timeout = "30s"; + smtp_helo_timeout = "15s"; + smtp_rcpt_timeout = "15s"; + smtpd_recipient_limit = "15s"; + minimal_backoff_time = "180s"; + maximal_backoff_time = "3h"; + + invalid_hostname_reject_code = 550; + non_fqdn_reject_code = 550; + unknown_address_reject_code = 550; + unknown_client_reject_code = 550; + unknown_hostname_reject_code = 550; + unverified_recipent_reject_code = 550; + unverified_sender_reject_code = 550; + }; + }; + }; + }); +} diff --git a/containers/zigbee2mqtt.nix b/containers/zigbee2mqtt.nix new file mode 100644 index 0000000..9aa8e74 --- /dev/null +++ b/containers/zigbee2mqtt.nix @@ -0,0 +1,59 @@ +{ nglib, nixpkgs }: +nglib.makeSystem { + system = "x86_64-linux"; + name = "nixng-zigbee2mqtt"; + inherit nixpkgs; + config = ({ pkgs, config, nglib, ... }: + let + ids = config.ids; + in + { + config = { + dumb-init = { + enable = true; + type.services = { }; + }; + + init.services.zigbee2mqtt = { + shutdownOnExit = true; + }; + + services.zigbee2mqtt = { + enable = true; + user = "root"; + envsubst = true; + package = (import (pkgs.fetchFromGitHub { + owner = "NixOS"; + repo = "nixpkgs"; + rev = "0c408a087b4751c887e463e3848512c12017be25"; + sha256 = "sha256-vBVwv3+kPrxbNyfo48cB5cc5/4tq5zlJGas/qw8XNBE="; + }) { inherit (pkgs.stdenv.hostPlatform) system; }).zigbee2mqtt; + config = + { homeassistant = true; + permit_join = true; + mqtt = { + base_topic = "zigbee2mqtt"; + server = "mqtt://localhost:1883"; + user = "\${MQTT_USER}"; + password = "\${MQTT_PASSWORD}"; + }; + + frontend = + { port = 8456; + host = "0.0.0.0"; + }; + + advanced = + { pan_id = 15408; + }; + + serial.port = "/dev/serial/by-id/usb-ITead_Sonoff_Zigbee_3.0_USB_Dongle_Plus_4c004e9c53c9eb118a9f8b4f1d69213e-if00-port0"; + + devices = "devices.yaml"; + groups = "groups.yaml"; + log_level = "debug"; + }; + }; + }; + }); + } diff --git a/flake.nix b/flake.nix index 2cb22e0..65e624a 100644 --- a/flake.nix +++ b/flake.nix @@ -35,6 +35,9 @@ camptules = (import ./containers/camptules.nix (base // { inherit (inputs) camptules; })); gitea = import ./containers/gitea.nix base; minecraft = import ./containers/minecraft.nix base; + mosquitto = import ./containers/mosquitto.nix base; + zigbee2mqtt = import ./containers/zigbee2mqtt.nix base; + home-assistant = import ./containers/home-assistant.nix base; }; in { diff --git a/infrastructure/home-assistant/home-assistant-policy.hcl b/infrastructure/home-assistant/home-assistant-policy.hcl new file mode 100644 index 0000000..31c8136 --- /dev/null +++ b/infrastructure/home-assistant/home-assistant-policy.hcl @@ -0,0 +1,3 @@ +path "kv/data/home-assistant" { + capabilities = ["read"] +} diff --git a/infrastructure/home-assistant/home-assistant_hass.hcl b/infrastructure/home-assistant/home-assistant_hass.hcl new file mode 100644 index 0000000..504b4cd --- /dev/null +++ b/infrastructure/home-assistant/home-assistant_hass.hcl @@ -0,0 +1,19 @@ +type = "csi" +id = "home-assistant_hass" +name = "home-assistant_hass" +plugin_id = "nfs" + +capability { + access_mode = "single-node-writer" + attachment_mode = "file-system" +} + +context { + server = "blowhole.in.redalder.org" + share = "/var/nfs/home-assistant_hass" +} + +mount_options { + fs_type = "nfs" + mount_flags = [ "nolock" ] +} diff --git a/infrastructure/home-assistant/home-assistant_mosquitto.hcl b/infrastructure/home-assistant/home-assistant_mosquitto.hcl new file mode 100644 index 0000000..227adc9 --- /dev/null +++ b/infrastructure/home-assistant/home-assistant_mosquitto.hcl @@ -0,0 +1,19 @@ +type = "csi" +id = "home-assistant_mosquitto" +name = "home-assistant_mosquitto" +plugin_id = "nfs" + +capability { + access_mode = "single-node-writer" + attachment_mode = "file-system" +} + +context { + server = "blowhole.in.redalder.org" + share = "/var/nfs/home-assistant_mosquitto" +} + +mount_options { + fs_type = "nfs" + mount_flags = [ "nolock" ] +} diff --git a/infrastructure/home-assistant/home-assistant_zigbee2mqtt.hcl b/infrastructure/home-assistant/home-assistant_zigbee2mqtt.hcl new file mode 100644 index 0000000..54684d3 --- /dev/null +++ b/infrastructure/home-assistant/home-assistant_zigbee2mqtt.hcl @@ -0,0 +1,19 @@ +type = "csi" +id = "home-assistant_zigbee2mqtt" +name = "home-assistant_zigbee2mqtt" +plugin_id = "nfs" + +capability { + access_mode = "single-node-writer" + attachment_mode = "file-system" +} + +context { + server = "blowhole.in.redalder.org" + share = "/var/nfs/home-assistant_zigbee2mqtt" +} + +mount_options { + fs_type = "nfs" + mount_flags = [ "nolock" ] +} diff --git a/infrastructure/home-assistant/mosquitto-policy.hcl b/infrastructure/home-assistant/mosquitto-policy.hcl new file mode 100644 index 0000000..dbc9a00 --- /dev/null +++ b/infrastructure/home-assistant/mosquitto-policy.hcl @@ -0,0 +1,3 @@ +path "kv/data/mqtt" { + capabilities = ["read"] +} diff --git a/infrastructure/home-assistant/nomad.hcl b/infrastructure/home-assistant/nomad.hcl new file mode 100644 index 0000000..1b10c68 --- /dev/null +++ b/infrastructure/home-assistant/nomad.hcl @@ -0,0 +1,249 @@ +job "home-assistant" { + datacenters = [ "homelab-1" ] + type = "service" + + constraint { + attribute = "${attr.unique.hostname}" + value = "blowhole" + } + + group "zigbee2mqtt" { + count = 1 + + restart { + attempts = 5 + delay = "5s" + } + + network { + mode = "bridge" + + port "http" { + static = 8456 + to = 8456 + } + } + + volume "home-assistant_zigbee2mqtt" { + type = "csi" + source = "home-assistant_zigbee2mqtt" + read_only = false + + attachment_mode = "file-system" + access_mode = "single-node-writer" + } + + service { + name = "zigbee2mqtt" + port = "8456" + + connect { + sidecar_service { + proxy { + upstreams { + destination_name = "mqtt" + local_bind_port = 1883 + datacenter = "homelab-1" + } + } + } + } + } + + task "zigbee2mqtt" { + driver = "docker" + + vault { + policies = ["zigbee2mqtt-policy"] + } + + config { + image = "nixng-zigbee2mqtt:local" + + memory_hard_limit = 256 + + devices = [ + { + host_path = "/dev/serial/by-id/usb-ITead_Sonoff_Zigbee_3.0_USB_Dongle_Plus_4c004e9c53c9eb118a9f8b4f1d69213e-if00-port0" + container_path = "/dev/serial/by-id/usb-ITead_Sonoff_Zigbee_3.0_USB_Dongle_Plus_4c004e9c53c9eb118a9f8b4f1d69213e-if00-port0" + } + ] + } + + resources { + cpu = 128 + memory = 128 + } + + volume_mount { + volume = "home-assistant_zigbee2mqtt" + destination = "/var/zigbee2mqtt" + read_only = false + } + + template { + data = <