新機能: プロジェクトの最新情報については、TwitterMastodonでご確認ください。

NGINX イングレスの保護

このチュートリアルでは、NGINX を使用してクラスタにイングレスをインストールおよび保護する方法について詳しく説明します。

ステップ 1 - Helm のインストール

Helm が既にインストールされている場合は、このセクションをスキップしてください。

cert-manager をインストールする最も簡単な方法は、Kubernetes リソースのテンプレート化とデプロイメントツールであるHelm を使用することです。

まず、Helm のインストール手順に従って、Helm クライアントがインストールされていることを確認してください。

例:macOS の場合

brew install kubernetes-helm

ステップ 2 - NGINX Ingress コントローラーのデプロイ

Kubernetes Ingress コントローラー は、クラスタ内で実行されているソフトウェアへの HTTP および HTTPS トラフィックのアクセスポイントとして設計されています。ingress-nginx-controller は、クラウドプロバイダーのロードバランサーによってサポートされている HTTP プロキシサービスを提供することでこれを実現します。

ingress-nginx の詳細と動作については、ingress-nginx のドキュメントを参照してください。

ingress-nginx の最新の Helm リポジトリを追加します。

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx

最新のチャートで Helm リポジトリを更新します。

$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Skip local chart repository
...Successfully got an update from the "stable" chart repository
...Successfully got an update from the "ingress-nginx" chart repository
...Successfully got an update from the "coreos" chart repository
Update Complete. ⎈ Happy Helming!

helm を使用して NGINX Ingress コントローラーをインストールします。

$ helm install quickstart ingress-nginx/ingress-nginx
NAME: quickstart
... lots of output ...

クラウドプロバイダーがパブリックIPアドレスを提供してリンクするには、1~2分かかります。完了したら、kubectl コマンドを使用して外部IPアドレスを確認できます。

$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 13m
quickstart-ingress-nginx-controller LoadBalancer 10.0.114.241 <pending> 80:31635/TCP,443:30062/TCP 8m16s
quickstart-ingress-nginx-controller-admission ClusterIP 10.0.188.24 <none> 443/TCP 8m16s

このコマンドは、クラスタ内のすべてのサービス(default 名前空間内)と、それらが持つ外部IPアドレスを表示します。コントローラーを最初に作成したとき、クラウドプロバイダーはまだLoadBalancer を介してIPアドレスを割り当てていません。割り当てられるまでは、サービスの外部IPアドレスは<pending> と表示されます。

クラウドプロバイダーには、Ingress コントローラーを作成する前に IP アドレスを予約し、プールから IP アドレスを割り当てるのではなく、その IP アドレスを使用するオプションがある場合があります。その方法については、クラウドプロバイダーのドキュメントを参照してください。

ステップ 3 - DNS 名の割り当て

Ingress コントローラーに割り当てられた外部IPアドレスは、すべての受信トラフィックをルーティングする必要があるIPアドレスです。これを有効にするには、管理するDNSゾーン(例:www.example.com)に追加します。

このクイックスタートでは、IPアドレスにDNSエントリを割り当てる方法を理解していることを前提としています。

ステップ 4 - サンプルサービスのデプロイ

サービスには独自のチャートがある場合や、マニフェストを使用して直接デプロイする場合があります。このクイックスタートでは、マニフェストを使用してサンプルサービスを作成および公開します。サンプルサービスは、デモアプリケーションであるkuard を使用します。

クイックスタートの例では、サンプルに3つのマニフェストを使用します。最初の2つは、サンプルデプロイメントとそれに関連付けられたサービスです。

apiVersion: apps/v1
kind: Deployment
metadata:
name: kuard
spec:
selector:
matchLabels:
app: kuard
replicas: 1
template:
metadata:
labels:
app: kuard
spec:
containers:
- image: gcr.io/kuar-demo/kuard-amd64:1
imagePullPolicy: Always
name: kuard
ports:
- containerPort: 8080
apiVersion: v1
kind: Service
metadata:
name: kuard
spec:
ports:
- port: 80
targetPort: 8080
protocol: TCP
selector:
app: kuard

これらのファイルをローカルにダウンロードして参照するか、このドキュメントのGitHubソースリポジトリから参照できます。チュートリアルフファイルからGitHubから直接サンプルサービスをインストールするには、次の手順を実行します。

kubectl apply -f https://raw.githubusercontent.com/cert-manager/website/master/content/docs/tutorials/acme/example/deployment.yaml
# expected output: deployment.extensions "kuard" created
kubectl apply -f https://raw.githubusercontent.com/cert-manager/website/master/content/docs/tutorials/acme/example/service.yaml
# expected output: service "kuard" created

