Add Vault-scoped agent kubernetes roles + agents AppRole (#109)
ci/woodpecker/push/apply Pipeline was successful
ci/woodpecker/push/apply Pipeline was successful
## Why Agentic workloads currently need cluster-admin/root kubeconfig contexts to do routine per-domain work. This adds domain-scoped, Vault-issued kubernetes credentials plus an `agents` AppRole so agents get least-privilege access instead of escalating. ## Changes - Add `kubernetes_secret_backend_role` configs `agent-dhcp`, `agent-dns`, `agent-certs`, `agent-storage` (au/syd1): - **agent-dhcp** (Role, ns `dhcp-system`): full verbs on `kea.unkin.net` CRDs; get/list/watch pods/services/configmaps/events + pods/log. - **agent-dns** (`service_account_name` mode): mints tokens for the static `agent-dns` SA (argocd-apps#332) whose per-namespace RoleBindings confine access to bind-system/bind-internal/bind-external/externaldns. `allowed_kubernetes_namespaces: [bind-system]` (the SA's namespace). - **agent-certs** (Role, ns `cert-manager`): full verbs on `cert-manager.io` + `acme.cert-manager.io` (closes the orders/challenges debugging gap); get/list/watch/delete secrets; get/list/watch pods + pods/log. Secret delete confined to `cert-manager`. - **agent-storage** (Role, ns `cephrgw-system`): full verbs on `ceph.unkin.net` CRDs (buckets/bucketaccesses/objectstoreusers); get/list/watch pods + pods/log. - Extend the `kubernetes_secret_backend_role` module with an optional `service_account_name`; when set, `generated_role_rules`/`kubernetes_role_type` are omitted (the SA's own bindings supply RBAC). - Add creds policies for each role, bound to the `kubernetes_au_syd1_cluster_operator` ldap group (human kubectl use) and the `agents` AppRole (programmatic use). - Add the `agents` AppRole (mirrors the certmanager approle schema): `bind_secret_id: false` (role_id-only login), `token_bound_cidrs: [10.10.12.200/32]` (agent workstation wg0 addr), deterministic role_id, 1h/4h TTLs. - Add `policies/kv/kubernetes/agents.yaml` granting the AppRole create/read/update/list on `kv/data/kubernetes/*` + read/list on `kv/metadata/kubernetes/*` (no delete). ## Ordering argocd-apps#332 (the `agent-dns` SA + ClusterRole + per-namespace RoleBindings) must sync **before** the `agent-dns` creds here are usable — Vault mints tokens for a service account that must already exist. https://claude.ai/code/session_01JUoARVdmhxKQHyyyp1pxeT Reviewed-on: #109 Co-authored-by: Ben Vincent <ben@unkin.net> Co-committed-by: Ben Vincent <ben@unkin.net>
This commit was merged in pull request #109.
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
token_ttl: 3600
|
||||
token_max_ttl: 14400
|
||||
bind_secret_id: false
|
||||
token_bound_cidrs:
|
||||
- "10.10.12.200/32"
|
||||
use_deterministic_role_id: true
|
||||
@@ -0,0 +1,4 @@
|
||||
allowed_kubernetes_namespaces:
|
||||
- "cert-manager"
|
||||
kubernetes_role_type: "Role"
|
||||
extra_labels: {}
|
||||
@@ -0,0 +1,4 @@
|
||||
allowed_kubernetes_namespaces:
|
||||
- "dhcp-system"
|
||||
kubernetes_role_type: "Role"
|
||||
extra_labels: {}
|
||||
@@ -0,0 +1,4 @@
|
||||
allowed_kubernetes_namespaces:
|
||||
- "bind-system"
|
||||
service_account_name: "agent-dns"
|
||||
extra_labels: {}
|
||||
@@ -0,0 +1,4 @@
|
||||
allowed_kubernetes_namespaces:
|
||||
- "cephrgw-system"
|
||||
kubernetes_role_type: "Role"
|
||||
extra_labels: {}
|
||||
@@ -299,6 +299,7 @@ module "kubernetes_secret_backend_role" {
|
||||
allowed_kubernetes_namespaces = each.value.allowed_kubernetes_namespaces
|
||||
kubernetes_role_type = each.value.kubernetes_role_type
|
||||
extra_labels = each.value.extra_labels
|
||||
service_account_name = each.value.service_account_name
|
||||
|
||||
depends_on = [module.kubernetes_secret_backend]
|
||||
}
|
||||
|
||||
@@ -2,6 +2,10 @@ locals {
|
||||
# Auto-generate role rules path: resources/secret_backend/{backend_path}/roles/{role_name}.yaml
|
||||
role_rules_file = "resources/secret_backend/${var.backend}/roles/${var.name}.yaml"
|
||||
|
||||
# service_account_name mode mints tokens for a pre-existing SA, so the
|
||||
# generated_role_rules / kubernetes_role_type binding fields must be unset.
|
||||
use_service_account = var.service_account_name != null
|
||||
|
||||
# Auto-generate extra labels based on country/region and role name
|
||||
auto_labels = merge(var.extra_labels, {
|
||||
vault-region = "${var.country}-${var.region}"
|
||||
@@ -13,7 +17,8 @@ resource "vault_kubernetes_secret_backend_role" "role" {
|
||||
backend = var.backend
|
||||
name = var.name
|
||||
allowed_kubernetes_namespaces = var.allowed_kubernetes_namespaces
|
||||
kubernetes_role_type = var.kubernetes_role_type
|
||||
generated_role_rules = file("${path.module}/../../../../../../../../${local.role_rules_file}")
|
||||
kubernetes_role_type = local.use_service_account ? null : var.kubernetes_role_type
|
||||
generated_role_rules = local.use_service_account ? null : file("${path.module}/../../../../../../../../${local.role_rules_file}")
|
||||
service_account_name = var.service_account_name
|
||||
extra_labels = local.auto_labels
|
||||
}
|
||||
|
||||
@@ -34,4 +34,10 @@ variable "extra_labels" {
|
||||
description = "Additional labels to apply to generated Kubernetes objects"
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
||||
|
||||
variable "service_account_name" {
|
||||
description = "Pre-existing service account to mint tokens for. When set, RBAC comes from that SA's own bindings instead of generated_role_rules."
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
@@ -285,6 +285,7 @@ variable "kubernetes_secret_backend_role" {
|
||||
allowed_kubernetes_namespaces = optional(list(string), ["*"])
|
||||
kubernetes_role_type = optional(string, "Role")
|
||||
extra_labels = optional(map(string), {})
|
||||
service_account_name = optional(string)
|
||||
}))
|
||||
default = {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
# Allow access to agent-certs Kubernetes credentials
|
||||
---
|
||||
rules:
|
||||
- path: "kubernetes/au/syd1/creds/agent-certs"
|
||||
capabilities:
|
||||
- update
|
||||
|
||||
auth:
|
||||
ldap:
|
||||
- kubernetes_au_syd1_cluster_operator
|
||||
approle:
|
||||
- agents
|
||||
@@ -0,0 +1,12 @@
|
||||
# Allow access to agent-dhcp Kubernetes credentials
|
||||
---
|
||||
rules:
|
||||
- path: "kubernetes/au/syd1/creds/agent-dhcp"
|
||||
capabilities:
|
||||
- update
|
||||
|
||||
auth:
|
||||
ldap:
|
||||
- kubernetes_au_syd1_cluster_operator
|
||||
approle:
|
||||
- agents
|
||||
@@ -0,0 +1,12 @@
|
||||
# Allow access to agent-dns Kubernetes credentials
|
||||
---
|
||||
rules:
|
||||
- path: "kubernetes/au/syd1/creds/agent-dns"
|
||||
capabilities:
|
||||
- update
|
||||
|
||||
auth:
|
||||
ldap:
|
||||
- kubernetes_au_syd1_cluster_operator
|
||||
approle:
|
||||
- agents
|
||||
@@ -0,0 +1,12 @@
|
||||
# Allow access to agent-storage Kubernetes credentials
|
||||
---
|
||||
rules:
|
||||
- path: "kubernetes/au/syd1/creds/agent-storage"
|
||||
capabilities:
|
||||
- update
|
||||
|
||||
auth:
|
||||
ldap:
|
||||
- kubernetes_au_syd1_cluster_operator
|
||||
approle:
|
||||
- agents
|
||||
@@ -0,0 +1,17 @@
|
||||
# Allow the agents AppRole to manage the kubernetes KV subtree (no delete)
|
||||
---
|
||||
rules:
|
||||
- path: "kv/data/kubernetes/*"
|
||||
capabilities:
|
||||
- create
|
||||
- read
|
||||
- update
|
||||
- list
|
||||
- path: "kv/metadata/kubernetes/*"
|
||||
capabilities:
|
||||
- read
|
||||
- list
|
||||
|
||||
auth:
|
||||
approle:
|
||||
- agents
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
rules:
|
||||
- apiGroups:
|
||||
- "cert-manager.io"
|
||||
- "acme.cert-manager.io"
|
||||
resources:
|
||||
- "*"
|
||||
verbs:
|
||||
- "*"
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- "secrets"
|
||||
verbs:
|
||||
- "get"
|
||||
- "list"
|
||||
- "watch"
|
||||
- "delete"
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- "pods"
|
||||
verbs:
|
||||
- "get"
|
||||
- "list"
|
||||
- "watch"
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- "pods/log"
|
||||
verbs:
|
||||
- "get"
|
||||
- "list"
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
rules:
|
||||
- apiGroups:
|
||||
- "kea.unkin.net"
|
||||
resources:
|
||||
- "*"
|
||||
verbs:
|
||||
- "*"
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- "pods"
|
||||
- "services"
|
||||
- "configmaps"
|
||||
- "events"
|
||||
verbs:
|
||||
- "get"
|
||||
- "list"
|
||||
- "watch"
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- "pods/log"
|
||||
verbs:
|
||||
- "get"
|
||||
- "list"
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
rules:
|
||||
- apiGroups:
|
||||
- "ceph.unkin.net"
|
||||
resources:
|
||||
- "*"
|
||||
verbs:
|
||||
- "*"
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- "pods"
|
||||
verbs:
|
||||
- "get"
|
||||
- "list"
|
||||
- "watch"
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- "pods/log"
|
||||
verbs:
|
||||
- "get"
|
||||
- "list"
|
||||
Reference in New Issue
Block a user