diff --git a/ops/nixos/lib/common.nix b/ops/nixos/lib/common.nix index 1cf194396f..89b258ac27 100644 --- a/ops/nixos/lib/common.nix +++ b/ops/nixos/lib/common.nix @@ -15,7 +15,10 @@ let mkDefault = lib.mkOverride 900; in { - imports = [ ../../../third_party/home-manager/nixos ]; + imports = [ + ../../../third_party/home-manager/nixos + ./vault-agent.nix + ]; options.my.specialisationName = lib.mkOption { type = lib.types.nullOr lib.types.str; diff --git a/ops/nixos/lib/vault-agent.nix b/ops/nixos/lib/vault-agent.nix new file mode 100644 index 0000000000..a96e983e24 --- /dev/null +++ b/ops/nixos/lib/vault-agent.nix @@ -0,0 +1,88 @@ +# SPDX-FileCopyrightText: 2020 Luke Granger-Brown +# +# SPDX-License-Identifier: Apache-2.0 + +{ pkgs, config, depot, lib, ... }: +let + inherit (lib) mkEnableOption mkOption types; + mkDefault = lib.mkOverride 900; + + format = pkgs.formats.json {}; +in +{ + options.my.vault = { + enable = mkEnableOption "vault agent"; + roleID = mkOption { + type = types.str; + default = config.networking.hostName; + }; + secretIDPath = mkOption { + type = types.str; + default = "/var/lib/vault-agent/secret-id"; + }; + settings = mkOption { + type = format.type; + default = {}; + }; + bindMountStateTo = mkOption { + type = types.nullOr types.str; + default = null; + }; + }; + + config = { + my.vault.enable = mkDefault true; + my.vault.settings = mkDefault { + pid_file = "/run/vault-agent/pid"; + vault.address = "https://vault.int.lukegb.com"; + auto_auth.method = [{ + type = "approle"; + config = { + role_id_file_path = pkgs.writeText "${config.my.vault.roleID}-role-id" config.my.vault.roleID; + secret_id_file_path = config.my.vault.secretIDPath; + remove_secret_id_file_after_reading = false; + }; + }]; + cache.use_auto_auth_token = true; + + listener.tcp = { + address = "127.0.0.1:8200"; + tls_disable = true; + }; + }; + + systemd = lib.optionalAttrs config.my.vault.enable { + services.vault-agent = { + description = "Hashicorp Vault Agent"; + wants = [ "network.target" ]; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + path = with pkgs; [ glibc.bin ]; + serviceConfig = { + RuntimeDirectory = "vault-agent"; + RuntimeDirectoryMode = "0700"; + StateDirectory = "vault-agent"; + StateDirectoryMode = "0700"; + + DynamicUser = true; + User = "vault-agent"; + + ProtectSystem = "strict"; + ProtectHome = "yes"; + + ExecStart = "${pkgs.vault}/bin/vault agent -config=${format.generate "vault-agent.json" config.my.vault.settings}"; + }; + }; + + mounts = lib.optional (config.my.vault.bindMountStateTo != null) { + unitConfig.RequiresMountsFor = "${config.my.vault.bindMountStateTo} /var/lib/private/vault-agent"; + options = "bind"; + what = config.my.vault.bindMountStateTo; + where = "/var/lib/private/vault-agent"; + requiredBy = [ "vault-agent.service" ]; + before = [ "vault-agent.service" ]; + wantedBy = [ "vault-agent.service" ]; + }; + }; + }; +} diff --git a/ops/nixos/lib/zfs.nix b/ops/nixos/lib/zfs.nix index adcfe36898..d8550aa920 100644 --- a/ops/nixos/lib/zfs.nix +++ b/ops/nixos/lib/zfs.nix @@ -4,7 +4,7 @@ { lib, config, ... }: let - inherit (lib) mkOption types mkAfter mkIf; + inherit (lib) mkOption types mkAfter mkIf mkDefault; robCfg = config.services.zfs.rollbackOnBoot; in { @@ -31,11 +31,13 @@ in enable = true; monthly = 1; }; - systemd.tmpfiles.rules = lib.mkAfter (builtins.map (x: "L ${x} - - - - /persist{x}") robCfg.keepPaths); + systemd.tmpfiles.rules = mkAfter (builtins.map (x: "L ${x} - - - - /persist{x}") robCfg.keepPaths); boot.initrd.postDeviceCommands = mkIf robCfg.enable (mkAfter '' zfs rollback -r ${robCfg.snapshot} ''); + + my.vault.bindMountStateTo = mkIf robCfg.enable (mkDefault "/persist/var/lib/vault-agent"); }; -} \ No newline at end of file +} diff --git a/ops/vault/create-server.sh b/ops/vault/create-server.sh new file mode 100755 index 0000000000..5283b166e4 --- /dev/null +++ b/ops/vault/create-server.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env nix-shell +#!nix-shell -p vault -p jq -i bash + +set -euo pipefail + +readonly server_name=${1} + +export VAULT_ADDR=https://vault.int.lukegb.com/ + +echo Checking login credentials... +vault token lookup >/dev/null || vault login -method=oidc role=admin + +echo Grabbing approle accessor... +APPROLE_ACCESSOR="$(vault auth list -format=json | jq -r '.["approle/"].accessor')" +echo -ne "\t${APPROLE_ACCESSOR}\n" + +echo Creating new approle... +vault write auth/approle/role/${server_name} \ + secret_id_num_uses=0 \ + secret_id_ttl="" \ + token_ttl=20m \ + token_max_ttl=30m \ + token_policies="default,server" \ + token_max_uses=0 + +echo Setting role-id... +vault write auth/approle/role/${server_name}/role-id role_id=${server_name} + +echo Creating new secret... +SECRET_ID="$(vault write -f -format=json auth/approle/role/${server_name}/secret-id | jq -r '.data.secret_id')" +echo -ne "\t$SECRET_ID\n" + +echo Creating entity... +ENTITY_ID="$(vault write -format=json identity/entity \ + name="${server_name}" \ + policies="server" \ + metadata="server=${server_name}" | jq -r '.data.id')" +echo -ne "\t$ENTITY_ID\n" + +echo Creating entity alias... +vault write identity/entity-alias \ + name="${server_name}" \ + canonical_id="${ENTITY_ID}" \ + mount_accessor="${APPROLE_ACCESSOR}"