Як налаштувати HTTPS Ingress з Kubernetes CSI на AKS, використовуючи Workload Identity

pic

Фото: Магнус Андерссон на Unsplash

У сучасному світі, де домінують екрани та миттєве задоволення, спосіб споживання інформації зазнав суттєвих змін. Для молодших поколінь візуальні матеріали стали переважним медіумом, залишаючи традиційне читання та уяву на другому плані. Ця трансформація — не випадковість, а наслідок швидкого ритму життя, розвитку технологій і всюдисущості соціальних мереж.

Ось приклад нещодавньої розмови з моїм сином. Коли я сказала йому, що пишу статтю, його негайна відповідь була: "Мам, хто зараз читає статті?" Я запитала його: "Тож, що, на твою думку, роблять люди?" Він без вагань відповів: "Вони дивляться відео." Його відповідь чудово відображає тенденцію, яка формується в нашому суспільстві: домінування візуального контенту над написаним текстом.

Перехід до візуального контенту

Візуальний контент — чи то короткі відео, інфографіка, чи меми — став основою комунікації. Платформи як TikTok, Instagram і YouTube процвітають завдяки здатності надавати швидкий, засвоюваний контент, який привертає увагу за лічені секунди. Середня тривалість уваги значно знизилася за останні роки, деякі дослідження оцінюють її в 8 секунд. Натомість читання статті чи книги вимагає концентрації уваги, розумових зусиль і часу — ресурсів, яких багатьом не вистачає.

Соціальні мережі та технології привчили нас очікувати миттєвих результатів і швидкого розваг. Відео, зі своєю динамічною природою, поєднують звук, візуальні ефекти і іноді текст, щоб донести повідомлення за дуже короткий час, порівняно з читанням статті. Такий формат не лише задовольняє потребу у зручності, а й стимулює кілька відчуттів, роблячи досвід більш захоплюючим і запам'ятовуючим.

Уява: жертва зручності

Перевага візуальних матеріалів має свою ціну: зменшення використання уяви. Читання, колись основне джерело розваг і навчання, вимагає від мозку активно візуалізувати сцени, персонажів і ідеї. Цей розумовий процес є важливим для розвитку творчості та критичного мислення. Однак коли все подається візуально — з високоякісними зображеннями, анімаціями та звуковими ефектами — потреба уявляти зменшується.

Наприклад, раніше діти, читаючи книгу про космос, уявляли свої версії планет, зірок і астронавтів. Сьогодні вони скоріше дивитимуться відео, яке покаже їм, як виглядає космос, не залишаючи місця для вільної уяви.

Роль швидкого ритму життя

Швидкість нашого життя також відіграє важливу роль у цьому переході. Між роботою, школою та численними сповіщеннями, що борються за нашу увагу, час став цінним ресурсом. Читання вимагає повільнішого темпу, що сприяє рефлексії та глибокому мисленню. Але в світі, де "швидше — це краще", відео виграють, оскільки надають контент швидко і ефективно.

Ця перевага швидкості також вплинула на спосіб, у який творці контенту створюють матеріали. Статті часто супроводжуються короткими відео або підсумками, навіть книги продаються з трейлерами або адаптуються у фільми для аудиторії, що потребує миттєвого задоволення.

Що це означає для майбутнього?

Зростання популярності візуального контенту замість читання не є негативним явищем; відео зробили освіту доступнішою та захоплюючою для багатьох. Складні концепції можна пояснити візуально так, що це долає мовні бар'єри, роблячи навчання більш інклюзивним. Проте важливо знайти баланс. Хоча відео зручні та розважальні, вони повинні доповнювати, а не заміняти глибину і багатство, яке надає читання.

Заохочення наступного покоління до читання не означає відмову від технологій або соціальних мереж; це означає нагадування їм про цінність уяви, фокусу та критичного мислення.
Для секретів та сертифікатів ми спеціально використовуємо Secrets Store CSI Driver.

Secrets Store CSI Driver: Що це дає нам?

  • Безпека: Дані залишаються у вашому зовнішньому сховищі. Вони лише монтуються як файл всередині Pod, і за замовчуванням не зберігаються в etcd Kubernetes.
  • Ротація: Якщо секрет у Key Vault змінюється, CSI драйвер може автоматично оновити файл у томі (залежно від вашого налаштування та тригерів).
  • Простота: Вам не потрібно вручну створювати або підтримувати Kubernetes Secrets, якщо ви віддаєте перевагу прямому монтуванню томів.

