ops/vault/cfg: initial configuration
This commit is contained in:
parent
92998b5d36
commit
23df8e3b18
17 changed files with 493 additions and 3 deletions
|
@ -7,6 +7,7 @@ result
|
|||
result-*
|
||||
|
||||
ops/vault/cfg/tf/
|
||||
ops/vault/cfg/secrets.nix
|
||||
|
||||
web/quotes/theme/static/
|
||||
|
||||
|
|
14
ops/vault/cfg/authbackend-approle.nix
Normal file
14
ops/vault/cfg/authbackend-approle.nix
Normal 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";
|
||||
};
|
||||
};
|
||||
}
|
19
ops/vault/cfg/authbackend-oidc.nix
Normal file
19
ops/vault/cfg/authbackend-oidc.nix
Normal 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";
|
||||
};
|
||||
}
|
|
@ -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 = {
|
||||
backend.gcs = {
|
||||
bucket = "lukegb-terraform-state";
|
||||
|
@ -12,4 +27,11 @@
|
|||
version = "3.3.1";
|
||||
};
|
||||
};
|
||||
|
||||
provider.vault = {
|
||||
address = "https://vault.int.lukegb.com";
|
||||
};
|
||||
|
||||
my.apps.pomerium = {};
|
||||
my.servers.etheroute-lon01.apps = [ "pomerium" ];
|
||||
}
|
||||
|
|
|
@ -4,8 +4,12 @@ let
|
|||
terranix = import "${pkgs.terranix}/core/default.nix" {
|
||||
inherit pkgs;
|
||||
terranix_config = { imports = [ ./config.nix ]; };
|
||||
strip_nulls = true;
|
||||
extraArgs = args;
|
||||
strip_nulls = false;
|
||||
extraArgs = args // {
|
||||
lib = args.lib // {
|
||||
mapToAttrs = pred: onWhat: builtins.listToAttrs (map pred onWhat);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = (pkgs.formats.json { }).generate "config.tf.json" terranix.config;
|
||||
|
|
38
ops/vault/cfg/module-authbackend.nix
Normal file
38
ops/vault/cfg/module-authbackend.nix
Normal 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);
|
||||
};
|
||||
}
|
22
ops/vault/cfg/module-kv.nix
Normal file
22
ops/vault/cfg/module-kv.nix
Normal 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;
|
||||
};
|
||||
}
|
37
ops/vault/cfg/policies-app.nix
Normal file
37
ops/vault/cfg/policies-app.nix
Normal 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);
|
||||
}
|
15
ops/vault/cfg/policies-raw.nix
Normal file
15
ops/vault/cfg/policies-raw.nix
Normal 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);
|
||||
}
|
3
ops/vault/cfg/policies/admin.hcl
Normal file
3
ops/vault/cfg/policies/admin.hcl
Normal file
|
@ -0,0 +1,3 @@
|
|||
path "*" {
|
||||
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
|
||||
}
|
96
ops/vault/cfg/policies/default.hcl
Normal file
96
ops/vault/cfg/policies/default.hcl
Normal 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"]
|
||||
}
|
27
ops/vault/cfg/policies/server.hcl
Normal file
27
ops/vault/cfg/policies/server.hcl
Normal 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"]
|
||||
}
|
29
ops/vault/cfg/policies/user.hcl
Normal file
29
ops/vault/cfg/policies/user.hcl
Normal 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
114
ops/vault/cfg/servers.nix
Normal 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);
|
||||
}
|
31
ops/vault/cfg/ssh-ca-client.nix
Normal file
31
ops/vault/cfg/ssh-ca-client.nix
Normal 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 = "";
|
||||
};
|
||||
};
|
||||
}
|
12
ops/vault/cfg/ssh-ca-server.nix
Normal file
12
ops/vault/cfg/ssh-ca-server.nix
Normal 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
6
ops/vault/cfg/terraform
Executable 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 '')" "$@"
|
Loading…
Reference in a new issue