Validate user and role names instead of munging binding names
Rancher requires global role binding names to be RFC 1123 labels. The per-user binding name lowercased the key and replaced only "/", so usernames containing ".", "_" or "@" still produced an invalid name and failed at apply, and hyphenated names could collide (foo-bar + baz vs foo + bar-baz) into one object. Add validation blocks on var.users requiring the username and every referenced role name to be RFC 1123 labels, so non-compliant input fails the plan with an actionable message. The binding name is then built from the two parts directly. A precondition rejects the remaining hyphen ambiguity at plan time rather than as a mid-apply conflict. Document the constraint in the users schema section of the README. The group binding path is unchanged.
This commit is contained in:
@@ -50,6 +50,15 @@ global_role_bindings: # optional
|
|||||||
The password is read from Vault **at plan time**, so seed the kv-v2 secret
|
The password is read from Vault **at plan time**, so seed the kv-v2 secret
|
||||||
before adding the file, or the plan fails.
|
before adding the file, or the plan fails.
|
||||||
|
|
||||||
|
The file name (the username) and every entry in `global_role_bindings` must be
|
||||||
|
an RFC 1123 label — `^[a-z0-9]([a-z0-9-]*[a-z0-9])?$`, i.e. lowercase
|
||||||
|
alphanumerics and `-`, starting and ending alphanumeric. No `.`, `_`, `@` or
|
||||||
|
uppercase. Rancher names each binding `akuser-<username>-<role>` and rejects
|
||||||
|
anything else, so non-compliant values fail the plan with an explicit error
|
||||||
|
instead of the apply. Names that are individually valid but collapse to the same
|
||||||
|
binding name (e.g. `foo-bar` + `baz` and `foo` + `bar-baz`) are rejected at plan
|
||||||
|
time too.
|
||||||
|
|
||||||
### `config/roles/<name>.yaml`
|
### `config/roles/<name>.yaml`
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|||||||
+16
-3
@@ -90,11 +90,24 @@ locals {
|
|||||||
resource "rancher2_global_role_binding" "user" {
|
resource "rancher2_global_role_binding" "user" {
|
||||||
for_each = local.user_global_role_bindings
|
for_each = local.user_global_role_bindings
|
||||||
|
|
||||||
# Binding name must be an RFC 1123 label, so lowercase it and swap the key's
|
# Both parts are RFC 1123 labels already (var.users validates them), so the
|
||||||
# "/" separator for "-".
|
# binding name needs no transformation.
|
||||||
name = "akuser-${lower(replace(each.key, "/", "-"))}"
|
name = "akuser-${each.value.username}-${each.value.global_role_id}"
|
||||||
global_role_id = each.value.global_role_id
|
global_role_id = each.value.global_role_id
|
||||||
user_id = rancher2_user.this[each.value.username].id
|
user_id = rancher2_user.this[each.value.username].id
|
||||||
|
|
||||||
|
lifecycle {
|
||||||
|
# Hyphens are legal inside both parts, so distinct bindings can still flatten
|
||||||
|
# to the same name (foo-bar + baz vs foo + bar-baz). Catch that at plan time
|
||||||
|
# rather than as a duplicate-object conflict mid-apply.
|
||||||
|
precondition {
|
||||||
|
condition = length(distinct([
|
||||||
|
for binding in local.user_global_role_bindings :
|
||||||
|
"akuser-${binding.username}-${binding.global_role_id}"
|
||||||
|
])) == length(local.user_global_role_bindings)
|
||||||
|
error_message = "Two user global role bindings collapse to the same Rancher binding name (akuser-<username>-<role>). Rename one of the users or roles involved."
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Custom global roles. Rules are optional: a role with none grants nothing on its
|
# Custom global roles. Rules are optional: a role with none grants nothing on its
|
||||||
|
|||||||
@@ -51,6 +51,28 @@ variable "users" {
|
|||||||
global_role_bindings = optional(list(string), [])
|
global_role_bindings = optional(list(string), [])
|
||||||
}))
|
}))
|
||||||
default = {}
|
default = {}
|
||||||
|
|
||||||
|
# Rancher names each global role binding as an RFC 1123 label and rejects
|
||||||
|
# anything else at apply time. The binding name is built from the username and
|
||||||
|
# the role name verbatim, so both must already be compliant — fail the plan
|
||||||
|
# with a clear message instead of munging the input and hoping.
|
||||||
|
validation {
|
||||||
|
condition = alltrue([
|
||||||
|
for username in keys(var.users) :
|
||||||
|
can(regex("^[a-z0-9]([a-z0-9-]*[a-z0-9])?$", username))
|
||||||
|
])
|
||||||
|
error_message = "Usernames must be RFC 1123 labels: lowercase alphanumerics and '-', starting and ending alphanumeric (no '.', '_', '@' or uppercase). Rename config/users/<username>.yaml to a compliant username."
|
||||||
|
}
|
||||||
|
|
||||||
|
validation {
|
||||||
|
condition = alltrue(flatten([
|
||||||
|
for user in values(var.users) : [
|
||||||
|
for role in user.global_role_bindings :
|
||||||
|
can(regex("^[a-z0-9]([a-z0-9-]*[a-z0-9])?$", role))
|
||||||
|
]
|
||||||
|
]))
|
||||||
|
error_message = "Values in global_role_bindings must be RFC 1123 labels: lowercase alphanumerics and '-', starting and ending alphanumeric."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Custom Rancher global roles (config/roles/<name>.yaml). Map key is the role
|
# Custom Rancher global roles (config/roles/<name>.yaml). Map key is the role
|
||||||
|
|||||||
Reference in New Issue
Block a user