AzureDNS
cert-manager は Azure DNS で DNS-01 レコードを作成および削除できますが、最初に Azure に認証する必要があります。利用可能な認証方法は 4 つあります。
- AAD ワークロード ID を使用したマネージド ID(推奨)
- AAD Pod ID を使用したマネージド ID(非推奨)
- AKS Kubelet ID を使用したマネージド ID
- サービス プリンシパル
AAD ワークロード ID を使用したマネージド ID
ℹ️ この機能は、cert-manager
>= v1.11.0
で利用可能です。📖 この認証方法のエンドツーエンドの例については、AKS + LoadBalancer + Let's Encrypt チュートリアルを参照してください。
Azure Kubernetes Service (AKS) 上の Azure AD ワークロード ID (プレビュー) を使用すると、cert-manager は Kubernetes ServiceAccount トークンを使用して Azure に認証し、Azure DNS で DNS-01 レコードを管理できます。これは、他の方法よりも安全で管理が容易であるため、推奨される認証方法です。
クラスターの再構成
クラスターでワークロード ID フェデレーション機能を有効にします。Azure AKS クラスターがある場合は、次のコマンドを使用できます。
az aks update \--name ${CLUSTER} \--enable-oidc-issuer \--enable-workload-identity # ℹ️ This option is currently only available when using the aks-preview extension.
ℹ️ Azure AKS を使用していない場合は、他のマネージドクラスターおよび自己管理クラスターに Azure ワークロード ID 拡張機能をインストールできます。
📖
--enable-workload-identity
機能の詳細については、Azure Kubernetes Service (AKS) クラスターにワークロード ID をデプロイおよび構成するを参照してください。
cert-manager の再構成
Azure ワークロード ID Webhook が注目するように、cert-manager コントローラー Pod と ServiceAccount にラベルを付けます。これにより、cert-manager コントローラー Pod に、Azure での認証に使用する Kubernetes ServiceAccount トークンを含む追加ボリュームが作成されます。
Helm を使用して cert-manager をインストールした場合、ラベルは Helm の値を使用して構成できます。
# values.yamlpodLabels:azure.workload.identity/use: "true"serviceAccount:labels:azure.workload.identity/use: "true"
成功すると、cert-manager Pod にいくつかの新しい環境変数が設定され、Azure ワークロード ID ServiceAccount トークンが projected ボリュームとして設定されます。
kubectl describe pod -n cert-manager -l app.kubernetes.io/component=controller
Containers:...cert-manager-controller:...Environment:...AZURE_CLIENT_ID:AZURE_TENANT_ID: f99bd6a4-665c-41cf-aff1-87a89d5c62d4AZURE_FEDERATED_TOKEN_FILE: /var/run/secrets/azure/tokens/azure-identity-tokenAZURE_AUTHORITY_HOST: https://login.microsoftonline.com/Mounts:/var/run/secrets/azure/tokens from azure-identity-token (ro)Volumes:...azure-identity-token:Type: Projected (a volume that contains injected data from multiple sources)TokenExpirationSeconds: 3600
📖 Kubernetes 用 Azure AD ワークロード ID における Mutating Admission Webhook の役割についてお読みください。
マネージド ID の作成
cert-manager が Azure API を使用して Azure DNS ゾーン内のレコードを操作するには、Azure アカウントが必要です。使用する最適なアカウントの種類は「マネージド ID」と呼ばれます。このアカウントにはパスワードや API キーは付属しておらず、人間ではなくマシンで使用するように設計されています。
マネージド ID 名を選択し、マネージド ID を作成します。
export IDENTITY_NAME=cert-manageraz identity create --name "${IDENTITY_NAME}"
DNS ゾーンのレコードを変更する権限を付与します。
export IDENTITY_CLIENT_ID=$(az identity show --name "${IDENTITY_NAME}" --query 'clientId' -o tsv)az role assignment create \--role "DNS Zone Contributor" \--assignee IDENTITY_CLIENT_ID \--scope $(az network dns zone show --name $DOMAIN_NAME -o tsv --query id)
📖 マネージド ID とその用途の概要については、Azure リソースのマネージド ID とは何ですか? をお読みください。
📖 「DNS ゾーン共同作成者」ロールの詳細については、Azure の組み込みロールをお読みください。
📖
az identity
コマンドの詳細をお読みください。
フェデレーション ID の追加
次に、以前に作成したマネージド ID にフェデレーション ID を関連付けます。cert-manager は、有効期間の短い Kubernetes ServiceAccount トークンを使用して Azure に認証し、前の手順で作成したマネージド ID を偽装できるようになります。
export SERVICE_ACCOUNT_NAME=cert-manager # ℹ️ This is the default Kubernetes ServiceAccount used by the cert-manager controller.export SERVICE_ACCOUNT_NAMESPACE=cert-manager # ℹ️ This is the default namespace for cert-manager.export SERVICE_ACCOUNT_ISSUER=$(az aks show --resource-group $AZURE_DEFAULTS_GROUP --name $CLUSTER --query "oidcIssuerProfile.issuerUrl" -o tsv)az identity federated-credential create \--name "cert-manager" \--identity-name "${IDENTITY_NAME}" \--issuer "${SERVICE_ACCOUNT_ISSUER}" \--subject "system:serviceaccount:${SERVICE_ACCOUNT_NAMESPACE}:${SERVICE_ACCOUNT_NAME}"
--subject
: Kubernetes ServiceAccount の識別名です。--issuer
: Azure が JWT 署名証明書およびその他のメタデータをダウンロードする URL です。
📖 Microsoft ID プラットフォームのドキュメントの ワークロード ID フェデレーションについてお読みください。
📖
az identity federated-credential
コマンドの詳細をお読みください。
ClusterIssuer の構成
例:
apiVersion: cert-manager.io/v1kind: ClusterIssuermetadata:name: letsencrypt-stagingspec:acme:server: https://acme-staging-v02.api.letsencrypt.org/directoryemail: $EMAIL_ADDRESSprivateKeySecretRef:name: letsencrypt-stagingsolvers:- dns01:azureDNS:hostedZoneName: $AZURE_ZONE_NAMEresourceGroupName: $AZURE_RESOURCE_GROUPsubscriptionID: $AZURE_SUBSCRIPTION_IDenvironment: AzurePublicCloudmanagedIdentity:clientID: $IDENTITY_CLIENT_ID
次の変数を入力する必要があります。
# An email address to which Let's Encrypt will send renewal reminders.export EMAIL_ADDRESS=<email-address># The Azure DNS zone in which the DNS-01 records will be created and deleted.export AZURE_ZONE_NAME=<domain.example.com># The Azure resource group containing the DNS zone.export AZURE_RESOURCE_GROUP=<azure-resource-group># The Azure billing account name and ID for the DNS zone.export AZURE_SUBSCRIPTION=<azure-billing-account-name>export AZURE_SUBSCRIPTION_ID=$(az account show --name $AZURE_SUBSCRIPTION --query 'id' -o tsv)
⚠️ ClusterIssuer および Issuer リソースでの「アンビエント認証情報」の使用
この認証方法は、cert-manager が「アンビエント認証情報」と呼ぶものの例です。アンビエント認証情報は ClusterIssuer リソースに対してデフォルトで有効になっていますが、Issuer リソースに対してはデフォルトで無効になっています。これは、Issuer リソースを作成する権限を持つ特権のないユーザーが、cert-manager がたまたまアクセスできる認証情報を使用して証明書を発行することを防ぐためです。ClusterIssuer リソースはクラスター スコープ(名前空間ではない)であり、プラットフォーム管理者のみがそれらを作成する権限を付与する必要があります。
この認証メカニズムを使用しており、アンビエント認証情報が有効になっていない場合は、このエラーが表示されます。
error instantiating azuredns challenge solver: ClientID is not set but neither --cluster-issuer-ambient-credentials nor --issuer-ambient-credentials are set.
⚠️
Issuer
リソースでこの認証メカニズムを有効にする(ただし推奨されない)には、cert-manager コントローラーの--issuer-ambient-credentials
フラグを true に設定する必要があります。
AAD Pod ID を使用したマネージド ID
⚠️ Azure Kubernetes Service のオープンソース Azure AD ポッドマネージド ID (プレビュー) は、2022 年 10 月 24 日で非推奨になりました。代わりにワークロード ID を使用してください。
AAD Pod ID を使用すると、マネージド ID をポッドに割り当てることができます。これにより、必要な DNS レコードを作成するために、明示的な認証情報をクラスターに追加する必要がなくなります。
注: ポッド ID を使用する場合、単一のポッドに複数の ID を割り当てることが許可されていますが、現在、cert-manager は、使用する ID を識別できないため、これをサポートしていません。
まず、DNS ゾーンへの投稿を許可するアクセス権を持つ ID を作成する必要があります。
azure-cli
およびjq
を使用した作成例
# Choose a unique Identity name and existing resource group to create identity in.IDENTITY=$(az identity create --name $IDENTITY_NAME --resource-group $IDENTITY_GROUP --output json)# Gets principalId to use for role assignmentPRINCIPAL_ID=$(echo $IDENTITY | jq -r '.principalId')# Used for identity bindingCLIENT_ID=$(echo $IDENTITY | jq -r '.clientId')RESOURCE_ID=$(echo $IDENTITY | jq -r '.id')# Get existing DNS Zone IdZONE_ID=$(az network dns zone show --name $ZONE_NAME --resource-group $ZONE_GROUP --query "id" -o tsv)# Create role assignmentaz role assignment create --role "DNS Zone Contributor" --assignee $PRINCIPAL_ID --scope $ZONE_ID
- Terraform を使用した作成例
variable resource_group_name {}variable location {}variable dns_zone_id {}# Creates Identityresource "azurerm_user_assigned_identity" "dns_identity" {name = "cert-manager-dns01"resource_group_name = var.resource_group_namelocation = var.location}# Creates Role Assignmentresource "azurerm_role_assignment" "dns_contributor" {scope = var.dns_zone_idrole_definition_name = "DNS Zone Contributor"principal_id = azurerm_user_assigned_identity.dns_identity.principal_id}# Client Id Used for identity bindingoutput "identity_client_id" {value = azurerm_user_assigned_identity.dns_identity.client_id}# Resource Id Used for identity bindingoutput "identity_resource_id" {value = azurerm_user_assigned_identity.dns_identity.id}
次に、ウォークスルーを使用して AAD Pod Identity がインストールされていることを確認する必要があります。これにより、ID の割り当てに必要な CRD とデプロイメントがインストールされます。
次に、次のマニフェストを例として使用して、ID リソースとバインディングを作成できます。
apiVersion: "aadpodidentity.k8s.io/v1"kind: AzureIdentitymetadata:annotations:# recommended to use namespaced identites https://azure.github.io/aad-pod-identity/docs/configure/match_pods_in_namespace/aadpodidentity.k8s.io/Behavior: namespacedname: certman-identitynamespace: cert-manager # change to your preferred namespacespec:type: 0 # MSIresourceID: <Identity_Id> # Resource Id From Previous stepclientID: <Client_Id> # Client Id from previous step---apiVersion: "aadpodidentity.k8s.io/v1"kind: AzureIdentityBindingmetadata:name: certman-id-bindingnamespace: cert-manager # change to your preferred namespacespec:azureIdentity: certman-identityselector: certman-label # This is the label that needs to be set on cert-manager pods
次に、ポッド ID バインディングを使用するための関連ラベルが cert-manager ポッドにあることを確認する必要があります。これは、デプロイメントを編集し、次のものを .spec.template.metadata.labels
フィールドに追加することで実行できます。
spec:template:metadata:labels:aadpodidbinding: certman-label # must match selector in AzureIdentityBinding
または、helm 値 podLabels
を使用します。
podLabels:aadpodidbinding: certman-label
最後に、証明書発行者を作成する場合、DNS ゾーンの hostedZoneName
、resourceGroupName
、および subscriptionID
フィールドのみを指定する必要があります。以下の例を参照してください。
apiVersion: cert-manager.io/v1kind: Issuermetadata:name: example-issuerspec:acme:...solvers:- dns01:azureDNS:subscriptionID: AZURE_SUBSCRIPTION_IDresourceGroupName: AZURE_DNS_ZONE_RESOURCE_GROUPhostedZoneName: AZURE_DNS_ZONE# Azure Cloud Environment, default to AzurePublicCloudenvironment: AzurePublicCloud
この認証メカニズムは、cert-manager が「アンビエント認証情報」とみなすものです。アンビエント認証情報の使用は、cert-manager の Issuer
に対してデフォルトで無効になっています。これは、発行者を作成する権限を持つ特権のないユーザーが、cert-manager がたまたまアクセスできる認証情報を使用して証明書を発行できないようにするためです。Issuer
でこの認証メカニズムを有効にするには、cert-manager コントローラーで --issuer-ambient-credentials
フラグを true に設定する必要があります。(対応する --cluster-issuer-ambient-credentials
フラグは、デフォルトで true
に設定されています)。
この認証メカニズムを使用しており、アンビエント認証情報が有効になっていない場合は、このエラーが表示されます。
error instantiating azuredns challenge solver: ClientID is not set but neither --cluster-issuer-ambient-credentials nor --issuer-ambient-credentials are set.
これらは、Azure マネージド ID を有効にするために必要です。
AKS Kubelet IDを使用したマネージドID
AzureでAKSクラスターを作成する際、kubeletに割り当てられるマネージドIDを使用するオプションがあります。このIDはAKSクラスター内の基盤となるノードプールに割り当てられ、cert-managerポッドがAzure Active Directoryへの認証に使用できます。
このアプローチにはいくつかの注意点があります。主なものは以下のとおりです。
- このIDに付与されたすべての権限は、Kubernetesクラスター内で実行されているすべてのコンテナからもアクセス可能になります。
Kube Dashboard
、Virtual Node
、HTTP Application Routing
などのAKS拡張機能(詳細なリストはこちら)を使用すると、ノードプールに割り当てられる追加のIDが作成されます。ノードプールに複数のIDが割り当てられている場合は、適切なIDを選択するためにclientID
またはresourceID
を指定する必要があります。
これを設定するには、まずAKSクラスターをクエリして、kubeletが使用しているIDを取得する必要があります。その後、これをDNSゾーンで適切な権限を作成するために使用できます。
azure-cli
を使用したコマンドの例
# Get AKS Kubelet IdentityPRINCIPAL_ID=$(az aks show -n $CLUSTERNAME -g $CLUSTER_GROUP --query "identityProfile.kubeletidentity.objectId" -o tsv)# Get existing DNS Zone IdZONE_ID=$(az network dns zone show --name $ZONE_NAME --resource-group $ZONE_GROUP --query "id" -o tsv)# Create role assignmentaz role assignment create --role "DNS Zone Contributor" --assignee $PRINCIPAL_ID --scope $ZONE_ID
- Terraformの例
variable dns_zone_id {}# Creating the AKS cluster, abbreviated.resource "azurerm_kubernetes_cluster" "cluster" {...# Creates Identity associated to kubeletidentity {type = "SystemAssigned"}...}resource "azurerm_role_assignment" "dns_contributor" {scope = var.dns_zone_idrole_definition_name = "DNS Zone Contributor"principal_id = azurerm_kubernetes_cluster.cluster.kubelet_identity[0].object_idskip_service_principal_aad_check = true # Allows skipping propagation of identity to ensure assignment succeeds.}
次に、cert-manager issuerを作成する際に、DNSゾーンのhostedZoneName
、resourceGroupName
、subscriptionID
フィールドを指定する必要があります。
また、複数のマネージドIDがノードプールに割り当てられている場合は、managedIdentity.clientID
またはmanagedIdentity.resourceID
を指定する必要があります。
managedIdentity.clientID
の値は、次のコマンドを実行して取得できます。
az aks show -n $CLUSTERNAME -g $CLUSTER_GROUP --query "identityProfile.kubeletidentity.clientId" -o tsv
以下の例
apiVersion: cert-manager.io/v1kind: Issuermetadata:name: example-issuerspec:acme:...solvers:- dns01:azureDNS:subscriptionID: AZURE_SUBSCRIPTION_IDresourceGroupName: AZURE_DNS_ZONE_RESOURCE_GROUPhostedZoneName: AZURE_DNS_ZONE# Azure Cloud Environment, default to AzurePublicCloudenvironment: AzurePublicCloud# optional, only required if node pools have more than 1 managed identity assignedmanagedIdentity:# client id of the node pool managed identity (can not be set at the same time as resourceID)clientID: YOUR_MANAGED_IDENTITY_CLIENT_ID# resource id of the managed identity (can not be set at the same time as clientID)# resourceID: YOUR_MANAGED_IDENTITY_RESOURCE_ID
サービスプリンシパル
KubernetesクラスターのAzureDNS DNS01チャレンジを設定するには、Azureにサービスプリンシパルを作成する必要があります。
サービスプリンシパルを作成するには、次のスクリプトを使用できます(azure-cli
とjq
が必要です)。
# Choose a name for the service principal that contacts azure DNS to present# the challenge.$ AZURE_CERT_MANAGER_NEW_SP_NAME=NEW_SERVICE_PRINCIPAL_NAME# This is the name of the resource group that you have your dns zone in.$ AZURE_DNS_ZONE_RESOURCE_GROUP=AZURE_DNS_ZONE_RESOURCE_GROUP# The DNS zone name. It should be something like domain.com or sub.domain.com.$ AZURE_DNS_ZONE=AZURE_DNS_ZONE$ DNS_SP=$(az ad sp create-for-rbac --name $AZURE_CERT_MANAGER_NEW_SP_NAME --output json)$ AZURE_CERT_MANAGER_SP_APP_ID=$(echo $DNS_SP | jq -r '.appId')$ AZURE_CERT_MANAGER_SP_PASSWORD=$(echo $DNS_SP | jq -r '.password')$ AZURE_TENANT_ID=$(echo $DNS_SP | jq -r '.tenant')$ AZURE_SUBSCRIPTION_ID=$(az account show --output json | jq -r '.id')
セキュリティ上の理由から、RBACを利用して、Azureのリソースへのアクセス制御を適切に維持することが適切です。このチュートリアルで生成されるサービスプリンシパルは、指定された特定のリソースグループのDNSゾーンにのみ、きめ細かいアクセス権を持っています。ゾーンに_acme_challenge TXTレコードを読み取り/書き込みできるようにするために、この権限が必要です。
サービスプリンシパルの権限を下げてください。
$ az role assignment delete --assignee $AZURE_CERT_MANAGER_SP_APP_ID --role Contributor
DNSゾーンへのアクセスを許可します。
$ DNS_ID=$(az network dns zone show --name $AZURE_DNS_ZONE --resource-group $AZURE_DNS_ZONE_RESOURCE_GROUP --query "id" --output tsv)$ az role assignment create --assignee $AZURE_CERT_MANAGER_SP_APP_ID --role "DNS Zone Contributor" --scope $DNS_ID
権限を確認します。次のコマンドの結果として、「DNSゾーン共同作成者」ロールを持つオブジェクトがpermissions配列に1つだけ表示されることを期待します。
$ az role assignment list --all --assignee $AZURE_CERT_MANAGER_SP_APP_ID
Azure DNSにチャレンジを提示するために、サービスプリンシパルのパスワードを含むシークレットをKubernetes上に作成する必要があります。次のコマンドでシークレットを作成できます。
$ kubectl create secret generic azuredns-config --from-literal=client-secret=$AZURE_CERT_MANAGER_SP_PASSWORD
issuerを構成するための変数を取得します。
$ echo "AZURE_CERT_MANAGER_SP_APP_ID: $AZURE_CERT_MANAGER_SP_APP_ID"$ echo "AZURE_CERT_MANAGER_SP_PASSWORD: $AZURE_CERT_MANAGER_SP_PASSWORD"$ echo "AZURE_SUBSCRIPTION_ID: $AZURE_SUBSCRIPTION_ID"$ echo "AZURE_TENANT_ID: $AZURE_TENANT_ID"$ echo "AZURE_DNS_ZONE: $AZURE_DNS_ZONE"$ echo "AZURE_DNS_ZONE_RESOURCE_GROUP: $AZURE_DNS_ZONE_RESOURCE_GROUP"
issuerを構成するには、大文字の変数を前のスクリプトの値に置き換えます。サブスクリプションIDはAzureポータルから取得できます。
apiVersion: cert-manager.io/v1kind: Issuermetadata:name: example-issuerspec:acme:...solvers:- dns01:azureDNS:clientID: AZURE_CERT_MANAGER_SP_APP_IDclientSecretSecretRef:# The following is the secret we created in Kubernetes. Issuer will use this to present challenge to Azure DNS.name: azuredns-configkey: client-secretsubscriptionID: AZURE_SUBSCRIPTION_IDtenantID: AZURE_TENANT_IDresourceGroupName: AZURE_DNS_ZONE_RESOURCE_GROUPhostedZoneName: AZURE_DNS_ZONE# Azure Cloud Environment, default to AzurePublicCloudenvironment: AzurePublicCloud