こんにちは、mkrydik です。
今回は OKE : Oracle Container Engine for Kubernetes を HTTPS 公開するための手順と注意点をご紹介します。
前提条件
この記事は以下を前提とします。
- OKE クラスタが作成済で、HTTP アクセスが可能な状態であること
- HTTP アクセスはできる状態を前提として、HTTPS 化する手順を紹介します
- PEM 形式のサーバ証明書、必要に応じて中間 CA 証明書、秘密鍵ファイルが用意してあること
- Let’s Encrypt などでサーバ証明書を発行してあることを前提とします
- Load Balancer のパブリック IP アドレスとドメインとの DNS 設定は別途行ってあることとします
念のため、前提条件の状態を確認しておきましょう。
1. OKE クラスタに HTTP アクセスが可能な状態であること
次のような service.yaml
ファイルで、OKE クラスタ内に Load Balancer Service を作成してある状態とします。
apiVersion: v1
kind: Service
metadata:
name: my-lb-service
labels:
name: my-lb-service
spec:
type: LoadBalancer
selector:
name: my-app
ports:
- name: http
port: 80
targetPort: 8080
以下の kubectl
コマンドで Service を確認すると、パブリック IP アドレスが確認できます。
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 13d
my-lb-service LoadBalancer 10.96.200.10 129.200.100.10 80:31920/TCP 13d
この例では 129.200.100.10
というパブリック IP アドレスが発行されているので、
http://129.200.100.10/
にアクセスすることで、HTTP 通信はできている状態とします。
2. PEM 形式のサーバ証明書等が用意できていること
独自ドメイン example.com
を取得してあり、上述のパブリック IP アドレスとの DNS 設定が完了している状態とします。
サーバ証明書を用意する手順は省略しますが、次のようなファイルが用意できていることを確認してください。
- 秘密鍵ファイル
- ここでは
private.key.pem
というファイル名だとします - テキストエディタで開くと以下のように見えます (パスフレーズは解除してあること)
- ここでは
-----BEGIN PRIVATE KEY-----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END PRIVATE KEY-----
- サーバ証明書
- ここでは
server.crt.pem
というファイル名だとします - テキストエディタで開くと以下のように見えます
- ここでは
-----BEGIN CERTIFICATE-----
yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
-----END CERTIFICATE-----
- 中間 CA 証明書
- 発行したサーバ証明書がルート認証局からのチェーンで受け付けられない場合は、中間 CA 証明書が必要になります
- テキストエディタで開いた内容は「サーバ証明書」とよく似た形式です
- ファイル名で表現する場合は
chain.crt.pem
と表現します
発行したサーバ証明書の内容に応じて、 チェーン証明書 を準備してください。
- チェーン証明書
- 「サーバ証明書 (
server.crt.pem
)」と「中間 CA 証明書 (chain.crt.pem
)」を結合したもの - 以下のように、本当に単純に
cat
コマンドで結合したような内容で大丈夫です - ファイル名で表現する場合は
fullchain.crt.pem
と表現します
- 「サーバ証明書 (
-----BEGIN CERTIFICATE-----
yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
-----END CERTIFICATE-----
HTTPS 化作業にあたっては、private.key.pem
と server.crt.pem
(もしくは fullchain.crt.pem
) の2ファイルを使用します。
新規に HTTPS 化する手順
OKE クラスタと証明書ファイルの準備ができたら、HTTPS 化していきます。
1. Secret を登録する
まずは kubectl
コマンドを使って、SSL/TLS 証明書用の Secret を作成します。チェーン証明書を使う場合は server.crt.pem
部分を fullchain.crt.pem
に読み替えてください。
$ kubectl create secret tls my-ssl-certificate-1 --key='./private.key.pem' --cert='./server.crt.pem'
コマンドを実行したら、Secret が登録されているか確認してみましょう。
$ kubectl get secret
NAME TYPE DATA AGE
default-token-8fv9m kubernetes.io/service-account-token 3 13d
my-ssl-certificate-1 kubernetes.io/tls 2 13d
my-ssl-certificate-1
という Secret が登録できていますね。
ちなみに、Secret は Base64 エンコードされているだけなので、次のように jq
ライブラリを組み合わせてデコードしてやれば、登録した Secret の中身を確認することもできます。
$ kubectl get secret my-ssl-certificate-1 -o json | jq -r '.data | to_entries | map( .key + "\n" + (.value|@base64d) )[]'
# サーバ証明書
tls.crt
-----BEGIN CERTIFICATE-----
yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
-----END CERTIFICATE-----
# 秘密鍵
tls.key
-----BEGIN RSA PRIVATE KEY-----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END RSA PRIVATE KEY-----
2. Load Balancer Service を更新する
続いて、先ほどの service.yaml
にいくつか行を追加して、登録した Secret を参照して HTTPS 化できるようにします。
apiVersion: v1
kind: Service
metadata:
name: my-lb-service
labels:
name: my-lb-service
# HTTPS 化のため以下を追加
annotations:
service.beta.kubernetes.io/oci-load-balancer-ssl-ports: "443"
service.beta.kubernetes.io/oci-load-balancer-tls-secret: my-ssl-certificate-1
spec:
type: LoadBalancer
selector:
name: my-app
ports:
- name: http
port: 80
targetPort: 8080
# HTTPS 化のため以下を追加
- name: https
port: 443
targetPort: 8080
追加したのは metadata.annotations
と、 spec.ports
の2つ目、 name: https
の設定です。
annotations
に Secret 名とポート番号を指定し、そのポート番号から転送する設定情報を spec.ports
に追加します。HTTPS 通信のみ許容し、HTTP 通信を禁止する場合は、 spec.ports
配下の name: http
の設定を削除してしまってください。80番ポートへの通信はどこにも転送されなくなります。
このように YAML ファイルを変更したら、
$ kubectl apply -f ./service.yaml
コマンドで適用しましょう。
設定が正しく変更できたら、次のコマンドで様子を見ます。
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 13d
my-lb-service LoadBalancer 10.96.200.10 129.200.100.10 80:31920/TCP,443:31890/TCP 13d
先ほどと比べて、 PORT(S)
部分に 443 ポートの転送設定が追加されていることが分かるかと思います。
これで、
https://example.com/
という HTTPS の URL でアクセスできるようになっているはずです。ブラウザでアクセスし、証明書情報を確認してみてください。
もしも、パブリック IP アドレスの方を指定して
https://129.200.100.10/
とアクセスしてしまった場合は、ブラウザが証明書不正の警告を表示するかと思いますが、それを無視すれば OKE で稼動する Web サーバにアクセスできるはずです。
OCI Load Balancer の変更を確認する
以上のように、Kubernetes Secret を追加し、Load balancer Service を編集することで、HTTPS 化ができました。
では、これらの設定は OCI 上のどこに表れるのでしょうか。OCI の管理コンソールを開いてみましょう。
メニューから Networking → Load Balancers と進み、対象の Load Balancer の詳細画面に移動します。
左下の「Resources」メニューを見ると、「Certificates (1)」と表示されているかと思います。押下して Certificates 一覧を見てみます。
するとここに、Kubernetes Secret として登録した my-ssl-certificate-1
があります。
続いて、「Resources」メニューから「Listeners」を選びます。恐らく「TCP-443」というデフォルト名でリスナが作られていると思いますので、右端のアイコンから「Edit」を選んでみます。
「Edit Listener」画面が開きますので、「Certificate Name」欄を見てみます。ここに my-ssl-certificate-1
と設定されているかと思います。
この「リスナ」というのは、Load Balancer が開放しているポート番号の定義とみなせます。このポート番号で受け付けたリクエストをどこに転送するか、という指定は、「Backend Set」で指定します。Backend Set には OKE の Worker Node が登録されているので、リクエストを最終的に Pod にまで転送できる、という関係になっています。
以上のように、 Kubernetes で Load Balancer にサーバ証明書を設定すると、それに付随して OCI Load Balancer に設定変更が行われる ことが確認できたかと思います。
- Secret で登録したサーバ証明書は「Certificate」に、
- 開放するポート指定は「Listener」に、
- サーバ証明書とポートの紐付けは「Listener」の「Certificate Name」欄で、
- Load Balancer と Worker Node 間の紐付けは「Listener」で指定する「Backend Set」で、
それぞれ設定されています。
サーバ証明書を更新する際の注意
新規に HTTPS 化設定を行う場合は以上のとおりで良いのですが、サーバ証明書の有効期限を迎えるなどの理由で、サーバ証明書を更新する機会が出てくるかと思います。
その際、これまでの手順を振り返ると、次のような作業でサーバ証明書の更新ができるのではないか、と考えますよね。
- 新しいサーバ証明書を Kubernetes Secret として登録する (例えば
my-ssl-certificate-2
といった名前で) service.yaml
のannotations
部分を書き換えてmy-ssl-certificate-2
を参照させる
しかし、自分が試した限りでは、このような Kubernetes 側の設定を変更するだけでは、 OCI Load Balancer 側にうまく設定変更が反映されませんでした。
Kubernetes 上では、新たな Secret が登録され、Load Balancer Service の annotations
も書き換わっているのですが、この設定が OCI Load Balancer の方に反映されません。 「Certificates」に新しいサーバ証明書が登録されない ので、当然「Listeners」との紐付けも更新されません。
仕方がないので、サーバ証明書を更新する場合は、上述の Kubernetes 側の設定変更と合わせて、 OCI 側にも同様の設定変更を行ってあげる ことにします。OCI CLI を使えばコマンドラインでも実施できますが、今回は OCI 管理画面から作業する手順を紹介します。
1. 新しいサーバ証明書を追加する
OCI 管理画面で Load Balancer の詳細画面を開いたら、「Resources」メニューから Certificates を選択し、 「Add Certificate」ボタン を押下します。
「Add Certificate」画面で、サーバ証明書と秘密鍵ファイルをアップロードします。
秘密鍵については、ファイルアップロードの代わりに PEM ファイルの内容をコピペしてのアップロードも可能です。OCI LB の機能としては、パスフレーズ付きの秘密鍵でも受け付けられるようです。
中間 CA 証明書もアップロードしたい場合は、「Specify CA Certificate」チェックボックスにチェックを入れ、現れたファイルアップロード欄に中間 CA 証明書ファイルをドラッグ & ドロップすることでアップロードできます。
ファイルを登録したら、下部の「Add Certificate」ボタンを押下して、Certificate 情報を追加します。
2. Listener 設定を変更し、追加した Certificate を利用させる
次に、「Resources」メニューから Listeners を選択し、「TCP-443」という名前の Listener を選択し、右端のアイコンから「Edit」を押下します。
現れた「Edit Listener」画面での「Certificate Name」欄はプルダウンになっているので、ここを選択し、先ほど登録した my-ssl-certificate-2
を選択します。
変更できたら、下部の「Save Changes」ボタンを押下して変更を反映します。
以上の操作で、サーバ証明書の更新が完了しました。
まとめ
- OKE クラスタを HTTPS 化するには、サーバ証明書と秘密鍵を Kubernetes Secret として登録し、Load Balancer Service の
annotations
設定を追加する - Load Balancer Service に
annotations
を初めて設定した場合は、OCI Load balancer の Certificate と Listener、Backend Set が自動的に生成され、問題なく HTTPS 化できる - サーバ証明書を更新する場合など、2回目以降の設定変更は、OCI Load Balancer に対して設定変更が反映されないことがある
- そのため、Kubernetes 側の操作とは別に、OCI Load Balancer に Certificate を追加し、Listener の設定を変更する必要がある
- Kubernetes Secret と
service.yaml
の変更は OCI Load Balancer に反映されないが、念のため設定内容を揃えておいた方が良いと思慮
- Kubernetes Secret と
「OKE クラスタに対して操作を行ったのに反映されていない」という場合は、それに対応する OCI のリソースの設定内容を確認できるようになっておくと、トラブルにも適切に対応できるようになりますね。