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 \
14 ${toString cert.keySize} |
15 ${pkgs.pass}/bin/pass insert --multiline "${cert.passPrefix}/${id}/key.pem"
18 generateCert = id: cert:
25 info " generateCert: ${openssl.opensslHome}/${id}/cert.self-signed.pem"
26 ${pkgs.pass}/bin/pass "${cert.passPrefix}/${id}/key.pem" |
28 ${pkgs.openssl}/bin/openssl req \
34 -config ${cert.opensslConf} \
35 -extensions self_signed_extensions \
36 -reqexts self_signed_extensions \
40 ${lib.optionalString (cert.days != null) "-days ${toString cert.days}"} \
41 -set_serial 0x$(sleep 1; date '+%Y%m%d%H%M%S') \
42 -reqopt no_pubkey,no_sigdump \
44 -out "${openssl.opensslHome}/${id}/cert.self-signed.pem"
46 if test ! -s "${openssl.opensslHome}/${id}/cert.self-signed.pem"
49 if test ! "${openssl.opensslHome}/${id}/cert.self-signed.pem" -nt \
50 "$PASSWORD_STORE_DIR/${cert.passPrefix}/${id}/key.pem.gpg"
53 test ! "''${DEEPCHECK:+set}" || {
54 info " generateCert: checking wether key and certificate match"
56 ${pkgs.pass}/bin/pass "${cert.passPrefix}/${id}/key.pem" |
57 ${pkgs.openssl}/bin/openssl rsa -modulus -noout -in /dev/stdin)"
59 ${pkgs.openssl}/bin/openssl x509 -noout -modulus \
60 -in "${openssl.opensslHome}/${id}/cert.self-signed.pem")"
61 test "$key_mod_pub" = "$crt_mod_pub" ||
62 error "key and certificate do not match"
69 echo >&2 "openssl-init: $*"
72 # Initialize the keyring according to openssl.certificates.
73 openssl-init = pkgs.writeShellScriptBin "openssl-init" (''
78 generateCerts openssl.certificates
80 openssl-cert-iter = pkgs.writeScriptBin "openssl-cert-iter" ''
82 # SYNTAX: $command .. <cert.pem
84 begin="-----BEGIN CERTIFICATE-----"
85 end="-----END CERTIFICATE-----"
88 ${pkgs.gnused}/bin/sed -n -e "/^$begin/,/^$end/p" |
89 while IFS= read -r line
93 ("$end") printf '%s\n%s\n%s\n' "$begin" "$(printf %s "$buf" | ${pkgs.gnused}/bin/sed -e 's/\(.\{64\}\)/\1\n/g')" "$end" | "$@";;
98 openssl-cert-print = pkgs.writeScriptBin "openssl-cert-print" ''
101 ${openssl-cert-iter}/bin/openssl-cert-iter \
102 ${pkgs.openssl}/bin/openssl x509 \
103 -nameopt multiline,-esc_msb,utf8 \
104 -certopt no_pubkey,no_sigdump \
106 ''${x509_md:+-"$x509_md"} \
110 ''${OPENSSL_FLAGS-} ''${OPENSSL_X509_FLAGS-}
112 openssl-cert-fetch = pkgs.writeScriptBin "openssl-cert-fetch" ''
115 x509="''${x509:-x509}"
119 ${pkgs.gnused}/bin/sed \
120 -e 's,^[a-z][a-z]*://,,' \
124 ${pkgs.gnused}/bin/sed \
125 -e 's,:[0-9][0-9]*,,')
126 mkdir -p "x509/$host"
127 begin="-----BEGIN CERTIFICATE-----"
128 end="-----END CERTIFICATE-----"
131 ${pkgs.openssl}/bin/openssl s_client \
133 -servername "$servername" \
136 ${pkgs.gnused}/bin/sed -n \
137 -e "/^$begin/,/^$end/p" \
138 >"$x509/$host/crt.pem"
143 enable = lib.mkEnableOption "Configuration of X.509 certificates";
144 opensslHome = lib.mkOption {
146 default = "sec/openssl";
151 certificates = lib.mkOption {
157 host = "example.coop";
158 distributionPoint = "http://example.coop/x509";
159 domains = [ "example.org" ];
165 type = types.attrsOf (types.submodule ({ name, ... }:
166 let cert = openssl.certificates."${name}"; in
168 #config.uid = lib.mkDefault uid;
170 host = lib.mkOption {
171 type = types.nullOr types.str;
172 example = "example.coop";
174 passPrefix = lib.mkOption {
179 distributionPoint = lib.mkOption {
180 type = types.nullOr types.str;
182 example = "http://example.coop/x509";
184 domains = lib.mkOption {
185 type = types.listOf types.str;
186 example = [ "example.coop" ];
189 days = lib.mkOption {
194 keySize = lib.mkOption {
199 digest = lib.mkOption {
203 description = "A digest from openssl list --digest-commands";
205 opensslConf = lib.mkOption {
206 type = types.attrsOf types.lines;
211 cert.opensslConf_common //
212 cert.opensslConf_self-signed //
213 cert.opensslConf_auth-signed //
214 cert.opensslConf_user-cert //
218 pkgs.writeText "openssl.conf"
221 (sectionHead: sectionBody:
222 (if sectionHead == "" then "" else "[ ${sectionHead} ]\n") +
226 opensslConf_common = lib.mkOption {
227 type = types.attrsOf types.lines;
230 RANDFILE = ${openssl.opensslHome}/openssl.rand
231 oid_section = extra_oids
232 default_md = ${cert.digest}
238 distinguished_name = distinguished_name
240 #x509_extensions = root_extensions
241 #req_extensions = extension
242 #attributes = req_attributes
244 distinguished_name = ''
245 commonName = ${cert.host}
247 #stateOrProvinceName =
249 #"0.organizationName" =
250 #organizationalUnitName =
255 opensslConf_self-signed = lib.mkOption {
256 type = types.attrsOf types.lines;
258 self_signed_extensions = ''
259 basicConstraints = critical,CA:TRUE,pathlen:0
260 keyUsage = keyCertSign,cRLSign,digitalSignature,keyEncipherment
261 subjectAltName = ${lib.concatMapStringsSep "," (dom: "DNS:${dom}") cert.domains}
262 subjectKeyIdentifier = hash
263 issuerAltName = issuer:copy
264 authorityKeyIdentifier = keyid:always,issuer:always
265 '' + lib.optionalString (cert.distributionPoint != null) ''
266 authorityInfoAccess = caIssuers;URI:${cert.distributionPoint}/${cert.host}.cert.pem
267 crlDistributionPoints = URI:${cert.distributionPoint}/${cert.host}.crl.self-signed.pem
271 dir = ${openssl.opensslHome}/${name}
272 private_key = $dir/key.pem
274 crlnumber = $dir/crl.self-signed.num
275 crl = $dir/crl.self-signed.pem
276 database = $dir/idx.self-signed.txt
281 opensslConf_auth-signed = lib.mkOption {
282 type = types.attrsOf types.lines;
284 auth_signed_extensions = ''
285 basicConstraints = critical,CA:FALSE
286 keyUsage = digitalSignature,keyEncipherment
287 subjectAltName = ${lib.concatMapStringsSep "," (dom: "DNS:${dom}") cert.domains}
288 subjectKeyIdentifier = hash
289 issuerAltName = issuer:copy
290 authorityKeyIdentifier = keyid:always,issuer:always
291 certificatePolicies = @certificate_policies
292 '' + lib.optionalString (cert.distributionPoint != null) ''
293 authorityInfoAccess = caIssuers;URI:${cert.distributionPoint}/${cert.host}.cert.pem
294 crlDistributionPoints = URI:${cert.distributionPoint}/${cert.host}.crl.pem
296 certificate_policies = lib.optionalString (cert.distributionPoint != null) ''
297 policyIdentifier = 1.2.250.1.42
298 CPS.1 = https://${cert.distributionPoint}/cps
302 dir = ${openssl.opensslHome}/${name}
303 private_key = $dir/key.pem
305 crlnumber = $dir/crl.num
307 database = $dir/idx.txt
312 opensslConf_user-cert = lib.mkOption {
313 type = types.attrsOf types.lines;
315 user_cert_extensions = ''
316 basicConstraints = critical,CA:FALSE,pathlen:0
317 keyUsage = digitalSignature,keyEncipherment
318 subjectAltName = email:$ENV::user@${cert.host}
319 subjectKeyIdentifier = hash
320 issuerAltName = issuer:copy
321 authorityKeyIdentifier = keyid:always,issuer:always
322 '' + lib.optionalString (cert.distributionPoint != null) ''
323 authorityInfoAccess = caIssuers;URI:${cert.distributionPoint}/${cert.host}.cert.pem
333 config = lib.mkIf openssl.enable {
334 nix-shell.buildInputs = [