From 734a2fc02f785e8885d9d070e7f57084302c1317 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Sat, 23 Sep 2023 16:02:44 +0200 Subject: [PATCH] frumar: move from acme.sh to lego --- nixos/machines/frumar/default.nix | 44 +++----- nixos/modules/acme-sh.nix | 166 ------------------------------ nixos/modules/nginx.nix | 10 +- nixos/roles/default.nix | 1 - 4 files changed, 17 insertions(+), 204 deletions(-) delete mode 100644 nixos/modules/acme-sh.nix diff --git a/nixos/machines/frumar/default.nix b/nixos/machines/frumar/default.nix index 35a31d0..a9c19b3 100644 --- a/nixos/machines/frumar/default.nix +++ b/nixos/machines/frumar/default.nix @@ -16,13 +16,13 @@ }) ]; systemd.tmpfiles.rules = lib.mkAfter [ - "d ${config.services.acme-sh.stateDir}/selfsign 0700 nginx nginx" + "d /var/lib/acme.sh/selfsign 0700 nginx nginx" ]; systemd.services."yori-selfsigned-ca" = { description = "Generate self-signed fallback"; path = with pkgs; [ minica ]; unitConfig = { - ConditionPathExists = "!${config.services.acme-sh.stateDir}/selfsign/selfsigned.local/key.pem"; + ConditionPathExists = "!/var/lib/acme.sh/selfsign/selfsigned.local/key.pem"; StartLimitIntervalSec = 0; }; serviceConfig = { @@ -31,7 +31,7 @@ UMask = "0077"; Type = "oneshot"; PrivateTmp = true; - WorkingDirectory = "${config.services.acme-sh.stateDir}/selfsign"; + WorkingDirectory = "/var/lib/acme.sh/selfsign"; }; script = "minica --domains selfsigned.local"; }; @@ -40,19 +40,11 @@ after = [ "yori-selfsigned-ca.service" ]; }; - services.nginx = let - cert = config.services.acme-sh.certs.wildcard-yori-cc; - sslCertificate = cert.certPath; - sslCertificateKey = cert.keyPath; - in { + services.nginx = { enable = true; - recommendedOptimisation = true; - recommendedTlsSettings = true; - recommendedProxySettings = true; - recommendedGzipSettings = true; virtualHosts."unifi.yori.cc" = { onlySSL = true; - inherit sslCertificate sslCertificateKey; + useACMEHost = "wildcard.yori.cc"; locations."/" = { proxyPass = "https://[::1]:8443"; proxyWebsockets = true; @@ -64,7 +56,7 @@ }; virtualHosts."home-assistant.yori.cc" = { onlySSL = true; - inherit sslCertificate sslCertificateKey; + useACMEHost = "wildcard.yori.cc"; locations."/" = { proxyPass = "http://[::1]:8123"; proxyWebsockets = true; @@ -72,10 +64,8 @@ }; virtualHosts."frumar.yori.cc" = { enableACME = lib.mkForce false; - forceSSL = true; sslCertificate = "/var/lib/acme.sh/selfsign/selfsigned.local/cert.pem"; sslCertificateKey = "/var/lib/acme.sh/selfsign/selfsigned.local/key.pem"; - default = true; }; }; boot.supportedFilesystems = [ "zfs" ]; @@ -235,11 +225,10 @@ age.secrets = { grafana.file = ../../../secrets/grafana.env.age; frumar-mail-pass.file = ../../../secrets/frumar-mail-pass.age; - transip-key = { + acme-transip-key = { file = ../../../secrets/transip-key.age; mode = "770"; - owner = "nginx"; - group = "nginx"; + group = "acme"; }; }; systemd.services.grafana.serviceConfig.EnvironmentFile = config.age.secrets.grafana.path; @@ -324,16 +313,15 @@ unzip yscripts.absorb ]; - 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; + security.acme.certs."wildcard.yori.cc" = { + domain = "*.yori.cc"; + dnsProvider = "transip"; + reloadServices = [ "nginx.service" ]; }; - systemd.services.acme-sh-wildcard-yori-cc.environment = { - TRANSIP_Username = "yorickvp"; - TRANSIP_Key_File = config.age.secrets.transip-key.path; + users.users.nginx.extraGroups = [ "acme" ]; + systemd.services."acme-wildcard.yori.cc".environment = { + TRANSIP_ACCOUNT_NAME = "yorickvp"; + TRANSIP_PRIVATE_KEY_PATH = config.age.secrets.acme-transip-key.path; }; programs.msmtp = { enable = true; diff --git a/nixos/modules/acme-sh.nix b/nixos/modules/acme-sh.nix deleted file mode 100644 index d7c19ce..0000000 --- a/nixos/modules/acme-sh.nix +++ /dev/null @@ -1,166 +0,0 @@ -# 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}_ecc/${mainDomain}.key"; - config.certPath = "${statePath}/${mainDomain}_ecc/fullchain.cer"; - }))); - default = {}; - }; - }; - config = { - systemd.tmpfiles.rules = if cfg.certs != {} then [ - "d ${cfg.stateDir} 0755 root root" - ] else []; - 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 "${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 b8608cf..e201465 100644 --- a/nixos/modules/nginx.nix +++ b/nixos/modules/nginx.nix @@ -1,14 +1,6 @@ { config, lib, pkgs, ... }: -let - sslcfg = dir: '' - ssl on; - ssl_certificate_key ${dir}/key.pem; - ssl_certificate ${dir}/fullchain.pem; - ssl_trusted_certificate ${dir}/fullchain.pem; - add_header Strict-Transport-Security max-age=15768000; - ''; -in { +{ config = lib.mkIf config.services.nginx.enable { services.nginx = { recommendedTlsSettings = true; diff --git a/nixos/roles/default.nix b/nixos/roles/default.nix index ece1c3d..1c935ef 100644 --- a/nixos/roles/default.nix +++ b/nixos/roles/default.nix @@ -5,7 +5,6 @@ let in { imports = [ inputs.agenix.nixosModules.default - ../modules/acme-sh.nix ../modules/tor-hidden-service.nix ../modules/nginx.nix ../modules/lumi-cache.nix