{ config, pkgs, lib, tflib, ... }: let cfg = config.prefab.nomadServer; inherit (lib) mapAttrsToList foldAttrs mergeAttrs fix flip mkOption types optionalString optionalAttrs mkMerge ; inherit ((a: builtins.break a) tflib) tf ; submoduleOptions = { datacenters = mkOption { description = '' ''; type = with types; listOf str; }; encryptionKey = mkOption { description = '' DO NOT hardcode the secret in Nix, generate it with Terraform and let Terraform substitute it. ''; type = types.str; }; paths = { encryptionKey = mkOption { description = '' ''; type = types.str; }; replicationToken = mkOption { description = '' ''; type = with types; nullOr str; default = null; }; vaultToken = mkOption { description = '' ''; type = types.str; }; consulToken = mkOption { description = '' ''; type = types.str; }; }; vaultKvMount = mkOption { description = '' ''; type = types.str; }; }; in { options.prefab.nomadServer = mkOption { description = '' ''; type = with types; attrsOf (submodule { options = submoduleOptions; }); default = {}; }; config.resource = mkMerge (flip mapAttrsToList cfg (hostname: value: fix (self: { "vault_policy"."${hostname}_nomad" = { name = "${hostname}-nomad-server-agent"; policy = '' path "${value.vaultKvMount}/data/${value.paths.encryptionKey}" { capabilities = ["read"] } path "${value.vaultKvMount}/data/${value.paths.vaultToken}" { capabilities = ["read"] } path "${value.vaultKvMount}/data/${value.paths.consulToken}" { capabilities = ["read"] } ${optionalString (value.paths.replicationToken != null) '' path "${value.vaultKvMount}/data/${value.paths.replicationToken}" { capabilities = ["read"] } ''} ''; }; "vault_kv_secret_v2"."${hostname}_nomad_encryption_key" = { mount = value.vaultKvMount; name = value.paths.encryptionKey; delete_all_versions = true; data_json = builtins.toJSON { key = value.encryptionKey; }; }; "consul_acl_policy"."${hostname}_nomad_server" = { name = "${hostname}_nomad_server"; rules = '' agent_prefix "" { policy = "read" } node_prefix "" { policy = "read" } service_prefix "" { policy = "write" } acl = "write" ''; }; "consul_acl_token"."${hostname}_nomad_server" = { description = "Consul token for nomad_server on ${hostname}"; policies = [ (tf "consul_acl_policy.${hostname}_nomad_server.name") ]; local = false; }; "vault_kv_secret_v2"."${hostname}_nomad_server_consul" = { mount = value.vaultKvMount; name = value.paths.consulToken; delete_all_versions = true; data_json = builtins.toJSON { secret = tf "data.consul_acl_token_secret_id.${hostname}_nomad_server.secret_id"; accessor = tf "consul_acl_token.${hostname}_nomad_server.accessor_id"; }; }; "vault_policy"."${hostname}_nomad_server" = { name = "${hostname}-nomad-server"; policy = '' # Allow creating tokens under "nomad-cluster" token role. The token role name # should be updated if "nomad-cluster" is not used. path "auth/token/create/nomad-cluster" { capabilities = ["update"] } # Allow looking up "nomad-cluster" token role. The token role name should be # updated if "nomad-cluster" is not used. path "auth/token/roles/nomad-cluster" { capabilities = ["read"] } # Allow looking up the token passed to Nomad to validate the token has the # proper capabilities. This is provided by the "default" policy. path "auth/token/lookup-self" { capabilities = ["read"] } # Allow looking up incoming tokens to validate they have permissions to access # the tokens they are requesting. This is only required if # `allow_unauthenticated` is set to false. path "auth/token/lookup" { capabilities = ["update"] } # Allow revoking tokens that should no longer exist. This allows revoking # tokens for dead tasks. path "auth/token/revoke-accessor" { capabilities = ["update"] } # Allow checking the capabilities of our own token. This is used to validate the # token upon startup. Note this requires update permissions because the Vault API # is a POST path "sys/capabilities-self" { capabilities = ["update"] } # Allow our own token to be renewed. path "auth/token/renew-self" { capabilities = ["update"] } ''; }; "vault_token_auth_backend_role"."${hostname}_nomad_server" = { role_name = "${hostname}_nomad_server"; allowed_policies = [ (tf "vault_policy.${hostname}_nomad_server.name") ]; orphan = true; renewable = true; }; "vault_token"."${hostname}_nomad_server" = { policies = [ (tf "vault_policy.${hostname}_nomad_server.name") ]; renewable = true; ttl = "24h"; explicit_max_ttl = 0; role_name = tf "vault_token_auth_backend_role.${hostname}_nomad_server.role_name"; display_name = "${hostname}-nomad-server-Vault-token"; }; "vault_kv_secret_v2"."${hostname}_nomad_server_vault" = { mount = value.vaultKvMount; name = value.paths.vaultToken; delete_all_versions = true; data_json = builtins.toJSON { secret = tf "vault_token.${hostname}_nomad_server.client_token"; }; }; })) ++ (flip mapAttrsToList cfg (hostname: value: (optionalAttrs (value.paths.replicationToken != null) { "nomad_acl_token"."${hostname}_replication" = { name = "${hostname} replication token"; type = "management"; }; "vault_kv_secret_v2"."${hostname}_nomad_replication" = { mount = value.vaultKvMount; name = value.paths.replicationToken; delete_all_versions = true; data_json = builtins.toJSON { secret = tf "nomad_acl_token.${hostname}_replication.secret_id"; accessor = tf "nomad_acl_token.${hostname}_replication.id"; }; }; } ) ))); config.data = mkMerge (flip mapAttrsToList cfg (hostname: value: { "consul_acl_token_secret_id"."${hostname}_nomad_server" = { accessor_id = tf "consul_acl_token.${hostname}_nomad_server.id"; }; })); }