diff --git a/morph/lib/customize-issuer.nix b/morph/lib/customize-issuer.nix index 2cbc52f689c2e69f78c6dc3634b09e3726815981..d195b06bf217d1c72827bf6ed2b7beef6c9fbc1f 100644 --- a/morph/lib/customize-issuer.nix +++ b/morph/lib/customize-issuer.nix @@ -78,6 +78,7 @@ networking.domain = domain; services.private-storage.sshUsers = sshUsers; + services.private-storage.monitoring.vpn.client = { enable = true; ip = monitoringvpnIPv4; @@ -86,41 +87,8 @@ }; services.private-storage-issuer = { - inherit allowedChargeOrigins; + inherit allowedChargeOrigins letsEncryptAdminEmail; domains = issuerDomains; - # Arbitrary non-priviledged port: - httpPort = 1061; - }; - - # nginx reverse proxy - security.acme.email = letsEncryptAdminEmail; - security.acme.acceptTerms = true; - services.nginx = { - enable = true; - - recommendedGzipSettings = true; - recommendedOptimisation = true; - recommendedProxySettings = true; - recommendedTlsSettings = true; - - # Only allow PFS-enabled ciphers with AES256: - sslCiphers = "AES256+EECDH:AES256+EDH:!aNULL"; - - virtualHosts."${config.networking.hostName}.${config.networking.domain}" = { - enableACME = true; - forceSSL = true; - locations."/" = { - proxyPass = "http://127.0.0.1:${toString config.services.private-storage-issuer.httpPort}"; - }; - locations."/metrics" = { - # Only allow our monitoringvpn subnet - extraConfig = '' - allow 172.23.23.0/24; - deny all; - ''; - proxyPass = "http://127.0.0.1:${toString config.services.private-storage-issuer.httpPort}"; - }; - }; }; system.stateVersion = "19.03"; diff --git a/morph/lib/issuer.nix b/morph/lib/issuer.nix index 716fe760d5f50e961dd40a732ab3792571fdcdd8..4330692313936845e7eb3c249b702bb61a39b7a8 100644 --- a/morph/lib/issuer.nix +++ b/morph/lib/issuer.nix @@ -50,8 +50,6 @@ rec { services.private-storage-issuer = { enable = true; - # We'll let NGINX handle TLS termination: - tls = false; ristrettoSigningKeyPath = deployment.secrets.ristretto-signing-key.destination; stripeSecretKeyPath = deployment.secrets.stripe-secret-key.destination; database = "SQLite3"; diff --git a/nixos/modules/issuer.nix b/nixos/modules/issuer.nix index 0db40f1983f8d32b934d075096e76b3294b2c327..676b60bce4bb687695252878049fbd1e0fd9ad52 100644 --- a/nixos/modules/issuer.nix +++ b/nixos/modules/issuer.nix @@ -111,29 +111,15 @@ in { PaymentServer. It just controls the CORS headers served. ''; }; - services.private-storage-issuer.httpPort = lib.mkOption { - type = lib.types.int; - description = '' - The port number for the HTTP endpoint. - ''; - default = 80; - }; - services.private-storage-issuer.httpsPort = lib.mkOption { - type = lib.types.int; - description = '' - The port number for the HTTPs endpoint. - ''; - default = 443; - }; }; config = let - certroot = "/var/lib/letsencrypt/live"; # We'll refer to this collection of domains by the first domain in the # list. domain = builtins.head cfg.domains; - certServiceName = "cert-${domain}"; + certServiceName = "acme-${domain}"; + in lib.mkIf cfg.enable { # Add a systemd service to run PaymentServer. systemd.services.zkapissuer = { @@ -143,7 +129,14 @@ in { # Make sure we have a certificate the first time, if we are running over # TLS and require a certificate. - requires = lib.optional cfg.tls "${certServiceName}.service"; + # ACME will issue an interim self-signed certificate, which we want to + # use at least in the local dev network. But if ACME cannot get the + # created key signed by LE (probably because the host is not reachable + # from outside, or the domain is not a legit TLD) the ACME cert service + # will "fail". We still want to start our PaymentServer. Hence a weaker + # "wants" instead of a "requires" dependency. + # When ACME receives a fully signed cert from LE, it will reload NGINX. + wants = lib.optional cfg.tls "${certServiceName}.service"; after = [ # Make sure there is a network so we can bind to all of the @@ -172,15 +165,8 @@ in { if cfg.database == "Memory" then "--database Memory" else "--database SQLite3 --database-path ${cfg.databasePath}"; - httpsArgs = - if cfg.tls - then - "--https-port ${toString cfg.httpsPort}" + - "--https-certificate-path ${certroot}/${domain}/cert.pem " + - "--https-certificate-chain-path ${certroot}/${domain}/chain.pem " + - "--https-key-path ${certroot}/${domain}/privkey.pem" - else - "--http-port ${toString cfg.httpPort}"; + # Arbitrary non-priviledged port: + httpArgs = "--http-port 1061"; prefixOption = s: "--cors-origin=" + s; originStrings = map prefixOption cfg.allowedChargeOrigins; @@ -192,33 +178,7 @@ in { "--stripe-endpoint-scheme ${cfg.stripeEndpointScheme} " + "--stripe-endpoint-port ${toString cfg.stripeEndpointPort}"; in - "${cfg.package}/bin/PaymentServer-exe ${originArgs} ${issuerArgs} ${databaseArgs} ${httpsArgs} ${stripeArgs}"; - }; - - # Certificate renewal. A short-lived service meant to be repeatedly - # activated to request a new certificate be issued, if the current one is - # close to expiring. - systemd.services.${certServiceName} = { - enable = cfg.tls; - description = "Certificate ${domain}"; - # Activate this unit periodically so that certbot can determine if the - # certificate expiration time is close enough to warrant a renewal - # request. - startAt = "weekly"; - - serviceConfig = { - ExecStart = - let - configArgs = "--config-dir /var/lib/letsencrypt --work-dir /var/run/letsencrypt --logs-dir /var/run/log/letsencrypt"; - in - pkgs.writeScript "cert-${domain}-start.sh" '' - #!${pkgs.runtimeShell} -e - # Register if necessary. - ${pkgs.certbot}/bin/certbot register ${configArgs} --non-interactive --agree-tos -m ${cfg.letsEncryptAdminEmail} || true - # Obtain the certificate. - ${pkgs.certbot}/bin/certbot certonly ${configArgs} --non-interactive --standalone --expand --domains ${builtins.concatStringsSep "," cfg.domains} - ''; - }; + "${cfg.package}/bin/PaymentServer-exe ${originArgs} ${issuerArgs} ${databaseArgs} ${httpArgs} ${stripeArgs}"; }; # Open 80 and 443 for the certbot HTTP server and the PaymentServer HTTPS server. @@ -226,5 +186,38 @@ in { 80 443 ]; + + # NGINX reverse proxy + security.acme.email = cfg.letsEncryptAdminEmail; + security.acme.acceptTerms = true; + services.nginx = { + enable = true; + + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + + # Only allow PFS-enabled ciphers with AES256: + sslCiphers = "AES256+EECDH:AES256+EDH:!aNULL"; + + virtualHosts."${domain}" = { + serverAliases = builtins.tail cfg.domains; + enableACME = cfg.tls; + forceSSL = cfg.tls; + locations."/" = { + proxyPass = "http://127.0.0.1:1061"; + }; + locations."/metrics" = { + # Only allow our monitoringvpn subnet + extraConfig = '' + allow 172.23.23.0/24; + deny all; + ''; + proxyPass = "http://127.0.0.1:1061"; + }; + }; + }; + }; }