Ingress リソース は、Kubernetes がこのサンプルサービスをクラスタ外部に公開するために使用するものです。この例を完了するには、所有または管理するドメインを反映するように、サンプルマニフェストをダウンロードして変更する必要があります。

開始できるサンプルIngressは次のとおりです。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kuard
annotations: {}
#cert-manager.io/issuer: "letsencrypt-staging"
spec:
ingressClassName: nginx
tls:
- hosts:
- example.example.com
secretName: quickstart-example-tls
rules:
- host: example.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kuard
port:
number: 80

GitHub からサンプルマニフェストをダウンロードし、編集して、以下のコマンドでマニフェストを Kubernetes に送信できます。エディターでファイルを編集し、保存したら

kubectl create --edit -f https://raw.githubusercontent.com/cert-manager/website/master/content/docs/tutorials/acme/example/ingress.yaml
# expected output: ingress.networking.k8s.io/kuard created

注:上記で示したIngressの例には、host 定義が含まれています。ingress-nginx-controller は、要求されたホスト名がIngress内の定義と一致する場合にトラフィックをルーティングします。host 定義なしでIngressをデプロイすることはできますが、完全修飾ドメイン名を期待するTLS証明書では使用できません。

デプロイ後、kubectl get ingress コマンドを使用して、Ingressのステータスを確認できます。

NAME HOSTS ADDRESS PORTS AGE
kuard * 80, 443 17s

サービスプロバイダーによっては、Ingressが完全に作成されるまでに数分かかります。作成されて配置されると、Ingressにもアドレスが表示されます。

NAME HOSTS ADDRESS PORTS AGE
kuard * 203.0.113.2 80 9m

注:IngressのIPアドレスは、ingress-nginx-controller のIPアドレスと一致しない場合があります。これは問題なく、Kubernetesクラスタをホストするサービスプロバイダーの特殊性/実装の詳細です。ingress-nginx-controller をクラウドプロバイダー固有のIngressバックエンドではなく使用しているため、サービスのプライマリアクセスポイントとしてquickstart-ingress-nginx-controller LoadBalancer リソースに定義および割り当てられたIPアドレスを使用します。

