blob: 4d742246f5d8a75283b565a785480f6877fd5ef2 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
# Getting HTTPS to work for local domains is pretty hard. The approach I've
# gone with is to request a wildcard domain for `*.rumpenettet.linus.onl`. We
# can do this because `linus.onl` is a public domain which we have control
# over.
#
# This module requests a certificate from letsencrypt using DNS-01
# verification. I have an API token which can modify DNS records for
# `linus.onl`. This is how Lego (i.e. `security.acme`) proves domain ownership
# when renewing the certificate.
#
# Any services running under `rumpenettet.local.onl` and use this certificate.
# For NGINX that happens via `useACMEHost` and one of the options that enable
# HTTPS.
{
lib,
config,
...
}: {
security.acme = {
certs.${config.linus.local-dns.domain} = {
dnsProvider = "cloudflare";
dnsResolver = "1.1.1.1:53";
environmentFile = config.age.secrets.cloudflare-acme-token.path;
dnsPropagationCheck = true;
domain = "*.${config.linus.local-dns.domain}";
# To avoid the following cyclical ordering, we want this certificate to
# be under a different account, as defined by the account hash (which
# includes email).
#
# 1. `nginx.service` is ordered before `acme-rumpenettet.linus.onl.service`
# because NGINX hard crashes when certificates are missing.
# 2. `acme-rumpenettet.linus.onl.service` ordered before
# `acme-account-….target` because it is part of the account and not the
# chosen group leader.
# 3. `acme-account-….target` is ordered after
# `acme-git.linus.onl.service` because it is the group leader.
# 4. `nginx.service` is ordered before `acme-*.service` because it has to
# be online for the challenge to work.
#
# So the issue ony arises because we have a DNS-01 certificate and a
# HTTP-01 certificate linked (ordering whise) by the account target. And
# those different types of certificates are ordered before/after NGINX
# respectively.
#
# We break the cycle by making the DNS certificate part of a different
# account. In the future, a more elegant solution might be to use the
# same selfsigned trick that NGINX already uses for certificates with
# HTTP-01 validation.
email = "linusvejlo+${config.networking.hostName}[email protected]";
group = config.services.nginx.group;
reloadServices = ["nginx"];
};
};
# This file contains the variables that Lego needs to authenticate to
# Cloudflare. This is how we prove ownership of the domain.
#
# See: https://go-acme.github.io/lego/dns/cloudflare/
# See: https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#EnvironmentFile=
age.secrets.cloudflare-acme-token.file = ../../../secrets/cloudflare-acme-token.env.age;
# Use the certificate for each subdomain in NGINX. Luckily, we can be pretty
# opinionated since this isn't reusable logic.
#
# NOTE: This assumes that each subdomain *has* an NGINX virtual host, which
# may not be the case in the future.
services.nginx.virtualHosts = let
virtualHostConfig = subdomain:
lib.nameValuePair "${subdomain}.${config.linus.local-dns.domain}" {
forceSSL = true;
useACMEHost = config.linus.local-dns.domain; # Same as security.acme.certs.${...} above.
};
in
builtins.listToAttrs (map virtualHostConfig config.linus.local-dns.subdomains);
}
|