🦭

Azure DNSとcert-managerを用いたLet's Encryptのワイルドカード証明書の発行と利用

2023/06/24に公開

Azure Kubernetes Service(AKS)とLet's Encryptを使ったワイルドカードTLS証明書の設定に苦労したのでメモをシェアしたいと思います。
この記事が同じ課題に直面している方々の助けとなれば幸いです。
ワイルドカードを使わない場合は、HTTP-01チャレンジを使用して証明書を発行できます。その手順は、Azureの公式ドキュメントに記載されているので、そちらを参照してください。

AKSクラスタを作成

まず始めに、Terraformを使用してAKSクラスタを作成します。
ここで特筆すべき重要な点は、サービスプリンシパルをDNSゾーンのコントリビューターとして追加することです。

resource "azurerm_resource_group" "example" {
  name     = "example-resources"
  location = "japaneast"
}

resource "azuread_application" "example" {
  name = "example"
}

resource "azuread_service_principal" "example" {
  application_id = azuread_application.example.application_id
}

resource "azuread_service_principal_password" "example" {
  service_principal_id = azuread_service_principal.example.id
  end_date             = "2099-01-01T01:02:03Z"
}

resource "azurerm_kubernetes_cluster" "example" {
  name                = "example-aks1"
  location            = azurerm_resource_group.example.location
  resource_group_name = azurerm_resource_group.example.name
  dns_prefix          = "exampleaks1"

  default_node_pool {
    name       = "default"
    node_count = 2
    vm_size    = "Standard_D2_v2"
  }

  identity {
    type = "SystemAssigned"
  }
}

resource "azurerm_dns_zone" "example" {
  # DNSゾーンを指定
  name                = "example.com"
  resource_group_name = azurerm_resource_group.example.name
}

# ※重要
resource "azurerm_role_assignment" "role_assignment" {
  scope                = azurerm_dns_zone.example.id
  role_definition_name = "DNS Zone Contributor"
  principal_id         = azuread_service_principal.example.id
}

data "azurerm_client_config" "config" {}

output "service_principal_info" {
  value = {
    client_id = azuread_application.example.application_id
    tenant_id = data.azurerm_client_config.config.tenant_id
    password  = azuread_service_principal_password.example.value
  }
  description = "The Service Principal information"
  sensitive   = true
}

Terraformの適用が完了した後、以下のコマンドを実行してサービスプリンシパルの情報を出力します。この情報は後のステップで必要になります。

terraform output service_principal_info

cert-managerをインストール

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.12.2/cert-manager.yaml

Azure DNS用のDNS-01チャレンジの設定

証明書を利用するnamespaceを用意(オプショナル)

まず、環境変数を設定します。以下のコマンドは、BashやZshなどのPOSIX互換シェルで動作します。もし異なるシェル(Fishなど)を使用している場合は、そのシェルの文法に従って環境変数を設定してください。
また、namespaceはdefaultでも構いませんが、後続のコマンドで$NAMESPACEが参照されるので注意してください。

export NAMESPACE=demo

次に、上で設定した$NAMESPACEを使ってKubernetesのnamespaceを作成します。

kubectl create namespace $NAMESPACE

サービスプリンシパルのパスワードを含んだSecretを用意

terraform outputで出力したパスワードをbase64エンコードします。

echo <the-password> | openssl base64

上記で出力したパスワードを使用してSecretを作成します。
ここで注意点として、namespaceをcert-managerに設定することが重要です。

secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: secret-azuredns-config
  namespace: cert-manager
data:
  password: <the-password-in-base64>

適用します。

kubectl apply -f secret.yaml

ドメインの所有を証明する設定を用意

以下を埋めて2つのファイル(ClusterIssuerCertificate)を作成します。

  • <your-email>
  • <service-principal-client-id>
  • <your-subscription-id>
  • <your-resource-group>
  • <your-dns-zone-name>
  • <your-tenant-id>
cluster-issuer.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: <your-email>
    privateKeySecretRef:
      name: letsencrypt
    solvers:
      - dns01:
          azureDNS:
            clientID: <service-principal-client-id>
            clientSecretSecretRef:
              name: secret-azuredns-config
              key: password
            subscriptionID: <your-subscription-id>
            resourceGroupName: <your-resource-group>
            hostedZoneName: <your-dns-zone-name>
            tenantID: <your-tenant-id>
certificate.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: wildcard
spec:
  # 指定した名前でSecretが作成される
  secretName: wildcard
  issuerRef:
    name: letsencrypt
    kind: ClusterIssuer
  dnsNames:
    - "*.<your-domain>"
  usages:
    - server auth

適用します。

kubectl apply -f cluster-issuer.yaml --namespace $NAMESPACE
kubectl apply -f certificate.yaml --namespace $NAMESPACE

証明書オブジェクトが作成されたことを確認

ここまでの手順が完了すると、Azure DNSゾーンにTXTレコードが一時的に作成され証明書が発行されます。タイミングが合えば以下のようなレコードが作成されていることが確認できます。
Azure DNS

以下のREADYTrueになっていれば証明書の発行が完了しているはずです。

kubectl get certificate --namespace $NAMESPACE
NAME       READY   SECRET     AGE
wildcard   True    wildcard   5m1s

TLSの Secret が作成されていることも確認します。

kubectl get secret --namespace $NAMESPACE
NAME       TYPE                 DATA   AGE
wildcard   kubernetes.io/tls    2      5m13s

証明書を利用する

最後にNGINX Ingress Controllerで証明書を利用するサンプルを紹介します。

ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-ingress
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - "*.<your-domain>"
      secretName: wildcard
  rules:
    - host: "*.<your-domain>"
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: nginx-default
                port:
                  number: 80

以上で終了になります。それではみなさん良いAzureライフを!

参考文献

Discussion