まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

タグ:cis

かなり以前に Bluemix(IBM Cloud) の CloudFoundry ランタイム環境でカスタムドメインを運用する手順を紹介したことがありました。今回はその IKS(IBM Kubernetes Services) 版とも言える内容で、特にカスタムドメイン管理を適用する部分については IBM Cloud から提供されるドメイン単位でのオール・イン・ワン・セキュリティーサービスでもある CIS(Cloud Internet Services) を使った場合の手順を確認する機会があったので、その流れを紹介します。

なお、今回紹介する手順を実行するための前提条件として (0) カスタムドメインを所有していること、(1) ベーシックプラン以上の IBM Cloud アカウントを取得していること、(2) IKS(+ Ingress)サービスが利用できること、そして (3) CIS サービスが利用できるという3点が必要です。(0) は今回の作業では当然必要です。また (1) がないと (2) も (3) も利用できないので IBM Cloud アカウントは取得しているとして、このアカウントは無料のライトプランではなく、ベーシックプラン以上でないと (2) も (3) も作成できない点にご注意ください。

また (2) について、IKS は1ヶ月間無料の Free プランもありますが、今回紹介する内容では(Free プランでは使えない) Ingress によるサーバー名管理が必要なため、事実上この (2) は無料では試せない点をご了承ください。シングルゾーンの最弱スペックで1ワーカーノード環境(1時間で約13円)でも構いませんが、IKS を利用するための料金が必要です。なお IKS ではなく上位版の OpenShift 環境でも大丈夫です:
2021062501


(3) の CIS は1アカウントにつき1回だけ、30日間無料プランを申し込むことが可能です。このプランを利用せずにスタンダードプランを利用する場合は、ドメイン1つにつき1ヶ月約30000円です:
2021062502



では以下で具体的な手順を追いながら説明します。

【IKS + Ingress を使ってアプリケーションをデプロイする】
まずは IKS + Ingress 環境で動くウェブアプリケーションを用意します。この時点ではカスタムドメインを意識せずに環境を作ります。

自分で IKS にデプロイしたアプリケーションがあればそれを使っていただいても構いませんが、動作確認用のサンプルとして、以前のブログエントリでも紹介したこのアプリケーションを使う方法も紹介します(アクセスすると、サーバーの /etc/hostname ファイルの内容を表示するだけのアプリケーションです)。ソースコード内に IKS + Ingress 環境を想定したデプロイ用の yaml ファイルが含まれているのでこれを自分の環境向けに編集してデプロイに使うことにします。

まずはソースコードを git clone するかダウンロードしてください:
$ git clone https://github.com/dotnsf/hostname

厳密には今回の作業で最低限必要になるのは yaml/app_deployment_ingresssample.yaml ファイル1つだけなので、ソースコード等に興味なく単に動作確認するだけであれば、このファイルをダウンロードしていただくだけでも構いません:
https://raw.githubusercontent.com/dotnsf/hostname/master/yaml/app_deployment_ingresssample.yaml


次にダウンロードした (yaml/)app_deployment_ingresssample.yaml ファイルを自分の環境向けに編集するのですが、まずは自分の Ingress サブドメインを確認しておく必要があります。IBM Cloud にログインし、利用中の IKS のダッシュボードを開いて Overview メニューから Ingress Subdomain 欄を参照して、その値を確認します:
2021062403


ダウンロードした app_deployment_ingresssample.yaml ファイルをテキストエディタで開き、以下の部分を上で参照した自分の Ingress Subdomain の値に書き換えます。また k8s クラスターを東京( jp-tok )以外のリージョンで作成した場合はサブドメイン情報に続くリージョン情報も自分のリージョンに書き換えてください。最後に保存します:
apiVersion: v1
kind: Service
metadata:
  name: hostname
spec:
  selector:
    app: hostname
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 8080
  type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hostname
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hostname
  template:
    metadata:
      labels:
        app: hostname
    spec:
      containers:
      - name: hostname
        image: dotnsf/hostname
        ports:
        - containerPort: 8080
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: hostname.yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000.jp-tok.containers.appdomain.cloud
spec:
  tls:
  - hosts:
    - hostname.yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000.jp-tok.containers.appdomain.cloud
    secretName: yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000
  rules:
  - host: hostname.yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000.jp-tok.containers.appdomain.cloud
    http:
      paths:
      - path: /
        backend:
          serviceName: hostname
          servicePort: 8080


これでデプロイの準備が完了したので、実際に IKS へデプロイします:
$ kubectl apply -f yaml/app_deployment_ingresssample.yaml 