На AKS з Azure Key Vault

  • Драйвер: secrets-store.csi.k8s.io
  • Постачальник: azure (часто називається csi-keyvault-provider)

Цей постачальник:

  1. Аутентифікується до Key Vault (використовуючи керовану ідентичність, сервісний принципал або Workload Identity).
  2. Отримує секрети/сертифікати.
  3. Монтує їх у ваші Pods без збереження в etcd.

3. Налаштування Secrets Store CSI на AKS

Крок 1: Увімкнення постачальника секретів у вашому AKS-кластері

Якщо ви використовуєте Terraform для управління AKS, просто увімкніть постачальника секретів:

resource "azurerm_kubernetes_cluster" "this" {  

 # Інші налаштування AKS...  

 key_vault_secrets_provider {  
 secret_rotation_enabled = true  
 secret_rotation_interval = "2m"  
 }  

}

Що це робить?

  • Встановлює та налаштовує Secrets Store CSI Driver і Azure постачальника в вашому AKS-кластері.
  • Увімкнення secret_rotation_enabled та налаштування secret_rotation_interval дозволяє періодично оновлювати секрети з Key Vault.

Якщо ви хочете, щоб керована ідентичність вашого кластера мала доступ до Key Vault, ви повинні надати їй роль Key Vault Secrets User для цього сховища. Однак будьте обережні: якщо ваш кластер обробляє кілька навантажень, що потребують секретів з різних Key Vault, ця одна ідентичність може отримати широкі дозволи для кількох сховищ. Це не дуже добре для безпеки…

4. Чому використовувати Workload Identity?

Замість використання керованої ідентичності кластера, Workload Identity (рідна інтеграція між Azure AD та Kubernetes OIDC) дозволяє кожному Pod (або Deployment) мати свою власну ідентичність. Потім ви застосовуєте дрібно налаштовані ролі, які необхідні вашому застосунку.

Уявіть, що у вас є 10 простору імен, кожне з яких належить окремій команді або проекту. Ви справді не хочете, щоб секрети кожної команди були доступні для всього кластера. З Workload Identity ви можете:

  • Створити Azure AD Application/Service Principal для цієї ідентичності Pod.
  • Надати мінімальні дозволи на доступ до Key Vault (наприклад, Key Vault Secrets User) тільки для цього застосунку.
  • Анотувати відповідний Kubernetes Service Account для використання цієї ідентичності.

Більше ніякого надмірного доступу!

5. Налаштування Workload Identity за допомогою Terraform

Давайте створимо основні компоненти для одного сертифіката.
Ми маємо такі файли:

variables.tf

variable "k8s_namespace" {  
 description = "The namespace of the deployment"  
 type = string  
}  

variable "workload_identity_oidc_issuer_url" {  
 description = "The OIDC issuer URL of the workload identity"  
 type = string  
}  

variable "entra_tenant_id" {  
 description = "The tenant id of the keyvault where the certificate is stored"  
 type = string  
}  

variable "keyvault_name" {  
 description = "The name of the keyvault where the certificate is stored"  
 type = string  
}  

variable "keyvault_id" {  
 description = "The id of the keyvault where the certificate is stored"  
 type = string  
}  

variable "certificate_name" {  
 description = "The name of the certificate to mount as k8s secret"  
 type = string  
}  

variable "certificate_content_type" {  
 description = "The content type of the certificate"  
 type = string  
 default = "application/x-pkcs12"  
}

Пояснення

  • k8s_namespace: Простір імен, в якому будуть знаходитися всі наші ресурси.
  • workload_identity_oidc_issuer_url: OIDC постачальник з AKS кластеру, який використовується Azure AD для перевірки токенів.
  • entra_tenant_id: Tenant ID для Azure AD (Entra).
  • keyvault_name / keyvault_id: Назва та ID Key Vault, де зберігається сертифікат.
  • certificate_name: Назва сертифікату в Key Vault.
  • certificate_content_type: Тип вмісту, за замовчуванням для сертифікатів на основі PFX — application/x-pkcs12.

main.tf

locals {  
 csi_name = "${var.certificate_name}-csi"  
 k8s_service_account_name = "${var.certificate_name}-csi-sa"  
 k8s_secret_name = "${var.certificate_name}-tls"  
}  

resource "azuread_application" "this" {  
 display_name = "${var.k8s_namespace}-${local.csi_name}"  
}  

resource "azuread_service_principal" "this" {  
 client_id = azuread_application.this.client_id  
}  

