You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
infra/modules/acme.nix

61 lines
2.0 KiB

# Let's Encrypt (ACME) certificate issuance, per-host HTTP-01.
#
# Termination & deployment scheme
# -------------------------------
# Each NixOS host terminates TLS itself for the subdomains it serves.
# There is no central certificate store and no distribution step.
#
# 1. A host declares which subdomains it serves via `domains = [ ... ]`
# in flake.nix (consumed by modules/nginx.nix).
# 2. With `infra.acme.enable = true`, the nginx module sets
# `enableACME = true; forceSSL = true;` on every served vhost.
# 3. security.acme requests one cert per vhost via the HTTP-01
# challenge; nginx answers /.well-known/acme-challenge/ on :80.
# 4. On issue/renewal, nginx is reloaded automatically
# (`reloadServices`), so the new certificate goes live at once.
# 5. Renewals run on a systemd timer; every host (core, deploy, ...)
# keeps its own certificates fresh — nothing is deployed by hand.
#
# Adding a new endpoint (e.g. deploy.domain.com):
# - add the host (or add the subdomain to an existing host's `domains`),
# - enable `infra.acme` on that host,
# - point the subdomain's DNS at the host and leave :80 reachable so
# the HTTP-01 challenge can complete.
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.infra.acme;
in {
options.infra.acme = {
enable = mkEnableOption "Let's Encrypt (ACME) TLS certificates";
email = mkOption {
type = types.str;
description = "Account email used for Let's Encrypt";
};
staging = mkOption {
type = types.bool;
default = false;
description = ''
Use the Let's Encrypt staging environment
(untrusted certificates, no rate limiting) for testing.
'';
};
};
config = mkIf cfg.enable {
security.acme = {
acceptTerms = true;
defaults = {
inherit (cfg) email;
reloadServices = [ "nginx.service" ];
} // optionalAttrs cfg.staging {
server = "https://acme-staging-v02.api.letsencrypt.org/directory";
};
};
};
}