diff --git a/examples/ske-encrypted-volumes/.terraform.lock.hcl b/examples/ske-encrypted-volumes/.terraform.lock.hcl index 39718fe..5a3bdb3 100644 --- a/examples/ske-encrypted-volumes/.terraform.lock.hcl +++ b/examples/ske-encrypted-volumes/.terraform.lock.hcl @@ -22,23 +22,24 @@ provider "registry.terraform.io/hashicorp/kubernetes" { } provider "registry.terraform.io/stackitcloud/stackit" { - version = "0.94.0" - constraints = ">= 0.94.0" + version = "0.117.0" + constraints = ">= 0.117.0" hashes = [ - "h1:ikFzd4yeJ1LR8ojP2PsZwiK2ZLhxBjRXkEg2HJrI07U=", - "zh:06c8da7d8a048216e825fa7d1e45949c1bda2a5f53f9bb0556b83b6610703fe6", + "h1:3UMzDKpnhaLxtnsfmBO5d1DJxxMvxXmssosPhPZQVUc=", + "h1:GPtyX5yigbBFjYQtOUIFJ6t/O6QJxpRbWuDDEE98u4M=", "zh:0dde99e7b343fa01f8eefc378171fb8621bedb20f59157d6cc8e3d46c738105f", - "zh:19e82636cfd52a65105e0cf030bc8a0c815082818ef953b84f9b1e349a87318c", - "zh:24af9b7d2f1bb38f480b1aa8cf5e4ecf483bd4403642a9e8a5accbe1ae212feb", - "zh:3b10850e9242bcd00c519ff4140130e8443002fd60b6dff90983e7cb1973b2c3", - "zh:54837a0fa4ddbcf0b8407718f8823b831322deba3bd7ec8492e4578928f50633", - "zh:5cfd6a6b1ca73826a03f8746ef84a5c4059648bc49abf8056c8e0f9b87800a23", - "zh:6ab3bcfef6ff65b4ce76d333b4ad99e5f91991fcf5bddbe1958aadde6ee05eab", - "zh:81b96dc29b055f15e475d8bc32482617a582785949b3c02f44ef15d19951f69c", - "zh:85f478c2fcf10219263462d0f06b5cc41603b1edad813c336e100b3e0a55bfe8", - "zh:9adbb7655fddfe4d4081746d0d7e39c3e8fbf8aa3d8b7d3b5164f30c16a6bd93", - "zh:9c24b39e788283ead8a8ce1f013a47562ff0dc1ccb642a8e18644cbdcda0f1c4", - "zh:a425f28d6a5f6f024cab56c848c55025e84a09db946f1b00a2655d9567251cea", - "zh:f28aa62d2f06e08fe6d18ef9103a8164aa9278540779bebd61120f810c603c6b", + "zh:1778fef5cb73508e14ac021ea905e840ade6b5ba093b86743eca295b1a101980", + "zh:1eca69d4054d1027248d0fa9f928d31a6649691a3d9e09611ad4f60b67c0a6af", + "zh:2ccb74a622c7f98af87a0889a281a4eb4cb82a9af210dea54508bf8a092f0765", + "zh:31aa1576e6b3b86a3a08e6293cdd1f29555f77c78550ea10ec5b13b6907b4218", + "zh:3e54805af3e78ed59b7f9331f6928d01707b9ee547b892111eb681900eecc3cd", + "zh:41693875f5354df3976fe576cbdb90779a84480931cc69dc98a8fbefa73157f9", + "zh:42f8dfb1b6f21d0a85d3f23d7b56c16f7ef3bc8987983ced62252f9ba674d044", + "zh:4c841a04769a401ca4ef96e52e4908915eb3bb18a5ecceef61cdbe2bf385b650", + "zh:5750987df198dbfe01a838ea019fbb4a4ef9943a39b2d90631223e6c3a0b5185", + "zh:8c7b2e3f488dc9f577dd01b9a279dae867135347d7336b9170e2da8fe7a2d3ad", + "zh:bc15bc4ef82f625f167589399adad532769c033eae12cc707ddf53236863e327", + "zh:d7dcc857ba301a477970cce8342eee95499bd4ce0f37df642b042d008214d7c8", + "zh:f9cd95acc1f40577f9bb345820925787de840104ec6ee4dede6ff1da224ae82c", ] } diff --git a/examples/ske-encrypted-volumes/010-provider.tf b/examples/ske-encrypted-volumes/010-provider.tf index 73efb3a..bddb953 100644 --- a/examples/ske-encrypted-volumes/010-provider.tf +++ b/examples/ske-encrypted-volumes/010-provider.tf @@ -13,10 +13,11 @@ # limitations under the License. terraform { + required_version = ">= 1.10.0" required_providers { stackit = { source = "stackitcloud/stackit" - version = ">= 0.94.0" + version = ">= 0.117.0" } kubernetes = { source = "hashicorp/kubernetes" @@ -26,13 +27,14 @@ terraform { } provider "stackit" { - default_region = "eu01" - service_account_key_path = "" + default_region = var.stackit_region + service_account_key_path = var.stackit_service_account_key_path + experiments = ["iam", "ske"] } provider "kubernetes" { - host = yamldecode(stackit_ske_kubeconfig.default.kube_config).clusters.0.cluster.server - client_certificate = base64decode(yamldecode(stackit_ske_kubeconfig.default.kube_config).users.0.user.client-certificate-data) - client_key = base64decode(yamldecode(stackit_ske_kubeconfig.default.kube_config).users.0.user.client-key-data) - cluster_ca_certificate = base64decode(yamldecode(stackit_ske_kubeconfig.default.kube_config).clusters.0.cluster.certificate-authority-data) + host = yamldecode(ephemeral.stackit_ske_kubeconfig.default.kube_config).clusters.0.cluster.server + client_certificate = base64decode(yamldecode(ephemeral.stackit_ske_kubeconfig.default.kube_config).users.0.user.client-certificate-data) + client_key = base64decode(yamldecode(ephemeral.stackit_ske_kubeconfig.default.kube_config).users.0.user.client-key-data) + cluster_ca_certificate = base64decode(yamldecode(ephemeral.stackit_ske_kubeconfig.default.kube_config).clusters.0.cluster.certificate-authority-data) } diff --git a/examples/ske-encrypted-volumes/015-variables.tf b/examples/ske-encrypted-volumes/015-variables.tf new file mode 100644 index 0000000..7cdaf3f --- /dev/null +++ b/examples/ske-encrypted-volumes/015-variables.tf @@ -0,0 +1,30 @@ +# Copyright 2026 Schwarz Digits Cloud GmbH & Co. KG +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +variable "stackit_project_id" { + type = string + description = "STACKIT project that holds the cluster, the KMS key and the service account" +} + +variable "stackit_region" { + type = string + description = "STACKIT region" + default = "eu01" +} + +variable "stackit_service_account_key_path" { + type = string + description = "Path to the service account key file. Unset falls back to the STACKIT_SERVICE_ACCOUNT_KEY_PATH environment variable, then to the credentials file $HOME/.stackit/credentials.json." + default = null +} diff --git a/examples/ske-encrypted-volumes/020-ske-cluster.tf b/examples/ske-encrypted-volumes/020-ske-cluster.tf index 0e4f745..5dfdab0 100644 --- a/examples/ske-encrypted-volumes/020-ske-cluster.tf +++ b/examples/ske-encrypted-volumes/020-ske-cluster.tf @@ -13,29 +13,32 @@ # limitations under the License. resource "stackit_ske_cluster" "default" { - project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" - name = "ske-enc-vol" - kubernetes_version_min = "1.33" + project_id = var.stackit_project_id + name = "ske-enc-vol" + # Unset, the cluster is created with the latest Kubernetes version that + # SKE supports. Uncomment to pin a minimum version instead. A pinned + # version stops working once SKE removes it. + # kubernetes_version_min = "1.36" node_pools = [{ name = "standard" machine_type = "c2i.4" minimum = 1 maximum = 3 - availability_zones = ["eu01-1"] + availability_zones = ["${var.stackit_region}-1"] os_name = "flatcar" volume_size = 32 }] } -resource "stackit_ske_kubeconfig" "default" { - project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" - cluster_name = stackit_ske_cluster.default.name - refresh = true +# The cluster ID condition defers the kubeconfig request until the cluster exists. +ephemeral "stackit_ske_kubeconfig" "default" { + project_id = var.stackit_project_id + cluster_name = stackit_ske_cluster.default.id != "" ? stackit_ske_cluster.default.name : "" } data "stackit_service_accounts" "ske_internal" { - project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + project_id = var.stackit_project_id email_suffix = "@ske.sa.stackit.cloud" depends_on = [stackit_ske_cluster.default] diff --git a/examples/ske-encrypted-volumes/030-kms.tf b/examples/ske-encrypted-volumes/030-kms.tf index 730d4a8..8e1f330 100644 --- a/examples/ske-encrypted-volumes/030-kms.tf +++ b/examples/ske-encrypted-volumes/030-kms.tf @@ -13,12 +13,12 @@ # limitations under the License. resource "stackit_kms_keyring" "encryption" { - project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + project_id = var.stackit_project_id display_name = "ske-volume-keyring" } resource "stackit_kms_key" "volume_key" { - project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + project_id = var.stackit_project_id keyring_id = stackit_kms_keyring.encryption.keyring_id display_name = "volume-encryption-key" protection = "software" diff --git a/examples/ske-encrypted-volumes/040-authorization.tf b/examples/ske-encrypted-volumes/040-authorization.tf index ca012df..aadb771 100644 --- a/examples/ske-encrypted-volumes/040-authorization.tf +++ b/examples/ske-encrypted-volumes/040-authorization.tf @@ -13,12 +13,12 @@ # limitations under the License. resource "stackit_service_account" "kms_manager" { - project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + project_id = var.stackit_project_id name = "volume-encryptor" } resource "stackit_authorization_project_role_assignment" "kms_user" { - resource_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + resource_id = var.stackit_project_id role = "kms.admin" subject = stackit_service_account.kms_manager.email } diff --git a/examples/ske-encrypted-volumes/050-k8s-storage.tf b/examples/ske-encrypted-volumes/050-k8s-storage.tf index 6d8340d..e17b635 100644 --- a/examples/ske-encrypted-volumes/050-k8s-storage.tf +++ b/examples/ske-encrypted-volumes/050-k8s-storage.tf @@ -27,7 +27,7 @@ resource "kubernetes_storage_class_v1" "encrypted_premium" { encrypted = "true" kmsKeyID = stackit_kms_key.volume_key.key_id kmsKeyringID = stackit_kms_keyring.encryption.keyring_id - kmsProjectID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + kmsProjectID = var.stackit_project_id kmsKeyVersion = "1" kmsServiceAccount = stackit_service_account.kms_manager.email } diff --git a/examples/ske-encrypted-volumes/060-outputs.tf b/examples/ske-encrypted-volumes/060-outputs.tf new file mode 100644 index 0000000..71aaed1 --- /dev/null +++ b/examples/ske-encrypted-volumes/060-outputs.tf @@ -0,0 +1,28 @@ +# Copyright 2026 Schwarz Digits Cloud GmbH & Co. KG +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +output "ske_cluster_name" { + description = "Name of the SKE cluster" + value = stackit_ske_cluster.default.name +} + +output "kubeconfig_command" { + description = "Fetch a kubeconfig for kubectl" + value = "stackit ske kubeconfig create ${stackit_ske_cluster.default.name} --project-id ${var.stackit_project_id} --region ${stackit_ske_cluster.default.region} --expiration 8h" +} + +output "storage_class_name" { + description = "Encrypted StorageClass to reference from a PersistentVolumeClaim" + value = kubernetes_storage_class_v1.encrypted_premium.metadata[0].name +} diff --git a/examples/ske-encrypted-volumes/README.md b/examples/ske-encrypted-volumes/README.md index c73371f..649ab49 100644 --- a/examples/ske-encrypted-volumes/README.md +++ b/examples/ske-encrypted-volumes/README.md @@ -7,3 +7,30 @@ ## Overview This guide demonstrates how to roll out an encrypted storage class for SKE using the STACKIT Key Management Service (KMS). To achieve this, we use a **Service Account Impersonation** (Act-As) pattern. This allows the internal SKE service account to perform encryption and decryption tasks on behalf of a user-managed service account that has been granted access to your KMS keys. + +## Prerequisites + +- Terraform 1.10 or later +- STACKIT provider 0.117.0 or later +- An authenticated `stackit` CLI (`stackit auth login` or `stackit auth activate-service-account`) and `kubectl` for the verify step + +## Usage + +```bash +cp terraform.tfvars.example terraform.tfvars +terraform init +terraform apply +``` + +`terraform.tfvars` needs `stackit_project_id`. Without `stackit_service_account_key_path`, the provider falls back to `STACKIT_SERVICE_ACCOUNT_KEY_PATH`, then to its credentials file `$HOME/.stackit/credentials.json` ([provider authentication](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs)). + +## Verify + +Check that the claim is bound and the pod is running: + +```bash +eval "$(terraform output -raw kubeconfig_command)" +kubectl config use-context "$(terraform output -raw ske_cluster_name)" +kubectl get pvc test-encryption-pvc +kubectl get pod encrypted-volume-test +``` diff --git a/examples/ske-encrypted-volumes/terraform.tfvars.example b/examples/ske-encrypted-volumes/terraform.tfvars.example new file mode 100644 index 0000000..5528a6f --- /dev/null +++ b/examples/ske-encrypted-volumes/terraform.tfvars.example @@ -0,0 +1 @@ +stackit_project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"