{ lib, config, pkgs, ... }: let inherit (lib) escapeShellArg stringAfter; mkActvationScript = name: cert-cfg: let pem-path = "${cert-cfg.dir}/${name}.pem"; key-path = "${cert-cfg.dir}/${name}.key"; in { name = "make-cert-${name}"; value = stringAfter [ "users" ] ('' if [[ ! -e ${escapeShellArg pem-path} ]];then ${pkgs.coreutils}/bin/mkdir -p ${escapeShellArg cert-cfg.dir} ${pkgs.openssl}/bin/openssl req -batch -x509 -newkey rsa:4096 \ -keyout ${escapeShellArg key-path} \ -out ${escapeShellArg pem-path} \ -days ${escapeShellArg cert-cfg.lifetime} \ -noenc ${pkgs.coreutils}/bin/chown ${escapeShellArg cert-cfg.user} ${ escapeShellArg key-path } fi '' + lib.optionalString cert-cfg.print '' echo Public certificate for ${escapeShellArg name}: >&2 ${pkgs.coreutils}/bin/cat ${escapeShellArg pem-path} >&2 ''); }; in { options = { chkno.make-certs = lib.mkOption { type = lib.types.attrsOf (lib.types.submodule { options = { dir = lib.mkOption { type = lib.types.str; description = "Where to put the certificate and key."; default = "/secrets"; }; lifetime = lib.mkOption { type = lib.types.str; description = "Lifetime of the generated certificate (in days)."; # This doesn't yet include any notion of certificate rotation, # so just make really long-lived certificates for now. default = "99999"; }; print = lib.mkOption { type = lib.types.bool; description = "If set, print the certificate (public key) during activation."; default = false; }; user = lib.mkOption { type = lib.types.str; description = "The username that owns (can read) the secret key."; }; }; }); }; }; config = { system.activationScripts = lib.mapAttrs' mkActvationScript config.chkno.make-certs; }; }