resource "kubernetes_service_account" "this" {  
 metadata {  
 name = local.k8s_service_account_name  
 namespace = var.k8s_namespace  
 annotations = {  
 "azure.workload.identity/client-id" = azuread_application.this.client_id  
 "azure.workload.identity/tenant-id" = var.entra_tenant_id  
 }  
 labels = {  
 "azure.workload.identity/use" : "true"  
 }  
 }  
}  

resource "azuread_application_federated_identity_credential" "this" {  
 application_id = azuread_application.this.id  
 display_name = "${var.k8s_namespace}-${var.certificate_name}-credential"  

 audiences = ["api://AzureADTokenExchange"]  
 issuer = var.workload_identity_oidc_issuer_url  
 subject = "system:serviceaccount:${var.k8s_namespace}:${local.k8s_service_account_name}"  
}  

resource "azurerm_role_assignment" "this" {  
 principal_id = azuread_service_principal.this.object_id  
 role_definition_name = "Key Vault Secrets User"  
 scope = var.keyvault_id  
}  

resource "kubernetes_manifest" "grafana_tls_secret" {  
 manifest = {  
 "apiVersion" = "secrets-store.csi.x-k8s.io/v1"  
 "kind" = "SecretProviderClass"  
 "metadata" = {  
 "name" = local.csi_name  
 "namespace" = var.k8s_namespace  
 }  
 "spec" = {  
 "provider" = "azure"  
 "secretObjects" = [  
 {  
 "secretName" = local.k8s_secret_name  
 "type" = "kubernetes.io/tls"  
 "data" = [  
 {  
 "objectName" = var.certificate_name  
 "key" = "tls.crt"  
 },  
 {  
 "objectName" = var.certificate_name  
 "key" = "tls.key"  
 }  
 ]  
 }  
 ]  
 "parameters" = {  
 "useWorkloadIdentity" = "true"  
 "usePodIdentity" = "false"  
 "useVMManagedIdentity" = "false"  
 "clientID" = azuread_application.this.client_id  
 "keyvaultName" = var.keyvault_name  
 "objects" = <<-EOF  
 array:  
 - |  
 objectName: "${var.certificate_name}"  
 objectType: "secret"  
 contentType: "application/x-pkcs12"  
 EOF  
 "tenantId" = var.entra_tenant_id  
 "cloudName" = "AzurePublicCloud"  
 }  
 }  
 }  
}  

resource "kubernetes_deployment" "this" {  
 depends_on = [kubernetes_manifest.grafana_tls_secret, azurerm_role_assignment.this]  
 metadata {  

name = local.csi_name  
 namespace = var.k8s_namespace  
 labels = {  
 provisioned_by = "terraform"  
 }  
 }  

 spec {  
 replicas = 1  

 selector {  
 match_labels = {  
 app = local.csi_name  
 }  
 }  

 template {  
 metadata {  
 labels = {  
 app = local.csi_name  
 "azure.workload.identity/use" = "true"  
 provisioned_by = "terraform"  
 }  
 }  

 spec {  


 service_account_name = local.k8s_service_account_name  

 container {  
 name = "alpine"  
 image = "alpine:3.21.2@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099"  
 command = ["/bin/sh", "-c"]  
 args = ["echo 'Triggering secrets-store creation...' && sleep 999999"]  

 volume_mount {  
 name = local.csi_name  
 mount_path = "/mnt/csi"  
 read_only = true  
 }  
 }  

 volume {  
 name = local.csi_name  
 csi {  
 driver = "secrets-store.csi.k8s.io"  
 read_only = true  
 volume_attributes = {  
 "secretProviderClass" = local.csi_name  
 }  
 }  
 }  
 }  
 }  
 }  
}

Пояснення

1.
1. azuread_application і azuread_service_principal: Створення ідентичності в Azure AD.
2. kubernetes_service_account: Додавання анотацій до Service Account з client-id і tenant-id ідентичності.
3. azuread_application_federated_identity_credential: Підключення OIDC токену AKS до ідентичності в Azure AD.
4. azurerm_role_assignment: Надання прав “Key Vault Secrets User” для доступу до секретів.
5. kubernetes_manifest "grafana_tls_secret": Створення SecretProviderClass, який вказує CSI драйверу, як отримувати сертифікат з Key Vault.
6. kubernetes_deployment: Простий Pod (що працює на Alpine), який монтує секрет через CSI. Використовуючи volume.csi.driver = "secrets-store.csi.k8s.io", він отримає сертифікат і створить/оновить Kubernetes Secret з іменем ${var.certificate_name}-tls у вашому просторі імен.