上記で追加したドメイン名(例:http://www.example.com)でサービスにアクセスできることを確認してください。最も簡単な方法は、ブラウザを開いてDNSで設定した名前を入力し、Ingressを追加するだけです。

curl のようなコマンドラインツールを使用して、Ingressを確認することもできます。

$ curl -kivL -H 'Host: www.example.com' 'http://203.0.113.2'

このcurlコマンドのオプションは、詳細な出力を提供し、リダイレクトに従い、出力にTLSヘッダーを表示し、安全でない証明書でエラーを出力しません。ingress-nginx-controller を使用すると、サービスはTLS証明書を使用して利用可能になりますが、ingress-nginx-controller からデフォルトで提供される自己署名証明書を使用します。ブラウザには、これは無効な証明書であるという警告が表示されます。これは、まだcert-managerを使用してサイトの完全な信頼できる証明書を取得していないため、予想され、正常です。

警告:Ingressがインターネット上で利用可能であり、正しく応答していることを確認することが重要です。このクイックスタートの例では、Let's Encryptを使用して証明書を提供していますが、サービスが利用可能であることと、証明書を発行するプロセス中にその検証を使用して、ドメインの要求がドメインを十分に制御できる人に属していることを証明しています。

ステップ 5 - cert-manager のデプロイ

証明書を要求して検証の課題に対応するために、Kubernetesで作業を行うためにcert-managerをインストールする必要があります。HelmまたはプレーンKubernetesマニフェストを使用してcert-managerをインストールできます。

既にHelmをインストールしているので、Helmを使用したいと仮定します。Helmガイドに従ってください。その他の方法については、cert-managerのインストールドキュメントを参照してください。

cert-managerは主に、操作方法と状態の保存方法を構成および制御するために、CRD(Custom Resource Definitions) として知られる2種類の異なるカスタムKubernetesリソースを使用します。これらのリソースは、IssuerとCertificateです。

Issuer

Issuerは、cert-managerがTLS証明書を要求する方法を定義します。IssuerはKubernetesの単一の名前空間に固有のものですが、クラスタ全体のバージョンのClusterIssuer もあります。

Issuerが、作成する証明書と同じ名前空間に作成されていることを確認してください。kubectl create コマンドに-n my-namespace を追加する必要がある場合があります。

別のオプションとして、IssuersClusterIssuers に置き換えることができます。ClusterIssuer リソースは、クラスタ内のすべてのIngressリソースに適用されます。ClusterIssuer を使用する場合は、Ingress アノテーション cert-manager.io/issuercert-manager.io/cluster-issuer に更新してください。

Issuerに問題がある場合は、「ACME証明書の発行に関するトラブルシューティング」ガイドを参照してください。

IssuersClusterIssuers の違い(それぞれを使用する状況を含む)に関する詳細情報は、「Issuerの概念」を参照してください。

証明書

証明書リソースを使用すると、要求する証明書の詳細を指定できます。発行方法を定義するために、Issuerを参照します。

詳細については、「証明書の概念」を参照してください。

ステップ6 - Let's Encrypt Issuerの設定

この例では、Let's Encryptのためにステージングと本番環境の2つのIssuerを設定します。

Let's Encryptの本番Issuerは非常に厳しいレート制限があります。実験や学習中に、これらの制限に簡単に達してしまう可能性があります。そのため、まずLet's EncryptのステージングIssuerから開始し、正常に動作することを確認してから本番Issuerに切り替えます。

ステージングIssuerからの信頼されていない証明書に関する警告が表示されますが、これは想定どおりです。

この定義をローカルに作成し、メールアドレスを自分のアドレスに更新してください。このメールアドレスはLet's Encryptによって必要とされ、証明書の期限切れや更新に関する通知に使用されます。

apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: letsencrypt-staging
spec:
acme:
# The ACME server URL
server: https://acme-staging-v02.api.letsencrypt.org/directory
# Email address used for ACME registration
email: user@example.com
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: letsencrypt-staging
# Enable the HTTP-01 challenge provider
solvers:
- http01:
ingress:
ingressClassName: nginx

編集後、カスタムリソースを適用します。

kubectl create --edit -f https://raw.githubusercontent.com/cert-manager/website/master/content/docs/tutorials/acme/example/staging-issuer.yaml
# expected output: issuer.cert-manager.io "letsencrypt-staging" created

本番Issuerも作成してデプロイします。ステージングIssuerと同様に、この例を更新して自分のメールアドレスを追加する必要があります。

apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: letsencrypt-prod
spec:
acme:
# The ACME server URL
server: https://acme-v02.api.letsencrypt.org/directory
# Email address used for ACME registration
email: user@example.com
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: letsencrypt-prod
# Enable the HTTP-01 challenge provider
solvers:
- http01:
ingress:
ingressClassName: nginx
kubectl create --edit -f https://raw.githubusercontent.com/cert-manager/website/master/content/docs/tutorials/acme/example/production-issuer.yaml
# expected output: issuer.cert-manager.io "letsencrypt-prod" created

これらのIssuerはどちらも、HTTP01チャレンジプロバイダーを使用するように構成されています。

作成後、Issuerのステータスを確認します。

$ kubectl describe issuer letsencrypt-staging
Name: letsencrypt-staging
Namespace: default
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"cert-manager.io/v1","kind":"Issuer","metadata":{"annotations":{},"name":"letsencrypt-staging","namespace":"default"},(...)}
API Version: cert-manager.io/v1
Kind: Issuer
Metadata:
Cluster Name:
Creation Timestamp: 2018-11-17T18:03:54Z
Generation: 0
Resource Version: 9092
Self Link: /apis/cert-manager.io/v1/namespaces/default/issuers/letsencrypt-staging
UID: 25b7ae77-ea93-11e8-82f8-42010a8a00b5
Spec:
Acme:
Email: email@example.com
Private Key Secret Ref:
Key:
Name: letsencrypt-staging
Server: https://acme-staging-v02.api.letsencrypt.org/directory
Solvers:
Http 01:
Ingress:
Class: nginx
Status:
Acme:
Uri: https://acme-staging-v02.api.letsencrypt.org/acme/acct/7374163
Conditions:
Last Transition Time: 2018-11-17T18:04:00Z
Message: The ACME account was registered with the ACME server
Reason: ACMEAccountRegistered
Status: True
Type: Ready
Events: <none>

登録されたアカウントとともに、Issuerが表示されます。

ステップ7 - TLS Ingressリソースのデプロイ

すべての前提条件の構成が完了したので、TLS証明書を要求するための手順を実行できます。これには主に2つの方法があります。ingress-shimを使用してIngressのアノテーションを使用するか、証明書リソースを直接作成します。

