1 { pkgs, lib, config, ... }:
3 inherit (builtins) toString;
4 inherit (pkgs.lib) types unlines;
5 inherit (config) openssl;
7 generateCerts = certs: unlines (lib.mapAttrsToList generateCert certs);
8 generateKey = id: cert: ''
9 mkdir -p "${openssl.opensslHome}/${id}"
10 test -s "$PASSWORD_STORE_DIR/${cert.passPrefix}/${id}/key.pem.gpg" || {
11 info " generateKey: $PASSWORD_STORE_DIR/${cert.passPrefix}/${id}/key.pem.gpg"
12 ${pkgs.openssl}/bin/openssl genrsa \
15 ${toString cert.keySize} |
16 ${pkgs.pass}/bin/pass insert --multiline "${cert.passPrefix}/${id}/key.pem"
19 generateCert = id: cert:
26 info " generateCert: ${openssl.opensslHome}/${id}/cert.self-signed.pem"
27 ${pkgs.pass}/bin/pass "${cert.passPrefix}/${id}/key.pem" |
29 ${pkgs.openssl}/bin/openssl req \
35 -config ${cert.opensslConf} \
36 -extensions self_signed_extensions \
37 -reqexts self_signed_extensions \
41 ${lib.optionalString (cert.days != null) "-days ${toString cert.days}"} \
42 -set_serial 0x$(sleep 1; date '+%Y%m%d%H%M%S') \
43 -reqopt no_pubkey,no_sigdump \
45 -out "${openssl.opensslHome}/${id}/cert.self-signed.pem"
47 if test ! -s "${openssl.opensslHome}/${id}/cert.self-signed.pem"
50 if test ! "${openssl.opensslHome}/${id}/cert.self-signed.pem" -nt \
51 "$PASSWORD_STORE_DIR/${cert.passPrefix}/${id}/key.pem.gpg"
54 test ! "''${DEEPCHECK:+set}" || {
55 info " generateCert: checking wether key and certificate match"
57 ${pkgs.pass}/bin/pass "${cert.passPrefix}/${id}/key.pem" |
58 ${pkgs.openssl}/bin/openssl rsa -modulus -noout -in /dev/stdin)"
60 ${pkgs.openssl}/bin/openssl x509 -noout -modulus \
61 -in "${openssl.opensslHome}/${id}/cert.self-signed.pem")"
62 test "$key_mod_pub" = "$crt_mod_pub" ||
63 error "key and certificate do not match"
70 echo >&2 "openssl-init: ERROR: $*"
76 echo >&2 "openssl-init: $*"
79 # Initialize the keyring according to openssl.certificates.
80 openssl-init = pkgs.writeShellScriptBin "openssl-init" (''
85 generateCerts openssl.certificates
87 openssl-cert-iter = pkgs.writeScriptBin "openssl-cert-iter" ''
89 # SYNTAX: $command .. <cert.pem
91 begin="-----BEGIN CERTIFICATE-----"
92 end="-----END CERTIFICATE-----"
95 ${pkgs.gnused}/bin/sed -n -e "/^$begin/,/^$end/p" |
96 while IFS= read -r line
100 ("$end") printf '%s\n%s\n%s\n' "$begin" "$(printf %s "$buf" | ${pkgs.gnused}/bin/sed -e 's/\(.\{64\}\)/\1\n/g')" "$end" | "$@";;
101 (*) buf="$buf$line";;
105 openssl-cert-print = pkgs.writeScriptBin "openssl-cert-print" ''
108 ${openssl-cert-iter}/bin/openssl-cert-iter \
109 ${pkgs.openssl}/bin/openssl x509 \
110 -nameopt multiline,-esc_msb,utf8 \
111 -certopt no_pubkey,no_sigdump \
113 ''${x509_md:+-"$x509_md"} \
117 ''${OPENSSL_FLAGS-} ''${OPENSSL_X509_FLAGS-}
119 openssl-cert-fetch = pkgs.writeScriptBin "openssl-cert-fetch" ''
122 x509="''${x509:-x509}"
126 ${pkgs.gnused}/bin/sed \
127 -e 's,^[a-z][a-z]*://,,' \
131 ${pkgs.gnused}/bin/sed \
132 -e 's,:[0-9][0-9]*,,')
133 mkdir -p "x509/$host"
134 begin="-----BEGIN CERTIFICATE-----"
135 end="-----END CERTIFICATE-----"
138 ${pkgs.openssl}/bin/openssl s_client \
140 -servername "$servername" \
143 ${pkgs.gnused}/bin/sed -n \
144 -e "/^$begin/,/^$end/p" \
145 >"$x509/$host/crt.pem"
150 enable = lib.mkEnableOption "Configuration of X.509 certificates";
151 opensslHome = lib.mkOption {
153 default = "sec/openssl";
158 certificates = lib.mkOption {
163 host = "example.coop";
164 distributionPoint = "http://example.coop/x509";
165 domains = [ "example.org" ];
171 type = types.attrsOf (types.submodule ({name, ...}:
172 let cert = openssl.certificates."${name}"; in
174 #config.uid = lib.mkDefault uid;
176 host = lib.mkOption {
177 type = types.nullOr types.str;
178 example = "example.coop";
180 passPrefix = lib.mkOption {
185 distributionPoint = lib.mkOption {
186 type = types.nullOr types.str;
188 example = "http://example.coop/x509";
190 domains = lib.mkOption {
191 type = types.listOf types.str;
192 example = [ "example.coop" ];
195 days = lib.mkOption {
200 keySize = lib.mkOption {
205 digest = lib.mkOption {
209 description = "A digest from openssl list --digest-commands";
211 opensslConf = lib.mkOption {
212 type = types.attrsOf types.lines;
216 cert.opensslConf_common //
217 cert.opensslConf_self-signed //
218 cert.opensslConf_auth-signed //
219 cert.opensslConf_user-cert //
222 pkgs.writeText "openssl.conf"
225 (sectionHead: sectionBody:
226 (if sectionHead == "" then "" else "[ ${sectionHead} ]\n") +
230 opensslConf_common = lib.mkOption {
231 type = types.attrsOf types.lines;
234 RANDFILE = ${openssl.opensslHome}/openssl.rand
235 oid_section = extra_oids
236 default_md = ${cert.digest}
242 distinguished_name = distinguished_name
244 #x509_extensions = root_extensions
245 #req_extensions = extension
246 #attributes = req_attributes
248 distinguished_name = ''
249 commonName = ${cert.host}
251 #stateOrProvinceName =
253 #"0.organizationName" =
254 #organizationalUnitName =
259 opensslConf_self-signed = lib.mkOption {
260 type = types.attrsOf types.lines;
262 self_signed_extensions = ''
263 basicConstraints = critical,CA:TRUE,pathlen:0
264 keyUsage = keyCertSign,cRLSign,digitalSignature,keyEncipherment
265 subjectAltName = ${lib.concatMapStringsSep "," (dom: "DNS:${dom}") cert.domains}
266 subjectKeyIdentifier = hash
267 issuerAltName = issuer:copy
268 authorityKeyIdentifier = keyid:always,issuer:always
269 '' + lib.optionalString (cert.distributionPoint != null) ''
270 authorityInfoAccess = caIssuers;URI:${cert.distributionPoint}/${cert.host}.cert.pem
271 crlDistributionPoints = URI:${cert.distributionPoint}/${cert.host}.crl.self-signed.pem
275 dir = ${openssl.opensslHome}/${name}
276 private_key = $dir/key.pem
278 crlnumber = $dir/crl.self-signed.num
279 crl = $dir/crl.self-signed.pem
280 database = $dir/idx.self-signed.txt
285 opensslConf_auth-signed = lib.mkOption {
286 type = types.attrsOf types.lines;
288 auth_signed_extensions = ''
289 basicConstraints = critical,CA:FALSE
290 keyUsage = digitalSignature,keyEncipherment
291 subjectAltName = ${lib.concatMapStringsSep "," (dom: "DNS:${dom}") cert.domains}
292 subjectKeyIdentifier = hash
293 issuerAltName = issuer:copy
294 authorityKeyIdentifier = keyid:always,issuer:always
295 certificatePolicies = @certificate_policies
296 '' + lib.optionalString (cert.distributionPoint != null) ''
297 authorityInfoAccess = caIssuers;URI:${cert.distributionPoint}/${cert.host}.cert.pem
298 crlDistributionPoints = URI:${cert.distributionPoint}/${cert.host}.crl.pem
300 certificate_policies = lib.optionalString (cert.distributionPoint != null) ''
301 policyIdentifier = 1.2.250.1.42
302 CPS.1 = https://${cert.distributionPoint}/cps
306 dir = ${openssl.opensslHome}/${name}
307 private_key = $dir/key.pem
309 crlnumber = $dir/crl.num
311 database = $dir/idx.txt
316 opensslConf_user-cert = lib.mkOption {
317 type = types.attrsOf types.lines;
319 user_cert_extensions = ''
320 basicConstraints = critical,CA:FALSE,pathlen:0
321 keyUsage = digitalSignature,keyEncipherment
322 subjectAltName = email:$ENV::user@${cert.host}
323 subjectKeyIdentifier = hash
324 issuerAltName = issuer:copy
325 authorityKeyIdentifier = keyid:always,issuer:always
326 '' + lib.optionalString (cert.distributionPoint != null) ''
327 authorityInfoAccess = caIssuers;URI:${cert.distributionPoint}/${cert.host}.cert.pem
337 config = lib.mkIf openssl.enable {
338 nix-shell.buildInputs = [