デプロイが成功すると Ingress が有効になり、https://(上述の spec.tls.hosts の値)/ でアプリケーションにアクセスできるようになります:
2021062401


IKS 上にデプロイされたアプリケーションの稼働までができました。この時点では IBM Cloud のドメインによるサーバー名でアクセスされていますが、引き続いて CIS を使い、このアプリケーションをカスタムドメインのサーバー名でアクセスできるようにしていきます。


【CIS にカスタムドメインを登録する】
次の手順は CIS 側の設定です。CIS はドメイン単位で設定を行います。したがってカスタムドメインを使う場合はそのカスタムドメインを登録した上で証明書等を発行する必要があります。

今回は私個人が所有する pi314.jp というドメインを使って説明します。最終的には上述の IKS 上で動く hostname アプリケーションを https://hostname.pi314.jp/ という URL で利用できるように設定していくことにします。以下、"pi314.jp" 部分を皆さんが所有するドメイン名に置き換えて参照してください。

カスタムドメインを CIS に登録する際の最初の手順は利用ドメインの登録ネームサーバーの置き換えです。まず何はともあれ自分のドメインを CIS に管理させるために登録を行います。自分の CIS サービスを開き、Overview メニューから Add domain ボタンをクリックします:
2021062401


画面右にウィザード形式のダイアログが現れるので、指示に従って入力していきます。まずは利用するドメイン(下図では pi314.jp)を入力して Next を選択します:
2021062402


次の DNS レコード登録画面はスキップします。そのまま Next を選択:
2021062403


最後にドメイン管理のためのネームサーバーを変更しろ、という以下のような画面が現れます:
2021062404


DNS で利用するネームサーバーを CIS が管理するネームサーバーに変更する必要があります。この具体的な手順は独自ドメインを取得した業者によって異なりますが、プライマリネームサーバーとセカンダリネームサーバーをそれぞれ以下の内容に変更してください(画面は GoDaddy での DNS 管理画面のものです):
2021062401

 プライマリネームサーバー: ns012.name.cloud.ibm.com
 セカンダリネームサーバー: ns091.name.cloud.ibm.com


変更が完了したら、少し待ってから上図の Next を選択します。変更内容がネット上に反映されるまで少し時間が必要なので、すぐに実行すると「1時間後に再実行しろ」といった内容のエラーになるかもしれません。その場合は少し待ってから再実行してください。

無事に変更内容が確認できた場合は以下のような画面になります。最後に Done を選択:
2021062405


CIS のコンソール画面に移動します。この時点で登録したカスタムドメインが Active になっていることが確認できます。これでドメインの登録とネームサーバーの置き換え作業は完了しました:
2021062406


【CIS に DNS を登録し、エッジとサーバーの接続方法の選択】
ネームサーバーの置き換え後に行う必要があるのは、DNS 設定でカスタムドメインを使ったサーバー名(今回は hostname.pi314.jp)で実際に動いているサーバー(IKS の yaml ファイルで spec.tls.hosts に書かれた値、つまりウェブブラウザで動作確認した時の URL のサーバー名)にアクセスさせるための CNAME の登録と、カスタムドメインのエッジ証明書の作成(確認)、そして CIS のエッジサーバーから IKS のサーバーへどのような方法で接続するかの接続方法の選択です。すべて CIS コンソールから行います。

まずは CNAME を登録します。"Reliability" メニューから "DNS" タブを選択します。画面下部に "DNS records" 欄がありますが、おそらく最初は中身が空のはずです。ここに新しいレコードを作成するため、Add ボタンを選択します:
2021062407


画面右に "Add record" ダイアログが表示されるので、ここで以下のように入力します:
 Type: "CNAME" を選択
 TTL: "Automatic" のまま
 Name: カスタムドメインを使ってアクセスさせるホスト名のドメインを抜いた部分
  (例えば hostname.pi314.jp でアクセスさせたい場合であれば hostname)
 Alias domain name: 現在動いているサーバー名
  (上述のウェブサーバーで動作確認した時に入力した IBM Cloud ドメインのサーバー名)

最後に Add ボタンをクリックします:
2021062408


1つ前の画面に戻り、DNS records が1件追加されていることを確認し、Proxy を ON の状態にしておきます。これで DNS の登録も完了して、hostname.pi314.jp というホスト名で IKS で稼働中のアプリケーションを指し示すようになりました:
2021062409


