Newer
Older
# A NixOS module which can run a Ristretto-based issuer for PrivacyStorage
# ZKAPs.
{ lib, pkgs, config, ... }: let
pspkgs = pkgs.callPackage ./pspkgs.nix { };
zkapissuer = pspkgs.callPackage ../pkgs/zkapissuer.nix { };
cfg = config.services.private-storage-issuer;
in {
options = {
services.private-storage-issuer.enable = lib.mkEnableOption "PrivateStorage ZKAP Issuer Service";
services.private-storage-issuer.package = lib.mkOption {
default = zkapissuer.components.exes."PaymentServer-exe";
type = lib.types.package;
example = lib.literalExample "pkgs.zkapissuer.components.exes.\"PaymentServer-exe\"";
description = ''
The package to use for the ZKAP issuer.
'';
};
services.private-storage-issuer.domain = lib.mkOption {
default = "payments.privatestorage.io";
type = lib.types.str;
example = lib.literalExample "payments.example.com";
description = ''
The domain name at which the issuer is reachable.
'';
};
services.private-storage-issuer.tls = lib.mkOption {
default = true;
type = lib.types.bool;
description = ''
Whether or not to listen on TLS. For real-world use you should always
listen on TLS. This is provided as an aid to automated testing where
it might be difficult to obtain a real certificate.
'';
};
services.private-storage-issuer.issuer = lib.mkOption {
default = "Ristretto";
example = lib.literalExample "Trivial";
description = ''
The issuer algorithm to use. Either Trivial for a fake no-crypto
algorithm or Ristretto for Ristretto-flavored PrivacyPass.
'';
};
services.private-storage-issuer.ristrettoSigningKeyPath = lib.mkOption {
type = lib.types.path;
The path to a file containing the Ristretto signing key to use.
Required if the issuer is ``Ristretto``.
services.private-storage-issuer.stripeSecretKeyPath = lib.mkOption {
type = lib.types.path;
description = ''
The path to a file containing a Stripe secret key to use for charge
and payment management.
'';
};
services.private-storage-issuer.database = lib.mkOption {
default = "Memory";
type = lib.types.enum [ "Memory" "SQLite3" ];
description = ''
The kind of voucher database to use.
'';
};
services.private-storage-issuer.databasePath = lib.mkOption {
default = null;
type = lib.types.str;
description = ''
The path to a database file in the filesystem, if the SQLite3 database
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.
'';
};
};
certroot = "/var/lib/letsencrypt/live";
systemd.services.zkapissuer = {
enable = true;
description = "ZKAP Issuer";
wantedBy = [ "multi-user.target" ];
# Make sure we have a certificate the first time, if we are running over
# TLS and require a certificate.
requires = lib.optional cfg.tls "cert-${cfg.domain}.service";
after = [
# Make sure there is a network so we can bind to all of the
# interfaces.
"network.target"
] ++
# Make sure we run after the certificate is issued, if we are running
# over TLS and require a certificate.
lib.optional cfg.tls "cert-${cfg.domain}.service";
# It really shouldn't ever exit on its own! If it does, it's a bug
# we'll have to fix. Restart it and hope it doesn't happen too much
# before we can fix whatever the issue is.
serviceConfig.Restart = "always";
serviceConfig.Type = "simple";
script =
let
# Compute the right command line arguments to pass to it. The
# signing key is only supplied when using the Ristretto issuer.
issuerArgs =
if cfg.issuer == "Trivial"
then "--issuer Trivial"
else "--issuer Ristretto --signing-key-path ${cfg.ristrettoSigningKeyPath}";
databaseArgs =
if cfg.database == "Memory"
then "--database Memory"
else "--database SQLite3 --database-path ${cfg.databasePath}";
httpsArgs =
if cfg.tls
then
"--https-port 443 " +
"--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";
stripeArgs = "--stripe-key ${builtins.readFile cfg.stripeSecretKeyPath}";
"${cfg.package}/bin/PaymentServer-exe ${issuerArgs} ${databaseArgs} ${httpsArgs} ${stripeArgs}";
# Certificate renewal. We must declare that we *require* it in our
# service above.
systemd.services."cert-${cfg.domain}" = {
enable = true;
description = "Issue/Renew certificate for ${cfg.domain}";
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} --non-interactive --agree-tos -m ${cfg.letsEncryptAdminEmail} || true
# Obtain the certificate.
${pkgs.certbot}/bin/certbot certonly ${configArgs} --non-interactive --standalone --domains ${cfg.domain}
'';
};
};
# Open 80 and 443 for the certbot HTTP server and the PaymentServer HTTPS server.
networking.firewall.allowedTCPPorts = [
80
443
];