// 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: consumer and management // each get a scoped ServiceAccount with a NON EXPIRING legacy token, valid // until a credential renewal is requested (the renewal deletes and recreates // the ServiceAccount, which revokes every previously issued token at once). // 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 + non expiring legacy token Secret. 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_secret_v1" "consumer_token" { metadata { name = "${var.username}-token" namespace = kubernetes_namespace_v1.access.metadata[0].name annotations = { "kubernetes.io/service-account.name" = kubernetes_service_account_v1.consumer.metadata[0].name } } type = "kubernetes.io/service-account-token" wait_for_service_account_token = true } // 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_secret_v1.consumer_token.data["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_secret_v1.consumer_token.data["token"] server = local.cluster_server ca_data = local.cluster_ca }) } 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 }) }