ops/vault/cfg: initial configuration

This commit is contained in:
Luke Granger-Brown 2022-03-14 23:34:33 +00:00
parent 92998b5d36
commit 23df8e3b18
17 changed files with 493 additions and 3 deletions

View file

@ -7,6 +7,7 @@ result
result-* result-*
ops/vault/cfg/tf/ ops/vault/cfg/tf/
ops/vault/cfg/secrets.nix
web/quotes/theme/static/ web/quotes/theme/static/

View file

@ -0,0 +1,14 @@
{ ... }:
{
imports = [ ./module-authbackend.nix ];
config.my.authBackend.approle = {
tune = {
default_lease_ttl = "1h";
max_lease_ttl = "1h";
listing_visibility = "hidden";
token_type = "default-service";
};
};
}

View file

@ -0,0 +1,19 @@
{ ... }:
{
resource.vault_jwt_auth_backend.oidc = {
default_role = "user";
namespace_in_state = true;
oidc_discovery_url = "https://accounts.google.com";
oidc_client_id = "620300851636-6ha1a7t9r4gatrn9gdqa82toem3cbq3b.apps.googleusercontent.com";
# oidc_client_secret in secrets.nix
};
my.authBackend.oidc = {
resourceType = "vault_jwt_auth_backend";
tune.default_lease_ttl = "24h";
tune.max_lease_ttl = "24h";
};
}

View file

@ -1,6 +1,21 @@
{ ... }: { lib, config, ... }:
{ {
imports = [
./secrets.nix
./policies-raw.nix
./policies-app.nix
./authbackend-approle.nix
./authbackend-oidc.nix
./ssh-ca-client.nix
./ssh-ca-server.nix
./servers.nix
];
terraform = { terraform = {
backend.gcs = { backend.gcs = {
bucket = "lukegb-terraform-state"; bucket = "lukegb-terraform-state";
@ -12,4 +27,11 @@
version = "3.3.1"; version = "3.3.1";
}; };
}; };
provider.vault = {
address = "https://vault.int.lukegb.com";
};
my.apps.pomerium = {};
my.servers.etheroute-lon01.apps = [ "pomerium" ];
} }

View file

@ -4,8 +4,12 @@ let
terranix = import "${pkgs.terranix}/core/default.nix" { terranix = import "${pkgs.terranix}/core/default.nix" {
inherit pkgs; inherit pkgs;
terranix_config = { imports = [ ./config.nix ]; }; terranix_config = { imports = [ ./config.nix ]; };
strip_nulls = true; strip_nulls = false;
extraArgs = args; extraArgs = args // {
lib = args.lib // {
mapToAttrs = pred: onWhat: builtins.listToAttrs (map pred onWhat);
};
};
}; };
config = (pkgs.formats.json { }).generate "config.tf.json" terranix.config; config = (pkgs.formats.json { }).generate "config.tf.json" terranix.config;

View file

@ -0,0 +1,38 @@
{ config, lib, ... }:
let
inherit (lib) types mkOption mapAttrsToList mkMerge;
in {
options = {
my.authBackend = mkOption {
default = {};
type = types.attrsOf (types.submodule ({ name, ... }: {
options = {
type = mkOption { type = types.str; default = name; };
path = mkOption { type = types.str; default = name; };
resourceType = mkOption { type = types.str; default = "vault_auth_backend"; };
tune = {
default_lease_ttl = mkOption { type = with types; nullOr str; default = null; };
max_lease_ttl = mkOption { type = with types; nullOr str; default = null; };
audit_non_hmac_response_keys = mkOption { type = with types; listOf str; default = []; };
audit_non_hmac_request_keys = mkOption { type = with types; listOf str; default = []; };
listing_visibility = mkOption { type = types.enum [ "unauth" "hidden" ]; default = "unauth"; };
passthrough_request_headers = mkOption { type = with types; listOf str; default = []; };
allowed_response_headers = mkOption { type = with types; listOf str; default = []; };
token_type = mkOption { type = types.enum [ "default-service" "default-batch" "service" "batch" ]; default = "default-service"; };
};
};
}));
};
};
config = {
resource = mkMerge (mapAttrsToList (name: cfg: {
${cfg.resourceType}.${name} = {
inherit (cfg) type path;
tune = [cfg.tune];
};
}) config.my.authBackend);
};
}

View file

@ -0,0 +1,22 @@
{ lib, config, ... }:
{
options = let
inherit (lib) mkOption types;
in {
my.secrets.apps = mkOption {
default = {};
type = with types; attrsOf attrs;
};
};
config = let
inherit (lib) nameValuePair mapAttrs';
in {
resource.vault_generic_secret = mapAttrs' (name: value: nameValuePair "apps_${name}" {
path = "kv/apps/${name}";
data_json = builtins.toJSON value;
}) config.my.secrets.apps;
};
}

View file