この例では、Ingressにアノテーションを追加し、ingress-shimを利用して代わりに証明書リソースを作成します。証明書を作成した後、cert-managerはIngressリソースを更新または作成し、それを使用してドメインを検証します。検証および発行されると、cert-managerは証明書に定義されているシークレットを作成または更新します。

注:Ingressで使用されるシークレットは、証明書に定義されているシークレットと一致する必要があります。明示的なチェックはないため、タイプミスによりingress-nginx-controllerが自己署名証明書にフォールバックします。この例では、Ingress(およびingress-shim)のアノテーションを使用しており、これにより適切なシークレットが作成されます。

Ingressを編集し、以前の例でコメントアウトされていたアノテーションを追加します。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kuard
annotations:
cert-manager.io/issuer: "letsencrypt-staging"
spec:
ingressClassName: nginx
tls:
- hosts:
- example.example.com
secretName: quickstart-example-tls
rules:
- host: example.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kuard
port:
number: 80

そして、適用します。

kubectl create --edit -f https://raw.githubusercontent.com/cert-manager/website/master/content/docs/tutorials/acme/example/ingress-tls.yaml
# expected output: ingress.networking.k8s.io/kuard configured

cert-managerはこれらのアノテーションを読み取り、それらを使用して証明書を作成します。これは要求して確認できます。

$ kubectl get certificate
NAME READY SECRET AGE
quickstart-example-tls True quickstart-example-tls 16m

cert-managerは、すべての要求のプロセス状態を証明書オブジェクトに反映します。kubectl describeコマンドを使用して、この情報を確認できます。

$ kubectl describe certificate quickstart-example-tls
Name: quickstart-example-tls
Namespace: default
Labels: <none>
Annotations: <none>
API Version: cert-manager.io/v1
Kind: Certificate
Metadata:
Cluster Name:
Creation Timestamp: 2018-11-17T17:58:37Z
Generation: 0
Owner References:
API Version: networking.k8s.io/v1
Block Owner Deletion: true
Controller: true
Kind: Ingress
Name: kuard
UID: a3e9f935-ea87-11e8-82f8-42010a8a00b5
Resource Version: 9295
Self Link: /apis/cert-manager.io/v1/namespaces/default/certificates/quickstart-example-tls
UID: 68d43400-ea92-11e8-82f8-42010a8a00b5
Spec:
Dns Names:
www.example.com
Issuer Ref:
Kind: Issuer
Name: letsencrypt-staging
Secret Name: quickstart-example-tls
Status:
Acme:
Order:
URL: https://acme-staging-v02.api.letsencrypt.org/acme/order/7374163/13665676
Conditions:
Last Transition Time: 2018-11-17T18:05:57Z
Message: Certificate issued successfully
Reason: CertIssued
Status: True
Type: Ready
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal CreateOrder 9m cert-manager Created new ACME order, attempting validation...
Normal DomainVerified 8m cert-manager Domain "www.example.com" verified with "http-01" validation
Normal IssueCert 8m cert-manager Issuing certificate...
Normal CertObtained 7m cert-manager Obtained certificate from ACME server
Normal CertIssued 7m cert-manager Certificate issued Successfully

describeの結果の下部に表示されるこのリソースに関連付けられたイベントは、リクエストの状態を示しています。上記の例では、証明書は数分以内に検証および発行されました。

完了すると、cert-managerはIngressリソースで使用されているシークレットに基づいて、証明書の詳細を含むシークレットを作成します。describeコマンドを使用して、いくつかの詳細を確認することもできます。

$ kubectl describe secret quickstart-example-tls
Name: quickstart-example-tls
Namespace: default
Labels: cert-manager.io/certificate-name=quickstart-example-tls
Annotations: cert-manager.io/alt-names=www.example.com
cert-manager.io/common-name=www.example.com
cert-manager.io/issuer-kind=Issuer
cert-manager.io/issuer-name=letsencrypt-staging
Type: kubernetes.io/tls
Data
====
tls.crt: 3566 bytes
tls.key: 1675 bytes

すべてが正しく構成されていることを確認したので、Ingressのアノテーションを更新して本番Issuerを指定できます。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kuard
annotations:
cert-manager.io/issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- example.example.com
secretName: quickstart-example-tls
rules:
- host: example.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kuard
port:
number: 80
$ kubectl create --edit -f https://raw.githubusercontent.com/cert-manager/website/master/content/docs/tutorials/acme/example/ingress-tls-final.yaml
ingress.networking.k8s.io/kuard configured

また、既存のシークレットを削除する必要があります。cert-managerはこれを監視しており、更新されたIssuerを使用してリクエストを再処理します。

