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-----"
137 "$tool"/openssl s_client \
139 -servername "$servername" \
142 ${pkgs.gnused}/bin/sed -n \
143 -e "/^$begin/,/^$end/p" \
144 >"$x509/$host/crt.pem"
149 enable = lib.mkEnableOption "Configuration of X.509 certificates";
150 opensslHome = lib.mkOption {
152 default = "sec/openssl";
157 certificates = lib.mkOption {
162 host = "example.coop";
163 distributionPoint = "http://example.coop/x509";
164 domains = [ "example.org" ];
170 type = types.attrsOf (types.submodule ({name, ...}:
171 let cert = openssl.certificates."${name}"; in
173 #config.uid = lib.mkDefault uid;
175 host = lib.mkOption {
176 type = types.nullOr types.str;
177 example = "example.coop";
179 passPrefix = lib.mkOption {
184 distributionPoint = lib.mkOption {
185 type = types.nullOr types.str;
187 example = "http://example.coop/x509";
189 domains = lib.mkOption {
190 type = types.listOf types.str;
191 example = [ "example.coop" ];
194 days = lib.mkOption {
199 keySize = lib.mkOption {
204 digest = lib.mkOption {
208 description = "A digest from openssl list --digest-commands";
210 opensslConf = lib.mkOption {
211 type = types.attrsOf types.lines;
215 cert.opensslConf_common //
216 cert.opensslConf_self-signed //
217 cert.opensslConf_auth-signed //
218 cert.opensslConf_user-cert //
221 pkgs.writeText "openssl.conf"
224 (sectionHead: sectionBody:
225 (if sectionHead == "" then "" else "[ ${sectionHead} ]\n") +
229 opensslConf_common = lib.mkOption {
230 type = types.attrsOf types.lines;
233 RANDFILE = ${openssl.opensslHome}/openssl.rand
234 oid_section = extra_oids
235 default_md = ${cert.digest}
241 distinguished_name = distinguished_name
243 #x509_extensions = root_extensions
244 #req_extensions = extension
245 #attributes = req_attributes
247 distinguished_name = ''
248 commonName = ${cert.host}
250 #stateOrProvinceName =
252 #"0.organizationName" =
253 #organizationalUnitName =
258 opensslConf_self-signed = lib.mkOption {
259 type = types.attrsOf types.lines;
261 self_signed_extensions = ''
262 basicConstraints = critical,CA:TRUE,pathlen:0
263 keyUsage = keyCertSign,cRLSign,digitalSignature,keyEncipherment
264 subjectAltName = ${lib.concatMapStringsSep "," (dom: "DNS:${dom}") cert.domains}
265 subjectKeyIdentifier = hash
266 issuerAltName = issuer:copy
267 authorityKeyIdentifier = keyid:always,issuer:always
268 '' + lib.optionalString (cert.distributionPoint != null) ''
269 authorityInfoAccess = caIssuers;URI:${cert.distributionPoint}/${cert.host}.cert.pem
270 crlDistributionPoints = URI:${cert.distributionPoint}/${cert.host}.crl.self-signed.pem
274 dir = ${openssl.opensslHome}/${name}
275 private_key = $dir/key.pem
277 crlnumber = $dir/crl.self-signed.num
278 crl = $dir/crl.self-signed.pem
279 database = $dir/idx.self-signed.txt
284 opensslConf_auth-signed = lib.mkOption {
285 type = types.attrsOf types.lines;
287 auth_signed_extensions = ''
288 basicConstraints = critical,CA:FALSE
289 keyUsage = digitalSignature,keyEncipherment
290 subjectAltName = ${lib.concatMapStringsSep "," (dom: "DNS:${dom}") cert.domains}
291 subjectKeyIdentifier = hash
292 issuerAltName = issuer:copy
293 authorityKeyIdentifier = keyid:always,issuer:always
294 certificatePolicies = @certificate_policies
295 '' + lib.optionalString (cert.distributionPoint != null) ''
296 authorityInfoAccess = caIssuers;URI:${cert.distributionPoint}/${cert.host}.cert.pem
297 crlDistributionPoints = URI:${cert.distributionPoint}/${cert.host}.crl.pem
299 certificate_policies = lib.optionalString (cert.distributionPoint != null) ''
300 policyIdentifier = 1.2.250.1.42
301 CPS.1 = https://${cert.distributionPoint}/cps
305 dir = ${openssl.opensslHome}/${name}
306 private_key = $dir/key.pem
308 crlnumber = $dir/crl.num
310 database = $dir/idx.txt
315 opensslConf_user-cert = lib.mkOption {
316 type = types.attrsOf types.lines;
318 user_cert_extensions = ''
319 basicConstraints = critical,CA:FALSE,pathlen:0
320 keyUsage = digitalSignature,keyEncipherment
321 subjectAltName = email:$ENV::user@${cert.host}
322 subjectKeyIdentifier = hash
323 issuerAltName = issuer:copy
324 authorityKeyIdentifier = keyid:always,issuer:always
325 '' + lib.optionalString (cert.distributionPoint != null) ''
326 authorityInfoAccess = caIssuers;URI:${cert.distributionPoint}/${cert.host}.cert.pem
336 config = lib.mkIf openssl.enable {
337 nix-shell.buildInputs = [