@ -0,0 +1,37 @@
{ lib, config, ... }:
let
inherit (lib) mkOption types mkMerge mapAttrsToList;
in {
options.my.apps = mkOption {
type = types.attrsOf (types.submodule ({ name, ... }: {
options = {
resourceName = mkOption {
type = types.str;
default = "app_${name}";
internal = true;
};
policy = mkOption {
type = types.lines;
default = ''
path "kv/data/apps/${name}" {
capabilities = ["read"]
}
path "kv/metadata/apps/${name}" {
capabilities = ["read"]
}
'';
};
};
}));
};
config.resource = mkMerge (mapAttrsToList (appName: appCfg: {
vault_policy.${appCfg.resourceName} = {
name = "app/${appName}";
policy = appCfg.policy;
};
}) config.my.apps);
}

View file

@ -0,0 +1,15 @@
{ lib, ... }:
let
inherit (lib) hasSuffix filterAttrs removeSuffix attrNames nameValuePair mapToAttrs;
policiesFiles = builtins.readDir ./policies;
rawPolicies = attrNames (filterAttrs (filename: filetype: filetype == "regular" && hasSuffix ".hcl" filename) policiesFiles);
in {
resource.vault_policy = (mapToAttrs (filename: let
name = removeSuffix ".hcl" filename;
in nameValuePair name {
inherit name;
policy = builtins.readFile (./policies + "/${filename}");
}) rawPolicies);
}

View file

@ -0,0 +1,3 @@
path "*" {
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}

View file

@ -0,0 +1,96 @@
# Allow tokens to look up their own properties
path "auth/token/lookup-self" {
capabilities = ["read"]
}
# Allow tokens to renew themselves
path "auth/token/renew-self" {
capabilities = ["update"]
}
# Allow tokens to revoke themselves
path "auth/token/revoke-self" {
capabilities = ["update"]
}
# Allow a token to look up its own capabilities on a path
path "sys/capabilities-self" {
capabilities = ["update"]
}
# Allow a token to look up its own entity by id or name
path "identity/entity/id/{{identity.entity.id}}" {
capabilities = ["read"]
}
path "identity/entity/name/{{identity.entity.name}}" {
capabilities = ["read"]
}
# Allow a token to look up its resultant ACL from all policies. This is useful
# for UIs. It is an internal path because the format may change at any time
# based on how the internal ACL features and capabilities change.
path "sys/internal/ui/resultant-acl" {
capabilities = ["read"]
}
# Allow a token to renew a lease via lease_id in the request body; old path for
# old clients, new path for newer
path "sys/renew" {
capabilities = ["update"]
}
path "sys/leases/renew" {
capabilities = ["update"]
}
# Allow looking up lease properties. This requires knowing the lease ID ahead
# of time and does not divulge any sensitive information.
path "sys/leases/lookup" {
capabilities = ["update"]
}
# Allow a token to manage its own cubbyhole
path "cubbyhole/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
# Allow a token to wrap arbitrary values in a response-wrapping token
path "sys/wrapping/wrap" {
capabilities = ["update"]
}
# Allow a token to look up the creation time and TTL of a given
# response-wrapping token
path "sys/wrapping/lookup" {
capabilities = ["update"]
}
# Allow a token to unwrap a response-wrapping token. This is a convenience to
# avoid client token swapping since this is also part of the response wrapping
# policy.
path "sys/wrapping/unwrap" {
capabilities = ["update"]
}
# Allow general purpose tools
path "sys/tools/hash" {
capabilities = ["update"]
}
path "sys/tools/hash/*" {
capabilities = ["update"]
}
# Allow checking the status of a Control Group request if the user has the
# accessor
path "sys/control-group/request" {
capabilities = ["update"]
}
# Everyone can read the SSH CA public properties
path "ssh-host/config/ca" {
capabilities = ["read"]
}
path "ssh-client/config/ca" {
capabilities = ["read"]
}

View file

@ -0,0 +1,27 @@
# Allow everyone to manage things under kv/server/<user>
path "kv/data/server/{{identity.entity.name}}/*" {
capabilities = ["create", "update", "read", "delete"]
}
path "kv/metadata/server/{{identity.entity.name}}/*" {
capabilities = ["list"]
}
path "kv/metadata/server" {
capabilities = ["list"]
}
path "kv/metadata/+" {
capabilities = ["list"]
}
path "acme/certs/*" {
capabilities = ["create"]
}
# Servers can always get nix-daemon data
path "kv/data/apps/nix-daemon" {
capabilities = ["read"]
}
path "kv/metadata/apps/nix-daemon" {
capabilities = ["read"]
}

View file

@ -0,0 +1,29 @@
# Allow everyone to manage things under kv/users/<user>
path "kv/data/user/{{identity.entity.name}}/*" {
capabilities = ["create", "update", "read", "delete"]
}
path "kv/metadata/user/{{identity.entity.name}}/*" {
capabilities = ["list"]
}
path "kv/metadata/user" {
capabilities = ["list"]
}
path "kv/metadata/+" {
capabilities = ["list"]
}
# Users can manage things under kv/server/<name> too.
path "kv/data/server/*" {
capabilities = ["create", "update", "read", "delete"]
}
path "kv/metadata/server/*" {
capabilities = ["list"]
}
# Users can get SSH keys signed.
path "ssh-client/sign/user" {
capabilities = ["update"]
}