$ kubectl delete secret quickstart-example-tls
secret "quickstart-example-tls" deleted

これにより、新しい証明書を取得するプロセスが開始され、describeを使用してステータスを確認できます。本番証明書が更新されると、署名付きTLS証明書を使用して、例としてKUARDがドメインで実行されていることがわかります。

$ kubectl describe certificate quickstart-example-tls
Name: quickstart-example-tls
Namespace: default
Labels: <none>
Annotations: <none>
API Version: cert-manager.io/v1
Kind: Certificate
Metadata:
Cluster Name:
Creation Timestamp: 2018-11-17T18:36:48Z
Generation: 0
Owner References:
API Version: networking.k8s.io/v1
Block Owner Deletion: true
Controller: true
Kind: Ingress
Name: kuard
UID: a3e9f935-ea87-11e8-82f8-42010a8a00b5
Resource Version: 283686
Self Link: /apis/cert-manager.io/v1/namespaces/default/certificates/quickstart-example-tls
UID: bdd93b32-ea97-11e8-82f8-42010a8a00b5
Spec:
Dns Names:
www.example.com
Issuer Ref:
Kind: Issuer
Name: letsencrypt-prod
Secret Name: quickstart-example-tls
Status:
Conditions:
Last Transition Time: 2019-01-09T13:52:05Z
Message: Certificate does not exist
Reason: NotFound
Status: False
Type: Ready
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Generated 18s cert-manager Generated new private key
Normal OrderCreated 18s cert-manager Created Order resource "quickstart-example-tls-889745041"

cert-managerが証明書のために作成したOrderリソースでkubectl describeを実行することで、ACME Orderの現在の状態を確認できます。

$ kubectl describe order quickstart-example-tls-889745041
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Created 90s cert-manager Created Challenge resource "quickstart-example-tls-889745041-0" for domain "www.example.com"

ここで、cert-managerがOrderを実行するために1つの「Challenge」リソースを作成したことがわかります。自動的に作成されたChallengeリソースでkubectl describeを実行することで、現在のACMEチャレンジの状態を詳しく調べることができます。

$ kubectl describe challenge quickstart-example-tls-889745041-0
...
Status:
Presented: true
Processing: true
Reason: Waiting for http-01 challenge propagation
State: pending
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Started 15s cert-manager Challenge scheduled for processing
Normal Presented 14s cert-manager Presented challenge using http-01 challenge mechanism

上記から、チャレンジが「提示」され、cert-managerがチャレンジレコードがIngressコントローラーに伝播されるのを待機していることがわかります。チャレンジリソースの新しいイベントに注意してください。「成功」イベントは、1分後程度(Ingressコントローラーがルールを更新する速度によって異なります)に出力されるはずです。

$ kubectl describe challenge quickstart-example-tls-889745041-0
...
Status:
Presented: false
Processing: false
Reason: Successfully authorized domain
State: valid
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Started 71s cert-manager Challenge scheduled for processing
Normal Presented 70s cert-manager Presented challenge using http-01 challenge mechanism
Normal DomainVerified 2s cert-manager Domain "www.example.com" verified with "http-01" validation

注:チャレンジが「有効」にならず、「保留中」の状態のままの場合(または「失敗」状態になる場合)、何らかの構成エラーが発生している可能性があります。失敗したチャレンジのデバッグに関する詳細については、「Challengeリソースリファレンスドキュメント」を参照してください。

チャレンジが完了すると、対応するチャレンジリソースが削除され、「Order」がOrderの新しい状態を反映するように更新されます。

$ kubectl describe order quickstart-example-tls-889745041
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Created 90s cert-manager Created Challenge resource "quickstart-example-tls-889745041-0" for domain "www.example.com"
Normal OrderValid 16s cert-manager Order completed successfully

最後に、「Certificate」リソースが発行プロセスの状態を反映するように更新されます。すべてが正常であれば、「Certificate」を「describe」して、以下のようなものを見ることができるはずです。

$ kubectl describe certificate quickstart-example-tls
Status:
Conditions:
Last Transition Time: 2019-01-09T13:57:52Z
Message: Certificate is up to date and has not expired
Reason: Ready
Status: True
Type: Ready
Not After: 2019-04-09T12:57:50Z
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Generated 11m cert-manager Generated new private key
Normal OrderCreated 11m cert-manager Created Order resource "quickstart-example-tls-889745041"
Normal OrderComplete 10m cert-manager Order "quickstart-example-tls-889745041" completed successfully