From 612781e8169bf13fde26091f2d6c55ebed6ccb6f Mon Sep 17 00:00:00 2001 From: Franz Pletz Date: Sun, 6 Dec 2015 16:55:09 +0100 Subject: [PATCH] simp_le service: letsencrypt cert auto-renewal This new service invokes `simp_le` for a defined set of certs on a regular basis with a systemd timer. `simp_le` is smart enough to handle account registration, domain validation and renewal on its own. The only thing required is an existing HTTP server that serves the path `/.well-known/acme-challenge` from the webroot cert parameter. Example: services.simp_le.certs."foo.example.com" = { webroot = "/var/www/challenges"; extraDomains = [ "www.example.com" ]; email = "foo@example.com"; validMin = 2592000; renewInterval = "weekly"; }; Example Nginx vhost: services.nginx.appendConfig = '' http { server { server_name _; listen 80; listen [::]:80; location /.well-known/acme-challenge { root /var/www/challenges; } location / { return 301 https://$host$request_uri; } } } ''; --- nixos/modules/module-list.nix | 1 + nixos/modules/services/security/simp_le.nix | 133 ++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 nixos/modules/services/security/simp_le.nix diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 963daf721ad..c708f095f40 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -388,6 +388,7 @@ ./services/security/hologram.nix ./services/security/munge.nix ./services/security/physlock.nix + ./services/security/simp_le.nix ./services/security/torify.nix ./services/security/tor.nix ./services/security/torsocks.nix diff --git a/nixos/modules/services/security/simp_le.nix b/nixos/modules/services/security/simp_le.nix new file mode 100644 index 00000000000..d578d5bb679 --- /dev/null +++ b/nixos/modules/services/security/simp_le.nix @@ -0,0 +1,133 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.simp_le; + + certOpts = { ... }: { + options = { + webroot = mkOption { + type = types.nullOr types.str; + default = null; + description = "Where the webroot of the HTTP vhost is located."; + }; + + validMin = mkOption { + type = types.int; + default = 2592000; + description = "Minimum remaining validity before renewal in seconds."; + }; + + renewInterval = mkOption { + type = types.str; + default = "weekly"; + description = "Systemd calendar expression when to check for renewal. See systemd.time(7)."; + }; + + email = mkOption { + type = types.nullOr types.str; + default = null; + description = "Contact email address for the CA to be able to reach you."; + }; + + plugins = mkOption { + type = types.listOf (types.enum [ + "cert.der" "cert.pem" "chain.der" "chain.pem" "external_pem.sh" + "fullchain.der" "fullchain.pem" "key.der" "key.pem" + ]); + default = [ "fullchain.pem" "key.pem" ]; + description = "Plugins to enable."; + }; + + extraDomains = mkOption { + default = [ ]; + type = types.listOf types.str; + description = "More domains to include in the certificate."; + example = [ "example.com" "foo.example.com:/var/www/foo" ]; + }; + }; + }; + +in + +{ + + ###### interface + + options = { + services.simp_le = { + directory = mkOption { + default = "/etc/ssl"; + type = types.str; + description = '' + Directory where certs will be stored by default. + ''; + }; + + certs = mkOption { + default = { }; + type = types.loaOf types.optionSet; + description = '' + Attribute set of certificates to get signed and renewed. + ''; + options = [ certOpts ]; + example = { + "foo.example.com" = { + webroot = "/var/www/challenges/"; + email = "foo@example.com"; + extraDomains = [ "www.example.com" "example.com" ]; + }; + "bar.example.com" = { + webroot = "/var/www/challenges/"; + email = "bar@example.com"; + }; + }; + }; + }; + }; + + ###### implementation + config = mkIf (cfg.certs != { }) { + + systemd.services = flip mapAttrs' cfg.certs (cert: data: nameValuePair + ("simp_le-${cert}") + ({ + description = "simp_le cert renewal for ${cert}"; + after = [ "network.target" ]; + serviceConfig = { + Type = "oneshot"; + SuccessExitStatus = "0 1"; + }; + preStart = '' + mkdir -p "${cfg.directory}/${cert}" + ''; + script = '' + WEBROOT="${optionalString (data.webroot == null) data.webroot}" + cd "${cfg.directory}/${cert}" + ${pkgs.simp_le}/bin/simp_le -v \ + ${concatMapStringsSep " " (p: "-f ${p}") data.plugins} \ + -d ${cert} --default_root "$WEBROOT" \ + ${concatMapStringsSep " " (p: "-d ${p}") data.extraDomains} \ + ${optionalString (data.email != null) "--email ${data.email}"} \ + --valid_min ${toString data.validMin} + ''; + }) + ); + + systemd.timers = flip mapAttrs' cfg.certs (cert: data: nameValuePair + ("simp_le-${cert}") + ({ + description = "timer for simp_le cert renewal of ${cert}"; + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = data.renewInterval; + Unit = "simp_le-${cert}.service"; + }; + }) + ); + + }; + +}