114
ops/vault/cfg/servers.nix Normal file
View file

@ -0,0 +1,114 @@
{ depot, lib, config, ... }:
let
inherit (lib) mkOption nameValuePair mapToAttrs types mkEnableOption mapAttrs' filterAttrs mkMerge mapAttrsToList concatStringsSep;
minutes = m: m * 60;
serversType = types.attrsOf (types.submodule ({ name, ... }: {
options = {
enable = mkOption {
type = types.bool;
default = true;
};
resourceName = mkOption {
type = types.str;
default = "server_${name}";
internal = true;
};
extraPolicies = mkOption {
type = with types; listOf str;
default = [];
};
apps = mkOption {
type = with types; listOf str;
default = [];
};
hostnames = mkOption {
type = with types; listOf str;
default = [
"${name}.as205479.net"
"${name}.blade.as205479.net"
"${name}.int.as205479.net"
];
};
policy = mkOption {
type = types.lines;
default = ''
path "ssh-host/sign/${name}" {
capabilities = ["update"]
allowed_parameters = {
"cert_type" = ["host"]
"public_key" = []
"valid_principals" = []
}
}
'';
};
};
}));
cfg = config.my.enabledServers;
in {
options = {
my.servers = mkOption {
type = serversType;
};
my.enabledServers = mkOption {
internal = true;
readOnly = true;
default = filterAttrs (n: v: v.enable) config.my.servers;
type = serversType;
};
};
config.my.servers = mapToAttrs (name: nameValuePair name {}) (builtins.attrNames depot.ops.nixos.systemConfigs);
config.resource = mkMerge (mapAttrsToList (serverName: serverCfg: {
vault_policy.${serverCfg.resourceName} = {
name = "server/${serverName}";
inherit (serverCfg) policy;
};
vault_approle_auth_backend_role.${serverCfg.resourceName} = {
backend = "\${vault_auth_backend.approle.path}";
role_name = serverName;
role_id = serverName;
secret_id_num_uses = 0;
token_ttl = minutes 20;
token_max_ttl = minutes 30;
};
vault_identity_entity.${serverCfg.resourceName} = {
name = serverName;
policies =
["default" "server" "\${vault_policy.${serverCfg.resourceName}.name}"]
++ serverCfg.extraPolicies
++ (map (name: "\${vault_policy.app_${name}.name}") serverCfg.apps);
metadata.server = serverName;
};
vault_identity_entity_alias.${serverCfg.resourceName} = {
name = serverName;
mount_accessor = "\${vault_auth_backend.approle.accessor}";
canonical_id = "\${vault_identity_entity.${serverCfg.resourceName}.id}";
};
vault_ssh_secret_backend_role.${serverCfg.resourceName} = {
name = serverName;
backend = "\${vault_mount.ssh-host.path}";
key_type = "ca";
allow_host_certificates = true;
allow_bare_domains = true;
allowed_domains = concatStringsSep "," serverCfg.hostnames;
ttl = 7 * 24 * 60 * 60;
max_ttl = 7 * 24 * 60 * 60;
};
}) cfg);
}

View file

@ -0,0 +1,31 @@
{ ... }:
{
resource.vault_mount.ssh-client = {
type = "ssh";
path = "ssh-client";
};
resource.vault_ssh_secret_backend_ca.ssh-client = {
backend = "\${vault_mount.ssh-client.path}";
};
resource.vault_ssh_secret_backend_role.ssh-client_user = {
name = "user";
backend = "\${vault_mount.ssh-client.path}";
key_type = "ca";
allow_user_certificates = true;
allowed_users_template = true;
allowed_users = "{{identity.entity.name}}";
allowed_extensions = "permit-agent-forwarding,permit-port-forwarding,permit-pty,permit-user-rc,permit-X11-forwarding";
ttl = 24 * 60 * 60;
max_ttl = 24 * 60 * 60;
default_extensions = {
permit-agent-forwarding = "";
permit-port-forwarding = "";
permit-pty = "";
permit-user-rc = "";
permit-X11-forwarding = "";
};
};
}

View file

@ -0,0 +1,12 @@
{ ... }:
{
resource.vault_mount.ssh-host = {
type = "ssh";
path = "ssh-host";
};
resource.vault_ssh_secret_backend_ca.ssh-host = {
backend = "\${vault_mount.ssh-host.path}";
};
}

6
ops/vault/cfg/terraform Executable file
View file

@ -0,0 +1,6 @@
#!/usr/bin/env bash
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
DEPOT_ROOT="${SCRIPT_DIR%/depot/*}/depot"
exec "$(nix-build "$DEPOT_ROOT" -A ops.vault.cfg.terraform --no-out-link --option builders '')" "$@"