続けて https 接続のためのエッジ証明書を確認します。CIS コンソールから "Security" メニューを選び、画面下部に Edge certificates 欄を参照します。ここにはカスタムドメインとそのワイルドカード名(今回の例であれば pi314.jp と *.pi314.jp)のエッジ証明書が入力されていればそのままで大丈夫です(2021/06/23 時点の仕様では自動的に作成されているはずです)。もしもここが空になっていたら、右上の Add ボタンを押して、この2つを指定してエッジ証明書を追加してください:
2021062401


最後に CIS のエッジサーバーと、実際のサーバーとの間の通信手段を選択します。同じ "Security" メニューの画面内の TLS mode を確認します(デフォルトは End-to-end CA signed)。ここでセキュリティ要件に合わせて通信方法を選択します。最もセキュリティが高いのは End-to-end CA signed ですが、この場合は別途ドメインのワイルドカード証明書を取得する必要があります。今回は1つ下の End-to-end flexible を選択します(自動発行する自己証明書を使った暗号化通信を行うモードです)。なおこれらの選択肢の違いについて、詳しくはこちらを参照ください:
2021062401


以上で CIS コンソールでの作業も完了です。


【デプロイ設定を変更して IKS にアプリケーションを再デプロイ】

最後にデプロイ設定を変更してアプリケーションを再デプロイします。現在は IBM Cloud ドメイン名でアクセスされる前提でデプロイしていますが、今後はカスタムドメインを使ったアクセスを想定する必要があり、そのための変更を app_deployment_ingressample.yaml ファイルに反映させた上で再デプロイします。

この方法には2つあります。1つは旧サーバー名でのアクセスはできないようにした上でカスタムドメインでの新サーバー名のアクセスを許可する場合、もう1つは旧サーバー名でのアクセスを許可しながら(残しながら)カスタムドメインでの新サーバー名でもアクセスを許可する場合です。両方のケースを順に説明します。

まず旧サーバー名でのアクセスが不要になる場合は前者の(旧サーバー名ではアクセスできないようにする)ケースになります。この場合は app_deployment_ingressample.yaml ファイルの2箇所を以下のように書き換えます:
apiVersion: v1
kind: Service
metadata:
  name: hostname
spec:
  selector:
    app: hostname
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 8080
  type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hostname
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hostname
  template:
    metadata:
      labels:
        app: hostname
    spec:
      containers:
      - name: hostname
        image: dotnsf/hostname
        ports:
        - containerPort: 8080
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: hostname.yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000.jp-tok.containers.appdomain.cloud
spec:
  tls:
  - hosts:
#   - hostname.yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000.jp-tok.containers.appdomain.cloud
    - hostname.pi314.jp
    secretName: yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000
  rules:
# - host: hostname.yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000.jp-tok.containers.appdomain.cloud
  - host: hostname.pi314.jp
    http:
      paths:
      - path: /
        backend:
          serviceName: hostname
          servicePort: 8080

"#" で始まる行はコメントなので無くても構いません。要するに spec.tls.hosts と spec.rules.hosts の値を旧サーバー名ではなく新サーバー名に変更する、という作業が必要になります。

もう一方の、旧サーバー名でのアクセスを残しながら新サーバー名でもアクセスできるようにする場合は app_deployment_ingressample.yaml ファイルに新しい Ingress 設定を書き足す形で以下のように書き換えます:
apiVersion: v1
kind: Service
metadata:
  name: hostname
spec:
  selector:
    app: hostname
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 8080
  type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hostname
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hostname
  template:
    metadata:
      labels:
        app: hostname
    spec:
      containers:
      - name: hostname
        image: dotnsf/hostname
        ports:
        - containerPort: 8080
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: hostname.yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000.jp-tok.containers.appdomain.cloud
spec:
  tls:
  - hosts:
    - hostname.yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000.jp-tok.containers.appdomain.cloud
    secretName: yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000
  rules:
 - host: hostname.yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000.jp-tok.containers.appdomain.cloud
    http:
      paths:
      - path: /
        backend:
          serviceName: hostname
          servicePort: 8080
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: hostname.yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000.jp-tok.containers.appdomain.cloud
spec:
  tls:
  - hosts:
    - hostname.pi314.jp
  rules:
 - host: hostname.pi314.jp
    http:
      paths:
      - path: /
        backend:
          serviceName: hostname
          servicePort: 8080

目的に応じた変更をした上で、改めて新しい yaml ファイルを使ってアプリケーションを再デプロイします:
$ kubectl apply -f yaml/app_deployment_ingresssample.yaml

再デプロイ後に新しいホスト名で https アクセスができるようになったことを確認します:
2021062401


