Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • privatestorage/PrivateStorageio
  • tomprince/PrivateStorageio
2 results
Show changes
Commits on Source (34)
Showing
with 332 additions and 142 deletions
Deployment notes
================
- 2021-09-30 `Enable alerting <https://whetstone.privatestorage.io/privatestorage/PrivateStorageio/-/merge_requests/185>`_ needs a secret in ``private-keys/grafana-slack-url`` looking like the template in ``morph/grid/local/private-keys/grafana-slack-url`` and pointing to the secret API endpoint URL saved in `this 1Password entry <https://privatestorage.1password.com/vaults/7flqasy5hhhmlbtp5qozd3j4ga/allitems/cgznskz2oix2tyx5xyntwaos5i>`_ (or create a new secret URL at https://www.slack.com/apps/A0F7XDUAZ).
- 2021-09-07 `Manage access to payment metrics <https://whetstone.privatestorage.io/privatestorage/PrivateStorageio/-/merge_requests/146>`_ requires moving and chown'ing the PaymentServer database on the ``payments`` host::
mkdir /var/lib/zkapissuer
......
......@@ -57,6 +57,20 @@ 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``.
Architecture overview
---------------------
......
......@@ -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,51 +70,60 @@ 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;
googleOAuthClientID = grid-config.monitoringGoogleOAuthClientID;
enableSlackAlert = false;
monitoringvpnIPv4 = "172.23.23.1";
stateVersion = "19.09";
})
grid-module
];
config = {
grid.publicIPv4 = "192.168.67.24";
};
};
# TBD: derive these automatically:
......@@ -129,6 +140,7 @@ let
in {
network = {
description = "PrivateStorage.io LocalDev Grid";
inherit (gridlib) pkgs;
};
inherit payments monitoring storage1 storage2;
}
......@@ -19,6 +19,13 @@ grafana-admin.password
This is the initial admin password for the Grafana web admin on the monitoring host.
grafana-slack-url
-----------------
This file is read by Grafana's systemd service to set an environment variable with a secret Slack WebHook URL to post alerts to.
The only line in the file should be ``SLACKURL=`` with the secret URL.
Use the url from `this 1Password entry <https://privatestorage.1password.com/vaults/7flqasy5hhhmlbtp5qozd3j4ga/allitems/cgznskz2oix2tyx5xyntwaos5i>`_ or get a new secret URL for your Slack channel at https://www.slack.com/apps/A0F7XDUAZ.
stripe.secret
-------------
......
SLACKURL=https://hooks.slack.com/services/x/y/z
......@@ -48,6 +48,7 @@ let
inherit hostsMap vpnClientIPs nodeExporterTargets paymentExporterTargets;
inherit (grid-config) letsEncryptAdminEmail;
googleOAuthClientID = grid-config.monitoringGoogleOAuthClientID;
enableSlackAlert = true;
monitoringvpnIPv4 = "172.23.23.1";
stateVersion = "19.09";
})
......@@ -130,6 +131,7 @@ let
in {
network = {
description = "PrivateStorage.io Production Grid";
inherit (gridlib) pkgs;
};
inherit payments;
inherit monitoring;
......
......@@ -61,6 +61,7 @@ let
inherit hostsMap vpnClientIPs nodeExporterTargets paymentExporterTargets;
inherit (grid-config) letsEncryptAdminEmail;
googleOAuthClientID = grid-config.monitoringGoogleOAuthClientID;
enableSlackAlert = true;
monitoringvpnIPv4 = "172.23.23.1";
stateVersion = "19.09";
})
......@@ -81,6 +82,7 @@ let
in {
network = {
description = "PrivateStorage.io Testing Grid";
inherit (gridlib) pkgs;
};
inherit payments monitoring storage001;
}
......@@ -31,6 +31,19 @@
# being configured and using variable names complicates a lot of things).
# Instead, just tell morph how to reach the node here - by using its fully
# qualified domain name.
deployment.targetHost = "${config.networking.hostName}.${config.networking.domain}";
deployment.targetHost = config.networking.fqdn;
assertions = [
# This is a check to save somebody in the future trying to debug why
# setting `nixpkgs.config` is not having an effect.
{
# `{}` is the default value for `nixpkgs.config`
assertion = config.nixpkgs.config == {};
message = ''
Since we set `nixpkgs.pkgs` via morph's `network.pkgs`, the value for `nixpkgs.config` is ignored.
See https://whetstone.privatestorage.io/privatestorage/PrivateStorageio/-/issues/85#note_15876 for details.
'';
}
];
};
}
......@@ -32,6 +32,10 @@
# logins to Grafana.
, googleOAuthClientID
# Whether to enable alerting via Slack.
# When true requires a grafana-slack-url file (see private-keys/README.rst).
, enableSlackAlert ? false
# A string giving the NixOS state version for the system.
, stateVersion
, ...
......@@ -71,12 +75,25 @@ in {
action = ["sudo" "systemctl" "restart" "grafana.service"];
};
};
grafanaSlackUrl =
if !enableSlackAlert
then { }
else {
"grafana-slack-url" = {
source = "${privateKeyPath}/grafana-slack-url";
destination = "/run/keys/grafana-slack-url";
owner.user = config.systemd.services.grafana.serviceConfig.User;
owner.group = config.users.users.grafana.group;
permissions = "0400";
action = ["sudo" "systemctl" "restart" "grafana.service"];
};
};
monitoringvpn = {
"monitoringvpn-private-key".source = "${privateKeyPath}/monitoringvpn/server.key";
"monitoringvpn-preshared-key".source = "${privateKeyPath}/monitoringvpn/preshared.key";
};
in
grafanaSSO // monitoringvpn;
grafanaSSO // grafanaSlackUrl // monitoringvpn;
networking.hosts = hostsMap;
......@@ -96,6 +113,7 @@ in {
services.private-storage.monitoring.grafana = {
inherit letsEncryptAdminEmail;
inherit googleOAuthClientID;
inherit enableSlackAlert;
domain = "${config.networking.hostName}.${config.networking.domain}";
};
......
......@@ -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;
......@@ -17,4 +17,16 @@
customize-monitoring = import ./customize-monitoring.nix;
modules = builtins.toString ../../nixos/modules;
# The nixpkgs version used in our deployments. This affects both the packages
# installed, as well as the NixOS module set that is used.
# This is intended to be used in a grid definition like:
# network = { ... ; inherit (gridlib) pkgs; ... }
pkgs = import ../../nixpkgs-2105.nix {
# Ensure that configuration of the system where this runs
# doesn't leak into what we build.
# See https://github.com/NixOS/nixpkgs/issues/62513
config = {};
overlays = [];
};
}
{ 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" ];
};
}
{ 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" ];
}
# 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
'';
}
......@@ -224,6 +224,12 @@ in {
'';
proxyPass = "http://127.0.0.1:${internalHttpPort}";
};
locations."/" = {
# Return a 404 error for any paths not specified above.
extraConfig = ''
return 404;
'';
};
};
};
......
......@@ -83,7 +83,7 @@
"targets": [
{
"exemplar": true,
"expr": "processors_stripe_charge_attempts",
"expr": "payment_processors_stripe_charge_attempts",
"hide": false,
"interval": "",
"legendFormat": "Attempts",
......@@ -91,7 +91,7 @@
},
{
"exemplar": true,
"expr": "processors_stripe_charge_successes",
"expr": "payment_processors_stripe_charge_successes",
"interval": "",
"legendFormat": "Successes",
"refId": "C"
......@@ -201,14 +201,14 @@
"targets": [
{
"exemplar": true,
"expr": "redemption_signatures_issued",
"expr": "payment_redemption_signatures_issued",
"interval": "",
"legendFormat": "Issued signatures",
"refId": "A"
},
{
"exemplar": true,
"expr": "redemption_voucher_redeemed",
"expr": "payment_redemption_vouchers_redeemed",
"format": "time_series",
"hide": false,
"interval": "",
......
......@@ -62,12 +62,36 @@ in {
default = /run/keys/grafana-admin.password;
description = "A file containing the password for the Grafana Admin account.";
};
enableSlackAlert = lib.mkOption
{ type = lib.types.bool;
default = false;
description = ''
Enables the slack alerter. Expects a file that contains
the definition of an environment variable named SLACKURL
pointing to the secret Slack Web Hook URL in
grafanaSlackUrlFile (see below).
'';
};
grafanaSlackUrlFile = lib.mkOption
{ type = lib.types.path;
default = /run/keys/grafana-slack-url;
description = ''
Where to find the Grafana Systemd EnvironmentFile that
sets the secret SLACKURL environment variable.
'';
};
};
config = {
# Port 80 for ACME ssl retrieval only. 443 for nginx -> grafana.
networking.firewall.allowedTCPPorts = [ 80 443 ];
# We pass the secret Slack URL using an environment variable.
systemd.services.grafana.serviceConfig.EnvironmentFile =
if cfg.enableSlackAlert
then [ cfg.grafanaSlackUrlFile ]
else [ ];
services.grafana = {
enable = true;
domain = cfg.domain;
......@@ -119,8 +143,23 @@ in {
# See https://grafana.com/docs/grafana/latest/administration/provisioning/#dashboards
dashboards = [{
name = "provisioned";
options.path = ./grafana-config;
options.path = ./grafana-dashboards;
}];
# See https://grafana.com/docs/grafana/latest/administration/provisioning/#example-alert-notification-channels-config-file
notifiers = [ ] ++ (lib.optionals (cfg.enableSlackAlert) [{
uid = "slack-notifier-1";
name = "Slack";
type = "slack";
is_default = true;
send_reminder = false;
settings = {
username = "${cfg.domain}";
uploadImage = true;
};
secure_settings = {
url = "$SLACKURL";
};
}]);
};
};
......
# 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;
};
}
# 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;
'';
};
};
};
};
}
......@@ -20,5 +20,6 @@ let
in
{
zkapissuer = callPackage ./zkapissuer {};
zkap-spending-service = callPackage ./zkap-spending-service {};
inherit (ourpkgs) privatestorage leasereport;
}