diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7a8b4e79d3675947cb6f24351fa4c4706313af27..205bedaf0b86d05a8f87ffcf368d6024d2fd57cb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,7 +1,19 @@ +default: + # Guide the choice of an appropriate runner for all these jobs. + # https://docs.gitlab.com/ee/ci/runners/#runner-runs-only-tagged-jobs + tags: + - "nixos" + - "shell" + docs: stage: "build" script: - "nix-shell --run 'nix-build docs.nix'" + - "cp --recursive --no-preserve=mode result/docs/. docs/build/" + artifacts: + paths: + - "docs/build/" + expose_as: "documentation" unit-tests: stage: "test" diff --git a/README.rst b/README.rst index d9e3e310ec775cbaec28019360befbcb535db9f6..d3d9f088db4f8b976f3f55852715762280bb93c0 100644 --- a/README.rst +++ b/README.rst @@ -13,7 +13,6 @@ Documentation There is documentation for: -* Users: ``docs/user/README.rst`` * Operators/Admins: ``docs/ops/README.rst`` * Developers: ``docs/dev/README.rst`` diff --git a/docs/source/dev/README.rst b/docs/source/dev/README.rst index e1a793f3ac70a320ed52c20e0b2128696455a7c9..904e8b3be07bdcc1473a3c1fe22afe8ffb0e15a2 100644 --- a/docs/source/dev/README.rst +++ b/docs/source/dev/README.rst @@ -45,5 +45,11 @@ Architecture overview .. graphviz:: architecture-overview.dot +.. include:: + ../../../morph/grid/local/README.rst + + + + .. _Nix: https://nixos.org/nix diff --git a/docs/source/index.rst b/docs/source/index.rst index 6ae149f7ce9845d877c953f3e095edba02455765..eb6b59ec783b8aa6dfb3227ca51fe47e5f1b6bc7 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -11,7 +11,6 @@ Howdy! We separated the documentation into parts addressing different audiences .. toctree:: :maxdepth: 2 - Users <user/README> Administrators <ops/README> Developers <dev/README> diff --git a/docs/source/user/README.rst b/docs/source/user/README.rst deleted file mode 100644 index 2a6e4b912cb6dfafb250149eb8aab095c34ce023..0000000000000000000000000000000000000000 --- a/docs/source/user/README.rst +++ /dev/null @@ -1,5 +0,0 @@ -User documentation -================== - -This will contain the user documentation for this project. - diff --git a/morph/README.rst b/morph/README.rst index d4a89a373a1bef767ad26859d495f1528a4fb7ca..1f48d5e0ad30b1f44a1a2cee8c5aa6f0669bcc75 100644 --- a/morph/README.rst +++ b/morph/README.rst @@ -42,6 +42,19 @@ grid Specific grid definitions live in subdirectories beneath this directory. +secrets +~~~~~~~ + +This must be created and populated before the grid can be built or deployed. + +This directory contains all of the secrets necessary to deploy the grid. +Secrets beneath this directory are referenced by ``config.json`` and ``grid.nix`` +(and possibly elsewhere). +Some of the paths are configurable and some are just convention. +This path is **ignored** by git. +The intended workflow is that the secrets will be maintained on secure storage and a symlink to the correct location created here. +This keeps the secrets themselves out of the git working tree as an extra protection against unintentionally committing them. + config.json ~~~~~~~~~~~ diff --git a/morph/grid/local/README.rst b/morph/grid/local/README.rst new file mode 100644 index 0000000000000000000000000000000000000000..8887c297bc0e2aa300e27f86ff7a0f08535028a5 --- /dev/null +++ b/morph/grid/local/README.rst @@ -0,0 +1,111 @@ +Set up and use a network of local development VMs +------------------------------------------------- + +... using `Vagrant <https://www.vagrantup.com/>`_ to manage VirtualBox VMs [#]_. +To get started, first install Vagrant and make sure it works. +One possible way to do it in NixOS: + +1. Install Vagrant, by adding the packages: + + - ``vagrant`` (orchestrating virtual machines on the command line) + - Only use when version >= 2.2.16 has become available. Else see below. + - Optional: ``packer`` (for creating your own VM images) + +2. Add configuration to install and enable VirtualBox: + + - ``virtualisation.virtualbox.host.enable = true;`` + +3. Add your user to the ``vboxusers`` group, for example: + + - ``users.extraGroups.vboxusers.members = [ "flo" "jp" ];`` + + +.. [#] The author of this documentation wasted a lot of time trying to get Vagrant to work with KVM/libvirt. Issues with networking that looked like guest misconfigurations vanished after changing to the better-tested combination of Vagrant and VirtualBox. + + +Pre-Vagrant 2.2.16: Get Vagrant with the required fixes for NixOS guests +```````````````````````````````````````````````````````````````````````` + +The Vagrant nixos-guest template `received a critical update on 2021-03-08 <https://github.com/hashicorp/vagrant/commit/990d94ed9d0b3092e855bc1bb9deeeb7aa7792cf>`_ which came out with Vagrant version 2.2.16. + +If you run an older Nixpkgs, retrieve and use the latest Vagrant development version like so:: + + NIX_PATH=nixpkgs=https://github.com/NixOS/nixpkgs/archive/refs/heads/master.tar.gz nix-shell -p vagrant + + +Generating and deploying keys +````````````````````````````` + +``config.json`` has the paths for the Ristretto and the Stripe secret key files. + +Here is a Ristretto key you can use, randomly generated just now:: + + SILOWzbnkBjxC1hGde9d5Q3Ir/4yLosCLEnEQGAxEQE= + +Generate your own like this:: + + [flo@la:~/PrivateStorageio]$ nix-shell + [nix-shell:~/PrivateStorageio]$ nix-shell -p zkapissuer.components.exes.PaymentServer-generate-key + [nix-shell:~/PrivateStorageio]$ PaymentServer-generate-key + SILOWzbnkBjxC1hGde9d5Q3Ir/4yLosCLEnEQGAxEQE= + +Make sure you write it into the key file `without any leading or trailing white space, also without newlines <https://github.com/LeastAuthority/python-challenge-bypass-ristretto/issues/37>`_. +For example:: + + echo -n "SILOWzbnkBjxC1hGde9d5Q3Ir/4yLosCLEnEQGAxEQE=" > ristretto.signing-key + +For the Stripe key any random bytes with a little light formatting "work" - at least to make our software happy - but if you want to be able to interact with Stripe and have payments (even pretend payments) move all the way through the system you should get a Stripe account and generate a key w/ them. +Lauri can get you added to our "dev" Stripe account, too, though I forget how important that is for ad hoc dev/testing. + +I think this will work for generating random Stripe secret keys (that our software will load, I think, but Stripe will reject):: + + >>> import base64, os + >>> print((b"sk_test_" + base64.b64encode(os.urandom(25)).strip(b"=")).decode("ascii")) + sk_test_Dr+XLVjkC0oO3Zw8Ws0yWtDLqR1sM+/fmw + +Public keys are the same but "pk_test" instead of "sk_test" ("test" is for "test mode" key that can only process pretend txns; for real txns there are keys with "live" embedded). + +The ZKAPIssuer.service needs a working TLS certificate and expects it in the certbot directory for the domain you configured, in my case:: + + openssl req -x509 -newkey rsa:4096 -nodes -keyout privkey.pem -out cert.pem -days 3650 + touch chain.pem + +Move the three .pem files into the payment's server ``/var/lib/letsencrypt/live/payments.localdev/`` directory and issue a ``sudo systemctl restart zkapissuer.service``. + + +Use the local development environment +````````````````````````````````````` + +1. Build and start the VMs:: + + VAGRANT_DEFAULT_PROVIDER=virtualbox vagrant up + +2. Then, once:: + + vagrant ssh-config > ./vagrant-ssh-config + +3. Edit the output: Add the IPs from ``grid.nix`` to the ``vagrant-ssh-config`` **Host match blocks** so the config reads like:: + + Host payments1 192.168.67.21 + HostName 192.168.67.21 + User vagrant + Port 22 + [...] + +4. Then, make morph use this ssh config either - with newer morph [#]_ - by pointing it to it:: + + export SSH_CONFIG_FILE=./vagrant-ssh-config + + Or, with older morph, adding the config to your user's ``~/.ssh/config`` file. + + .. [#] Morph honors the ``SSH_CONFIG_FILE`` environment variable `since 3f90aa88 (March 2020, v 1.5.0) <https://github.com/DBCDK/morph/commit/3f90aa885fac1c29fce9242452fa7c0c505744ef#diff-d155ad793bd62e6ea4c44ba985049ecb13a4f4f32f799791b2bce695a16c0101>`_. + +5. Then, build and deploy our software to the Vagrant VMs:: + + morph build grid.nix + morph push grid.nix + morph upload-secrets grid.nix + morph deploy grid.nix switch + + You will now be able to log in with the users and keys you set in your ``localdev-users.nix`` file. + diff --git a/morph/grid/local/Vagrantfile b/morph/grid/local/Vagrantfile new file mode 100644 index 0000000000000000000000000000000000000000..81dec21ee701d761b4347f11df43398d78d4b528 --- /dev/null +++ b/morph/grid/local/Vagrantfile @@ -0,0 +1,45 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +# This Vagrantfile worked for Florian Sesser using Vagrant 2.2.16dev and +# the VirtualBox Hypervisor. Earlier Vagrant and LibVirt did not work. + +Vagrant.configure("2") do |config| + # For a complete reference, please see the online documentation at + # https://docs.vagrantup.com. + + config.vm.define "payments1" do |config| + config.vm.hostname = "payments1" + config.vm.box = "esselius/nixos" + config.vm.box_version = "20.09" + config.vm.box_check_update = false + config.vm.network "private_network", ip: "192.168.67.21" + end + + config.vm.define "storage1" do |config| + config.vm.hostname = "storage1" + config.vm.box = "esselius/nixos" + config.vm.box_version = "20.09" + config.vm.box_check_update = false + config.vm.network "private_network", ip: "192.168.67.22" + end + + config.vm.define "storage2" do |config| + config.vm.hostname = "storage2" + config.vm.box = "esselius/nixos" + config.vm.box_version = "20.09" + config.vm.box_check_update = false + config.vm.network "private_network", ip: "192.168.67.23" + end + + # To make the VMs assign the static IPs to the network interfaces we need a rebuild: + config.vm.provision "shell", inline: "echo '{nix.trustedUsers = [ \"@wheel\" \"root\" \"vagrant\" ];}' > /etc/nixos/custom-configuration.nix" + config.vm.provision "shell", inline: "nixos-rebuild switch" + + config.trigger.after :up do |trigger| + trigger.info = "Hostname and IP address this host actually uses:" + trigger.run_remote = {inline: "echo `hostname` `ifconfig | egrep -o '192.168.67.[0-9]* '`"} + end + +end + diff --git a/morph/grid/local/config.json b/morph/grid/local/config.json new file mode 100644 index 0000000000000000000000000000000000000000..e970c3c6c29498497e5fad19d04ed56a56471ec1 --- /dev/null +++ b/morph/grid/local/config.json @@ -0,0 +1,10 @@ +{ "publicStoragePort": 8898 +, "ristrettoSigningKeyPath": "../../PrivateStorageSecrets/ristretto.signing-key" +, "stripeSecretKeyPath": "../../PrivateStorageSecrets/privatestorageio-testing-stripe.secret" +, "passValue": 1000000 +, "issuerDomains": ["payments.localdev"] +, "letsEncryptAdminEmail": "florian@privatestorage.io" +, "allowedChargeOrigins": [ + "http://localhost:5000" + ] +} diff --git a/morph/grid/local/grid.nix b/morph/grid/local/grid.nix new file mode 100644 index 0000000000000000000000000000000000000000..fdda12e4537cd281a8d0768f0a95aa0608eca36b --- /dev/null +++ b/morph/grid/local/grid.nix @@ -0,0 +1,32 @@ +# Load the helper function and call it with arguments tailored for the local +# grid. It will make the morph configuration for us. We share this function +# with the production grid and have one fewer possible point of divergence. +import ../../lib/make-grid.nix { + name = "LocalDev"; + config = ./config.json; + nodes = cfg: + let + sshUsers = import ../../../../PrivateStorageSecrets/localdev-users.nix; + in { + "payments1" = import ../../lib/make-issuer.nix (rec { + publicIPv4 = "192.168.67.21"; + inherit sshUsers; + hardware = import ./virtual-hardware.nix ({ inherit publicIPv4; }); + stateVersion = "19.03"; + } // cfg); + + "storage1" = import ../../lib/make-testing.nix (rec { + publicIPv4 = "192.168.67.22"; + inherit sshUsers; + hardware = import ./virtual-hardware.nix ({ inherit publicIPv4; }); + stateVersion = "19.09"; + } // cfg); + + "storage2" = import ../../lib/make-testing.nix (rec { + publicIPv4 = "192.168.67.23"; + inherit sshUsers; + hardware = import ./virtual-hardware.nix ({ inherit publicIPv4; }); + stateVersion = "19.09"; + } // cfg); + }; +} diff --git a/morph/grid/local/vagrant-guest.nix b/morph/grid/local/vagrant-guest.nix new file mode 100644 index 0000000000000000000000000000000000000000..8505b2f34dba067bf2c39a1645145bc626d30cf8 --- /dev/null +++ b/morph/grid/local/vagrant-guest.nix @@ -0,0 +1,86 @@ +# Minimal configuration that vagrant depends on + +{ config, pkgs, ... }: +let + # Vagrant uses an insecure shared private key by default, but we + # don't use the authorizedKeys attribute under users because it should be + # removed on first boot and replaced with a random one. This script sets + # the correct permissions and installs the temporary key if no + # ~/.ssh/authorized_keys exists. + install-vagrant-ssh-key = pkgs.writeScriptBin "install-vagrant-ssh-key" '' + #!${pkgs.runtimeShell} + if [ ! -e ~/.ssh/authorized_keys ]; then + mkdir -m 0700 -p ~/.ssh + echo "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key" >> ~/.ssh/authorized_keys + chmod 0600 ~/.ssh/authorized_keys + fi + ''; +in +{ + # Services to enable: + + # Enable the OpenSSH daemon. + services.openssh.enable = true; + + # Enable DBus + services.dbus.enable = true; + + # Replace ntpd by timesyncd + services.timesyncd.enable = true; + + # Packages for Vagrant + environment.systemPackages = with pkgs; [ + findutils + gnumake + iputils + jq + nettools + netcat + nfs-utils + rsync + ]; + + users.users.root = { password = "vagrant"; }; + + # Creates a "vagrant" group & user with password-less sudo access + users.groups.vagrant = { + name = "vagrant"; + members = [ "vagrant" ]; + }; + users.extraUsers.vagrant = { + isNormalUser = true; + createHome = true; + group = "vagrant"; + extraGroups = [ "users" "wheel" ]; + password = "vagrant"; + home = "/home/vagrant"; + useDefaultShell = true; + }; + + systemd.services.install-vagrant-ssh-key = { + description = "Vagrant SSH key install (if needed)"; + after = [ "fs.target" ]; + wants = [ "fs.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + ExecStart = "${install-vagrant-ssh-key}/bin/install-vagrant-ssh-key"; + User = "vagrant"; + # So it won't be (needlessly) restarted: + RemainAfterExit = true; + }; + }; + + security.sudo.wheelNeedsPassword = false; + + security.sudo.extraConfig = + '' + Defaults:root,%wheel env_keep+=LOCALE_ARCHIVE + Defaults:root,%wheel env_keep+=NIX_PATH + Defaults:root,%wheel env_keep+=TERMINFO_DIRS + Defaults env_keep+=SSH_AUTH_SOCK + Defaults lecture = never + root ALL=(ALL) SETENV: ALL + %wheel ALL=(ALL) NOPASSWD: ALL, SETENV: ALL + ''; +} + diff --git a/morph/grid/local/virtual-hardware.nix b/morph/grid/local/virtual-hardware.nix new file mode 100644 index 0000000000000000000000000000000000000000..d5e9067bd5f3b3ca2ea1bb46746253fa39b25cf6 --- /dev/null +++ b/morph/grid/local/virtual-hardware.nix @@ -0,0 +1,36 @@ +{ publicIPv4, ... }: +{ + imports = [ ./vagrant-guest.nix ]; + + virtualisation.virtualbox.guest.enable = true; + + # Use the GRUB 2 boot loader. + boot.loader.grub.enable = true; + boot.loader.grub.version = 2; + boot.loader.grub.device = "/dev/sda"; + + boot.initrd.availableKernelModules = [ "ata_piix" "sd_mod" "sr_mod" ]; + boot.initrd.kernelModules = [ ]; + boot.kernelModules = [ ]; + boot.extraModulePackages = [ ]; + + # remove the fsck that runs at startup. It will always fail to run, stopping + # your boot until you press *. + boot.initrd.checkJournalingFS = false; + + networking.interfaces.enp0s8.ipv4.addresses = [{ + address = publicIPv4; + prefixLength = 24; + }]; + + fileSystems."/storage" = { fsType = "tmpfs"; }; + fileSystems."/" = + { device = "/dev/sda1"; + fsType = "ext4"; + }; + swapDevices = [ ]; + + # We want to push packages with morph without having to sign them + nix.trustedUsers = [ "@wheel" "root" "vagrant" ]; +} + diff --git a/morph/grid/production/.gitignore b/morph/grid/production/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..db2fc0de62d01d6d7eec83f8f3e8c3b13b20392a --- /dev/null +++ b/morph/grid/production/.gitignore @@ -0,0 +1 @@ +secrets diff --git a/morph/grid/production/config.json b/morph/grid/production/config.json index ec60acc70dcdc90409b84e0b19ce9c2cb3d27cfa..e71cb8b4b5f999e3059f0669c2bc3f92f29242a6 100644 --- a/morph/grid/production/config.json +++ b/morph/grid/production/config.json @@ -1,11 +1,16 @@ { "publicStoragePort": 8898 -, "ristrettoSigningKeyPath": "../../PrivateStorageSecrets/ristretto.signing-key" -, "stripeSecretKeyPath": "../../PrivateStorageSecrets/stripe.secret" +, "ristrettoSigningKeyPath": "./secrets/ristretto.signing-key" +, "stripeSecretKeyPath": "./secrets/stripe.secret" , "passValue": 1000000 -, "issuerDomain": "payments.privatestorage.io" +, "issuerDomains": [ + "payments.privatestorage.io" + , "payments.private.storage" + ] , "letsEncryptAdminEmail": "jean-paul@privatestorage.io" , "allowedChargeOrigins": [ "https://privatestorage.io" , "https://www.privatestorage.io" + , "https://private.storage" + , "https://www.private.storage" ] } diff --git a/morph/grid/production/grid.nix b/morph/grid/production/grid.nix index fc3b9b5b6a5f3a532c05a42159bbcb2f0e6c798d..69a17602f0499acaa8b45adca5e45c8acc637110 100644 --- a/morph/grid/production/grid.nix +++ b/morph/grid/production/grid.nix @@ -6,7 +6,7 @@ import ../../lib/make-grid.nix { config = ./config.json; nodes = cfg: let - sshUsers = import ../../../../PrivateStorageSecrets/production-users.nix; + sshUsers = import ./users.nix; in { # 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 @@ -20,7 +20,7 @@ import ../../lib/make-grid.nix { # doesn't specify one. # # The names must be unique! - "payments.privatestorage.io" = import ../../lib/issuer.nix ({ + "payments.privatestorage.io" = import ../../lib/make-issuer.nix ({ publicIPv4 = "18.184.142.208"; inherit sshUsers; hardware = ../../lib/issuer-aws.nix; diff --git a/morph/grid/production/users.nix b/morph/grid/production/users.nix new file mode 100644 index 0000000000000000000000000000000000000000..d3520076636b56c0b07055a135becaf6a77b798f --- /dev/null +++ b/morph/grid/production/users.nix @@ -0,0 +1,2 @@ +let key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGN4VQm3BIQKEFTw6aPrEwNuShf640N+Py2LOKznFCRT exarkun@bottom"; +in { "root" = key; jcalderone = key; } diff --git a/morph/grid/testing/.gitignore b/morph/grid/testing/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..db2fc0de62d01d6d7eec83f8f3e8c3b13b20392a --- /dev/null +++ b/morph/grid/testing/.gitignore @@ -0,0 +1 @@ +secrets diff --git a/morph/grid/testing/config.json b/morph/grid/testing/config.json index e9b3b96940ab6cc73ef6b6f8dc2ec89a83a463d9..ec28840a2857c621a22658efc14368e4c07aa5db 100644 --- a/morph/grid/testing/config.json +++ b/morph/grid/testing/config.json @@ -1,8 +1,11 @@ { "publicStoragePort": 8898 -, "ristrettoSigningKeyPath": "../../PrivateStorageSecrets/ristretto.signing-key" -, "stripeSecretKeyPath": "../../PrivateStorageSecrets/privatestorageio-testing-stripe.secret" +, "ristrettoSigningKeyPath": "./secrets/ristretto.signing-key" +, "stripeSecretKeyPath": "./secrets/stripe.secret" , "passValue": 1000000 -, "issuerDomain": "payments.privatestorage-staging.com" +, "issuerDomains": [ + "payments.privatestorage-staging.com" + , "payments.extra.privatestorage-staging.com" + ] , "letsEncryptAdminEmail": "jean-paul@privatestorage.io" , "allowedChargeOrigins": [ "http://localhost:5000" diff --git a/morph/grid/testing/grid.nix b/morph/grid/testing/grid.nix index 9d5de19491b02409909fdbb9ca649d22c402ffd2..90acab60b7bf6a12f2c5ff3ff93cb93e479491aa 100644 --- a/morph/grid/testing/grid.nix +++ b/morph/grid/testing/grid.nix @@ -6,15 +6,10 @@ import ../../lib/make-grid.nix { config = ./config.json; nodes = cfg: let - importDef = default: path: ( - if builtins.pathExists path - then import path - else default - ); - sshUsers = importDef {} ../../../../PrivateStorageSecrets/staging-users.nix; + sshUsers = import ./users.nix; in { - "payments" = import ../../lib/issuer.nix ({ - publicIPv4 = "18.197.42.120"; + "payments" = import ../../lib/make-issuer.nix ({ + publicIPv4 = "18.194.183.13"; inherit sshUsers; hardware = ../../lib/issuer-aws.nix; stateVersion = "19.03"; diff --git a/morph/grid/testing/users.nix b/morph/grid/testing/users.nix new file mode 100644 index 0000000000000000000000000000000000000000..d3520076636b56c0b07055a135becaf6a77b798f --- /dev/null +++ b/morph/grid/testing/users.nix @@ -0,0 +1,2 @@ +let key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGN4VQm3BIQKEFTw6aPrEwNuShf640N+Py2LOKznFCRT exarkun@bottom"; +in { "root" = key; jcalderone = key; } diff --git a/morph/lib/issuer.nix b/morph/lib/make-issuer.nix similarity index 75% rename from morph/lib/issuer.nix rename to morph/lib/make-issuer.nix index 7510b6b82e8b298387394e5ff8082445a2fe18e7..5625b565c452d1fc9c8bf84eb1720cecd197f124 100644 --- a/morph/lib/issuer.nix +++ b/morph/lib/make-issuer.nix @@ -1,21 +1,22 @@ { hardware , ristrettoSigningKeyPath , stripeSecretKeyPath -, issuerDomain +, issuerDomains , letsEncryptAdminEmail , allowedChargeOrigins , sshUsers , stateVersion , publicIPv4 , ... -}: { +}: rec { deployment = { + targetUser = "root"; targetHost = publicIPv4; secrets = { "ristretto-signing-key" = { source = ristrettoSigningKeyPath; - destination = "/var/secrets/ristretto.signing-key"; + destination = "/run/keys/ristretto.signing-key"; owner.user = "root"; owner.group = "root"; permissions = "0400"; @@ -23,7 +24,7 @@ }; "stripe-secret-key" = { source = stripeSecretKeyPath; - destination = "/var/secrets/stripe.secret-key"; + destination = "/run/keys/stripe.secret-key"; owner.user = "root"; owner.group = "root"; permissions = "0400"; @@ -41,12 +42,12 @@ services.private-storage-issuer = { enable = true; tls = true; - ristrettoSigningKeyPath = "/var/secrets/ristretto.signing-key"; - stripeSecretKeyPath = "/var/secrets/stripe.secret-key"; + ristrettoSigningKeyPath = deployment.secrets.ristretto-signing-key.destination; + stripeSecretKeyPath = deployment.secrets.stripe-secret-key.destination; database = "SQLite3"; databasePath = "/var/db/vouchers.sqlite3"; inherit letsEncryptAdminEmail; - domain = issuerDomain; + domains = issuerDomains; inherit allowedChargeOrigins; }; diff --git a/morph/lib/make-storage.nix b/morph/lib/make-storage.nix index c286106a18caca4a575305f0fa897aedaac60c55..25f3a95bfb5e66a4b42c5f2f82b4fdacbaed4b41 100644 --- a/morph/lib/make-storage.nix +++ b/morph/lib/make-storage.nix @@ -14,12 +14,13 @@ , ... }: rec { deployment = { + targetUser = "root"; targetHost = cfg.publicIPv4; secrets = { "ristretto-signing-key" = { source = ristrettoSigningKeyPath; - destination = "/var/secrets/ristretto.signing-key"; + destination = "/run/keys/ristretto.signing-key"; owner.user = "root"; owner.group = "root"; permissions = "0400"; diff --git a/morph/lib/make-testing.nix b/morph/lib/make-testing.nix index ee1e2db49ba337578312866f737e216f961dc152..ed6ba27e70726cb08c38850cd09f08b37897fcfa 100644 --- a/morph/lib/make-testing.nix +++ b/morph/lib/make-testing.nix @@ -1,12 +1,13 @@ { publicIPv4, hardware, publicStoragePort, ristrettoSigningKeyPath, passValue, sshUsers, stateVersion, ... }: rec { deployment = { + targetUser = "root"; targetHost = publicIPv4; secrets = { "ristretto-signing-key" = { source = ristrettoSigningKeyPath; - destination = "/var/secrets/ristretto.signing-key"; + destination = "/run/keys/ristretto.signing-key"; owner.user = "root"; owner.group = "root"; permissions = "0400"; diff --git a/nixos/modules/issuer.nix b/nixos/modules/issuer.nix index 6ad4f1b8fa01d570e8b6e1d5c4acfbeb42757822..fb93ce35cce8c9cadbad5a04e888b0cca991f9c7 100644 --- a/nixos/modules/issuer.nix +++ b/nixos/modules/issuer.nix @@ -18,12 +18,11 @@ in { 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"; + services.private-storage-issuer.domains = lib.mkOption { + type = lib.types.listOf lib.types.str; + example = lib.literalExample [ "payments.example.com" ]; description = '' - The domain name at which the issuer is reachable. + The domain names at which the issuer is reachable. ''; }; services.private-storage-issuer.tls = lib.mkOption { @@ -115,6 +114,10 @@ in { 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}"; in lib.mkIf cfg.enable { # Add a systemd service to run PaymentServer. systemd.services.zkapissuer = { @@ -124,7 +127,7 @@ 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 "cert-${cfg.domain}.service"; + requires = lib.optional cfg.tls "${certServiceName}.service"; after = [ # Make sure there is a network so we can bind to all of the @@ -133,7 +136,7 @@ in { ] ++ # 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"; + lib.optional cfg.tls "${certServiceName}.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 @@ -157,9 +160,9 @@ in { 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" + "--https-certificate-path ${certroot}/${domain}/cert.pem " + + "--https-certificate-chain-path ${certroot}/${domain}/chain.pem " + + "--https-key-path ${certroot}/${domain}/privkey.pem" else # Only for automated testing. "--http-port 80"; @@ -179,20 +182,20 @@ in { # Certificate renewal. We must declare that we *require* it in our # service above. - systemd.services."cert-${cfg.domain}" = { + systemd.services."${certServiceName}" = { enable = true; - description = "Issue/Renew certificate for ${cfg.domain}"; + description = "Certificate ${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.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 --domains ${cfg.domain} + ${pkgs.certbot}/bin/certbot certonly ${configArgs} --non-interactive --standalone --expand --domains ${builtins.concatStringsSep "," cfg.domains} ''; }; }; diff --git a/nixos/modules/tests/private-storage.nix b/nixos/modules/tests/private-storage.nix index e085f8bc7142da4067745bdee233c82e7b1e8d1c..cbf4c5937ca6780ce9e931d6ceec91c29643fbc3 100644 --- a/nixos/modules/tests/private-storage.nix +++ b/nixos/modules/tests/private-storage.nix @@ -134,7 +134,7 @@ in { services.private-storage-issuer = { enable = true; - domain = "issuer"; + domains = ["issuer"]; tls = false; issuer = "Ristretto"; inherit ristrettoSigningKeyPath;