+++ kubernetes/README.md +++ kubernetes/ovh/credentials.tf +++ kubernetes/ovh/variables.tf
190 lines
6.1 KiB
HCL
190 lines
6.1 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: 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]
|
|
}
|
|
|
|
// Rotation anchor: replacing this resource (any change to credentials_version)
|
|
// cascades to the consumer ServiceAccount and its token Secret through
|
|
// replace_triggered_by, performing the credential rotation inside Terraform.
|
|
resource "terraform_data" "credentials_version" {
|
|
input = var.credentials_version
|
|
}
|
|
|
|
// 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 rotation. Recreating the
|
|
// ServiceAccount changes its UID, which revokes every token issued for it.
|
|
resource "kubernetes_service_account_v1" "consumer" {
|
|
metadata {
|
|
name = var.username
|
|
namespace = kubernetes_namespace_v1.access.metadata[0].name
|
|
}
|
|
|
|
lifecycle {
|
|
replace_triggered_by = [terraform_data.credentials_version]
|
|
}
|
|
}
|
|
|
|
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
|
|
|
|
lifecycle {
|
|
replace_triggered_by = [terraform_data.credentials_version]
|
|
}
|
|
}
|
|
|
|
// 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
|
|
})
|
|
}
|