diff --git a/devshell.toml b/devshell.toml index 9b32d56e..6fd303fc 100644 --- a/devshell.toml +++ b/devshell.toml @@ -1,5 +1,6 @@ imports = [ - "language.go" + "language.go", + "ca.mkcert" ] [devshell] @@ -45,3 +46,78 @@ category = "utilites" help = "golang linter" package = "golangci-lint" category = "linters" + +[mkcert] +enable = true +root-ca.key = """ +-----BEGIN PRIVATE KEY----- +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 +xS044vroff+d0i4NxpOfBXyf +-----END PRIVATE KEY----- +""" +root-ca.cert = """ +-----BEGIN CERTIFICATE----- +MIIEzjCCAzagAwIBAgIRAKGyGtqQLfNkWqnQWQ5mhx0wDQYJKoZIhvcNAQELBQAw +fzEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMSowKAYDVQQLDCFuaXhi +bGRAbG9jYWxob3N0IChOaXggYnVpbGQgdXNlcikxMTAvBgNVBAMMKG1rY2VydCBu +aXhibGRAbG9jYWxob3N0IChOaXggYnVpbGQgdXNlcikwHhcNMjEwMTI3MTg1NTM1 +WhcNMzEwMTI3MTg1NTM1WjB/MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQg +Q0ExKjAoBgNVBAsMIW5peGJsZEBsb2NhbGhvc3QgKE5peCBidWlsZCB1c2VyKTEx +MC8GA1UEAwwobWtjZXJ0IG5peGJsZEBsb2NhbGhvc3QgKE5peCBidWlsZCB1c2Vy +KTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAJpNoK7FKbvIdPHxYEGu +HKoCyMNi+OxTDXRrrUPWgkDr64KIQwevv1ePddIzSE1levtOfL2l6oquzTtOqZFj +jaAzrD/VafQqXs4PBtMpN5IZzNqmrTCZ3tvVm2sjh3diPd2N2BfEuVf4KgE7aN91 +BK/k5s4MFZcTvERA1CBZ1Dg6q65v6r7y2v3q/vuDN4LGsVC0Nk3ySSbhQZ1IGCPC +uqalnyghdP8SM7v2UiK9F5nyiVwmAsRE/AE0agGZO/0MDPJmLYbrBwi2L+Q+MU0n +8MBW9fGEDSmazISdKCdlxNLCZZKsA6v5ZMuQvuCGy/kN4gF66WVp+zr9+9AwCocO +F7yW7igWYdDzPoeqZvu0KsqcFnYYxCgkBFIC2LoubsuOBegPN3cqdxOVSgdTkREh ++BlUOoGtlrC8wnxXqvvD4Us/Az2DAmoIuRi6xS9wHjrN1+BkznPeAcsiFcWh917G +KCTHmEwveH74Z3UOiXhi+1CZT4Rs6LsR9WB2jJrKzsh8TwIDAQABo0UwQzAOBgNV +HQ8BAf8EBAMCAgQwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUvFZHpiR1 +SsZgx18IXS4sV2goJwwwDQYJKoZIhvcNAQELBQADggGBAEhEXonraBKooVsYVdd6 +EuLD5LJ9rLuVN0QCOWzMDgqc1iHX+nTEjH6OctBpdGo+1RRE4UB1SHoaCW7LcJJx +1UCPHvMTby2U0Y1E7NS9dOdt3slUQUF86KfecV8UfTLgqWvN8W3pdyNqk8X7g5FO +vnoWMRXopNyI/AnnAfEMC2rA1+fgJXFIi7Ny5vHiPoaPolvULDU8gwcBPpGB80z0 +py4x/DcO4UoNB11fs2+XNV6UGHfLB1R1OJmhdCbEQ7cmIetg11ThuvD8YiomKb1O +wJO5moU86AJ52o19C3AmYKsClhGwnGLGNgOslFIozRi7eSPX7B9UdEJUvj2tGkUE +mpSjE6hTUtEinmuQ0gkf4jaqcWetJnhUVaNGsbixWdRtkNkvICkToCGhHXFKDGfr +qrM5Rs0xtUAp91/DZ+vbPsuWzvRibCrck89vyg1lGemhMf8g34U1ocf5KlyLvO3c +o3E+CRMwgG1IP4ktc63dfXPvbfN+lKjk1LVhMBPXeqhJ2A== +-----END CERTIFICATE----- +""" diff --git a/extra/ca/mkcert.nix b/extra/ca/mkcert.nix new file mode 100644 index 00000000..484ee228 --- /dev/null +++ b/extra/ca/mkcert.nix @@ -0,0 +1,113 @@ +{ lib, pkgs, config, ... }: +with lib; +let + cfg = config.mkcert; + + maybeReadFile = obj: + if (builtins.typeOf obj == "path") then + builtins.readFile obj + else + obj + ; + + # These are all the options available for specifying a certificate/key file. + rootCAOption = { + key = mkOption { + description = "Text or file of the root CA key to install"; + default = null; + type = types.nullOr (types.either types.str types.path); + }; + cert = mkOption { + description = "Text or file of the root CA certificate to install"; + default = null; + type = types.nullOr (types.either types.str types.path); + }; + }; + + # A collection of development certificate authority files + rootCADir = + let + adHoc = pkgs.runCommandLocal "rootCA" {} '' + export CAROOT=$out + ${pkgs.mkcert}/bin/mkcert 2>/dev/null + ''; + + isPreconfigured = + assert assertMsg ( + cfg.root-ca != {} && cfg.root-ca.cert != null && cfg.root-ca.key != null + ) "[mkcert]: either set, both, root CA key and certificate or none."; + (cfg.root-ca.key != null && cfg.root-ca.cert != null); + + key = pkgs.writeText "rootCA-key.pem" (maybeReadFile cfg.root-ca.key); + cert = pkgs.writeText "rootCA.pem" (maybeReadFile cfg.root-ca.cert); + + preconfigured = pkgs.runCommand "rootCA" {} '' + mkdir $out + cp ${key} $out/${key.name} + cp ${cert} $out/${cert.name} + ''; + in + if isPreconfigured then preconfigured else adHoc; + + # Execute this script to install the project's development certificate authority + install-mkcert-ca = pkgs.writeShellScriptBin "install-mkcert-ca" '' + set -euo pipefail + shopt -s nullglob + + log() { + IFS=$'\n' loglines=($*) + for line in ${"$"}{loglines[@]}; do echo -e "[mkcert] $line" >&2; done + } + + # Set the CA root files directory for mkcert via env variable + export CAROOT=${rootCADir} + + # Install local CA into system, java and nss (includes Firefox) trust stores + log "Install development CA into the system stores..." + log $(sudo -K; ${pkgs.mkcert}/bin/mkcert -install 2>&1) + log "root CA directory: $(${pkgs.mkcert}/bin/mkcert -CAROOT 2>&1)" + + uninstall() { + log $(${pkgs.mkcert}/bin/mkcert -uninstall 2>&1) + } + + # TODO: Uninstall when leaving the devshell + # trap uninstall EXIT + + ''; +in +{ + options.mkcert = { + enable = mkEnableOption "provide a development CA within the shell"; + + root-ca = mkOption { + type = types.submodule { options = rootCAOption; }; + default = {}; + description = "preconfigure root CA files"; + example = literalExample " + { + key = '' + -----BEGIN PRIVATE KEY----- + ... + -----END PRIVATE KEY----- + ''; + cert = ./path/tocert.pem; + } + "; + }; + }; + + config = mkIf cfg.enable { + env = [{ + name = "CAROOT"; + value = "${rootCADir}"; + }]; + commands = [ { package = pkgs.mkcert; category = "certs"; } ]; + devshell = { + packages = [ install-mkcert-ca ]; + startup.install-mkcert-ca.text = " + $DEVSHELL_DIR/bin/install-mkcert-ca + "; + }; + }; +}