有効な SSL/TLS 証明書は、現代のアプリケーション環境における中核的な要件です。 残念ながら、証明書(または cert)の更新の管理は、アプリケーションを展開するときに後回しにされることがよくあります。 証明書の有効期間は限られており、 DigiCert の証明書の場合は約 13 か月、 Let's Encrypt の証明書の場合は 90 日間です。 安全なアクセスを維持するために、これらの証明書は有効期限が切れる前に更新/再発行する必要があります。 ほとんどの運用チームの作業負荷が膨大であることを考えると、証明書の更新が見落とされ、証明書の有効期限が近づいたり、最悪の場合有効期限が過ぎたりすると、慌てることになりかねません。
こうなる必要はない。 ある程度の計画と準備を行えば、証明書の管理を自動化し、効率化することができます。 ここでは、次の 3 つのテクノロジーを使用した Kubernetes のソリューションについて説明します。
このブログでは、エンドポイントに一意の自動更新および更新された証明書を提供することで、証明書管理を簡素化する方法を学びます。
技術的な詳細に入る前に、いくつかの用語を定義する必要があります。 「TLS 証明書」という用語は、Ingress コントローラーで HTTPS 接続を有効にするために必要な 2 つのコンポーネントを指します。
証明書と秘密鍵は両方ともLet's Encryptによって発行されます。 TLS 証明書の仕組みの詳細については、DigiCert の投稿「TLS/SSL 証明書の仕組み」を参照してください。
Kubernetes では、これら 2 つのコンポーネントはSecretsとして保存されます。 NGINX Ingress Controllerやcert-managerなどの Kubernetes ワークロードは、これらの Secret の書き込みと読み取りを実行できます。また、Kubernetes インストールにアクセスできるユーザーがこれらの Secret を管理することもできます。
cert-managerプロジェクトは、Kubernetes および OpenShift で動作する証明書コントローラーです。 Kubernetes にデプロイされると、cert-manager は Ingress コントローラーに必要な証明書を自動的に発行し、それらが有効で最新であることを確認します。 さらに、証明書の有効期限を追跡し、設定された時間間隔で更新を試みます。 多数の公的および民間の発行者と連携しますが、ここでは Let's Encrypt との統合を紹介します。
Let’s Encrypt を使用すると、すべての証明書管理が自動的に処理されます。 これは非常に便利ですが、次のような問題も生じます。 サービスはどのようにして、問題の完全修飾ドメイン名 (FQDN) を所有していることを保証しますか?
この問題はチャレンジを使用して解決されます。チャレンジでは、特定のドメインの DNS レコードにアクセスできるユーザーだけが提供できる検証要求に回答する必要があります。 チャレンジには次の 2 つの形式があります。
HTTP-01 は、DNS プロバイダーへの直接アクセスを必要としないため、証明書を生成する最も簡単な方法です。 このタイプのチャレンジは常にポート 80 (HTTP) 経由で実行されます。 HTTP-01 チャレンジを使用する場合、cert-manager は Ingress コントローラーを使用してチャレンジ トークンを提供することに注意してください。
Ingress コントローラーは、クラスター外部からトラフィックを取得し、それを内部Pod (1 つ以上のコンテナーのグループ) に負荷分散し、出力トラフィックを管理する Kubernetes 専用のサービスです。 さらに、Ingress コントローラーは Kubernetes API を介して制御され、ポッドが追加、削除、または失敗すると、負荷分散構成を監視および更新します。
Ingress コントローラーの詳細については、次のブログをお読みください。
以下の例では、F5 NGINX によって開発および保守されている NGINX Ingress Controller を使用します。
これらの例では、テストに使用できる稼働中の Kubernetes インストールがあり、そのインストールで外部 IP アドレス (Kubernetes LoadBalancer オブジェクト) を割り当てることができることを前提としています。 さらに、ポート 80 とポート 443 の両方 (HTTP-01 チャレンジを使用している場合)、またはポート 443 のみ (DNS-01 チャレンジを使用している場合) でトラフィックを受信できることを前提としています。 これらの例は Mac OS X を使用して示されていますが、Linux または WSL でも使用できます。
また、A レコードを調整できる DNS プロバイダーと FQDN も必要になります。 HTTP-01 チャレンジを使用している場合は、A レコードを追加する機能 (または A レコードを追加してもらう機能) のみが必要です。 DNS-01 チャレンジを使用している場合は、サポートされている DNS プロバイダーまたはサポートされている Webhook プロバイダーへの API アクセスが必要になります。
最も簡単な方法は、 Helm経由でデプロイすることです。 このデプロイメントでは、Kubernetes Ingress と NGINX 仮想サーバー CRD の両方を使用できます。
$ helm repo add nginx-stable https://helm.nginx.com/stable 「nginx-stable」がリポジトリに追加されました
$ helm repo update チャート リポジトリから最新の情報を取得するまでしばらくお待ちください...
...「nginx-stable」チャート リポジトリからの更新に成功しました
更新が完了しました。⎈Helming をお楽しみください!⎈
$ helm install nginx-kic nginx-stable/nginx-ingress \ --namespace nginx-ingress --set controller.enableCustomResources=true \
--create-namespace --set controller.enableCertManager=true
名前: nginx-kic
最終デプロイ日: 2022 年 9 月 1 日木曜日 15:58:15
名前空間: nginx-ingress
ステータス: デプロイ済み
リビジョン: 1
テストスイート: なし
注記:
NGINX Ingress Controller がインストールされました。
$ kubectl get deploys --namespace nginx-ingress NAME READY UP-TO-DATE AVAILABLE AGE
nginx-kic-nginx-ingress 1/1 1 1 23s
$ kubectl get services --namespace nginx-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-kic-nginx-ingress LoadBalancer 10.128.60.190 www.xxx.yyy.zzz 80:31526/TCP,443:32058/TCP 30s
ここでのプロセスは DNS プロバイダーによって異なります。 この DNS 名は Let's Encrypt サーバーから解決可能である必要があり、機能する前にレコードが伝播するまで待つ必要がある場合があります。 詳細については、SiteGround の記事「DNS 伝播とは何か、なぜ時間がかかるのか?」を参照してください。
選択した FQDN を解決できたら、次のステップに進むことができます。
$ ホスト cert.example.com cert.example.com のアドレスは www.xxx.yyy.zzz です
次のステップは、cert-manager の最新バージョンをデプロイすることです。 ここでも、デプロイメントには Helm を使用します。
$ helm repo add jetstack https://charts.jetstack.io 「jetstack」がリポジトリに追加されました
$ helm repo update チャート リポジトリから最新の情報を取得するまでしばらくお待ちください...
...「nginx-stable」チャート リポジトリからの更新に成功しました
...「jetstack」チャート リポジトリからの更新に成功しました
更新が完了しました。⎈Helming をお楽しみください!⎈
$ helm install cert-manager jetstack/cert-manager \ --namespace cert-manager --create-namespace \ --version v1.9.1 --set installCRDs=true 名前: cert-manager 最終デプロイ日: 2022年9月1日木曜日 16:01:52 名前空間: cert-manager ステータス: デプロイ済み リビジョン: 1 テストスイート: なし 注記: cert-manager v1.9.1 が正常にデプロイされました。
証明書の発行を開始するには、ClusterIssuer または Issuer リソースを設定する必要があります (たとえば、「letsencrypt-staging」発行者を作成します)。
さまざまなタイプの発行者とその設定方法の詳細については、次のドキュメントをご覧ください。
https://cert-manager.io/docs/configuration/
Ingress リソースの証明書を自動的にプロビジョニングするように cert-manager を構成する方法については、`ingress-shim` ドキュメントを参照してください。
https://cert-manager.io/docs/usage/ingress/
$ kubectl get deploys --namespace cert-manager NAME READY UP-TO-DATE AVAILABLE AGE
cert-manager 1/1 1 1 4m30s
cert-manager-cainjector 1/1 1 1 4m30s
cert-manager-webhook 1/1 1 1 4m30s
バックエンドのデプロイメントとサービスを提供するために、NGINX Cafe の例を使用します。 これは、NGINX が提供するドキュメント内で使用される一般的な例です。この一部として Ingress をデプロイすることはありません。
$ git clone https://github.com/nginxinc/kubernetes-ingress.git 'kubernetes-ingress' にクローンしています...
リモート: オブジェクトの列挙: 44979、完了。
リモート: オブジェクトのカウント: 100% (172/172)、完了。
リモート: オブジェクトの圧縮: 100% (108/108)、完了。
リモート: 合計 44979 (差分 87)、再利用 120 (差分 63)、パック再利用 44807
受信オブジェクト: 100% (44979/44979)、60.27 MiB | 27.33 MiB/s、完了。
デルタの解決: 100% (26508/26508)、完了。
$ cd ./kubernetes-ingress/examples/ingress-resources/complete-example
$ kubectl apply -f ./cafe.yaml
deployment.apps/coffee が作成されました
service/coffee-svc が作成されました
deployment.apps/tea が作成されました
service/tea-svc が作成されました
kubectl
get コマンドを使用して、デプロイメントとサービスを検証します。 Pod がREADY
として表示され、サービスがrunning
として表示されていることを確認します。 以下の例は、あなたが探しているものの代表的なサンプルを示しています。 kubernetes
サービスは、NGINX Cafe の例と同じ名前空間 (デフォルト) で実行されるシステム サービスであることに注意してください。$ kubectl get deploys,services --namespace default NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/coffee 2/2 2 2 69s
deployment.apps/tea 3/3 3 68s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/coffee-svc ClusterIP 10.128.154.225 <none> 80/TCP 68s
service/kubernetes ClusterIP 10.128.0.1 <none> 443/TCP 29m
service/tea-svc ClusterIP 10.128.96.145 <なし> 80/TCP 68s
cert-manager 内では、 ClusterIssuer を使用して証明書を発行できます。 これは、任意の名前空間から参照でき、定義された証明書発行機関による任意の証明書要求で使用できるクラスター スコープのオブジェクトです。 この例では、Let's Encrypt 証明書のすべての証明書要求がこの ClusterIssuer によって処理されます。
選択したチャレンジ タイプに合わせて ClusterIssuer をデプロイします。 この投稿の範囲外ですが、ClusterIssuer で複数のリゾルバー (セレクター フィールドに基づいて選択) を指定できる高度な構成オプションがあります。
自動証明書管理環境 (ACME) プロトコルは、ドメイン名を所有しているかどうか、また Let’s Encrypt 証明書を発行できるかどうかを判断するために使用されます。 このチャレンジでは、渡す必要があるパラメータは次のとおりです。
この例では、HTTP-01 チャレンジを使用してドメインの所有権を証明し、証明書を受け取るように ClusterIssuer を設定する方法を示します。
$ cat << EOF | kubectl apply -f apiVersion: cert-manager.io/v1
種類: ClusterIssuer
メタデータ:
名前: prod-issuer
仕様:
acme:
メール: example@example.com
サーバー: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
名前: prod-issuer-account-key
ソルバー:
- http01:
ingress:
クラス: nginx
EOF
clusterissuer.cert-manager.io/prod-issuer が作成されました
$ kubectl get clusterissuer NAME READY AGE
prod-issuer True 34 秒
この例では、DNS-01 チャレンジを使用してドメインの所有権を認証するように ClusterIssuer を設定する方法を示します。 DNS プロバイダーによっては、トークンを保存するために Kubernetes Secret を使用する必要がある可能性があります。 この例ではCloudflare を使用しています。 名前空間の使用に注意してください。 cert-manager 名前空間にデプロイされる cert-manager アプリケーションは、 Secret にアクセスできる必要があります。
この例では、アカウントから作成できるCloudflare API トークンが必要になります。 これを以下の 行に入力する必要があります。 Cloudflare を使用していない場合は、プロバイダーのドキュメントに従う必要があります。
$ cat << EOF | kubectl apply -f apiVersion: v1
kind: シークレット
メタデータ:
名前: cloudflare-api-token-secret
名前空間: cert-manager
タイプ: 不透明
stringData:
api-token: <API トークン>
EOF
$ cat << EOF | kubectl apply -f apiVersion: cert-manager.io/v1
種類: ClusterIssuer
メタデータ:
名前: prod-issuer
仕様:
acme:
メール: example@example.com
サーバー: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
名前: prod-issuer-account-key
ソルバー:
- dns01:
cloudflare:
apiTokenSecretRef:
名前: cloudflare-api-token-secret
キー: api-token
EOF
$ kubectl get clusterissuer NAME READY AGE
prod-issuer True 31m
これが私たちが目指してきたポイント、つまりアプリケーション用の Ingress リソースのデプロイです。 これにより、先ほどデプロイした NGINX Cafe アプリケーションにトラフィックがルーティングされます。
標準の Kubernetes Ingress リソースを使用している場合は、次のデプロイメント YAML を使用して Ingress を構成し、証明書を要求します。
apiバージョン: networking.k8s.io/v1 種類: Ingress
メタデータ:
name: cafe-ingress
アノテーション:
cert-manager.io/cluster-issuer: prod-issuer
acme.cert-manager.io/http01-edit-in-place: "true"
仕様:
ingressClassName: nginx
tls:
- ホスト:
- cert.example.com
secretName: cafe-secret
ルール:
- ホスト: cert.example.com
http:
パス:
- パス: /tea
パスタイプ: プレフィックス
バックエンド:
サービス:
名前: tea-svc
ポート:
番号: 80
- パス: /coffee
パスタイプ: プレフィックス
バックエンド:
サービス:
名前: coffee-svc
ポート:
番号: 80
マニフェストのいくつかの重要な部分を確認する価値があります。
metadata.annotations
の下にあり、 acme.cert-manager.io
/http01-edit-in-place を「true」に設定します。 この値は必須であり、チャレンジの提供方法を調整します。 詳細については、「サポートされている注釈」ドキュメントを参照してください。 これは、マスター/ミニオン設定を使用して処理することもできます。spec.ingressClassName
は、インストールして使用する NGINX Ingress コントローラーを参照します。spec.tls.secret
Kubernetes Secret リソースには、Let's Encrypt によって証明書が発行されたときに返される証明書キーが格納されます。 spec.tls.hosts
とspec.rules.host
には、 cert.example.com
のホスト名が指定されています。 これは、ClusterIssuer が証明書を発行したホスト名です。spec.rules.http
セクションでは、パスと、それらのパス上のリクエストを処理するバックエンド サービスを定義します。 たとえば、 /tea
へのトラフィックはtea-svc
のポート 80 に送信されます。spec.rules.host
とspec.tls.hosts
の値を変更する必要がありますが、構成内のすべてのパラメータを確認する必要があります。 $ kubectl apply -f ./cafe-virtual-server.yaml virtualserver.k8s.nginx.org/cafe が作成されました
$ kubectl get certificates NAME READY SECRET AGE
certificate.cert-manager.io/cafe-secret True cafe-secret 37m
NGINX CRD を使用している場合は、次のデプロイメント YAML を使用して Ingress を構成する必要があります。
apiバージョン: k8s.nginx.org/v1
種類: VirtualServer
metadata:
name: cafe
spec:
host: cert.example.com
tls:
secret: cafe-secret
cert-manager:
cluster-issuer: prod-issuer
uploads:
- name: tea
service: tea-svc
port: 80
- 名前: coffee
サービス: coffee-svc
ポート: 80
ルート:
- パス: /tea
アクション:
pass: tea
- パス: /coffee
アクション:
pass: coffee
もう一度、マニフェストの重要な部分をいくつか確認しておく価値があります。
spec.tls.secret
Kubernetes Secret リソースには、Let's Encrypt によって証明書が発行されたときに返される証明書キーが格納されます。 spec.host
には、ホスト名cert.example.com
が指定されています。 これは、ClusterIssuer が証明書を発行したホスト名です。spec.upstreams の
値は、ポートを含むバックエンド サービスを指します。spec.routes は
、ルートと、そのルートにヒットしたときに実行されるアクションの両方を定義します。spec.host
値の変更が必要になりますが、構成内のすべてのパラメータを確認する必要があります。 $ kubectl apply -f ./cafe-virtual-server.yaml virtualserver.k8s.nginx.org/cafe が作成されました
$ kubectl get VirtualServers NAME STATE HOST IP PORTS AGE
cafe Valid cert.example.com www.xxx.yyy.zzz [80,443] 51m
Kubernetes API を介して証明書を表示できます。これにより、証明書のサイズや関連付けられている秘密キーなど、証明書の詳細が表示されます。
$ kubectl describe secret cafe-secret 名前: cafe-secret 名前空間: default ラベル: <なし> 注釈: cert-manager.io/alt-names : cert.example.com cert-manager.io/certificate-name : cafe-secret cert-manager.io/common-name : cert.example.com cert-manager.io/ip-sans : cert-manager.io/issuer-group : cert-manager.io/issuer-kind : ClusterIssuer cert-manager.io/issuer-name : prod-issuer cert-manager.io/uri-sans : タイプ: kubernetes.io/tlsデータ ==== tls.crt: 5607 バイト tls.key: 1675 バイト
実際の証明書とキーを確認したい場合は、次のコマンドを実行します。 (注記: これは、Kubernetes Secrets の弱点を示しています。 つまり、必要なアクセス権限を持つ人なら誰でも読み取ることができます。
$ kubectl シークレットを取得 cafe-secret -o yaml
証明書をテストします。 ここでは任意の方法を使用できます。 以下の例ではcURLを使用します。 成功は、サーバー名、サーバーの内部アドレス、日付、選択された URI (ルート) (コーヒーまたは紅茶)、およびリクエスト ID を含む、前に示したものと同様のブロックによって示されます。失敗は、HTTP エラー コードの形式 (ほとんどの場合、400 または 301) になります。
$ curl https://cert.example.com/tea
サーバーアドレス: 10.2.0.6:8080
サーバー名: tea-5c457db9-l4pvq
日付: 02/Sep/2022:15:21:06 +0000
URI: /tea
リクエスト ID: d736db9f696423c6212ffc70cd7ebecf
$ curl https://cert.example.com/coffee
サーバー アドレス: 10.2.2.6:8080
サーバー名: coffee-7c86d7d67c-kjddk
日付: 2022年9月2日:15:21:10 +0000
URI: /coffee
リクエストID: 4ea3aa1c87d2f1d80a706dde91f31d54
当初、このアプローチにより証明書の更新を管理する必要がなくなると約束しました。 ただし、その方法についてはまだ説明していません。 なぜ? これは cert-manager のコアの組み込み部分だからです。 この自動プロセスでは、証明書が存在しない、期限が切れている、有効期限が 15 日以内である、またはユーザーが CLI 経由で新しい証明書を要求していることを cert-manager が認識すると、新しい証明書が自動的に要求されます。 それ以上に簡単なことはありません。
NGINX Plus サブスクライバーの場合、唯一の違いは NGINX Ingress Controller をインストールすることです。 これを実現するために上記の Helm コマンドを変更する方法については、NGINX ドキュメントの「Helm のインストール」セクションを参照してください。
これは主に使用事例によって異なります。
HTTP-01 チャレンジ メソッドでは、ポート 80 がインターネットに対して開かれており、Ingress コントローラーの IP アドレスに対して DNS A レコードが適切に構成されている必要があります。 この方法では、A レコードを作成する以外に DNS プロバイダーにアクセスする必要はありません。
DNS-01 チャレンジ メソッドは、ポート 80 をインターネットに公開できない場合に使用でき、cert-manager が DNS プロバイダーへの出力アクセス権を持っていることのみが必要です。 ただし、この方法では DNS プロバイダーの API にアクセスできる必要がありますが、必要なアクセス レベルはプロバイダーによって異なります。
Kubernetes は非常に複雑なため、対象を絞ったトラブルシューティング情報を提供することは困難です。 問題が発生した場合は、 NGINX コミュニティ Slackで質問してください (NGINX Plus サブスクライバーは通常のサポート オプションを使用できます)。
まずは、NGINX App Protect WAF および DoS を備えた NGINX Ingress Controller の30 日間無料トライアルをリクエストし、常時無料の NGINX Service Meshをダウンロードしてください。
「このブログ投稿には、入手できなくなった製品やサポートされなくなった製品が参照されている場合があります。 利用可能な F5 NGINX 製品およびソリューションに関する最新情報については、 NGINX 製品ファミリーをご覧ください。 NGINX は現在 F5 の一部です。 以前の NGINX.com リンクはすべて、F5.com の同様の NGINX コンテンツにリダイレクトされます。"