У YAML це виглядало б так:

apiVersion: apps/v1  
kind: Deployment  
metadata:  
 name: my-cert-csi  
 namespace: my-namespace  
spec:  
 replicas: 1  
 selector:  
 matchLabels:  
 app: my-cert-csi  
 template:  
 metadata:  
 labels:  
 app: my-cert-csi  
 azure.workload.identity/use: "true"  
 provisioned_by: "terraform"  
 spec:  
 serviceAccountName: my-cert-csi-sa  
 containers:  
 - name: alpine  
 image: alpine:3.21.2@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099  
 command: ["/bin/sh", "-c"]  
 args: ["echo 'Triggering secrets-store creation...' && sleep 999999"]  
 volumeMounts:  
 - name: my-cert-csi  
 mountPath: "/mnt/csi"  
 readOnly: true  
 volumes:  
 - name: my-cert-csi  
 csi:  
 driver: secrets-store.csi.k8s.io  
 readOnly: true  
 volumeAttributes:  
 secretProviderClass: my-cert-csi

6. Збираємо все разом в модуль

Ви можете зібрати це в Terraform модуль з трьох файлів:

variables.tf

(Як показано раніше.)

main.tf

(Як показано раніше — містить локальні змінні, створення ідентичності, призначення ролей, SecretProviderClass і Deployment.)

outputs.tf

output "k8s_secret_name" {  
 value = local.k8s_secret_name  
}

Таким чином, ви зможете легко використовувати цю логіку в кількох просторах імен або для кількох сертифікатів, передаючи різні значення змінних.

7. Приклад: Використання цього модуля

Ось приклад, як ви можете викликати модуль:

data "azurerm_client_config" "current" {}  

data "azurerm_kubernetes_cluster" "this" {  
 name = "using-system-aks"  
 resource_group_name = "using-system-aks-rg"  
}  

data "azurerm_key_vault" "this" {  
 name = "using-system-vault"  
 resource_group_name = "using-system-vault-rg"  
}  

resource "kubernetes_namespace" "this" {  
 metadata {  
 name = "ns1"  
 labels = {  
 provisioned_by = "terraform"  
 }  
 }  
}  

module "cob_tls_csi" {  
 source = "../modules/k8s-csi-certificate"  

 k8s_namespace = kubernetes_namespace.this.metadata[0].name  

 workload_identity_oidc_issuer_url = data.azurerm_kubernetes_cluster.this.oidc_issuer_url  

 entra_tenant_id = data.azurerm_client_config.current.tenant_id  
 keyvault_name = data.azurerm_key_vault.this.name  
 keyvault_id = data.azurerm_key_vault.this.id  

 certificate_name = "using-system-cert"  
}

Що це робить?

  1. Створює/перевіряє наявність простору імен ns1.
  2. Отримує інформацію про AKS і Key Vault.
  3. Викликає модуль:
  • Налаштовує Workload Identity в Azure AD.
  • Створює SecretProviderClass, що посилається на зазначений сертифікат.
  • Виконує мінімальний Deployment, який монтує сертифікат і автоматично створює kubernetes.io/tls секрет.

Потім виводить результат k8s_secret_name, який ви можете використовувати в об'єкті Ingress.

8. Додавання Ingress з TLS

Якщо ви використовуєте Traefik, Nginx або будь-який інший Ingress Controller, ви можете посилатися на секрет у тому ж просторі імен, що й ваш Ingress.
Наприклад, з Traefik:

resource "kubernetes_ingress" "traefik_example" {  
 metadata {  
 name = "my-https-ingress"  
 namespace = kubernetes_namespace.this.metadata[0].name  
 annotations = {  
 "kubernetes.io/ingress.class" = "traefik"  
 }  
 }  

 spec {  
 tls {  
 secret_name = module.cob_tls_csi.k8s_secret_name  
 hosts = ["example.mydomain.com"]  
 }  

 rule {  
 host = "example.mydomain.com"  

 http {  
 path {  
 path = "/"  
 path_type = "Prefix"  

 backend {  
 service {  
 name = "my-service"  
 port {  
 number = 80  
 }  
 }  
 }  
 }  
 }  
 }  
 }  
}

Примітка: Переконайтесь, що цей ingress знаходиться в тому ж просторі імен, що й секрет. За замовчуванням, Ingress може посилатися лише на секрети у своєму просторі імен.