以上、CIS + IKS( + Ingress ) の環境でカスタムドメインを使った https アクセスを実現するための設定手順でした。CIS は今回紹介した DNS 関連の機能だけでなく、本来得意とするセキュリティ強化の機能が簡単に使える強みもあるので、IBM Cloud を使って k8s 環境のアプリケーションを安全に使う上での非常に便利なコンパニオンサービスだと感じています。



このブログエントリの続きです。IBM CIS(Cloud Internet Services) とその設定方法についてはこちらをどうぞ:
IBM Cloud で CIS を使って証明書作成なしにカスタムドメインの https アクセスを行う


上記リンク先の設定を行うことで、アプリケーションファイアウォールや DDOS 対策など、簡単に安全なインターネット運用環境を構築することができます。これはこれで便利なのですが・・・

一方でこの設定が不要なタイミングもあります。典型的な例は「アプリケーション開発時」です。CIS の防御モードが有効になっている環境では、ウェブブラウザからアクセスした時にいったんそのリクエストを受け取った上で「そのリクエストが攻撃でないことを確認」します:
2019110706
 ↑リクエスト確認中の画面

 ↓問題ないと判断されるとオリジンサーバーへ転送されます
2019110707


つまりウェブブラウザからのアクセスであればリクエストの転送機能を使って安全なアクセスが可能になる、という仕組みで実現されています。サービスの運用段階であれば、これが問題になることはあまりないと考えられます。

一方でサービスの開発段階においては少し事情が異なります。API の単体テストなど、ウェブブラウザを使って行うこともありますが、curl などのコマンドやツールを使って行われることも珍しくありません。この場合、curl のリクエストに対して上記「リクエスト確認中」画面の HTML が返ってくるだけで、確認された結果のレスポンス(テストではこれを知りたい)が返ってくるわけではないのです:
$ curl https://XXXXX.XXXXX.com/api/users

