diff --git a/nixos/hosts/durandal/default.nix b/nixos/hosts/durandal/default.nix index 0f2561d..646cd72 100644 --- a/nixos/hosts/durandal/default.nix +++ b/nixos/hosts/durandal/default.nix @@ -14,7 +14,6 @@ - }; mySystem.system.systemd.pushover-alerts.enable = false; diff --git a/nixos/hosts/shodan/default.nix b/nixos/hosts/shodan/default.nix index ea4dbbe..dafc17f 100644 --- a/nixos/hosts/shodan/default.nix +++ b/nixos/hosts/shodan/default.nix @@ -25,6 +25,7 @@ redlib.enable = true; mosquitto.enable = true; + zigbee2mqtt.enable = true; }; diff --git a/nixos/modules/nixos/containers/factorio/default.nix b/nixos/modules/nixos/containers/factorio/default.nix index 59ebc3a..f7b7bb6 100644 --- a/nixos/modules/nixos/containers/factorio/default.nix +++ b/nixos/modules/nixos/containers/factorio/default.nix @@ -70,12 +70,6 @@ in }; environmentFiles = [ config.sops.secrets."services/${app}/env".path ]; ports = [ (builtins.toString port) ]; # expose port - labels = lib.myLib.mkTraefikLabels { - name = app; - domain = config.networking.domain; - - inherit port; - }; }; networking.firewall = mkIf cfg.openFirewall { diff --git a/nixos/modules/nixos/services/default.nix b/nixos/modules/nixos/services/default.nix index 52417d6..2213008 100644 --- a/nixos/modules/nixos/services/default.nix +++ b/nixos/modules/nixos/services/default.nix @@ -16,5 +16,6 @@ ./powerdns ./adguardhome ./mosquitto + ./zigbee2mqtt ]; } diff --git a/nixos/modules/nixos/services/mosquitto/default.nix b/nixos/modules/nixos/services/mosquitto/default.nix index 1b5608c..6f4e101 100644 --- a/nixos/modules/nixos/services/mosquitto/default.nix +++ b/nixos/modules/nixos/services/mosquitto/default.nix @@ -18,7 +18,8 @@ in sops.secrets."services/mosquitto/mq/hashedPassword" = { sopsFile = ./secrets.sops.yaml; - owner = config.users.users.mosquitto.name; + owner = "mosquitto"; + group = "mosquitto"; restartUnits = [ "${app}.service" ]; }; diff --git a/nixos/modules/nixos/services/mosquitto/secrets.sops.yaml b/nixos/modules/nixos/services/mosquitto/secrets.sops.yaml index 72aa578..fa37de9 100644 --- a/nixos/modules/nixos/services/mosquitto/secrets.sops.yaml +++ b/nixos/modules/nixos/services/mosquitto/secrets.sops.yaml @@ -1,7 +1,8 @@ services: mosquitto: mq: - hashedPassword: ENC[AES256_GCM,data:l6QVTtfZJhsMfKoN/pIuKevjq6avIroUMSJQpj/53Lhuw/Okw2E9o1QBECBYvNUoU/367rCR82TBmPF5jguunWLI15bxnKxBvcPda33SlYwPGuiXDaALfk0WAPs11mpwvpgNNiVPOD5gDCW7YSNG8w==,iv:VF9Cm8Yp7SZY/CH5V6aLTGWb0CA4N50Kl7RPJJ/aKBc=,tag:dq9HwYCakSSel3cv/t3BjQ==,type:str] + hashedPassword: ENC[AES256_GCM,data:FI2KLDYBW+HCXbOfmdyRYEwI8IeEX2iNWAiyGyn6FE2kkP1K1GETLbissBds+6DAPgbBULhxh8PZ9nkOCtR9QGKLkImxgMU1o8Zmwc6UxfkmFWvEDyt0/3g5gEVoO2rsLx4GMp4qO6Wbz0QCxJWJwQ==,iv:Pt9g/c3A1aChcgxQMSa76f1/c7uq8ctf9CDTFKNSvcs=,tag:zSS9heWyjvwFwKoEmYo+GA==,type:str] + plainPassword.yaml: ENC[AES256_GCM,data:z+/SQE+PfsIoDekjf5D6f6nkxsfiNmcym4YSVdIl,iv:heh5N8HazUUP251llJLVGdka+65Ofjm4by1kug5uKYI=,tag:NS5K47rZRLH6OzyQopB3+A==,type:str] sops: kms: [] gcp_kms: [] @@ -11,68 +12,68 @@ sops: - recipient: age1lj5vmr02qkudvv2xedfj5tq8x93gllgpr6tzylwdlt7lud4tfv5qfqsd5u enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBFVlVjV3BBQjJHT1ZOVm9n - RGhhOGcxanhNWEIxTTBUMU9DcEd3Tjc1c25VCjZVOXhVajVhVzR4WWlEbTQ5amFD - WWN4bXJueVBrSHBocU93MDB5ZDdjT0EKLS0tIFhXY2xNZnVLYnIyYmxTSXU3Q3g4 - dElVV3ZsNFZZM083Tm10dVJJeDlkTWMK3/QJ1Gni+zj7J7gc0x3xL35rfBr6UNVa - 4ii+q0pHpMBTMb0S1nGbazi3wb1I5KxnINzS6mFSWXkMFU63l3b2YA== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjNmJPbGFaRXkrYnh6RHR4 + dHBWNWs0RDB1UnFycXdoWmFFYkNGcHh4YUZjCm9LYnZIU1dvNzVTNHp0NjBEMTE2 + MHpOdVh5aVR3c2o5V21LU1ZDWnpXdFEKLS0tIDB4emRQRGVVYzFYQ2JsakVIeFhn + QlFEYjBSVjVKUFVEV3gwK2NZaW1XSGMKHBPiLSd7Ixs68xMcpOJzw2Vu4GbKAglY + 1yAcQgIRvFxCV/uz4BwAFvyCtYYsUD4GhEAFp/wwq6W1hTEAseV0RA== -----END AGE ENCRYPTED FILE----- - recipient: age17edew3aahg3t5nte5g0a505sn96vnj8g8gqse8q06ccrrn2n3uysyshu2c enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPejZ3NVMrZnRtMkNQV3dp - ejJraDFpcG5mNEs5WE5YMGJKTEx5OGs4ZkJNCk05NDA4NTVxTFQ4UU5Mck9hYlZS - Skx0L0xDVkxqU0ZROTBBTjQzaHdNSUkKLS0tIHpRQWtub3Bmc29aSjdBeHhKK1pq - L0dLRm4wRTZsS0thc3VUSit5cDgxL2MKXGdQz7a9oEoqxNnGCQODcpb4W1RUKcli - josRkGYVeOfRY0+1BKwk5XIbAKMohz+YbTKUKkWDYEXiffSjd7Ae2Q== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGRGE0OWZaR0ZhY2piMkxY + cG1HRFIrWjJFeG8vbzhPWW51cWVvN2FXV2hvCjhLNmVwRkRWQm54N3FPczFudGVB + N096dmFaUktqMDBIRWxlTnVaa2VOcVkKLS0tIEFZSmYweDRqVlpOdUhESXFjbnR0 + RXpBU2t0bFlJQW1RSXZ0dzZFT3RXT0UKraC9wurjhf+SiKPewE5L3auOI59kaj/h + 7nUUnWUQfkebO5wxtmbbmenXK//oY1tRbC/Dp2JPUTQo2wwrXMF/JQ== -----END AGE ENCRYPTED FILE----- - recipient: age1u4tht685sqg6dkmjyer96r93pl425u6353md6fphpd84jh3jwcusvm7mgk enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBtNWhPRkt3M1NJajBlOCty - RlEwQ1ZCQXhNQnIxbzVwcHpJUE11U2ZvcmxnClYyZ3dqSC91K2I2TmhSMEVIUWJy - N1dMbjdsNjVUdVM3Uk9BeElRaUhzRXcKLS0tIGxhS1pTUEt3aU9nckNHMnQ2Q0xw - WjdCRTRMdzVMekk1YXZBejdxdWF4UEkK7SPmOHbup24/IhITZzOnl7YSSYXl/ShW - gOqyzkXxe079LHOadm/nqn+XVgnYdTeGf4budabp1TxUaMPsxgjG8g== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBEVjdTRkdDMElXV0pLL3Fv + MFpxaStjaHNPSmY1RnFUbUhVS09hUEZYYVFVCk4yUEsxV2FvYm5ybVdGS0pmVW5m + b1FYb3RxYXFhVVhjNms1bjBtVEYyMm8KLS0tIDk2Uy9xT3VZdGdSY1Rjc3l5OGE4 + ZzJvWTdDRlFNMmliUGZZbnRCc0UwRHcKdJllfqPlYBNf/nWkT5E1Is1moIFvN6lc + XVGQ9tzH1tpZC9PwmC1nL08fmuzwI5k9zqL2D+eG+vH7yjBLRFzxVg== -----END AGE ENCRYPTED FILE----- - recipient: age1cp6vegrmqfkuj8nmt2u3z0sur7n0f7e9x9zmdv4zygp8j2pnucpsdkgagc enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkcFpSOE16bVlKalc4eEJn - cVdwdWJwV3grR0dUUkZ2V1kvVyt5UVNmQVNrCnJLakF1aWhKaGYyYnR3Zlh0dUxI - WStFbmVjWEM3QkU0NmlodjY0OXJLOWsKLS0tIFRPOC96M3IxcXdsaDB5RnJJb1dZ - a3hpczhWUDVQM3JiVU5ja3ZDMVRGb3MKowyjuHf0SO6zJ4+dnnuxWUn17uTDh0Iy - 4x0cAKbwuSLlna3miG0Vxvfy/EehDuiZFW3c3EM7ITdayodM4lQNwQ== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzOUJOTFJFSHp1QTdKVmZk + UVZhVEpqQzVTOTZHOXBYbDJUVjBpL2ErV2tJCm1jOUVmVE8wc3ZLSDZJRVkxQkNU + eEw2RVNpVzJFcTlPbnF1Q2g0RVo5OUEKLS0tIFpvcU11RGVNR0dNQWc1aGJFd1lm + Ym1kellUNjRsTTdQZTViZnBkRllqbWsKWFwR8WeIxJunlgzT3GpYkIdFRLJZlhm4 + Vr4N5CMnNXJYWRiNJDrHDKvVw6EeUUhyJ+7aJ9UAfR0YfX47yIlW5A== -----END AGE ENCRYPTED FILE----- - recipient: age1ekt5xz7u2xgdzgsrffhd9x22n80cn4thxd8zxjy2ey5vq3ca7gnqz25g5r enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBXMy9XTFEvMExXQUJqbnlL - YTdLd0c4bnVkOENtVGlocnRVQmhocHRiTlNnCm5Cc08yK3lLTkU0ZkNzQUViOC9q - a1JGRFlaVnJyMVRod2NKWHJ6VVlSMzQKLS0tIFI4Z01hKzN5Y1VZKzIwb1VvVnZz - bXFlZ1hCV3ZGOXJBMDY3M1RzOVZzbWMKlCRZZ+cKYyxd5VusNklUqJkVGp4/A7/U - TBmsn2lHgLi+mnoCN6YLNcLz0gxG27VFMAQSaDECMW6HP0Yy2soAKA== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBnWUpaOUVnRS9Va211UXl0 + dUFmOVlmR2d1REVNRXJKMFV1OTVsSDZGRldvCk5CcGpWZHFBYmtsbVRjWW1QejNJ + Y0FPSVJ1UlhhcVhRaTVXMERyRURIRjAKLS0tIDloZ2J0anNqSEZmUXg4eDFyTVZ0 + WXAvTmRpUjRKMnJlZmV0SEEwZndtNkEK99oVyOIlfKP6zHnuS1LKGORuOLfX3vAU + Gw7IHXRUSpZhElOSK0jc9F2O6IEGYpfu5PYC4m8uojhRXsG6W/XTXA== -----END AGE ENCRYPTED FILE----- - recipient: age1jpeh4s553taxkyxhzlshzqjfrtvmmp5lw0hmpgn3mdnmgzku332qe082dl enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBZSnZKNjRGamJGZ2gxbnBR - S2t5TkpTcll6QVpmclhrWEtGODQrUkVyVVRjCmgwcWpBTWZhMFc4NC9MVDV4UVox - WVY1WjMxajVsNG9YRTJOYktrSTVZRUkKLS0tIE01dkZXRTFmM3lWN0RqLzJWQVF0 - aSt3SUE2N3BJeFJUVnR6K2UzUDl0a28K2WcNLOYihCBoL5KMTQWvtbgqtTosA3Y6 - s+2XzBnz/RonDVe2Wh+trkwfKfiwyEvhcyBHQIjU6g4eWovVDMq7Vw== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSArUVZScTVpWjZvUzlFeDVQ + RjRreTBPTEF3bXZ1Sk5sWmgzUURxcU5talFzClFRSjFFUEtQMis4dkNLRFFaaHlq + QXdhQWxEMXdHSGgzSXlqZm4xbEFVaGMKLS0tIG9OOHUwZWI3SXJ2SWZvdS9EVzV5 + aVpWNmk5b1owbjJHSWtnOHRJWmhEbGMKT4VdLG/8qX+KWO6vQ2TsrT1+w4XgDani + KOAS/DimV22EjmHljs9ByWdqkVw0KIBfp2oXo9m0Jqn0H1M2yEMozg== -----END AGE ENCRYPTED FILE----- - recipient: age1j2r8mypw44uvqhfs53424h6fu2rkr5m7asl7rl3zn3xzva9m3dcqpa97gw enc: | -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkaEVuZWRaeExoaStDQW14 - SFdwM1VxTFQ4dGdNRUdQOUF1djVnOUQ5M3hFCmc4NWxtTDNRcjlmVGxLSW45c0lm - ZzRURjZmTTFibk43Q0RiZkk1NmdOL2sKLS0tIDZ0L3U2M2JlamJwTVUwRFVHR1FM - aUsvNG95K0lxdUFjRUpXdHhDZjIyV0EKEYU4IkFdNXqWHD6+ukmcOkiB7UW5Fn1w - M009nesLsOp1j1sVEStgPzPJLnw/j2OZQgkiMSzeLE1CrGLaOLdpCw== + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWNzZiS1pVTXMxQW9UUldr + dFNHUDhxV1lnOFNaK2J6SEY3WUtId0FLSTNjCkJhUm1sVnlJMFk5Wm9lNGdMYnZv + eVR6MGNVYjBSNENqU1BJWituMnJJeFUKLS0tIEk0ZUVETHRmSmJjT2NDMGNrajlZ + NUMxdEhvQnRES2hwck5jeEw3bnprZ00KUnkLNK+hmCihLwPiWt3NHjASuQwgBOlf + AsW8BkUjjQY/ABR3X6sNSI8uXQt+8BHP14U4cO8jwggcyE2W1Pq03A== -----END AGE ENCRYPTED FILE----- - lastmodified: "2024-04-25T03:13:36Z" - mac: ENC[AES256_GCM,data:YC1lPTCSUZPgZte0WHGbqh1k3vAfcvVNyxR7+daPH5xwrXyZa/jTIY5U/UuD+nVCr2YFB89QKGko6c76sqoDxhMRf/FDZt0YSa+RaOJO0LNinRBokV2sm0oYxeWoKEUB1B5cuXVfBqqsgw6qQqvIuysXGk0A8vjXUT/btZSd2/A=,iv:Gu3X3xxZ5dmb2Y6Ve/7xznmS0/x5uU9iIDRiOE/DR9Y=,tag:1l7GtZTxBFLvV4JzRVt4Ow==,type:str] + lastmodified: "2024-04-25T05:21:30Z" + mac: ENC[AES256_GCM,data:aN2YsPhDMl8vvLgi98iRCl5f7llJ8VTt7y1vh7liBAwG6hW1vZeetqSxvWiVDugozjsbK6n44AUDjhnfwo8EXp8iWQmJDXuS1PoEph4C18oo4Eu8n9ZpLQFGSKRMYXuetwrzOkn42tMYadYbwLPIandKqaTGUD4FcoqZtGxaH3c=,iv:Wfb1r8JNO74KjBWTIGwW2bXTVv3AIpj5KW9FXYrDm7c=,tag:FEnXv1S0J3XaxZ6TQz0bhQ==,type:str] pgp: [] unencrypted_suffix: _unencrypted version: 3.8.1 diff --git a/nixos/modules/nixos/services/traefik/default.nix b/nixos/modules/nixos/services/traefik/default.nix index 8ea7efd..a6d0a58 100644 --- a/nixos/modules/nixos/services/traefik/default.nix +++ b/nixos/modules/nixos/services/traefik/default.nix @@ -7,7 +7,88 @@ with lib; let cfg = config.mySystem.services.traefik; - routersFile = builtins.toFile "routers.yaml" (builtins.toJSON cfg.routers); + + # core dynamic options to define middleware + # sso etc + dynamicOptions = [{ + + http.middlewares = { + # Whitelist local network and VPN addresses + local-ip-only.ipWhiteList.sourceRange = [ + "127.0.0.1/32" # localhost + "192.168.0.0/16" # RFC1918 + "10.0.0.0/8" # RFC1918 + "172.16.0.0/12" # RFC1918 (docker network) + ]; + + # authelia = { + # # Forward requests w/ middlewares=authelia@file to authelia. + # forwardAuth = { + # # address = cfg.autheliaUrl; + # address = "http://localhost:9092/api/verify?rd=https://auth.dhupar.xyz:444/"; + # trustForwardHeader = true; + # authResponseHeaders = [ + # "Remote-User" + # "Remote-Name" + # "Remote-Email" + # "Remote-Groups" + # ]; + # }; + # }; + # authelia-basic = { + # # Forward requests w/ middlewares=authelia-basic@file to authelia. + # forwardAuth = { + # address = "http://localhost:9092/api/verify?auth=basic"; + # trustForwardHeader = true; + # authResponseHeaders = [ + # "Remote-User" + # "Remote-Name" + # "Remote-Email" + # "Remote-Groups" + # ]; + # }; + # }; + # https://oauth2-proxy.github.io/oauth2-proxy/docs/configuration/overview/#forwardauth-with-static-upstreams-configuration + # auth-headers = { + # browserXssFilter = true; + # contentTypeNosniff = true; + # forceSTSHeader = true; + # frameDeny = true; + # sslHost = domain; + # sslRedirect = true; + # stsIncludeSubdomains = true; + # stsPreload = true; + # stsSeconds = 315360000; + # }; + }; + + tls.options.default = { + minVersion = "VersionTLS13"; + sniStrict = true; + }; + + # Set up wildcard domain certificates for both *.hostname.domain and *.local.domain + http.routers = { + traefik = { + entrypoints = "websecure"; + rule = "Host(`traefik-${config.networking.hostName}.${config.mySystem.domain}`)"; + tls.certresolver = "letsencrypt"; + tls.domains = [{ + main = "${config.mySystem.domain}"; + sans = "*.${config.mySystem.domain}"; + }]; + middlewares = "local-ip-only@file"; + service = "api@internal"; + }; + }; + }]; + + # Combine the above 'core 'options with the (dynamicOptions) + # list of ingress routers for each serfie defined in various + # modules (cfg.routers) + # this folds the list and iterates each element to add them together + dynamicOptionsAttrset = lib.foldl' (acc: elem: lib.recursiveUpdate acc elem) { } (dynamicOptions ++ cfg.routers); + routersFile = builtins.toFile "routers.yaml" (builtins.toJSON dynamicOptionsAttrset); in { @@ -21,185 +102,124 @@ in }; - config = mkIf cfg.enable { + config = mkIf cfg.enable + { - networking.firewall.allowedTCPPorts = [ 80 443 ]; + # put the dynamic configs in a file + # i put this in a file instead of piping directly into + # the traefik module, so that if i update the file + # with a new router nix doesnt restart traefik, it just updates + # the etc file and traefik picks up the changes. + environment.etc."traefik/config.yaml".source = routersFile; - sops.secrets."system/services/traefik/apiTokenFile".sopsFile = ./secrets.sops.yaml; + networking.firewall.allowedTCPPorts = [ 80 443 ]; - # Restart when secret changes - sops.secrets."system/services/traefik/apiTokenFile".restartUnits = [ "traefik.service" ]; + sops.secrets."system/services/traefik/apiTokenFile".sopsFile = ./secrets.sops.yaml; - systemd.services.traefik = { - serviceConfig.EnvironmentFile = [ - config.sops.secrets."system/services/traefik/apiTokenFile".path - ]; - }; + # Restart when secret changes + sops.secrets."system/services/traefik/apiTokenFile".restartUnits = [ "traefik.service" ]; - # add user to group to view files/storage - users.users.truxnell.extraGroups = [ "traefik" ]; + systemd.services.traefik = { + serviceConfig.EnvironmentFile = [ + config.sops.secrets."system/services/traefik/apiTokenFile".path + ]; + }; - services.traefik = { - # TODO refactor into subfiles - enable = true; - group = "podman"; # podman backend, required to access socket + # add user to group to view files/storage + users.users.truxnell.extraGroups = [ "traefik" ]; - dataDir = "${config.mySystem.persistentFolder}/traefik"; - # Required so traefik is permitted to watch docker events - # group = "docker"; + services.traefik = { + # TODO refactor into subfiles + enable = true; + group = "podman"; # podman backend, required to access socket - staticConfigOptions = { + dataDir = "${config.mySystem.persistentFolder}/traefik"; + # Required so traefik is permitted to watch docker events + # group = "docker"; - global = { - checkNewVersion = false; - sendAnonymousUsage = false; - }; + staticConfigOptions = { - api.dashboard = true; - log.level = "DEBUG"; - - # Allow backend services to have self-signed certs - serversTransport.insecureSkipVerify = true; - - providers = { - docker = { - endpoint = "unix:///var/run/podman/podman.sock"; - exposedByDefault = false; - defaultRule = "Host(`{{ normalize .Name }}.${config.mySystem.domain}`)"; - # network = "proxy"; - }; - file = { - filename = routersFile; - watch = true; + global = { + checkNewVersion = false; + sendAnonymousUsage = false; }; - }; + api.dashboard = true; + log.level = "DEBUG"; - # Listen on port 80 and redirect to port 443 - entryPoints.web = { - address = ":80"; - http.redirections.entrypoint.to = "websecure"; - }; + # Allow backend services to have self-signed certs + serversTransport.insecureSkipVerify = true; - # Run everything SSL - entryPoints.websecure = { - address = ":443"; - http = { - tls = { - certresolver = "letsencrypt"; - domains.main = "${config.mySystem.domain}"; - domains.sans = "*.${config.mySystem.domain}"; + providers = { + docker = { + endpoint = "unix:///var/run/podman/podman.sock"; + exposedByDefault = false; + defaultRule = "Host(`{{ normalize .Name }}.${config.mySystem.domain}`)"; + # network = "proxy"; + }; + file = { + filename = "/etc/traefik/config.yaml"; + watch = true; + }; + + }; + + # Listen on port 80 and redirect to port 443 + entryPoints.web = { + address = ":80"; + http.redirections.entrypoint.to = "websecure"; + }; + + # Run everything SSL + entryPoints.websecure = { + address = ":443"; + http = { + tls = { + certresolver = "letsencrypt"; + domains.main = "${config.mySystem.domain}"; + domains.sans = "*.${config.mySystem.domain}"; + }; + }; + http3 = { }; + }; + + certificatesResolvers.letsencrypt.acme = { + dnsChallenge.provider = "cloudflare"; + dnsChallenge.resolvers = [ "1.1.1.1:53" ]; + keyType = "EC256"; + storage = "${config.services.traefik.dataDir}/acme.json"; + }; + # }; + }; + # Dynamic configuration + # refer the etc file defined above with the build + # dynamic options + dynamicConfigFile = "/etc/traefik/config.yaml"; + }; + + mySystem.services.homepage.infrastructure = [ + { + "Traefik ${config.networking.hostName}" = { + icon = "traefik.png"; + href = "https://traefik-${config.networking.hostName}.${config.mySystem.domain}/dashboard/"; + + description = "Reverse Proxy"; + widget = { + type = "traefik"; + url = "https://traefik-${config.networking.hostName}.${config.mySystem.domain}"; }; }; - http3 = { }; - }; + } + ]; - certificatesResolvers.letsencrypt.acme = { - dnsChallenge.provider = "cloudflare"; - dnsChallenge.resolvers = [ "1.1.1.1:53" ]; - keyType = "EC256"; - storage = "${config.services.traefik.dataDir}/acme.json"; - }; - # }; - }; - # Dynamic configuration - dynamicConfigOptions = { + mySystem.services.gatus.monitors = [{ - http.middlewares = { - # Whitelist local network and VPN addresses - local-ip-only.ipWhiteList.sourceRange = [ - "127.0.0.1/32" # localhost - "192.168.0.0/16" # RFC1918 - "10.0.0.0/8" # RFC1918 - "172.16.0.0/12" # RFC1918 (docker network) - ]; + name = "Traefik ${config.networking.hostName}"; + group = "infrastructure"; + url = "https://traefik-${config.networking.hostName}.${config.mySystem.domain}"; + interval = "1m"; + conditions = [ "[CONNECTED] == true" "[STATUS] == 200" "[RESPONSE_TIME] < 50" ]; + }]; - # authelia = { - # # Forward requests w/ middlewares=authelia@file to authelia. - # forwardAuth = { - # # address = cfg.autheliaUrl; - # address = "http://localhost:9092/api/verify?rd=https://auth.dhupar.xyz:444/"; - # trustForwardHeader = true; - # authResponseHeaders = [ - # "Remote-User" - # "Remote-Name" - # "Remote-Email" - # "Remote-Groups" - # ]; - # }; - # }; - # authelia-basic = { - # # Forward requests w/ middlewares=authelia-basic@file to authelia. - # forwardAuth = { - # address = "http://localhost:9092/api/verify?auth=basic"; - # trustForwardHeader = true; - # authResponseHeaders = [ - # "Remote-User" - # "Remote-Name" - # "Remote-Email" - # "Remote-Groups" - # ]; - # }; - # }; - # https://oauth2-proxy.github.io/oauth2-proxy/docs/configuration/overview/#forwardauth-with-static-upstreams-configuration - # auth-headers = { - # browserXssFilter = true; - # contentTypeNosniff = true; - # forceSTSHeader = true; - # frameDeny = true; - # sslHost = domain; - # sslRedirect = true; - # stsIncludeSubdomains = true; - # stsPreload = true; - # stsSeconds = 315360000; - # }; - }; - - tls.options.default = { - minVersion = "VersionTLS13"; - sniStrict = true; - }; - - # Set up wildcard domain certificates for both *.hostname.domain and *.local.domain - http.routers = { - traefik = { - entrypoints = "websecure"; - rule = "Host(`traefik-${config.networking.hostName}.${config.mySystem.domain}`)"; - tls.certresolver = "letsencrypt"; - tls.domains = [{ - main = "${config.mySystem.domain}"; - sans = "*.${config.mySystem.domain}"; - }]; - middlewares = "local-ip-only@file"; - service = "api@internal"; - }; - }; - }; }; - - mySystem.services.homepage.infrastructure = [ - { - "Traefik ${config.networking.hostName}" = { - icon = "traefik.png"; - href = "https://traefik-${config.networking.hostName}.${config.mySystem.domain}/dashboard/"; - - description = "Reverse Proxy"; - widget = { - type = "traefik"; - url = "https://traefik-${config.networking.hostName}.${config.mySystem.domain}"; - }; - }; - } - ]; - - mySystem.services.gatus.monitors = [{ - - name = "Traefik ${config.networking.hostName}"; - group = "infrastructure"; - url = "https://traefik-${config.networking.hostName}.${config.mySystem.domain}"; - interval = "1m"; - conditions = [ "[CONNECTED] == true" "[STATUS] == 200" "[RESPONSE_TIME] < 50" ]; - }]; - - }; } diff --git a/nixos/modules/nixos/services/zigbee2mqtt/default.nix b/nixos/modules/nixos/services/zigbee2mqtt/default.nix new file mode 100644 index 0000000..7fec079 --- /dev/null +++ b/nixos/modules/nixos/services/zigbee2mqtt/default.nix @@ -0,0 +1,113 @@ +{ lib +, config +, pkgs +, ... +}: +with lib; +let + cfg = config.mySystem.services.zigbee2mqtt; + persistentFolder = "${config.mySystem.persistentFolder}/nixos/services/${app}/"; + app = "zigbee2mqtt"; + user = app; + group = app; + +in +{ + options.mySystem.services.zigbee2mqtt = { + enable = mkEnableOption "zigbee2mqtt"; + addToHomepage = mkEnableOption "Add ${app} to homepage" // { default = true; }; + }; + + + config = mkIf cfg.enable { + + # ensure folder exist and has correct owner/group + systemd.tmpfiles.rules = [ + "d ${persistentFolder} 0750 ${user} ${group} -" #The - disables automatic cleanup, so the file wont be removed after a period + ]; + + sops.secrets."services/mosquitto/mq/plainPassword.yaml" = { + sopsFile = ../mosquitto/secrets.sops.yaml; + owner = config.users.users.zigbee2mqtt.name; + group = config.users.users.zigbee2mqtt.group; + restartUnits = [ "${app}.service" ]; + }; + + services.zigbee2mqtt = { + enable = true; + dataDir = persistentFolder; + settings = { + advanced.log_level = "warn"; + homeassistant = true; + permit_join = false; + include_device_information = true; + frontend = + { + port = 8080; + url = "https://${app}.${config.networking.domain}"; + }; + client_id = "z2m"; + serial = { + port = "tcp://10.8.30.110:6638"; + }; + mqtt = { + server = "mqtt://mqtt.trux.dev:1883"; + user = "mq"; + password = "!${config.sops.secrets."services/mosquitto/mq/plainPassword.yaml".path} password"; + }; + + }; + }; + + users.users.truxnell.extraGroups = [ app ]; + + mySystem.services.traefik.routers = [{ + http.routers.${app} = { + rule = "Host(`${app}.${config.mySystem.domain}`)"; + entrypoints = "websecure"; + middlewares = "local-ip-only@file"; + service = "${app}"; + }; + http.services.${app} = { + loadBalancer = { + servers = [{ + url = "http://localhost:8080"; + }]; + }; + }; + + }]; + + + mySystem.services.homepage.media = mkIf cfg.addToHomepage [ + { + ${app} = { + icon = "${app}.svg"; + href = "https://${app}.${config.mySystem.domain}"; + description = "Zigbee bridge to MQTT"; + container = "${app}"; + }; + } + ]; + + mySystem.services.gatus.monitors = [{ + + name = app; + group = "services"; + url = "https://${app}.${config.mySystem.domain}"; + interval = "1m"; + conditions = [ "[CONNECTED] == true" "[STATUS] == 200" "[RESPONSE_TIME] < 50" ]; + }]; + + services.restic.backups = config.lib.mySystem.mkRestic + { + inherit app; + user = builtins.toString user; + paths = [ persistentFolder ]; + appFolder = app; + inherit persistentFolder; + }; + + + }; +}