From e0af30a2f58fac01b179921adf5354afc2959e4a Mon Sep 17 00:00:00 2001 From: Bertrand Lanson Date: Wed, 29 May 2024 20:21:47 +0200 Subject: [PATCH 01/10] feat: allow passing extra policies to the tenant root role, start migrating away from old 'extra roles' approach --- root.tf | 52 +++++++++++++++++++++++++++++++++++++++++++++++----- variables.tf | 33 ++++++++++++++++++++------------- 2 files changed, 67 insertions(+), 18 deletions(-) diff --git a/root.tf b/root.tf index 6a756cb..529fc6f 100644 --- a/root.tf +++ b/root.tf @@ -1,3 +1,50 @@ +locals { + root_policy_default_rules = { + tenant_prefix_rw = { + path = "${var.prefix}/*" + capabilities = ["create", "update", "read", "delete", "list"] + } + tenant_prefix_mount = { + path = "sys/mounts/${var.prefix}/*" + capabilities = ["create", "update", "read", "delete", "list"] + } + tenant_prefix_remount = { + path = "sys/remount" + capabilities = ["update", "sudo"] + allowed_parameters = { + "from" = ["${var.prefix}/*"] + "to" = ["${var.prefix}/*"] + } + } + tenant_prefix_remount_status = { + path = "sys/remount/status/*" + capabilities = ["read"] + } + } + root_policy_rules = merge(local.root_policy_default_rules, var.root_policy_extra_rules) +} + +data "vault_policy_document" "root" { + dynamic "rule" { + for_each = local.root_policy_rules + content { + path = each.value.path + capabilities = each.value.capabilities + description = try(each.value.description, null) + required_parameters = try(each.value.required_parameters, null) + allowed_parameter = try(each.value.allowed_parameter, null) + denied_parameter = try(each.value.denied_parameter, null) + min_wrapping_ttl = try(each.value.min_wrapping_ttl, null) + max_wrapping_ttl = try(each.value.max_wrapping_ttl, null) + } + } +} + +resource "vault_policy" "root" { + name = "${var.name}-root" + policy = data.vault_policy_document.root.hcl +} + resource "vault_approle_auth_backend_role" "root" { backend = vault_auth_backend.approle.path role_name = "${var.name}-root" @@ -12,11 +59,6 @@ resource "vault_approle_auth_backend_role_secret_id" "root" { secret_id = random_uuid.root_secret_id.result } -resource "vault_policy" "root" { - name = "${var.name}-root" - policy = var.root_policy_file == null ? templatefile("${path.module}/policies/root.policy.hcl", { tenant_prefix = var.prefix }) : file(var.root_policy_file) -} - resource "vault_identity_entity" "root" { name = "${var.prefix}-root" } diff --git a/variables.tf b/variables.tf index 89cc6a1..2461365 100644 --- a/variables.tf +++ b/variables.tf @@ -12,26 +12,33 @@ variable "prefix" { description = "The prefix to use for the tenant in vault (this will prefix mount points, policies, etc..)" } -variable "root_policy_file" { - type = string - default = null - description = "The path to the admin policy file for this tenant" -} - variable "additional_roles" { - type = map(object({ - policy_file = string - })) + type = map(string) default = {} description = <-approle) including all the roles declared in this variable. The variable should look like: additional_roles = { - devs = { - policy_file = "/some/path/to/policy.hcl" - } - admins = {...} + devs = file("path/to/policy.hcl") + admins = data.vault_policy_document.admins.hcl } EOT } + +variable "root_policy_extra_rules" { + type = map( + object({ + path = string + capabilities = list(string) + description = optional(string) + required_parameters = optional(map(list(any))) + allowed_parameter = optional(map(list(any))) + denied_parameter = optional(map(list(any))) + min_wrapping_ttl = optional(number) + max_wrapping_ttl = optional(number) + }) + ) + description = "A map of additional policies to attach to the root policy. These are merged with the default policies for the root role so that oyu can customize it to your needs" + default = {} +} From d28fe5c0994351a02af0def1a4e608d54d2e7583 Mon Sep 17 00:00:00 2001 From: Bertrand Lanson Date: Wed, 29 May 2024 20:22:24 +0200 Subject: [PATCH 02/10] feat: pass extra roles as key value pairs, required the full policy as value --- README.md | 5 +++-- extra_policies.tf | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fb6d51c..5f30d55 100644 --- a/README.md +++ b/README.md @@ -44,15 +44,16 @@ No modules. | [vault_identity_group.this](https://registry.terraform.io/providers/hashicorp/vault/latest/docs/resources/identity_group) | resource | | [vault_policy.extra](https://registry.terraform.io/providers/hashicorp/vault/latest/docs/resources/policy) | resource | | [vault_policy.root](https://registry.terraform.io/providers/hashicorp/vault/latest/docs/resources/policy) | resource | +| [vault_policy_document.root](https://registry.terraform.io/providers/hashicorp/vault/latest/docs/data-sources/policy_document) | data source | ### Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [additional_roles](#input_additional_roles) | A map of additional role names, with the path to the associated policy file to add for this tenant.
A separate approle auth method is created for this tenant (mounted at auth/-approle) including all the roles declared in this variable.
The variable should look like:
additional_roles = {
devs = {
policy_file = "/some/path/to/policy.hcl"
}
admins = {...}
} |
map(object({
policy_file = string
}))
| `{}` | no | +| [additional_roles](#input_additional_roles) | A map of additional role names, with the path to the associated policy file to add for this tenant.
A separate approle auth method is created for this tenant (mounted at auth/-approle) including all the roles declared in this variable.
The variable should look like:
additional_roles = {
devs = file("path/to/policy.hcl")
admins = data.vault_policy_document.admins.hcl
} | `map(string)` | `{}` | no | | [name](#input_name) | The name of the tenant you want to create | `string` | n/a | yes | | [prefix](#input_prefix) | The prefix to use for the tenant in vault (this will prefix mount points, policies, etc..) | `string` | n/a | yes | -| [root_policy_file](#input_root_policy_file) | The path to the admin policy file for this tenant | `string` | `null` | no | +| [root_policy_extra_rules](#input_root_policy_extra_rules) | A map of additional policies to attach to the root policy. These are merged with the default policies for the root role so that oyu can customize it to your needs |
map(
object({
path = string
capabilities = list(string)
description = optional(string)
required_parameters = optional(map(list(any)))
allowed_parameter = optional(map(list(any)))
denied_parameter = optional(map(list(any)))
min_wrapping_ttl = optional(number)
max_wrapping_ttl = optional(number)
})
)
| `{}` | no | ### Outputs diff --git a/extra_policies.tf b/extra_policies.tf index 4d9535c..d50ddd7 100644 --- a/extra_policies.tf +++ b/extra_policies.tf @@ -20,7 +20,7 @@ resource "vault_policy" "extra" { for_each = var.additional_roles name = "${var.prefix}-${each.key}" - policy = file(each.value.policy_file) + policy = each.value } resource "vault_identity_entity" "extra" { From 7b337f47f510d555fbc6be690af642bcfae6b140 Mon Sep 17 00:00:00 2001 From: Bertrand Lanson Date: Wed, 29 May 2024 23:09:53 +0200 Subject: [PATCH 03/10] fix: adjust code for root policy document to generate blocks instead of map of lists --- root.tf | 52 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/root.tf b/root.tf index 529fc6f..50b8e2a 100644 --- a/root.tf +++ b/root.tf @@ -24,18 +24,56 @@ locals { root_policy_rules = merge(local.root_policy_default_rules, var.root_policy_extra_rules) } +# data "vault_policy_document" "root" { +# dynamic "rule" { +# for_each = local.root_policy_rules +# content { +# path = each.value.path +# capabilities = each.value.capabilities +# description = try(each.value.description, null) +# required_parameters = try(each.value.required_parameters, null) +# allowed_parameter = try(each.value.allowed_parameter, null) +# denied_parameter = try(each.value.denied_parameter, null) +# min_wrapping_ttl = try(each.value.min_wrapping_ttl, null) +# max_wrapping_ttl = try(each.value.max_wrapping_ttl, null) +# } +# } +# } + data "vault_policy_document" "root" { dynamic "rule" { for_each = local.root_policy_rules content { - path = each.value.path - capabilities = each.value.capabilities - description = try(each.value.description, null) + path = rule.value.path + capabilities = rule.value.capabilities + description = try(rule.value.description, null) + min_wrapping_ttl = try(rule.value.min_wrapping_ttl, null) + max_wrapping_ttl = try(rule.value.max_wrapping_ttl, null) required_parameters = try(each.value.required_parameters, null) - allowed_parameter = try(each.value.allowed_parameter, null) - denied_parameter = try(each.value.denied_parameter, null) - min_wrapping_ttl = try(each.value.min_wrapping_ttl, null) - max_wrapping_ttl = try(each.value.max_wrapping_ttl, null) + + # dynamic "required_parameters" { + # for_each = rule.value.required_parameters != null ? rule.value.required_parameters : {} + # content { + # key = required_parameters.key + # value = required_parameters.value + # } + # } + + dynamic "allowed_parameter" { + for_each = rule.value.allowed_parameter != null ? rule.value.allowed_parameter : {} + content { + key = allowed_parameter.key + value = allowed_parameter.value + } + } + + dynamic "denied_parameter" { + for_each = rule.value.denied_parameter != null ? rule.value.denied_parameter : {} + content { + key = denied_parameter.key + value = denied_parameter.value + } + } } } } From c9a7ea7908b1fb337d5a197029112be2dd236db8 Mon Sep 17 00:00:00 2001 From: Bertrand Lanson Date: Wed, 29 May 2024 23:13:30 +0200 Subject: [PATCH 04/10] fix: use rule context for block scoped values --- root.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/root.tf b/root.tf index 50b8e2a..11457f8 100644 --- a/root.tf +++ b/root.tf @@ -49,7 +49,7 @@ data "vault_policy_document" "root" { description = try(rule.value.description, null) min_wrapping_ttl = try(rule.value.min_wrapping_ttl, null) max_wrapping_ttl = try(rule.value.max_wrapping_ttl, null) - required_parameters = try(each.value.required_parameters, null) + required_parameters = try(rule.value.required_parameters, null) # dynamic "required_parameters" { # for_each = rule.value.required_parameters != null ? rule.value.required_parameters : {} From 670b0f24806f922c5d8465eea8c35cfbd5a6b790 Mon Sep 17 00:00:00 2001 From: Bertrand Lanson Date: Wed, 29 May 2024 23:21:09 +0200 Subject: [PATCH 05/10] fix: use try on dynamic block to not raise errors on non-existent attributes --- root.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/root.tf b/root.tf index 11457f8..e29d1bf 100644 --- a/root.tf +++ b/root.tf @@ -60,7 +60,7 @@ data "vault_policy_document" "root" { # } dynamic "allowed_parameter" { - for_each = rule.value.allowed_parameter != null ? rule.value.allowed_parameter : {} + for_each = try(rule.value.allowed_parameter, {}) != {} ? rule.value.allowed_parameter : {} content { key = allowed_parameter.key value = allowed_parameter.value @@ -68,7 +68,7 @@ data "vault_policy_document" "root" { } dynamic "denied_parameter" { - for_each = rule.value.denied_parameter != null ? rule.value.denied_parameter : {} + for_each = try(rule.value.denied_parameter, {}) != {} ? rule.value.denied_parameter : {} content { key = denied_parameter.key value = denied_parameter.value From 3a5ce135eac2e0b277687909d6f7b2496375b4dd Mon Sep 17 00:00:00 2001 From: Bertrand Lanson Date: Wed, 29 May 2024 23:24:22 +0200 Subject: [PATCH 06/10] fix: typo in default root permissions --- root.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/root.tf b/root.tf index e29d1bf..49fe3c7 100644 --- a/root.tf +++ b/root.tf @@ -11,7 +11,7 @@ locals { tenant_prefix_remount = { path = "sys/remount" capabilities = ["update", "sudo"] - allowed_parameters = { + allowed_parameter = { "from" = ["${var.prefix}/*"] "to" = ["${var.prefix}/*"] } From 71eef0590e93990e5e4131598c79d4cd8d9ccae4 Mon Sep 17 00:00:00 2001 From: Bertrand Lanson Date: Wed, 29 May 2024 23:45:12 +0200 Subject: [PATCH 07/10] chore: removed old code comments --- root.tf | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/root.tf b/root.tf index 49fe3c7..7ff5ef3 100644 --- a/root.tf +++ b/root.tf @@ -24,22 +24,6 @@ locals { root_policy_rules = merge(local.root_policy_default_rules, var.root_policy_extra_rules) } -# data "vault_policy_document" "root" { -# dynamic "rule" { -# for_each = local.root_policy_rules -# content { -# path = each.value.path -# capabilities = each.value.capabilities -# description = try(each.value.description, null) -# required_parameters = try(each.value.required_parameters, null) -# allowed_parameter = try(each.value.allowed_parameter, null) -# denied_parameter = try(each.value.denied_parameter, null) -# min_wrapping_ttl = try(each.value.min_wrapping_ttl, null) -# max_wrapping_ttl = try(each.value.max_wrapping_ttl, null) -# } -# } -# } - data "vault_policy_document" "root" { dynamic "rule" { for_each = local.root_policy_rules @@ -51,14 +35,6 @@ data "vault_policy_document" "root" { max_wrapping_ttl = try(rule.value.max_wrapping_ttl, null) required_parameters = try(rule.value.required_parameters, null) - # dynamic "required_parameters" { - # for_each = rule.value.required_parameters != null ? rule.value.required_parameters : {} - # content { - # key = required_parameters.key - # value = required_parameters.value - # } - # } - dynamic "allowed_parameter" { for_each = try(rule.value.allowed_parameter, {}) != {} ? rule.value.allowed_parameter : {} content { From e91376012eb30664726941b780428d548c81e265 Mon Sep 17 00:00:00 2001 From: Bertrand Lanson Date: Thu, 30 May 2024 00:11:39 +0200 Subject: [PATCH 08/10] fix: evaluate parameters against null to avoid failing when null values are injected in place of optionals --- README.md | 2 +- root.tf | 4 ++-- variables.tf | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5f30d55..bbb2547 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ No modules. | [additional_roles](#input_additional_roles) | A map of additional role names, with the path to the associated policy file to add for this tenant.
A separate approle auth method is created for this tenant (mounted at auth/-approle) including all the roles declared in this variable.
The variable should look like:
additional_roles = {
devs = file("path/to/policy.hcl")
admins = data.vault_policy_document.admins.hcl
} | `map(string)` | `{}` | no | | [name](#input_name) | The name of the tenant you want to create | `string` | n/a | yes | | [prefix](#input_prefix) | The prefix to use for the tenant in vault (this will prefix mount points, policies, etc..) | `string` | n/a | yes | -| [root_policy_extra_rules](#input_root_policy_extra_rules) | A map of additional policies to attach to the root policy. These are merged with the default policies for the root role so that oyu can customize it to your needs |
map(
object({
path = string
capabilities = list(string)
description = optional(string)
required_parameters = optional(map(list(any)))
allowed_parameter = optional(map(list(any)))
denied_parameter = optional(map(list(any)))
min_wrapping_ttl = optional(number)
max_wrapping_ttl = optional(number)
})
)
| `{}` | no | +| [root_policy_extra_rules](#input_root_policy_extra_rules) | A map of additional policies to attach to the root policy. These are merged with the default policies for the root role so that you can customize it to your needs |
map(
object({
path = string
capabilities = list(string)
description = optional(string)
required_parameters = optional(map(list(any)))
allowed_parameter = optional(map(list(any)))
denied_parameter = optional(map(list(any)))
min_wrapping_ttl = optional(number)
max_wrapping_ttl = optional(number)
})
)
| `{}` | no | ### Outputs diff --git a/root.tf b/root.tf index 7ff5ef3..cc974c8 100644 --- a/root.tf +++ b/root.tf @@ -36,7 +36,7 @@ data "vault_policy_document" "root" { required_parameters = try(rule.value.required_parameters, null) dynamic "allowed_parameter" { - for_each = try(rule.value.allowed_parameter, {}) != {} ? rule.value.allowed_parameter : {} + for_each = try(rule.value.allowed_parameter, null) != null ? rule.value.allowed_parameter : {} content { key = allowed_parameter.key value = allowed_parameter.value @@ -44,7 +44,7 @@ data "vault_policy_document" "root" { } dynamic "denied_parameter" { - for_each = try(rule.value.denied_parameter, {}) != {} ? rule.value.denied_parameter : {} + for_each = try(rule.value.denied_parameter, null) != null ? rule.value.denied_parameter : {} content { key = denied_parameter.key value = denied_parameter.value diff --git a/variables.tf b/variables.tf index 2461365..2b6d6e2 100644 --- a/variables.tf +++ b/variables.tf @@ -39,6 +39,6 @@ variable "root_policy_extra_rules" { max_wrapping_ttl = optional(number) }) ) - description = "A map of additional policies to attach to the root policy. These are merged with the default policies for the root role so that oyu can customize it to your needs" + description = "A map of additional policies to attach to the root policy. These are merged with the default policies for the root role so that you can customize it to your needs" default = {} } From f9acfc46759edbe468ac650c9460cf89fadad5df Mon Sep 17 00:00:00 2001 From: Bertrand Lanson Date: Thu, 30 May 2024 00:12:41 +0200 Subject: [PATCH 09/10] fix: required_parameters type in root_policy_extra_rules --- README.md | 2 +- variables.tf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bbb2547..1b6171b 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ No modules. | [additional_roles](#input_additional_roles) | A map of additional role names, with the path to the associated policy file to add for this tenant.
A separate approle auth method is created for this tenant (mounted at auth/-approle) including all the roles declared in this variable.
The variable should look like:
additional_roles = {
devs = file("path/to/policy.hcl")
admins = data.vault_policy_document.admins.hcl
} | `map(string)` | `{}` | no | | [name](#input_name) | The name of the tenant you want to create | `string` | n/a | yes | | [prefix](#input_prefix) | The prefix to use for the tenant in vault (this will prefix mount points, policies, etc..) | `string` | n/a | yes | -| [root_policy_extra_rules](#input_root_policy_extra_rules) | A map of additional policies to attach to the root policy. These are merged with the default policies for the root role so that you can customize it to your needs |
map(
object({
path = string
capabilities = list(string)
description = optional(string)
required_parameters = optional(map(list(any)))
allowed_parameter = optional(map(list(any)))
denied_parameter = optional(map(list(any)))
min_wrapping_ttl = optional(number)
max_wrapping_ttl = optional(number)
})
)
| `{}` | no | +| [root_policy_extra_rules](#input_root_policy_extra_rules) | A map of additional policies to attach to the root policy. These are merged with the default policies for the root role so that you can customize it to your needs |
map(
object({
path = string
capabilities = list(string)
description = optional(string)
required_parameters = optional(list(string))
allowed_parameter = optional(map(list(any)))
denied_parameter = optional(map(list(any)))
min_wrapping_ttl = optional(number)
max_wrapping_ttl = optional(number)
})
)
| `{}` | no | ### Outputs diff --git a/variables.tf b/variables.tf index 2b6d6e2..e639487 100644 --- a/variables.tf +++ b/variables.tf @@ -32,7 +32,7 @@ variable "root_policy_extra_rules" { path = string capabilities = list(string) description = optional(string) - required_parameters = optional(map(list(any))) + required_parameters = optional(list(string)) allowed_parameter = optional(map(list(any))) denied_parameter = optional(map(list(any))) min_wrapping_ttl = optional(number) From ecd09aa07da816593e938f75a03b32783eb2d549 Mon Sep 17 00:00:00 2001 From: Bertrand Lanson Date: Thu, 30 May 2024 00:15:16 +0200 Subject: [PATCH 10/10] fix: required_parameters type in root_policy_extra_rules --- root.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/root.tf b/root.tf index cc974c8..33f4bd2 100644 --- a/root.tf +++ b/root.tf @@ -33,7 +33,7 @@ data "vault_policy_document" "root" { description = try(rule.value.description, null) min_wrapping_ttl = try(rule.value.min_wrapping_ttl, null) max_wrapping_ttl = try(rule.value.max_wrapping_ttl, null) - required_parameters = try(rule.value.required_parameters, null) + required_parameters = try(rule.value.required_parameters, []) dynamic "allowed_parameter" { for_each = try(rule.value.allowed_parameter, null) != null ? rule.value.allowed_parameter : {}