diff --git a/docs/source/dev/README.rst b/docs/source/dev/README.rst index f9d10518ac9c352186e894ef8e439b19f7ca3878..deb304ccba7f3490322c014a8ce42421f2cc7333 100644 --- a/docs/source/dev/README.rst +++ b/docs/source/dev/README.rst @@ -56,6 +56,18 @@ That will update ``nixpkgs-2015.json`` to the latest release on the nixos-21.05 To update the channel, the script will need to be updated, along with the filenames that have the channel in them. +Gitlab Repositories +``````````````````` +To update the version of packages we import from gitlab, run: + +.. code: shell + + nix-shell --command 'tools/update-gitlab nixos/pkgs/<package>/repo.json' + +That will update the package to point at the latest version of the project.\ +The command uses branch and repository owner specified in the ``repo.json`` file, +but you can override them by passing the ``--branch`` or ``-owner`` arguments to the command. +A specific revision can also be pinned, by passing ``-rev``. Interactions ------------ diff --git a/morph/grid/local/grid.nix b/morph/grid/local/grid.nix index 15afcc8f600018f2cd50fe7f8a0cf4383da27ead..f977876037c7a3769852f43cab0e5d3e59fce154 100644 --- a/morph/grid/local/grid.nix +++ b/morph/grid/local/grid.nix @@ -27,6 +27,8 @@ let ../../../nixos/modules/deployment.nix # Give it a good SSH configuration. ../../../nixos/modules/ssh.nix + # Configure things specific to the virtualisation environment. + gridlib.hardware-vagrant ]; services.private-storage.sshUsers = ssh-users; @@ -46,7 +48,7 @@ let # depend on the format we use. mode = "0666"; text = '' - # Include the ssh-users config + # Include the ssh-users config builtins.fromJSON (builtins.readFile ./ssh-users.json) ''; }; @@ -68,42 +70,47 @@ let payments = { imports = [ gridlib.issuer - (gridlib.hardware-virtual ({ publicIPv4 = "192.168.67.21"; })) (gridlib.customize-issuer (grid-config // { monitoringvpnIPv4 = "172.23.23.11"; })) grid-module ]; + config = { + grid.publicIPv4 = "192.168.67.21"; + }; }; storage1 = { imports = [ gridlib.storage - (gridlib.hardware-virtual ({ publicIPv4 = "192.168.67.22"; })) (gridlib.customize-storage (grid-config // { monitoringvpnIPv4 = "172.23.23.12"; stateVersion = "19.09"; })) grid-module ]; + config = { + grid.publicIPv4 = "192.168.67.22"; + }; }; storage2 = { imports = [ gridlib.storage - (gridlib.hardware-virtual ({ publicIPv4 = "192.168.67.23"; })) (gridlib.customize-storage (grid-config // { monitoringvpnIPv4 = "172.23.23.13"; stateVersion = "19.09"; })) grid-module ]; + config = { + grid.publicIPv4 = "192.168.67.23"; + }; }; monitoring = { imports = [ gridlib.monitoring - (gridlib.hardware-virtual ({ publicIPv4 = "192.168.67.24"; })) (gridlib.customize-monitoring { inherit hostsMap vpnClientIPs nodeExporterTargets paymentExporterTargets; inherit (grid-config) letsEncryptAdminEmail; @@ -114,6 +121,9 @@ let }) grid-module ]; + config = { + grid.publicIPv4 = "192.168.67.24"; + }; }; # TBD: derive these automatically: diff --git a/morph/lib/default.nix b/morph/lib/default.nix index 34f5e8b5171f211ededf38efb25440769113a8e4..78de2506382d023bc76b723eb866efc90f20ef7f 100644 --- a/morph/lib/default.nix +++ b/morph/lib/default.nix @@ -5,7 +5,7 @@ base = import ./base.nix; hardware-aws = import ./issuer-aws.nix; - hardware-virtual = import ./hardware-virtual.nix; + hardware-vagrant = import ./hardware-vagrant.nix; issuer = import ./issuer.nix; customize-issuer = import ./customize-issuer.nix; diff --git a/morph/lib/hardware-vagrant.nix b/morph/lib/hardware-vagrant.nix new file mode 100644 index 0000000000000000000000000000000000000000..150944cd5b64b7a3eb620cd40ea39e00544779a7 --- /dev/null +++ b/morph/lib/hardware-vagrant.nix @@ -0,0 +1,43 @@ +{ config, lib, modulesPath, ... }: +{ + imports = [ + # modulesPath points at the upstream nixos/modules directory. + "${modulesPath}/virtualisation/vagrant-guest.nix" + ]; + + options.grid = { + publicIPv4 = lib.mkOption { + type = lib.types.str; + description = '' + The primary IPv4 address of the virtual machine. + ''; + }; + }; + + config = { + virtualisation.virtualbox.guest.enable = true; + + boot.loader.grub.device = "/dev/sda"; + + boot.initrd.availableKernelModules = [ "ata_piix" "sd_mod" "sr_mod" ]; + boot.kernel.sysctl = { "vm.swappiness" = 0; }; + + # 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 = config.grid.publicIPv4; + prefixLength = 24; + }]; + + fileSystems."/storage" = { fsType = "tmpfs"; }; + fileSystems."/" = + { device = "/dev/sda1"; + fsType = "ext4"; + }; + + # We want to push packages with morph without having to sign them + nix.trustedUsers = [ "@wheel" "root" "vagrant" ]; + }; +} diff --git a/morph/lib/hardware-virtual.nix b/morph/lib/hardware-virtual.nix deleted file mode 100644 index cf1582792bff77c491210ee5e91f99bfbffbf9f3..0000000000000000000000000000000000000000 --- a/morph/lib/hardware-virtual.nix +++ /dev/null @@ -1,36 +0,0 @@ -{ 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.kernel.sysctl = { "vm.swappiness" = 0; }; - 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/lib/vagrant-guest.nix b/morph/lib/vagrant-guest.nix deleted file mode 100644 index 360671f5e8391571d37da6db37b2de8dc02b66bd..0000000000000000000000000000000000000000 --- a/morph/lib/vagrant-guest.nix +++ /dev/null @@ -1,91 +0,0 @@ -# Minimal configuration that vagrant depends on - -{ config, pkgs, lib, ... }: -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; - - # Wireguard kernel module for Kernels < 5.6 - boot = lib.mkIf (lib.versionOlder pkgs.linuxPackages.kernel.version "5.6") { - extraModulePackages = [ config.boot.kernelPackages.wireguard ] ; - }; - - # 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/nixos/modules/monitoring/server/grafana-dashboards/resources-overview.json b/nixos/modules/monitoring/server/grafana-dashboards/resources-overview.json index cb5bc91da7c3adbb1c9377473b053d31d53550f0..5ecbcb9b709f7093592e54f368166da064b1ae73 100644 --- a/nixos/modules/monitoring/server/grafana-dashboards/resources-overview.json +++ b/nixos/modules/monitoring/server/grafana-dashboards/resources-overview.json @@ -41,7 +41,7 @@ "description": "Some of our software runs in a single thread, so this shows max CPU per core (instead of averaged over all cores)", "fieldConfig": { "defaults": { - "custom": {} + "links": [] }, "overrides": [] }, @@ -68,11 +68,10 @@ "linewidth": 1, "nullPointMode": "null", "options": { - "alertThreshold": true, - "dataLinks": [] + "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.3.5", + "pluginVersion": "7.5.10", "pointradius": 2, "points": false, "renderer": "flot", @@ -173,8 +172,8 @@ "datasource": null, "fieldConfig": { "defaults": { - "custom": {}, - "displayName": "${__field.labels.instance}" + "displayName": "${__field.labels.instance}", + "links": [] }, "overrides": [ { @@ -213,11 +212,10 @@ "linewidth": 1, "nullPointMode": "null", "options": { - "alertThreshold": true, - "dataLinks": [] + "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.3.5", + "pluginVersion": "7.5.10", "pointradius": 2, "points": false, "renderer": "flot", @@ -241,7 +239,7 @@ "line": true, "op": "gt", "value": 1, - "yaxis": "left" + "visible": true } ], "timeFrom": null, @@ -328,7 +326,7 @@ "description": "How much RAM is in use? Relative to available system memory.", "fieldConfig": { "defaults": { - "custom": {} + "links": [] }, "overrides": [] }, @@ -356,11 +354,10 @@ "linewidth": 1, "nullPointMode": "null", "options": { - "alertThreshold": true, - "dataLinks": [] + "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.3.5", + "pluginVersion": "7.5.10", "pointradius": 2, "points": false, "renderer": "flot", @@ -384,7 +381,7 @@ "line": true, "op": "gt", "value": 0.8, - "yaxis": "left" + "visible": true } ], "timeFrom": null, @@ -448,10 +445,10 @@ "dashLength": 10, "dashes": false, "datasource": null, - "description": "Shows most saturated network link for every node. Baseline is the reported NIC link speed - that might not be the actual limit.", + "description": "Shows most saturated network link for every node. Bit/s.", "fieldConfig": { "defaults": { - "custom": {} + "links": [] }, "overrides": [] }, @@ -478,11 +475,10 @@ "linewidth": 1, "nullPointMode": "null", "options": { - "alertThreshold": true, - "dataLinks": [] + "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.3.5", + "pluginVersion": "7.5.10", "pointradius": 2, "points": false, "renderer": "flot", @@ -492,14 +488,14 @@ "steppedLine": false, "targets": [ { - "expr": "max by (instance) (rate(node_network_transmit_bytes_total{device!~\"lo|monitoringvpn\"}[5m]) / node_network_speed_bytes)", + "expr": "max by (instance) (rate(node_network_transmit_bytes_total{device!~\"lo|monitoringvpn\"}[5m]) * 8)", "interval": "", "intervalFactor": 4, "legendFormat": "{{instance}} out", "refId": "A" }, { - "expr": "- max by (instance) (rate(node_network_receive_bytes_total{device!~\"lo|monitoringvpn\"}[5m]) / node_network_speed_bytes)", + "expr": "- max by (instance) (rate(node_network_receive_bytes_total{device!~\"lo|monitoringvpn\"}[5m]) * 8)", "interval": "", "intervalFactor": 4, "legendFormat": "{{instance}} in", @@ -510,7 +506,7 @@ "timeFrom": null, "timeRegions": [], "timeShift": null, - "title": "Throughput %", + "title": "Throughput", "tooltip": { "shared": false, "sort": 2, @@ -527,15 +523,17 @@ }, "yaxes": [ { + "$$hashKey": "object:226", "decimals": null, - "format": "percentunit", + "format": "bps", "label": null, "logBase": 1, - "max": "1", - "min": "-1", + "max": null, + "min": null, "show": true }, { + "$$hashKey": "object:227", "format": "short", "label": null, "logBase": 1, @@ -558,7 +556,7 @@ "description": "Packet and error count. Positive values mean transmit, negative receive.", "fieldConfig": { "defaults": { - "custom": {} + "links": [] }, "overrides": [] }, @@ -585,11 +583,10 @@ "linewidth": 1, "nullPointMode": "null as zero", "options": { - "alertThreshold": true, - "dataLinks": [] + "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.3.5", + "pluginVersion": "7.5.10", "pointradius": 2, "points": false, "renderer": "flot", @@ -602,28 +599,28 @@ "expr": "- rate(node_network_receive_packets_total{device!~\"lo|monitoringvpn\"}[5m])", "interval": "", "intervalFactor": 4, - "legendFormat": "{{instance}} {{device}}", + "legendFormat": "{{instance}} in", "refId": "A" }, { "expr": "- rate(node_network_receive_errs_total{device!~\"lo|monitoringvpn\"}[5m])", "interval": "", "intervalFactor": 4, - "legendFormat": "{{instance}} {{device}}", + "legendFormat": "{{instance}} in err", "refId": "B" }, { "expr": "rate(node_network_transmit_packets_total{device!~\"lo|monitoringvpn\"}[5m])", "interval": "", "intervalFactor": 4, - "legendFormat": "{{instance}} {{device}}", + "legendFormat": "{{instance}} out", "refId": "C" }, { "expr": "rate(node_network_transmit_errs_total{device!~\"lo|monitoringvpn\"}[5m])", "interval": "", "intervalFactor": 4, - "legendFormat": "{{instance}} {{device}}", + "legendFormat": "{{instance}} out err", "refId": "D" } ], @@ -647,7 +644,7 @@ }, "yaxes": [ { - "format": "short", + "format": "pps", "label": null, "logBase": 1, "max": null, @@ -781,7 +778,7 @@ "description": "Network errors, drops etc. Should all be 0.", "fieldConfig": { "defaults": { - "custom": {} + "links": [] }, "overrides": [] }, @@ -808,11 +805,10 @@ "linewidth": 1, "nullPointMode": "null", "options": { - "alertThreshold": true, - "dataLinks": [] + "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.3.5", + "pluginVersion": "7.5.10", "pointradius": 2, "points": false, "renderer": "flot", @@ -852,7 +848,8 @@ "fill": true, "line": true, "op": "gt", - "value": 10 + "value": 10, + "visible": true } ], "timeFrom": null, @@ -953,7 +950,7 @@ "description": "Watch filesystems filling up. Shows only mounts over 10 % of available bytes used.", "fieldConfig": { "defaults": { - "custom": {}, + "links": [], "unit": "percentunit" }, "overrides": [] @@ -981,11 +978,10 @@ "linewidth": 1, "nullPointMode": "null", "options": { - "alertThreshold": true, - "dataLinks": [] + "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.3.5", + "pluginVersion": "7.5.10", "pointradius": 2, "points": false, "renderer": "flot", @@ -1012,7 +1008,7 @@ "line": true, "op": "gt", "value": 0.8, - "yaxis": "left" + "visible": true } ], "timeFrom": null, @@ -1035,6 +1031,7 @@ }, "yaxes": [ { + "$$hashKey": "object:131", "format": "percentunit", "label": null, "logBase": 1, @@ -1043,6 +1040,7 @@ "show": true }, { + "$$hashKey": "object:132", "format": "short", "label": null, "logBase": 1, @@ -1065,7 +1063,7 @@ "description": "Input Output Operations per second. Positive values mean read, negative write.", "fieldConfig": { "defaults": { - "custom": {} + "links": [] }, "overrides": [] }, @@ -1092,11 +1090,10 @@ "linewidth": 1, "nullPointMode": "null as zero", "options": { - "alertThreshold": true, - "dataLinks": [] + "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.3.5", + "pluginVersion": "7.5.10", "pointradius": 2, "points": false, "renderer": "flot", @@ -1170,7 +1167,7 @@ "description": "Max average storage latency per node. Positive values mean read, negative write.", "fieldConfig": { "defaults": { - "custom": {} + "links": [] }, "overrides": [] }, @@ -1197,11 +1194,10 @@ "linewidth": 1, "nullPointMode": "null as zero", "options": { - "alertThreshold": true, - "dataLinks": [] + "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.3.5", + "pluginVersion": "7.5.10", "pointradius": 2, "points": false, "renderer": "flot", @@ -1268,14 +1264,14 @@ } ], "refresh": "30s", - "schemaVersion": 20, + "schemaVersion": 27, "style": "dark", "tags": [], "templating": { "list": [] }, "time": { - "from": "now-1h", + "from": "now-3h", "to": "now" }, "timepicker": {}, diff --git a/nixos/modules/packages.nix b/nixos/modules/packages.nix index d6518dcf290c27b95e3428434623a63cfbdb8e19..c4390dc00f3948e04e3e90ef270261cc0dd1cdbb 100644 --- a/nixos/modules/packages.nix +++ b/nixos/modules/packages.nix @@ -1,8 +1,13 @@ # A NixOS module which exposes custom packages to other modules. { pkgs, ...}: -{ +let + ourpkgs = pkgs.callPackage ../../nixos/pkgs {}; +in { config = { # Expose `nixos/pkgs` as a new module argument `ourpkgs`. - _module.args.ourpkgs = pkgs.callPackage ../../nixos/pkgs {}; + _module.args.ourpkgs = ourpkgs; + # Also expose it as a config setting, for usage by tests, + # since the `_module` config is not exposed in the result. + passthru.ourpkgs = ourpkgs; }; } diff --git a/nixos/modules/spending.nix b/nixos/modules/spending.nix new file mode 100644 index 0000000000000000000000000000000000000000..238fbe8f939c4ddb0c78b9a34e106dbea8e39921 --- /dev/null +++ b/nixos/modules/spending.nix @@ -0,0 +1,139 @@ +# A NixOS module which can run a Ristretto-based issuer for PrivateStorage +# ZKAPs. +{ lib, pkgs, config, ourpkgs, ... }@args: let + cfg = config.services.private-storage-spending; +in +{ + options = { + services.private-storage-spending = { + enable = lib.mkEnableOption "PrivateStorage Spending Service"; + package = lib.mkOption { + default = ourpkgs.zkap-spending-service; + type = lib.types.package; + example = lib.literalExample "ourpkgs.zkap-spending-service"; + description = '' + The package to use for the spending service. + ''; + }; + unixSocket = lib.mkOption { + default = "/run/zkap-spending-service/api.socket"; + type = lib.types.path; + description = '' + The unix socket that the spending service API listens on. + ''; + }; + }; + services.private-storage-spending.domain = lib.mkOption { + default = config.networking.fqdn; + type = lib.types.str; + example = lib.literalExample [ "spending.example.com" ]; + description = '' + The domain name at which the spending service is reachable. + ''; + }; + }; + + config = + lib.mkIf cfg.enable { + systemd.sockets.zkap-spending-service = { + enable = true; + wantedBy = [ "sockets.target" ]; + listenStreams = [ cfg.unixSocket ]; + }; + # Add a systemd service to run zkap-spending-service. + systemd.services.zkap-spending-service = { + enable = true; + description = "ZKAP Spending Service"; + wantedBy = [ "multi-user.target" ]; + + serviceConfig.NonBlocking = true; + + # 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"; + + # Use a unnamed user. + serviceConfig.DynamicUser = true; + + serviceConfig = { + # Work around https://twistedmatrix.com/trac/ticket/10261 + # Create a runtime directory so that the service has permission + # to change the mode on the socket. + RuntimeDirectory = "zkap-spending-service"; + + # This set of restrictions is mostly dervied from + # - running `systemd-analyze security zkap-spending-service.service + # - Looking at the restrictions from the nixos nginx config. + AmbientCapabilities = ""; + CapabilityBoundingSet = ""; + LockPersonality = true; + MemoryDenyWriteExecute = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateMounts = true; + PrivateNetwork = true; + PrivateTmp = true; + PrivateUsers = true; + ProcSubset = "pid"; + ProtectClock = true; + ProtectControlGroups = true; + ProtectHome = true; + ProtectHostname = true; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectProc = "invisible"; + ProtectSystem = "strict"; + RemoveIPC = true; + RestrictAddressFamilies = "AF_UNIX"; + RestrictNamespaces = true; + RestrictRealtime = true; + RestrictSUIDSGID = true; + SystemCallArchitectures = "native"; + # Lines starting with "~" are deny-list the others are allow-list + # Since the first line is allow, that bounds the set of allowed syscalls + # and the further lines restrict it. + SystemCallFilter = [ + # From systemd.exec(5), @system-service is "A reasonable set of + # system calls used by common system [...]" + "@system-service" + # This is from the nginx config, except that `@ipc` is not removed, + # since twisted uses a self-pipe. + "~@cpu-emulation @debug @keyring @mount @obsolete @privileged @setuid" + ]; + Umask = "0077"; + }; + + script = let + httpArgs = "--http-endpoint systemd:domain=UNIX:index=0"; + in + "exec ${cfg.package}/bin/${cfg.package.meta.mainProgram} run ${httpArgs}"; + }; + + services.nginx = { + enable = true; + + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + + virtualHosts."${cfg.domain}" = { + locations."/v1/" = { + # Only forward requests beginning with /v1/ so + # we pass less scanning spam on to our backend + # Want a regex instead? try locations."~ /v\d+/" + proxyPass = "http://unix:${cfg.unixSocket}"; + }; + locations."/" = { + # Return a 404 error for any paths not specified above. + extraConfig = '' + return 404; + ''; + }; + }; + }; + }; +} diff --git a/nixos/pkgs/default.nix b/nixos/pkgs/default.nix index 3d534430377cb5fbbf0739d60a8a7ca9bb0419f6..efcff08333a8c28e110e95f01c6c284c2411b594 100644 --- a/nixos/pkgs/default.nix +++ b/nixos/pkgs/default.nix @@ -20,5 +20,6 @@ let in { zkapissuer = callPackage ./zkapissuer {}; + zkap-spending-service = callPackage ./zkap-spending-service {}; inherit (ourpkgs) privatestorage leasereport; } diff --git a/nixos/pkgs/zkap-spending-service/default.nix b/nixos/pkgs/zkap-spending-service/default.nix new file mode 100644 index 0000000000000000000000000000000000000000..4716109e7add7af74f032ee1668be1394cf05b17 --- /dev/null +++ b/nixos/pkgs/zkap-spending-service/default.nix @@ -0,0 +1,12 @@ +{ callPackage, fetchFromGitLab, lib }: +let + repo-data = lib.importJSON ./repo.json; + + repo = fetchFromGitLab (builtins.removeAttrs repo-data [ "branch" ]); +in +# We want to check the revision the service reports against the revsion +# that we install. The upsream derivation doesn't currently know its own +# version, but we do have it here. Thus, we add it as a meta attribute +# to the derviation provided from upstream. +lib.addMetaAttrs { inherit (repo-data) rev; } + (callPackage repo {}) diff --git a/nixos/pkgs/zkap-spending-service/repo.json b/nixos/pkgs/zkap-spending-service/repo.json new file mode 100644 index 0000000000000000000000000000000000000000..39aeb8404c890e4781ee77f2a93d85d68acee5c3 --- /dev/null +++ b/nixos/pkgs/zkap-spending-service/repo.json @@ -0,0 +1,9 @@ +{ + "owner": "privatestorage", + "repo": "zkap-spending-service", + "rev": "e0d63b79213d16f2de6629167ea8f1236ba22e14", + "branch": "main", + "domain": "whetstone.privatestorage.io", + "outputHash": "30abb0g9xxn4lp493kj5wmz8kj5q2iqvw40m8llqvb3zamx60gd8cy451ii7z15qbrbx9xmjdfw0k4gviij46fkx1s8nbich5c8qx57", + "outputHashAlgo": "sha512" +} diff --git a/nixos/system-tests.nix b/nixos/system-tests.nix index 73b6665ab91e4d9a8a2200fb0eec7ff596f79b39..7b6d382ada53c1121a1bc3d0edbf82964d644ad2 100644 --- a/nixos/system-tests.nix +++ b/nixos/system-tests.nix @@ -3,5 +3,6 @@ let pkgs = import ../nixpkgs-2105.nix { }; in { private-storage = pkgs.nixosTest ./tests/private-storage.nix; + spending = pkgs.nixosTest ./tests/spending.nix; tahoe = pkgs.nixosTest ./tests/tahoe.nix; } diff --git a/nixos/tests/spending.nix b/nixos/tests/spending.nix new file mode 100644 index 0000000000000000000000000000000000000000..c970157b9375e0d99e2be8d4f782992163a6c948 --- /dev/null +++ b/nixos/tests/spending.nix @@ -0,0 +1,32 @@ +{ pkgs, lib, ... }: +{ + name = "zkap-spending-service"; + nodes = { + spending = { config, pkgs, ourpkgs, modulesPath, ... }: { + imports = [ + ../modules/packages.nix + ../modules/spending.nix + ]; + + services.private-storage-spending.enable = true; + services.private-storage-spending.domain = "localhost"; + }; + }; + testScript = { nodes }: let + revision = nodes.spending.config.passthru.ourpkgs.zkap-spending-service.meta.rev; + curl = "${pkgs.curl}/bin/curl -sSf"; + in + '' + import json + + start_all() + + spending.wait_for_open_port(80) + with subtest("Ensure we can ping the spending service"): + output = spending.succeed("${curl} http://localhost/v1/_ping") + assert json.loads(output)["status"] == "ok", "Could not ping spending service." + with subtest("Ensure that the spending service version matches the expected version"): + output = spending.succeed("${curl} http://localhost/v1/_version") + assert json.loads(output)["revision"] == "${revision}", "Spending service revision does not match." + ''; +} diff --git a/tools/default.nix b/tools/default.nix index f9a0b1ff8d902f3072886939ad11e1e223ffbb7e..fb44c660e7d4a4a62cec6bb58a008a1bf00429dc 100644 --- a/tools/default.nix +++ b/tools/default.nix @@ -15,6 +15,7 @@ let }; python-commands = [ ./update-nixpkgs + ./update-gitlab-repo ]; in # This derivation creates a package that wraps our tools to setup an environment diff --git a/tools/update-gitlab-repo b/tools/update-gitlab-repo new file mode 100755 index 0000000000000000000000000000000000000000..ddc82cb7bfd943ed3b4b80f79cf9e47b447c8b7d --- /dev/null +++ b/tools/update-gitlab-repo @@ -0,0 +1,89 @@ +#!/usr/bin/env python + +""" +Update a pinned gitlab repository. + +Pass this path to a JSON file and it will update it to the latest +version of the branch it specifies. You can also pass a different +branch or repository owner, which will update the file to point at +the new branch/repository, and update to the latest version. +""" + +import argparse +import json +from pathlib import Path + +import httpx +from ps_tools import get_url_hash + +HASH_TYPE = "sha512" + +ARCHIVE_TEMPLATE = "https://{domain}/api/v4/projects/{owner}%2F{repo}/repository/archive.tar.gz?sha={rev}" +BRANCH_TEMPLATE = ( + "https://{domain}/api/v4/projects/{owner}%2F{repo}/repository/branches/{branch}" +) + + +def get_gitlab_commit(config): + response = httpx.get(BRANCH_TEMPLATE.format(**config)) + response.raise_for_status() + return response.json()["commit"]["id"] + + +def get_gitlab_archive_url(config): + return ARCHIVE_TEMPLATE.format(**config) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "repo_file", + metavar="repo-file", + type=Path, + help="JSON file with pinned configuration.", + ) + parser.add_argument( + "--branch", + type=str, + help="Branch to update to.", + ) + parser.add_argument( + "--owner", + type=str, + help="Repository owner to update to.", + ) + parser.add_argument( + "--rev", + type=str, + help="Revision to pin.", + ) + parser.add_argument( + "--dry-run", + action="store_true", + ) + args = parser.parse_args() + + repo_file = args.repo_file + config = json.loads(repo_file.read_text()) + + for key in ["owner", "branch"]: + if getattr(args, key) is not None: + config[key] = getattr(args, key) + + if args.rev is not None: + config["rev"] = args.rev + else: + config["rev"] = get_gitlab_commit(config) + + archive_url = get_gitlab_archive_url(config) + config.update(get_url_hash(HASH_TYPE, "source", archive_url)) + + output = json.dumps(config, indent=2) + if args.dry_run: + print(output) + else: + repo_file.write_text(output) + + +if __name__ == "__main__": + main()