diff --git a/morph/grid.config.json b/morph/grid.config.json index 178f44d39e5dd88ef709f92713d579c2cd32caff..5b848d31264fb84017752a76376467466c717f35 100644 --- a/morph/grid.config.json +++ b/morph/grid.config.json @@ -1,3 +1,5 @@ { "publicStoragePort": 8898 , "ristrettoSigningKeyPath": "../../PrivateStorageSecrets/ristretto.signing-key" +, "issuerDomain": "payments.privatestorage.io" +, "letsEncryptAdminEmail": "jean-paul@privatestorage.io" } diff --git a/morph/grid.nix b/morph/grid.nix index 466398b10e91858bb0fc61767a8daa979a849a77..3d005f96da0f229086722e6552a221838d8c1868 100644 --- a/morph/grid.nix +++ b/morph/grid.nix @@ -3,6 +3,7 @@ # with the testing grid and have one fewer possible point of divergence. import ./make-grid.nix { name = "Production"; + config = ./grid.config.json; nodes = cfg: { # Here are the hosts that are in this morph network. This is sort of like # a server manifest. We try to keep as many of the specific details as diff --git a/morph/issuer.nix b/morph/issuer.nix index aac471932e391145914eb236872855bb2300fa55..ddf01bdfc832a13aee357475ef639c539ea5bda3 100644 --- a/morph/issuer.nix +++ b/morph/issuer.nix @@ -1,5 +1,7 @@ { hardware , ristrettoSigningKeyPath +, issuerDomain +, letsEncryptAdminEmail , stateVersion , ... }: { @@ -27,6 +29,8 @@ ristrettoSigningKey = builtins.readFile (./.. + ristrettoSigningKeyPath); database = "SQLite3"; databasePath = "/var/db/vouchers.sqlite3"; + inherit letsEncryptAdminEmail; + domain = issuerDomain; }; system.stateVersion = stateVersion; diff --git a/morph/make-grid.nix b/morph/make-grid.nix index 32e0b98ed3ffd5741522f9b0e3ef67037fa6a180..de10df1e9a62ee0ac7fde98070743ee4a9cf484b 100644 --- a/morph/make-grid.nix +++ b/morph/make-grid.nix @@ -3,11 +3,11 @@ # and a function that takes the grid configuration as an argument and returns # a set of nodes specifying the addresses and NixOS configurations for each # server in the morph network. -{ name, nodes }: +{ name, config, nodes }: let pkgs = import <nixpkgs> { }; # Load our JSON configuration for later use. - cfg = pkgs.lib.trivial.importJSON ./grid.config.json; + cfg = pkgs.lib.trivial.importJSON config; in { network = { diff --git a/morph/make-storage.nix b/morph/make-storage.nix index 768cdb55f304ef0708a2af434df772861572502d..84a13be3c4089194b0d1a1ff6218b1161c1537f0 100644 --- a/morph/make-storage.nix +++ b/morph/make-storage.nix @@ -9,7 +9,7 @@ # to avoid breaking some software such as # database servers. You should change this only # after NixOS release notes say you should. - +, ... }: rec { deployment = { secrets = { diff --git a/morph/testing-grid.config.json b/morph/testing-grid.config.json new file mode 100644 index 0000000000000000000000000000000000000000..018367db9da09364c718a521dd28ef06a2642288 --- /dev/null +++ b/morph/testing-grid.config.json @@ -0,0 +1,5 @@ +{ "publicStoragePort": 8898 +, "ristrettoSigningKeyPath": "../../PrivateStorageSecrets/ristretto.signing-key" +, "issuerDomain": "payments.privatestorage-staging.com" +, "letsEncryptAdminEmail": "jean-paul@privatestorage.io" +} diff --git a/morph/testing-grid.nix b/morph/testing-grid.nix index 5591827e3e02423abeeb31123c383bd42dfdf1c5..b4b0649d8af349cd08e2b147ffdd207f32e8d1c6 100644 --- a/morph/testing-grid.nix +++ b/morph/testing-grid.nix @@ -3,8 +3,14 @@ # with the production grid and have one fewer possible point of divergence. import ./make-grid.nix { name = "Testing"; + config = ./testing-grid.config.json; nodes = cfg: { - "testing000" = import ./testing000.nix (cfg // { + "payments.privatestorage-staging.com" = import ./issuer.nix ({ + hardware = ./issuer-aws.nix; + stateVersion = "19.03"; + } // cfg); + + "35.157.216.200" = import ./testing000.nix (cfg // { publicIPv4 = "35.157.216.200"; }); }; diff --git a/morph/testing000.nix b/morph/testing000.nix index e5f9c3f32bf4c75fea438a309a92c372f44f8ff8..d45086ae90fb5dfd64b5181d1723c757d219e6bb 100644 --- a/morph/testing000.nix +++ b/morph/testing000.nix @@ -1,4 +1,4 @@ -{ publicIPv4, publicStoragePort, ristrettoSigningKeyPath }: rec { +{ publicIPv4, publicStoragePort, ristrettoSigningKeyPath, ... }: rec { deployment = { secrets = { diff --git a/nixos/modules/issuer.nix b/nixos/modules/issuer.nix index 2cd63cea2fabb7c937fc3d782e3106c696bb94b8..88413333047313838ef0859dc039c0dd984c6fbb 100644 --- a/nixos/modules/issuer.nix +++ b/nixos/modules/issuer.nix @@ -64,11 +64,18 @@ in { type is being used. ''; }; + services.private-storage-issuer.letsEncryptAdminEmail = lib.mkOption { + type = lib.types.str; + description = '' + An email address to give to Let's Encrypt as an operational contact + for the service's TLS certificate. + ''; + }; }; config = let - acme = "/var/lib/acme"; + certroot = "/var/lib/letsencrypt/live"; in lib.mkIf cfg.enable { # Add a systemd service to run PaymentServer. systemd.services.zkapissuer = { @@ -80,8 +87,8 @@ in { # interfaces. "network.target" ]; - # Make sure we at least have a self-signed certificate. - requires = lib.optional cfg.tls "acme-selfsigned-${cfg.domain}.service"; + # Make sure we at least have a certificate. + requires = lib.optional cfg.tls "cert-${cfg.domain}"; serviceConfig = { ExecStart = @@ -100,12 +107,9 @@ in { if cfg.tls then "--https-port 443 " + - # acme has plugins to write the files in different ways but the - # self-signed certificate generator doesn't. The files it - # writes are weirdly named and shaped but they work. - "--https-certificate-path ${acme}/${cfg.domain}/full.pem " + - "--https-certificate-chain-path ${acme}/${cfg.domain}/fullchain.pem " + - "--https-key-path ${acme}/${cfg.domain}/key.pem" + "--https-certificate-path ${certroot}/${cfg.domain}/cert.pem " + + "--https-certificate-chain-path ${certroot}/${cfg.domain}/chain.pem " + + "--https-key-path ${certroot}/${cfg.domain}/privkey.pem" else # Only for automated testing. "--http-port 80"; @@ -121,25 +125,30 @@ in { # Certificate renewal. Note that preliminarySelfsigned only creates the # service. We must declare that we *require* it in our service above. - security.acme = if cfg.tls - then { - production = false; - preliminarySelfsigned = true; - certs."${cfg.domain}" = { - email = "jean-paul@privatestorage.io"; - postRun = "systemctl restart zkapissuer.service"; - webroot = "${acme}/acme-challenges"; - plugins = [ "account_key.json" "full.pem" "fullchain.pem" "key.pem" ]; - }; - } - else {}; - - services.nginx.virtualHosts = if cfg.tls - then { - "${cfg.domain}" = { - locations."/" = "${acme}/acme-challenges"; - }; - } - else {}; + systemd.services."cert-${cfg.domain}" = { + enable = true; + description = "Issue/Renew certificate for ${cfg.domain}"; + wantedBy = [ "zkapissuer.service" ]; + serviceConfig = { + ExecStart = + let + configArgs = "--config-dir /var/lib/letsencrypt --work-dir /var/run/letsencrypt --logs-dir /var/run/log/letsencrypt"; + in + pkgs.writeScript "cert-${cfg.domain}-start.sh" '' + #!${pkgs.runtimeShell} -e + # Register if necessary. + ${pkgs.certbot}/bin/certbot register ${configArgs} --agree-tos -m ${cfg.letsEncryptAdminEmail} || true + # Obtain the certificate. + ${pkgs.certbot}/bin/certbot certonly ${configArgs} -n --standalone --domains ${cfg.domain} + # Restart the server so the new certificate gets used. + systemctl restart zkapissuer.service + ''; + }; + }; + # Open 80 and 443 for the certbot HTTP server and the PaymentServer HTTPS server. + networking.firewall.allowedTCPPorts = [ + 80 + 443 + ]; }; } diff --git a/nixpkgs.rev b/nixpkgs.rev index 41802ea669e9d5bb48fff0fe62e5da506b741bf8..339195d572f2a1784ed2ed911834e0e571d639e2 100644 --- a/nixpkgs.rev +++ b/nixpkgs.rev @@ -1 +1 @@ -8bf142e001b6876b021c8ee90c2c7cec385fe8e9 \ No newline at end of file +353333ef340952c05332e3c271dff953264cb017 \ No newline at end of file