From ad5f2b6b091f37796bb8b712c58739278d35038e Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Wed, 14 Sep 2022 14:35:06 +0200 Subject: [PATCH] enable acme.sh wildcard cert on frumar, disable samba --- flake.lock | 188 +++++++++++++++++++++++++++++++++++++- flake.nix | 7 +- nixos/logical/frumar.nix | 138 +++++++++++++++++++++++----- nixos/modules/acme-sh.nix | 166 +++++++++++++++++++++++++++++++++ nixos/modules/nginx.nix | 1 + nixos/roles/default.nix | 1 + secrets/secrets.nix | Bin 1358 -> 1410 bytes secrets/transip-key.age | Bin 0 -> 2115 bytes 8 files changed, 473 insertions(+), 28 deletions(-) create mode 100644 nixos/modules/acme-sh.nix create mode 100644 secrets/transip-key.age diff --git a/flake.lock b/flake.lock index 47e4e38..3da6f9e 100644 --- a/flake.lock +++ b/flake.lock @@ -52,6 +52,25 @@ "type": "github" } }, + "deploy-rs": { + "inputs": { + "flake-compat": "flake-compat_2", + "nixpkgs": "nixpkgs_2", + "utils": "utils_3" + }, + "locked": { + "lastModified": 1648475189, + "narHash": "sha256-gAGAS6IagwoUr1B0ohE3iR6sZ8hP4LSqzYLC8Mq3WGU=", + "owner": "serokell", + "repo": "deploy-rs", + "rev": "83e0c78291cd08cb827ba0d553ad9158ae5a95c3", + "type": "github" + }, + "original": { + "id": "deploy-rs", + "type": "indirect" + } + }, "emacs-overlay": { "inputs": { "flake-utils": "flake-utils", @@ -88,6 +107,37 @@ "type": "github" } }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1648199409, + "narHash": "sha256-JwPKdC2PoVBkG6E+eWw3j6BMR6sL3COpYWfif7RVb8Y=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "64a525ee38886ab9028e6f61790de0832aa3ef03", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_3": { + "flake": false, + "locked": { + "lastModified": 1627913399, + "narHash": "sha256-hY8g6H2KFL8ownSiFeMOjwPC8P0ueXpCVEbxgda3pko=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "12c64ca55c1014cdc1b16ed5a804aa8576601ff2", + "type": "github" + }, + "original": { + "id": "flake-compat", + "type": "indirect" + } + }, "flake-utils": { "locked": { "lastModified": 1656928814, @@ -118,6 +168,36 @@ "type": "github" } }, + "flake-utils_3": { + "locked": { + "lastModified": 1631561581, + "narHash": "sha256-3VQMV5zvxaVLvqqUrNz3iJelLw30mIVSfZmAaauM3dA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "7e5bf3925f6fbdfaf50a2a7ca0be2879c4261d19", + "type": "github" + }, + "original": { + "id": "flake-utils", + "type": "indirect" + } + }, + "gitignore-nix": { + "flake": false, + "locked": { + "lastModified": 1611672876, + "narHash": "sha256-qHu3uZ/o9jBHiA3MEKHJ06k7w4heOhA+4HCSIvflRxo=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "211907489e9f198594c0eb0ca9256a1949c9d412", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, "home-manager": { "inputs": { "nixpkgs": [ @@ -157,6 +237,40 @@ "type": "github" } }, + "lowdown-src": { + "flake": false, + "locked": { + "lastModified": 1632468475, + "narHash": "sha256-NNOm9CbdA8cuwbvaBHslGbPTiU6bh1Ao+MpEPx4rSGo=", + "owner": "kristapsdz", + "repo": "lowdown", + "rev": "6bd668af3fd098bdd07a1bedd399564141e275da", + "type": "github" + }, + "original": { + "owner": "kristapsdz", + "repo": "lowdown", + "type": "github" + } + }, + "nix": { + "inputs": { + "lowdown-src": "lowdown-src", + "nixpkgs": "nixpkgs_3" + }, + "locked": { + "lastModified": 1633098935, + "narHash": "sha256-UtuBczommNLwUNEnfRI7822z4vPA7OoRKsgAZ8zsHQI=", + "owner": "nixos", + "repo": "nix", + "rev": "4f496150eb4e0012914c11f0a3ff4df2412b1d09", + "type": "github" + }, + "original": { + "id": "nix", + "type": "indirect" + } + }, "nixos-hardware": { "locked": { "lastModified": 1656933710, @@ -294,6 +408,37 @@ "type": "github" } }, + "nixpkgs_2": { + "locked": { + "lastModified": 1648219316, + "narHash": "sha256-Ctij+dOi0ZZIfX5eMhgwugfvB+WZSrvVNAyAuANOsnQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "30d3d79b7d3607d56546dd2a6b49e156ba0ec634", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1632864508, + "narHash": "sha256-d127FIvGR41XbVRDPVvozUPQ/uRHbHwvfyKHwEt5xFM=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "82891b5e2c2359d7e58d08849e4c89511ab94234", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "nixos-21.05-small", + "type": "indirect" + } + }, "root": { "inputs": { "agenix": "agenix", @@ -304,7 +449,33 @@ "nixpkgs": "nixpkgs", "nixpkgs-mozilla": "nixpkgs-mozilla", "nixpkgs-stable": "nixpkgs-stable", - "nixpkgs-wayland": "nixpkgs-wayland" + "nixpkgs-wayland": "nixpkgs-wayland", + "serokell-nix": "serokell-nix" + } + }, + "serokell-nix": { + "inputs": { + "deploy-rs": "deploy-rs", + "flake-compat": "flake-compat_3", + "flake-utils": "flake-utils_3", + "gitignore-nix": "gitignore-nix", + "nix": "nix", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1655492394, + "narHash": "sha256-bmiET3gLIv6T3+jPJi0hm9GurjwrddlmtuxTpFDYJZw=", + "owner": "serokell", + "repo": "serokell.nix", + "rev": "ca5235bd249a64991d2e813084aea5d521ca0088", + "type": "github" + }, + "original": { + "owner": "serokell", + "repo": "serokell.nix", + "type": "github" } }, "utils": { @@ -336,6 +507,21 @@ "repo": "flake-utils", "type": "github" } + }, + "utils_3": { + "locked": { + "lastModified": 1648297722, + "narHash": "sha256-W+qlPsiZd8F3XkzXOzAoR+mpFqzm3ekQkJNa+PIh1BQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "0f8662f1319ad6abf89b3380dd2722369fc51ade", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index dc69cfd..5219a74 100644 --- a/flake.nix +++ b/flake.nix @@ -12,10 +12,13 @@ nixos-mailserver.inputs.nixpkgs.follows = "nixpkgs"; agenix.url = "github:ryantm/agenix"; agenix.inputs.nixpkgs.follows = "nixpkgs"; - + serokell-nix.url = "github:serokell/serokell.nix"; + serokell-nix.inputs = { + nixpkgs.follows = "nixpkgs"; + }; }; outputs = inputs@{ nixpkgs, home-manager, nixpkgs-mozilla, emacs-overlay - , nixpkgs-wayland, nixpkgs-stable, nixos-hardware, agenix, self, ... }: { + , nixpkgs-wayland, nixpkgs-stable, nixos-hardware, agenix, serokell-nix, self, ... }: { overlays.default = nixpkgs.lib.composeManyExtensions [ nixpkgs-wayland.overlay #nixpkgs-mozilla.overlay diff --git a/nixos/logical/frumar.nix b/nixos/logical/frumar.nix index 5384060..a17fb94 100644 --- a/nixos/logical/frumar.nix +++ b/nixos/logical/frumar.nix @@ -14,12 +14,34 @@ }; }) ]; - services.nginx.enable = false; - # services.nginx.virtualHosts."${config.networking.hostName}" = { - # enableACME = lib.mkForce false; - # forceSSL = lib.mkForce false; - # default = true; - # }; + services.nginx = let + cert = config.services.acme-sh.certs.wildcard-yori-cc; + sslCertificate = cert.certPath; + sslCertificateKey = cert.keyPath; + in { + enable = true; + recommendedOptimisation = true; + recommendedTlsSettings = true; + recommendedProxySettings = true; + recommendedGzipSettings = true; + virtualHosts."unifi.yori.cc" = { + onlySSL = true; + inherit sslCertificate sslCertificateKey; + locations."/" = { + proxyPass = "https://[::1]:8443"; + proxyWebsockets = true; + extraConfig = '' + proxy_ssl_verify off; + proxy_ssl_session_reuse on; + ''; + }; + }; + virtualHosts."frumar.yori.cc" = { + enableACME = lib.mkForce false; + forceSSL = lib.mkForce false; + default = true; + }; + }; boot.supportedFilesystems = [ "zfs" ]; services.yorick.torrent-vpn = { enable = true; @@ -49,12 +71,12 @@ # victoriametrics remoteWrite = [{ url = "http://127.0.0.1:8428/api/v1/write"; }]; scrapeConfigs = [ - { - job_name = "smartmeter"; - # prometheus doesn't support mdns :thinking_face: - static_configs = [{ targets = [ "192.168.178.30" ]; }]; - scrape_interval = "10s"; - } + # { + # job_name = "smartmeter"; + # # prometheus doesn't support mdns :thinking_face: + # static_configs = [{ targets = [ "192.168.178.30" ]; }]; + # scrape_interval = "10s"; + # } { job_name = "node"; static_configs = [{ targets = [ "localhost:9100" ]; }]; @@ -62,14 +84,14 @@ # job_name = "unifi"; # static_configs = [ { targets = [ "localhost:9130" ]; } ]; } - { - job_name = "thermometer"; - static_configs = [{ targets = [ "192.168.178.21:8000" ]; }]; - } - { - job_name = "esphome"; - static_configs = [{ targets = [ "192.168.178.77" ]; }]; - } + # { + # job_name = "thermometer"; + # static_configs = [{ targets = [ "192.168.178.21:8000" ]; }]; + # } + # { + # job_name = "esphome"; + # static_configs = [{ targets = [ "192.168.178.77" ]; }]; + # } ]; exporters.node.enable = true; # exporters.unifi = { @@ -82,7 +104,7 @@ }; boot.zfs.requestEncryptionCredentials = false; networking.firewall.interfaces.wg-y.allowedTCPPorts = [ 3000 9090 8443 ]; - networking.firewall.allowedTCPPorts = [ 1883 5357 ]; + networking.firewall.allowedTCPPorts = [ 1883 5357 443 ]; networking.firewall.allowedUDPPorts = [ 1883 3702 ]; services.rabbitmq = { enable = true; @@ -100,7 +122,61 @@ AUTH_GOOGLE_ALLOW_SIGN_UP = "false"; }; }; - age.secrets.grafana.file = ../../secrets/grafana.env.age; + services.zigbee2mqtt = { + enable = true; + settings.availability = true; + settings.device_options = { + retain = true; + legacy = false; + }; + settings.serial.port = "/dev/ttyUSB0"; + }; + services.home-assistant = { + enable = true; + openFirewall = true; + extraComponents = [ + "default_config" + "androidtv" + "esphome" + "met" + "unifi" "yeelight" "plex" "frontend" + "automation" "device_automation" + ]; + config = { + media_player = [ + { + platform = "androidtv"; + host = "192.168.2.181"; + name = "shield"; + device_class = "androidtv"; + } + ]; + mobile_app = {}; + default_config = {}; + system_log = {}; + "map" = {}; + + frontend.themes = "!include_dir_merge_named themes"; + automation = "!include automations.yaml"; + homeassistant = { + name = "Home"; + latitude = "51.84"; + longitude = "5.85"; + elevation = "0"; + unit_system = "metric"; + time_zone = "Europe/Amsterdam"; + }; + }; + }; + age.secrets = { + grafana.file = ../../secrets/grafana.env.age; + transip-key = { + file = ../../secrets/transip-key.age; + mode = "770"; + owner = "nginx"; + group = "nginx"; + }; + }; systemd.services.grafana.serviceConfig.EnvironmentFile = config.age.secrets.grafana.path; services.zfs = { trim.enable = false; # no ssd's @@ -110,8 +186,8 @@ }; }; services.samba = { - enable = true; - openFirewall = true; + enable = false; + openFirewall = false; shares.public = { path = "/data/plexmedia"; browseable = "yes"; @@ -124,7 +200,7 @@ }; services.samba-wsdd = { enable = true; - interface = "eno2"; + interface = "eno1"; hostname = "NAS"; }; services.sonarr = { @@ -166,4 +242,16 @@ pyroscope rtorrent ]; + services.acme-sh.certs.wildcard-yori-cc = { + mainDomain = "*.yori.cc"; + dns = "dns_transip"; + production = true; + postRun = "systemctl reload nginx || true"; + inherit (config.services.nginx) user group; + }; + systemd.services.acme-sh-wildcard-yori-cc.environment = { + TRANSIP_Username = "yorickvp"; + TRANSIP_Key_File = config.age.secrets.transip-key.path; + }; + } diff --git a/nixos/modules/acme-sh.nix b/nixos/modules/acme-sh.nix new file mode 100644 index 0000000..2af49a2 --- /dev/null +++ b/nixos/modules/acme-sh.nix @@ -0,0 +1,166 @@ +# SPDX-FileCopyrightText: 2020 Serokell +# +# SPDX-License-Identifier: MPL-2.0 + +{ config, lib, pkgs, ... }: + +let + cfg = config.services.acme-sh; + # todo: upstream into serokell.nix + dnstype = lib.types.enum [ "dns_aws" "dns_dnsimple" "dns_transip" ]; + submod = with lib; config: { + domains = mkOption { + type = types.coercedTo + (types.listOf types.str) + (f: lib.genAttrs f (x: config.dns)) (types.attrsOf dnstype); + default = { "${config.mainDomain}" = config.dns; }; + }; + mainDomain = mkOption { + type = types.str; + description = "domain to use as primary domain for the cert"; + }; + postRun = mkOption { + type = types.str; + default = "true"; + }; + keyFile = mkOption { + type = types.str; + default = "/dev/null"; + }; + user = mkOption { + type = types.str; + default = "root"; + description = "User running the ACME client."; + }; + + group = mkOption { + type = types.str; + default = "root"; + description = "Group running the ACME client."; + }; + + server = mkOption { + type = types.str; + default = "letsencrypt"; + description = "Certificate Authority to use"; + }; + dns = mkOption { + type = dnstype; + }; + renewInterval = mkOption { + type = types.str; + default = "weekly"; + description = '' + Systemd calendar expression when to check for renewal. See + systemd.time + 7. + ''; + }; + production = mkOption { + type = types.bool; + default = true; + }; + statePath = mkOption { + readOnly = true; + type = types.str; + }; + keyPath = mkOption { + readOnly = true; + type = types.str; + }; + certPath = mkOption { + readOnly = true; + type = types.str; + }; + consulLock = mkOption { + type = types.nullOr types.str; + default = null; + example = "vault"; + }; +}; +in +{ + options.services.acme-sh = with lib; { + stateDir = lib.mkOption { + type = types.str; + default = "/var/lib/acme.sh"; + }; + certs = lib.mkOption { + type = types.attrsOf (types.submodule ({config, name, ...}: (with config; { + options = submod config; + config.statePath = "${cfg.stateDir}/${name}"; + config.keyPath = "${statePath}/${mainDomain}/${mainDomain}.key"; + config.certPath = "${statePath}/${mainDomain}/fullchain.cer"; + }))); + default = {}; + }; + }; + config = { + systemd.services = lib.mapAttrs' (name: value: lib.nameValuePair "acme-sh-${name}" (with value; { + description = "Renew ACME Certificate for ${name}"; + after = + [ "network.target" "network-online.target" ] + # wait for consul if we use it for locking + ++ lib.optionals (consulLock != null) [ "consul.service" ]; + wants = [ "network-online.target" ]; + serviceConfig = { + Type = "oneshot"; + PermissionsStartOnly = true; + User = user; + Group = group; + PrivateTmp = true; + EnvironmentFile = keyFile; + SuccessExitStatus = "0 2"; + }; + path = with pkgs; [ acme-sh systemd util-linuxMinimal procps ]; + preStart = '' + mkdir -p ${cfg.stateDir} + chown 'root:root' ${cfg.stateDir} + chmod 755 ${cfg.stateDir} + mkdir -p "${statePath}" + chown -R '${user}:${group}' "${statePath}" + chmod 750 "${statePath}" + rm -f "${statePath}/renewed" + ''; + environment.LE_WORKING_DIR = statePath; + environment.SHELL = "${pkgs.bash}/bin/bash"; + script = let + mapDomain = name: dns: ''-d "${name}" --dns ${dns}''; + primary = mapDomain mainDomain domains."${mainDomain}"; + domainsStr = lib.concatStringsSep " " ([primary] ++ (lib.remove primary (lib.mapAttrsToList mapDomain domains))); + cmd = ''acme.sh --server ${server} --issue ${lib.optionalString (!production) "--test"} ${domainsStr} --reloadcmd "touch ${statePath}/renewed" --syslog 6 > /dev/null''; + in + if consulLock == null then '' + ${cmd} + rm -f "$LE_WORKING_DIR/account.conf" + '' else '' + # consul lock does not expose the exit code, because of platform compatiblity or something + # write it to the 'ecode' file, or exit 1 if it fails altogether + if ${config.services.consul.package}/bin/consul lock -verbose "${consulLock}" '${cmd}; echo $? > ${statePath}/ecode'; then + rm -f "$LE_WORKING_DIR/account.conf" + exit $(cat ${statePath}/ecode) + else + rm -f "$LE_WORKING_DIR/account.conf" + exit 1 + fi + ''; + postStart = '' + if [ -e "${statePath}/renewed" ]; then + ${postRun} + rm -f "${statePath}/renewed" + fi + ''; + })) cfg.certs; + systemd.timers = lib.mapAttrs' (name: value: lib.nameValuePair "acme-sh-${name}" (with value; { + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = renewInterval; + Unit = "acme-sh-${name}.service"; + Persistent = "yes"; + AccuracySec = "5m"; + RandomizedDelaySec = "1h"; + }; + })) cfg.certs; + }; + +} diff --git a/nixos/modules/nginx.nix b/nixos/modules/nginx.nix index 6982e14..b8608cf 100644 --- a/nixos/modules/nginx.nix +++ b/nixos/modules/nginx.nix @@ -28,6 +28,7 @@ in { if ! [[ -e /etc/nginx/dhparam.pem ]]; then mkdir -p /etc/nginx/ ${pkgs.openssl}/bin/openssl dhparam -out /etc/nginx/dhparam.pem 2048 + chown nginx:nginx /etc/nginx/dhparam.pem fi ''; }; diff --git a/nixos/roles/default.nix b/nixos/roles/default.nix index 96aa32b..b3eb7ff 100644 --- a/nixos/roles/default.nix +++ b/nixos/roles/default.nix @@ -6,6 +6,7 @@ let in { imports = [ inputs.agenix.nixosModule + ../modules/acme-sh.nix ../modules/tor-hidden-service.nix ../modules/nginx.nix ../modules/lumi-cache.nix diff --git a/secrets/secrets.nix b/secrets/secrets.nix index 3728ca7bd3a60d5d12c40f4d5ccef6805fec3b99..52722993fc3553614734949c7522976e2362c0bd 100644 GIT binary patch literal 1410 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 Q16^c72c6W*P{XpEq`mm0asU7T diff --git a/secrets/transip-key.age b/secrets/transip-key.age new file mode 100644 index 0000000000000000000000000000000000000000..a3ef8e2c6fc77d099f0bcbf65c97a9106b461098 GIT binary patch literal 2115 zcmV-J2)y@UXJsvAZewzJaCB*JZZ2{wb3N1b$U3XYyS2HbVa%Ew2WeP<^Z$@=Y zF;GHUHd=K!XEjP^T5L8)F)wy=PG&GKGD1#8Z%S`dc57u&aWY{mVOLQ>SWGxWXf;Q1 zM0hfBNKXo4adk0pP%>0dZdET$c`$WPQ3@?BEg(xTHa1~Tc{EH>b#XOFH#0U(b5b!v zdNXiTcWzBMN?2w^Qej##Vp32>RSMk9T|o|Nf5cYMn2B=_#cfcW|8cgyH=03ixf(h% zpq9*Y?eS#7a3kl)Uf1moo{0;kCNgmf5-S4oP|Mkiw;a&VSNGIJD|RhS>UB;HdSOA% z;|4HMpfn{Qc0=~oT%u8;mRLFi!p3t@_Y$S|=T4G|Gq-l=Fu$ov7*ZLQ8f*9M3KVb8AK+m*QkIChkq>!+dwR@DC zuMhlWGs;GyIW>-iLh8kyG*!%&B#W&~p4a=Z?A?6DP$|;FXL_Z^1vhKtyp4kXgtQtx z|CCP=2}bSThFU)pPH~4kAlU6xLTc_aJ(ZN`0_swvm`>AI^Cv5z8=o(*;5`h5^NPJ$ zX*FE`G7fL5UpJe8LRLrLQR&)#D3ctyNHo9=~?o1`!}o1V6AJ zkXfO54EF&=9iX&cS0f9>uv}Q^k1fh>{GYo}gH?5x+oEFSi#H4*IhHX@k8Xw#suy+( z+lM^3Ub#f?Z1YcTD$8cCQ9!O8g@05Qcv_d?%r)RNoh~Q+dms6rM?_Yf)1wr}-}sn} zz_C&&2Y-3*j&sHrCOj)4HaJjoN>rD#+y36;!AQ%&R^(B^evC51C@fa4@&y1@CQN~b zQ$*$Qp580e)J1y7p#nrKlkhAog-E?ib)YD!Bd=aae2u?^zjXV&n_8r1M;XRYPRTY` zrclo8LaV%H6h7!-LcCBe^AJ~M-gHnfVgzkuseq})}CH1=#L(@KZDp^ z9izh$=sFv0HIQ4`2m4|@RuX&zir0~h;+~#WF zv>fvCdbG2Os>q`@l38CH7{q!@)2snm8+hGriE(cX?9;v&yPYR<0Rbh!5upjC_^XzU zz9e-@W=JWtc>c5XQA1?exriky+ho$k?sj14DKnlH$hDfcgofPuf3MX15rf>EPH7LY z;@MQbe@1N@r7}~`qC1CG1q##gTUR1TFLY2Ww7I_@8_CW+-r#YNpgs4Txx6<-g6P z+bFFOI0H6!;Vg-!`&Q$sNyA=FyGq*nNggyHiKFuutRT}il7P2iXqh0mv{ieSp5 zWm4#r+>`#^Koi@lG{q+GY%TJxuM&h7X+jKHV- zR13ViN)l||>H>+ZKIKDq7#Q@z`{N7isn79*H8vL!l#T)-A8|Pr~wJgV4?jj$$DD znI#yaWg~AKyD60QT`fK6Rzwgd*`K1aibcnr@dhz3SFs-SrQOl`Z+H3j&$+u?W?SJF tbn!61$VGDSMv%*b&G)Yeq$$%WU)M|$y{&&Ltk3NoYr0B!zE@IiC}62L;hF#d literal 0 HcmV?d00001