OKE の Load Balancer にサーバ証明書を適用して HTTPS 化する際の手順と注意点

Pocket

こんにちは、mkrydik です。

今回は OKE : Oracle Container Engine for Kubernetes を HTTPS 公開するための手順と注意点をご紹介します。

前提条件

この記事は以下を前提とします。

  1. OKE クラスタが作成済で、HTTP アクセスが可能な状態であること
    • HTTP アクセスはできる状態を前提として、HTTPS 化する手順を紹介します
  2. 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.pemserver.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 一覧を見てみます。

OCI LB Certificates

するとここに、Kubernetes Secret として登録した my-ssl-certificate-1 があります。

続いて、「Resources」メニューから「Listeners」を選びます。恐らく「TCP-443」というデフォルト名でリスナが作られていると思いますので、右端のアイコンから「Edit」を選んでみます。

OCI LB Listeners

「Edit Listener」画面が開きますので、「Certificate Name」欄を見てみます。ここに my-ssl-certificate-1 と設定されているかと思います。

OCI LB Edit Listener

この「リスナ」というのは、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 化設定を行う場合は以上のとおりで良いのですが、サーバ証明書の有効期限を迎えるなどの理由で、サーバ証明書を更新する機会が出てくるかと思います。

その際、これまでの手順を振り返ると、次のような作業でサーバ証明書の更新ができるのではないか、と考えますよね。

  1. 新しいサーバ証明書を Kubernetes Secret として登録する (例えば my-ssl-certificate-2 といった名前で)
  2. service.yamlannotations 部分を書き換えて 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」画面で、サーバ証明書と秘密鍵ファイルをアップロードします。

OCI LB 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 を選択します。

OCI LB Edit Listener To Use New Certificate

変更できたら、下部の「Save Changes」ボタンを押下して変更を反映します。

以上の操作で、サーバ証明書の更新が完了しました。

まとめ

  1. OKE クラスタを HTTPS 化するには、サーバ証明書と秘密鍵を Kubernetes Secret として登録し、Load Balancer Service の annotations 設定を追加する
  2. Load Balancer Service に annotations を初めて設定した場合は、OCI Load balancer の Certificate と Listener、Backend Set が自動的に生成され、問題なく HTTPS 化できる
  3. サーバ証明書を更新する場合など、2回目以降の設定変更は、OCI Load Balancer に対して設定変更が反映されないことがある
  4. そのため、Kubernetes 側の操作とは別に、OCI Load Balancer に Certificate を追加し、Listener の設定を変更する必要がある
    • Kubernetes Secret と service.yaml の変更は OCI Load Balancer に反映されないが、念のため設定内容を揃えておいた方が良いと思慮

「OKE クラスタに対して操作を行ったのに反映されていない」という場合は、それに対応する OCI のリソースの設定内容を確認できるようになっておくと、トラブルにも適切に対応できるようになりますね。

Pocket

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です