tfmodules/kubernetes/ovh/credentials.tf
gitops_test 25b528bd66 sync-modules: mirrored modules of release 3.2.0-F-feature-simpl-31668.650.242a7609
+++ kubernetes/README.md
+++ kubernetes/ovh/credentials.tf
+++ kubernetes/ovh/main.tf
+++ kubernetes/ovh/outputs.tf
+++ kubernetes/ovh/variables.tf
+++ kubernetes/ovh/versions.tf
2026-09-14 14:42:05 +00:00

171 lines
5.2 KiB
HCL

// Consumer and management credentials are generated here, never injected by
// the backend and never delivered through outputs. The OVH admin kubeconfig
// (client certificates) is never delivered either: the consumer receives a
// scoped ServiceAccount token with a TTL, day 2 renewal uses a dedicated
// root token. Vault entries follow the existing contract:
// secret/{provider}/{cloud_account}/{requester_unique_id}/{username} consumer kubeconfig
// secret/{provider}/{cloud_account}/{requester_unique_id}/admin root kubeconfig + admin username
// Only the Vault path of the consumer entry is exposed as an output.
resource "kubernetes_namespace_v1" "access" {
metadata {
name = "${var.name_prefix}-access"
}
depends_on = [ovh_cloud_project_kube_nodepool.this]
}
// Consumer access: ServiceAccount + bound token with TTL. The binding
// references the ServiceAccount by name, so it survives the delete/recreate
// performed by the credential renewal workflow.
resource "kubernetes_service_account_v1" "consumer" {
metadata {
name = var.username
namespace = kubernetes_namespace_v1.access.metadata[0].name
}
}
resource "kubernetes_cluster_role_binding_v1" "consumer_admin" {
metadata {
name = "${var.username}-admin"
}
role_ref {
api_group = "rbac.authorization.k8s.io"
kind = "ClusterRole"
name = "cluster-admin"
}
subject {
kind = "ServiceAccount"
name = kubernetes_service_account_v1.consumer.metadata[0].name
namespace = kubernetes_namespace_v1.access.metadata[0].name
}
}
resource "kubernetes_token_request_v1" "consumer" {
metadata {
name = kubernetes_service_account_v1.consumer.metadata[0].name
namespace = kubernetes_namespace_v1.access.metadata[0].name
}
spec {
expiration_seconds = var.token_ttl_seconds
}
}
// Management (renewer) access: ServiceAccount + NON EXPIRING legacy token
// Secret, the credential the renewal workflow uses with kubectl on day 2.
// Revocable by deleting the ServiceAccount.
resource "kubernetes_service_account_v1" "renewer" {
metadata {
name = var.admin_username
namespace = kubernetes_namespace_v1.access.metadata[0].name
}
}
resource "kubernetes_cluster_role_binding_v1" "renewer_admin" {
metadata {
name = "${var.admin_username}-admin"
}
role_ref {
api_group = "rbac.authorization.k8s.io"
kind = "ClusterRole"
name = "cluster-admin"
}
subject {
kind = "ServiceAccount"
name = kubernetes_service_account_v1.renewer.metadata[0].name
namespace = kubernetes_namespace_v1.access.metadata[0].name
}
}
resource "kubernetes_secret_v1" "renewer_token" {
metadata {
name = "${var.admin_username}-token"
namespace = kubernetes_namespace_v1.access.metadata[0].name
annotations = {
"kubernetes.io/service-account.name" = kubernetes_service_account_v1.renewer.metadata[0].name
}
}
type = "kubernetes.io/service-account-token"
wait_for_service_account_token = true
}
// Kubeconfigs assembled with the scoped tokens, never the admin certificates
locals {
cluster_server = ovh_cloud_project_kube.this.kubeconfig_attributes[0].host
cluster_ca = ovh_cloud_project_kube.this.kubeconfig_attributes[0].cluster_ca_certificate
consumer_kubeconfig = yamlencode({
apiVersion = "v1"
kind = "Config"
"current-context" = "simpl"
clusters = [{
name = "simpl"
cluster = {
server = local.cluster_server
"certificate-authority-data" = local.cluster_ca
}
}]
users = [{
name = var.username
user = { token = kubernetes_token_request_v1.consumer.token }
}]
contexts = [{
name = "simpl"
context = { cluster = "simpl", user = var.username }
}]
})
renewer_kubeconfig = yamlencode({
apiVersion = "v1"
kind = "Config"
"current-context" = "simpl"
clusters = [{
name = "simpl"
cluster = {
server = local.cluster_server
"certificate-authority-data" = local.cluster_ca
}
}]
users = [{
name = var.admin_username
user = { token = kubernetes_secret_v1.renewer_token.data["token"] }
}]
contexts = [{
name = "simpl"
context = { cluster = "simpl", user = var.admin_username }
}]
})
}
// The kubeconfig is the primary artifact of both entries (the credential
// delivered and later renewed), the remaining keys are companions.
resource "vault_kv_secret_v2" "consumer" {
mount = "secret"
name = "ovh/${var.cloud_account}/${var.requester_unique_id}/${var.username}"
data_json = jsonencode({
kubeconfig = local.consumer_kubeconfig
token = kubernetes_token_request_v1.consumer.token
server = local.cluster_server
ca_data = local.cluster_ca
ttl_seconds = var.token_ttl_seconds
})
}
resource "vault_kv_secret_v2" "admin" {
mount = "secret"
name = "ovh/${var.cloud_account}/${var.requester_unique_id}/admin"
data_json = jsonencode({
username = var.admin_username
kubeconfig = local.renewer_kubeconfig
token = kubernetes_secret_v1.renewer_token.data["token"]
server = local.cluster_server
ca_data = local.cluster_ca
})
}