<!DOCTYPE HTML>
<html lang="en-US">
<head>
  <meta charset="UTF-8" />
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" />
  <meta name="robots" content="noindex, nofollow" />
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
  <title>Just a moment...</title>
  <style type="text/css">
    html, body {width: 100%; height: 100%; margin: 0; padding: 0;}
    body {background-color: #ffffff; font-family: Helvetica, Arial, sans-serif; font-size: 100%;}
      :
      :
  ↑青字部分はリクエスト確認中画面の HTML 、これでは動作確認にならない


つまり開発段階で CIS を使う場合、ドメイン設定や SSL 対応などはそのまま使えますが、防御モードに関しては一時的に無効にしたくなるケースが出てくるのでした。この防御モードを無効にするための設定方法を紹介します。


といってもその手順は極めてシンプルで、サービスモードを無効にするだけです:
2019111500


この解除を行うことでドメインの DNS や SSL 対応などを残したまま、防御チェックだけを一時的に無効にすることができるようになります。



IBM Cloud から提供されている、Web コンテンツ向けにセキュリティやパフォーマンスといった非機能要件を統合的に設計するサービス、それが CIS(Cloud Internet Services)です。mybluemix.net などの IBM Cloud から提供される標準ドメインではなく、独自ドメインを使うことが前提となりますが、そのドメインで運用するウェブアプリケーションやコンテンツ全般を保護して、安全なインターネット環境を提供することができる便利なサービスです:

https://www.ibm.com/jp-ja/cloud/cloud-internet-services
2019111600


この CIS は(独自ドメインの)DNS を使うことでオリジンサーバーとは別の( Cloudflare 社の)サーバーを経由してオリジンサーバーにアクセスします。この中間サーバーを経由することで必要なセキュリティ要件やコンテンツ保護要件を提供する、というものです。

この CIS を利用して、クライアント(ウェブブラウザ)からの SSL(https) 通信を実現するには大きく3種類の方法があります:

(1)クライアント→中間サーバー→オリジンサーバー 間すべての通信をセキュアに行う方法
(2)クライアント→中間サーバー 間はセキュアに行い、 中間サーバー→オリジンサーバー 間の通信は自己証明書を使った暗号化通信を行う方法
(3)クライアント→中間サーバー 間はセキュアに行い、 中間サーバー→オリジンサーバー 間の通信は HTTP 通信で行う方法
2019110703


①、②、③いずれも共通でクライアントと中間サーバーとの間はセキュアな SSL 通信を行います。中間サーバーとオリジンサーバー間の通信をどう考えるか、という点が異なります。ざっくりいうと①がもっともセキュアで、③がもっともセキュアでない方法となります。

①は理想的といえる方法で、全ての区間がセキュアな通信となります。②も通信自体は SSL で暗号化通信となりますが、その際に利用する証明書はいわゆる「自己証明書」となります。①と比較すると(外部の業者などを使わずに)自分で証明書を作ることができるので、コストや有効期限の自由度などの面でメリットがありますが、「要は誰でも(ドメインのオーナーでなくても)作れる証明書」を使った暗号化通信を行う点が異なります。この証明書を普通に使って直接 SSL 通信を行うと、普通は「セキュアな通信でないが、それでも通信するか?」といった確認メッセージを経て通信可能になったりしますが、中間サーバーとして CIS を入れることにより、クライアントはそういった手順を経ずに通信できるようになります。

そして③の方法ですが、これは中間サーバーとオリジンサーバーとの間の通信は暗号化しない方法です。セキュアかどうか、という観点では暗号化しない経路が存在しているわけで他の2つよりは劣っています。一方で、この方法では証明書の作成を必要としません。クライアントから見た証明書は CIS の中間サーバーが自動で発行されたものが使われ、オリジンサーバーには(そもそも暗号化通信をしないので)証明書が不要です。開発段階や試験段階など、インフラに関わる権限が小さい状況下で、かつ(API の仕様など)https 通信が必須となるような場合においては非常に有要な選択肢となります。またこの方法であれば(そもそも取得しないので)証明書の有効期限切れを心配する必要もありません。 また特にオリジンサーバーとして IBM Cloud の PaaS 環境を利用する場合であれば、オリジンサーバーのルーティングを編集してカスタムドメインでのホスト名でしかオリジンサーバーにアクセスできないように設定することで(HTTPS を使わずにアクセスするような)迂回路を遮断する設定にすることも可能です。

今回はこの③の方法でカスタムドメインの https アクセスを行うための設定を紹介します。

まず IBM Cloud の CIS サービスを利用してプロビジョン済みの状態とし、普通にドメインを登録して設定します。その上で「セキュリティ」カテゴリーの「TLS」内で動作モードを「クライアントからエッジ」に設定します(これが③の通信のための設定となります):
2019110701


なお、このモードの設定内容によって上記図の①~③の通信方法が変わります。以下のような関係です:
2019110704


また DNS の CNAME でオリジンサーバーとの紐付けを行います(下図の「信頼性」-「DNS」)。このカスタムドメインの頭につけるホスト名を指定して、CNAME の参照先となる別名を指定します。IBM Cloud の PaaS (米国南部リージョン)を使う場合は参照先を us-south.cf.cloud.ibm.com を指定します。また「プロキシー」を有効にします:
2019110702


なお、ここで指定する参照先と IBM Cloud PaaS のリージョンとの関係については以下を参照ください:
https://cloud.ibm.com/docs/cloud-foundry-public?topic=cloud-foundry-public-custom-domains

最後にオリジンサーバー側を用意します。IBM Cloud PaaS の場合であれば(CNAME で指定した名前).mybluemix.net でアクセスできるようなアプリケーションサーバーを CloudFoundry ランタイムとして作成します(このルーティングは最終的に削除します)。


また IBM Cloud PaaS の場合は「管理」メニューからカスタムドメインを登録する必要があります。この手順の詳細はこちらを参照ください(このリンク先では証明書をインポートする手順まで紹介していますが、今回ここでは SSL の設定を行う必要がないので不要です):
http://dotnsf.blog.jp/archives/1075713365.html


最後に上記で作成した Cloud Foundry ランタイムのルーティングを編集します。経路の編集から「経路の追加」を実行し、mybluemix.net ドメインと同じ名前を指定したカスタムドメイン用の経路を追加します(証明書をインポートしていないので鍵マークには×がついているはずです)。そしてもともとの mybluemix.net 側の経路を削除します。これでカスタムドメインを使った指定でないとアクセスすることはできなくなりました。最後に「保存」します:
2019110705


この状態でカスタムドメインを指定したホスト名で https アクセスを行ってみます。まず最初に CIS がリクエストを受け取ってアクセス内容を(アタックに相当するアクセスでないか)判断します:
2019110706


問題がないと判断されるとオリジンサーバーにルーティングが行われます。結果的に証明書を独自に発行することなくカスタムドメインを使った https 通信ができるようになりました:
2019110707



【参考】
IBM Cloud Certificate Manager で Let's Encrypt 無料 SSL 証明書を取得する



このページのトップヘ