9. Візуалізація потоку

Ось спрощена діаграма, щоб показати, як всі частини працюють разом:

+------------+  
 | Key Vault |  
 | (Secrets) |  
 +-----+------+  
 |  
 | (Azure AD auth через Workload Identity)  
 |  
AKS Cluster <----+----> Azure AD Application / Service Principal  
|   
| (Pod/Deployment)   
| - використовує ServiceAccount  
| з анотацією  
| "azure.workload.identity/client-id"  
|  
| CSI драйвер монтує сертифікат з Key Vault  
| безпосередньо до /mnt/csi  
v  
Kubernetes Secret (Опціональний автосинхронізований)
  1. Pod отримує токен з OIDC кінцевої точки AKS.
  2. Azure AD перевіряє, чи співпадає цей токен з полем subject Service Account.
  3. Pod отримує дозвіл на читання з Key Vault.
  4. CSI драйвер отримує секрет/сертифікат з Key Vault.
  5. Дані монтуються як файл у Pod (і опціонально синхронізуються як Kubernetes Secret, якщо налаштовано).

Інші можливі рішення

Окрім використання Secrets Store CSI Driver і Workload Identity на AKS, є ще два популярні альтернативи, які варто розглянути:

AKS з Azure Application Gateway (AGIC)
Використовуючи AGIC (Application Gateway Ingress Controller), ви можете перекласти управління TLS сертифікатами на Azure Application Gateway.

Переваги:

  • SSL термінація обробляється на рівні Application Gateway, тому ваш Kubernetes кластер може не потребувати зберігання або управління сертифікатами безпосередньо.
  • Ви все одно можете примусово застосовувати HTTPS між Application Gateway і вашим кластером через SSL налаштування на бекенді (наприклад, інтеграція з Istio).

Недоліки:

  • Вартість може стати надмірною для менших проєктів, оскільки вам потрібна принаймні Standard SKU для Azure Application Gateway для використання AGIC.
  • Налаштування та конфігурація можуть бути дещо складнішими, порівняно з нативними ingress контролерами, такими як Nginx або Traefik.

Kubernetes Cert-Manager з Let’s Encrypt
Ще одна широко поширена стратегія — використовувати Cert-Manager для автоматичного отримання та поновлення сертифікатів з Let’s Encrypt (або інших постачальників).

Переваги:

  • Цілком безкоштовні SSL сертифікати (якщо ви використовуєте Let’s Encrypt).
  • Автоматичне поновлення — Cert-Manager відстежує терміни закінчення дії і оновлює сертифікати до їхнього закінчення.
  • Плавна інтеграція з різноманітними ingress контролерами.

Недоліки:

  • Ви повинні мати правильні DNS записи, які вказують на ваш кластер для проходження HTTP/HTTPS перевірок Let’s Encrypt.
  • Ви вводите ще один компонент, схожий на оператора, до вашого кластера, який потрібно підтримувати і моніторити.

Я детальніше розгляну Cert-Manager з Let’s Encrypt у майбутній статті на Medium.
Залишайтеся на зв'язку!

Висновок

Використовуючи Secrets Store CSI Driver та Workload Identity на AKS, ви:

  • Виключаєте необхідність зберігати сертифікати безпосередньо в Terraform state або Kubernetes Secrets.
  • Автоматизуєте ротацію сертифікатів — коли сертифікат у вашому Key Vault змінюється, ваші Pods можуть автоматично отримувати нові сертифікати з мінімальними зусиллями.
  • Покращуєте безпеку завдяки детальному контролю доступу, надаючи кожному простору імен або робочому навантаженню власну ідентичність в Azure AD.

Цей підхід є незалежним від хмарного провайдера, оскільки драйвер CSI також доступний для AWS, GCP та інших — достатньо замінити посилання на Azure Key Vault на відповідного провайдера.

Щасливого кодування та безпечного монтування сертифікатів! Якщо у вас є питання або ви хочете поділитися своїм досвідом, не соромтеся залишити коментар нижче.

Додаткові матеріали

Дякую за прочитання! Якщо ця стаття була корисною, поділіться нею з друзями, колегами або на своїх соціальних каналах. Маєте поради або досвід з використанням Secrets Store CSI та Workload Identity? Поділіться в коментарях!

Перекладено з: How to Set Up an HTTPS Ingress with the Kubernetes CSI on AKS Using Workload Identity

Leave a Reply

Your email address will not be published. Required fields are marked *