]> Git — Sourcephile - sourcephile-nix.git/blob - nixos/modules/services/security/x509.nix
mermet: add nginx and fix stuffs
[sourcephile-nix.git] / nixos / modules / services / security / x509.nix
1 {pkgs, lib, config, ...}:
2 let inherit (builtins) toString;
3 inherit (lib) types;
4 inherit (config.services) x509;
5 in
6 {
7 options.services.x509 = {
8 enable = lib.mkEnableOption "Configuration of one X.509 certificate";
9 scheme = lib.mkOption {
10 type = types.enum [ "manual" "self-signed" "letsencrypt" ];
11 default = "self-signed";
12 description = ''
13 Scheme for X.509 material.
14
15 manual:
16 Use certificate and key manually copied on the target at certFile and keyFile.
17 self-signed:
18 Generate a self-signed certificate.
19 letsencrypt:
20 Generate a certificate signed by Let's Encrypt.
21 '';
22 };
23 dir = lib.mkOption {
24 type = types.string;
25 default = "/var/x509";
26 description = ''In "self-signed" scheme: directory of the certificate and key.'';
27 };
28 certFile = lib.mkOption {
29 type = types.path;
30 example = "/var/x509/cert.pem";
31 description = ''In "manual" scheme: location of the certificate'';
32 };
33 keyFile = lib.mkOption {
34 type = types.path;
35 example = "/var/x509/key.pem";
36 description = ''In "manual" scheme: location of the key file.'';
37 };
38 host = lib.mkOption {
39 type = types.nullOr types.string;
40 default = config.networking.domain;
41 };
42 distributionHost = lib.mkOption {
43 type = types.string;
44 default = config.networking.domain;
45 };
46 domains = lib.mkOption {
47 type = with types; listOf string;
48 example = [ "example.coop" ];
49 default = [];
50 };
51 days = lib.mkOption {
52 type = types.int;
53 default = 3650;
54 };
55 keySize = lib.mkOption {
56 type = types.int;
57 default = 4096;
58 };
59 cert = lib.mkOption {
60 type = types.string;
61 default =
62 if x509.scheme == "manual"
63 then x509.certFile
64 else if x509.scheme == "self-signed"
65 then "${x509.dir}/${x509.host}.cert.self-signed.pem"
66 else if x509.scheme == "letsencrypt"
67 then "/var/lib/acme/${x509.host}/fullchain.pem"
68 else throw ''Error: Certificate Scheme must be in [ "manual" "self-signed" "letsencrypt" ]'';
69 };
70 key = lib.mkOption {
71 type = types.string;
72 default =
73 if x509.scheme == "manual"
74 then x509.keyFile
75 else if x509.scheme == "self-signed"
76 then "${x509.dir}/${x509.host}.key.pem"
77 else if x509.scheme == "letsencrypt"
78 then "/var/lib/acme/${x509.host}/key.pem"
79 else throw ''Error: Certificate Scheme must be in [ "manual" "self-signed" "letsencrypt" ]'';
80 };
81 opensslConf = lib.mkOption {
82 type = (with types; attrsOf string);
83 default = x509.opensslConf_common //
84 x509.opensslConf_self-signed //
85 x509.opensslConf_auth-signed //
86 x509.opensslConf_user-cert;
87 apply = cnf:
88 pkgs.writeText "openssl.conf"
89 (lib.concatStrings
90 (lib.mapAttrsToList
91 (title: body:
92 (if title == "" then "" else "[ ${title} ]\n") +
93 body)
94 cnf));
95 };
96 opensslConf_common = lib.mkOption {
97 type = (with types; attrsOf string);
98 default = {
99 "" = ''
100 RANDFILE = /var/x509/openssl.rand
101 oid_section = extra_oids
102 '';
103 extra_oids = ''
104 # NOTE: only useful for Extended Validation (EV)
105 jurisdictionOfIncorporationLocalityName = 1.3.6.1.4.1.311.60.2.1.1
106 jurisdictionOfIncorporationStateOrProvinceName = 1.3.6.1.4.1.311.60.2.1.2
107 jurisdictionOfIncorporationCountryName = 1.3.6.1.4.1.311.60.2.1.3
108 '';
109 req = ''
110 prompt = no
111 distinguished_name = distinguished_name
112 string_mask = pkix
113 #x509_extensions = root_extensions
114 #req_extensions = extension
115 #attributes = req_attributes
116 '';
117 distinguished_name = ''
118 commonName = ${x509.host}
119 #countryName =
120 #stateOrProvinceName =
121 #localityName =
122 #"0.organizationName" =
123 #organizationalUnitName =
124 #businessCategory =
125 #jurisdictionOfIncorporationLocalityName = $stateOrProvinceName
126 #jurisdictionOfIncorporationStateOrProvinceName = $stateOrProvinceName
127 #jurisdictionOfIncorporationCountryName = $countryName
128 '';
129 };
130 };
131 opensslConf_self-signed = lib.mkOption {
132 type = (with types; attrsOf string);
133 default = {
134 self_signed_extensions = ''
135 basicConstraints = critical,CA:TRUE,pathlen:0
136 keyUsage = keyCertSign,cRLSign,digitalSignature,keyEncipherment
137 subjectAltName = ${lib.concatMapStringsSep "," (dom: "DNS:${dom}") x509.domains}
138 subjectKeyIdentifier = hash
139 issuerAltName = issuer:copy
140 authorityKeyIdentifier = keyid:always,issuer:always
141 authorityInfoAccess = caIssuers;URI:http://${x509.distributionHost}/x509/${x509.host}.cert.pem
142 crlDistributionPoints = URI:http://${x509.distributionHost}/x509/${x509.host}.crl.self-signed.pem
143 '';
144 self_signed_ca = ''
145 dir = ${x509.dir}/pub
146 private_key = $dir/sec/key.pem
147 crl_dir = $dir
148 crlnumber = $dir/crl.self-signed.num
149 crl = $dir/crl.self-signed.pem
150 database = $dir/idx.self-signed.txt
151 '';
152 };
153 };
154 opensslConf_auth-signed = lib.mkOption {
155 type = (with types; attrsOf string);
156 default = {
157 auth_signed_extensions = ''
158 basicConstraints = critical,CA:FALSE
159 keyUsage = digitalSignature,keyEncipherment
160 subjectAltName = ${lib.concatMapStringsSep "," (dom: "DNS:${dom}") x509.domains}
161 subjectKeyIdentifier = hash
162 issuerAltName = issuer:copy
163 authorityKeyIdentifier = keyid:always,issuer:always
164 authorityInfoAccess = caIssuers;URI:http://${x509.distributionHost}/x509/${x509.host}.cert.pem
165 crlDistributionPoints = URI:http://${x509.distributionHost}/x509/${x509.host}.crl.pem
166 certificatePolicies = @certificate_policies
167 '';
168 certificate_policies = ''
169 policyIdentifier = 1.2.250.1.42
170 CPS.1 = https://${x509.host}/x509/cps
171 '';
172 ca = ''
173 dir = ${x509.dir}/pub
174 private_key = $dir/sec/key.pem
175 crl_dir = $dir
176 crlnumber = $dir/crl.num
177 crl = $dir/crl.pem
178 database = $dir/idx.txt
179 '';
180 };
181 };
182 opensslConf_user-cert = lib.mkOption {
183 type = (with types; attrsOf string);
184 default = {
185 user_cert_extensions = ''
186 basicConstraints = critical,CA:FALSE,pathlen:0
187 keyUsage = digitalSignature,keyEncipherment
188 subjectAltName = email:$ENV::user@${x509.host}
189 subjectKeyIdentifier = hash
190 issuerAltName = issuer:copy
191 authorityKeyIdentifier = keyid:always,issuer:always
192 authorityInfoAccess = caIssuers;URI:http://${x509.distributionHost}/x509/${x509.host}.cert.pem
193 '';
194 };
195 };
196 };
197
198 config = lib.mkIf (x509.scheme == "self-signed") {
199 systemd.services.x509 = {
200 enable = true;
201 wantedBy = [ "multi-user.target" ];
202 before = [ "keys.target" ];
203 serviceConfig = {
204 ExecStart = pkgs.writeShellScript "x509.service" ''
205 # Make private key
206 [ -s "${x509.key}" ] || {
207 mkdir -p ${x509.dir}
208 (
209 umask 077
210 ${pkgs.openssl}/bin/openssl genrsa \
211 -out "${x509.key}" \
212 -rand /dev/urandom \
213 ${toString x509.keySize}
214 )
215 }
216 # Make self-signed certificate
217 [ -s "${x509.cert}" ] &&
218 [ "${x509.cert}" -nt "${x509.key}" ] || {
219 user= \
220 ${pkgs.openssl}/bin/openssl req \
221 -batch \
222 -new \
223 -x509 \
224 -utf8 \
225 -rand /dev/urandom \
226 -config ${x509.opensslConf} \
227 -extensions self_signed_extensions \
228 -reqexts self_signed_extensions \
229 -inform PEM \
230 -outform PEM \
231 -keyform PEM \
232 ${lib.optionalString (x509.days != null) "-days ${toString x509.days}"} \
233 -set_serial 0x$(sleep 1; date '+%Y%m%d%H%M%S') \
234 -reqopt no_pubkey,no_sigdump \
235 -key ${x509.key} \
236 -out ${x509.cert}
237 }
238 '';
239 };
240 };
241 };
242
243 }