AzureでクラウドネイティブなShibbolethを動かす!!〜コンテナ化からCI/CD、APMまで〜

こんにちは、サイオステクノロジー武井です。今回は、AzureでクラウドネイティブなShibbolethを動かしてみようと思います。

目次

Shibbolethとは?

Shibbolethとは、大学などでよく使われる学術系オープンソースシングルサインオンシステムです。要はSMALをベースとしたシングルサインオンシステムです。KeyCloakなどとは違うのは、学術系SPに特化したということですね。認証のデータソースにはOpenLDAPやRDBなどを使うことができたり、その他様々な条件でSAML Responseを生成できます。詳細は以下にて!!

https://wiki.shibboleth.net/

クラウドネイティブなShibbolethって?

Azureで提供しているクラウドネイティブなリソースを使って、運用を楽にしようという試みです。もちろん、仮想マシンでもShibbolethを構築できますが、せっかくAzureには様々な便利機能を持ったクラウドネイティブなリソースが多々あります。これを活かさない手はありません。これらのリソースを使うと、Shibbolethの運用が劇的に楽になります。今回は、その構成や構築方法を説明します。

ということで、クラウドネイティブなShibbolethのシステム構成図は以下の通りとなります。

構成図をもとに本システムの概要を説明します。

App Serviceを使ったマネージドな環境による運用負荷の劇的な削減

本システムの核となるのは、Webアプリケーションを動かすマネージドなPaaSサービスであるApp Serviceとなります。すでにWebアプリケーションを動作させるOSやミドルウェアは準備済みで、ビルド済みのWebアプリケーションをデプロイするだけでアプリケーションが稼働する優れものです。ただ、ShibbolethのミドルウェアはJettyが推奨なのですが、App ServiceはJettyをサポートしていないので、App Serviceのカスタムコンテナーという機能を使います。これはApp Service上で任意のコンテナを稼働することが出来る機能で、これであればミドルウェアの制限はありません。つまりShibbolethをコンテナ化してApp Service上にデプロイします。

リリースについても便利な面があります。App Serviceのスロット機能を使って、まずステージングスロットにデプロイして、稼働確認を行います。そのあと、スロットを入れ替える機能である「スワップ」を使って、ステージングスロットと本番スロットをひっくり返して、リリースします。いわゆるBlue-Greenデプロイというやつです。

また、負荷が増大したときにはAzureポータルからポチポチっという操作一つで簡単にスケールアウトが可能です。

つまりApp Serviceを使うことによって運用負荷を減らすことが出来て、Blue-Greenデプロイを使うことにより検証を十分に行った上での確実なリリースを実現し、そして負荷が増大したときのスケールアウトも簡単というわけです。

マネージド証明書による無料で更新作業不要な証明書管理を実現

App Serviceには「マネージド証明書」という機能があります。これを使うと以下のことを実現が出来ます。

  • 証明書の取得費用が無料となる。
  • 証明書の更新作業を自動で行ってくれる。

証明書取得費用が無料の件ですが、App Serviceにカスタムドメインを登録してそのドメインの所有権確認を行うことにより、そのドメインをCNとした証明書が無料で発行出来ます。ちゃんとした認証局から発行されたちゃんとした証明書であることは当然なのですが、エクスポートすることが出来なかったり、ワイルドカードを使うことが出来なかったりと、いくつか制限事項はあります。それでもやはり無料で正当な証明書を利用することができるアドバンテージは大きいと思います。

またApp Service側で証明書更新も自動で行ってくれます。証明書の有効期限は6ヶ月で、その1ヶ月前に自動で更新されます。

これでいつも悩ましい証明書の運用管理から解放されるというわけです。ヤッタネ!!

VNET統合による仮想ネットワーク上リソースへのシームレスなアクセス

Shibbolethの認証データストアとして最もよく利用されるのがOpenLDAPかと思いますが、これに相当するマネージドなサービスがAzureにはないので、これだけは仮想マシン上に構築する必要があります。VNET統合という機能を使うと、本来仮想ネットワーク上にないApp Serviceから、仮想ネットワーク上にある仮想マシンへのシームレスなアクセスが実現可能になります。

Azure Database for MySQLによるフルマネージドなデータストア

Shibbolethの属性送信確認機能の結果保持など、至るところで結構RDBMSが必要になるケースがあります。そんなときAzureではフルマネージドなRDBMSであるAzure Database for MySQLがありますので、今回活用することとします。

マネージドIDとAzure Key Vaultによる資格情報へのセキュアなアクセス

ShibbolethにはLDAPへの接続パスワードなど、管理すべき資格情報がいくつかあります。後述しますが、今回はShibbolethの設定ファイル群をAzure Repos(AzureのマネージドなGitレポジトリ)で管理するので、自ずと資格情報もAzure Reposの管理対象となります。ただ、本番環境の資格情報をAzure Reposに保存すると、本来それを知る必要でない末端の開発者が本番の資格情報を知ることが出来てしまいます。もし開発者に悪い人がいたら、その資格情報を悪用して本番LDAPの中身を盗み見するかもしれませんし、そうでなくとも生のパスワードをAzure ReposのようなGitレポジトリに保存するのはあまり望ましいことではありません。

そこで、マネージドIDというものを使います。マネージドIDは平たく言えば、マネージドIDを有効にしたリソース自身からしか利用できないサービスプリンシパルになります。こちらの詳細な仕組みの説明は今回割愛します。以下の公式ドキュメントが参考になるかと思います。

https://docs.microsoft.com/ja-jp/azure/active-directory/managed-identities-azure-resources/overview

 

特に以下の図は、マネージドIDの理解の助けになるシーケンス図です。

※ 出典:マイクロソフト公式ドキュメント

マネージド サービス ID と Azure VM

 

前置きが長くなりましたが、Azure Key Vaultの説明です。Azure Key Vaultは証明書やパスワードなどの資格情報をセキュアに保存するAzureのサービスです。保存された資格情報が暗号化されるのはもちろんのこと、RBACによる資格情報への細かい認可も行うことが出来ます。

つまり、Azure Key VaultにLDAPやRDBなどのパスワードを登録し、マネージドIDにのみ参照権限を与え、App ServiceからマネージドIDを使って資格情報を取得し、環境変数経由でShibbolethに渡せば、末端の開発者が本番のパスワードを知ることなく、本番へのデプロイが可能になります。

サービスエンドポイントによるアクセス制御

仮想ネットワーク上にあるOpenLDAPはネットワークセキュリティグループでアクセス制御を施せばよいのですが、Azure Database for MySQLやAzure Key Vaultのような、仮想ネットワークに所属しないPaaSサービスについては、もちろんネットワークセキュリティグループでアクセス制御をかけることができません。やはり、Azure Database for MySQLやAzure Key Vaultはセキュアな情報を管理するので、Shibbolethが稼働するApp Serviceからのみ接続できるようにアクセス制御をしたほうがよいのは明らかです。

そこでサービスエンドポイントを使います。サービスエンドポイントを有効にしたサブネットは、そのサブネット内からのリソースへの接続は仮想ネットワーク内でルーティングされるようになります。例えば、Azure Database for MySQLのサービスエンドポイントを有効にしたサブネット内からのAzure Database for MySQLへの接続要求は、SNAT を経由して Azure が保有しているパブリック IPアドレスが送信元になるのではなく、Azure Database for MySQLへの接続を要求したリソースのプライベートIPアドレスが送信元となります。

このプライベートIPアドレスが送信元としてAzure Database for MySQLなどのPaaSに渡るので、PaaS側でこのリソースのプライベートIPアドレスをベースとした(実際はサブネット単位ですが)アクセス制御が可能となります。

ただし、App Service自体はPaaSサービスなので、特定の仮想ネットワークに所属するわけではないので、サービスエンドポイントが使えないようにも思いますが、そこは先程ご説明したVNET統合を使えば可能になります。

VNET統合を使うと、基本的な通信は先の構成図に記載のVNET統合用サブネットを経由するようになります。つまりこのVNET統合用サブネットに対してサービスエンドポイントを有効にして、Azure Database for MySQLなどのPaaSサービス側でVNET統合用サブネットからのアクセスに限定することにより、ShibbolethからしかアクセスすることができないPaaSサービスが出来上がるというわけです。

Azure DevOpsによるビルド・デプロイ・リリース作業の自動化

Azure DevOpsを使うことで、Shibbolethのビルド・デプロイ・リリースを完全に自動化することが可能です。その流れを以下に簡単にまとめてみました。ほとんどの作業が自動化されているのがわかるかと思います。

  1. 【手動】ステージング環境のShibbolethの設定ファイルを変更し、Azure ReposにPushする。
  2. 【自動】Azure Pipelinesにより、Dockerイメージのビルド、Container RegistryへのPushが行われる。
  3. 【自動】ステージング環境のApp ServiceがContainer Registryから最新のDockerイメージをPullし、App Serviceの再起動を行う。
  4. 【手動】ステージング環境の動作確認を行う。(自動化も可能だが今回の作業からは除外)
  5. 【手動】本番への適用を行うため、ステージングスロットと本番スロットのスワップを行う
  6. 【手動】本番環境の動作確認を行う。(自動化も可能だが今回の作業からは除外)

基本的にどうしても手動でやらなければいけないのは1と5だけになります。4と6の動作確認は、SeleniumなどのE2Eテストツールを使うことにより自動化も十分可能ですが、今回のスコープからは外しています。

手動でやらなければいけない1と5の作業もほとんど手間がかからない作業ですので、ビルド・デプロイ・リリースはAzure DevOpsによって劇的に楽になっているというのがおわかりになるかと思います。

Application Insightsによるパフォーマンス管理

アプリケーションは常にパフォーマンスとの戦いで、それはShibbolethも同じです。多数の同時接続によりパフォーマンスが出なくなったり、予期しないエラーが発生しサービスに影響が出ることもあります。

仮想マシン上に構築したShibbolethは、それらの解析をログとニラメッコしながら行うこととなりますが、AzureのAPM(Application Performance Management)であるApplication Insightsを使うと、その解析作業が劇的に楽になります。

Shibbolethを起動するときのJavaの実行引数に、Application InsightsのSDKを渡してあげることにより、Shibboleth側のソースコードを全く変更せずに、CPU使用率などのメトリクスや各種ログ情報(idp-process.logやidp-audit.logなど)などをApplication Insightsに送信することが出来ます。

Application Insightsではそのメトリクスやログをもとにパフォーマンスや例外情報などをグラフなどにして可視化することで、分析作業の手間を大幅に削減することが出来ます。

例えばパフォーマンス情報は以下のようにグラフ化されて表示することが出来ます。このグラフに基づき、Shibbolethのパフォーマンスが悪いのはどの時間帯か、何がボトルネックとなっているのかなどを簡単に分析することが可能です。

 

Application Insightsの詳細については以下のブログをご参考ください。

世界一わかりみの深いAPM 〜Application Insightsでアプリケーションパフォーマンス管理に全集中!!〜

処理の流れ

では、クラウドネイティブなShibbolethがどのような流れでビルド・デプロイ・リリースが行われるかを本章でご説明したいと思います。

  1. Shibboleth構築エンジニアは、Shibbolethの設定ファイルをAzure ReposにPushする。
  2. Azure Reposは、リポジトリにPushされたことをAzure Pipelinesに伝える。
  3. Azure Pipelinesは、Azure ReposからShibbolethの設定ファイルを取得する。
  4. Azure Pipelinesは、設定ファイルに基づきDockerイメージをビルドする。
  5. Azure Pipelinesは、4でビルドしたDockerイメージをContainer RegistryにPushする。
  6. Container Registryは、自身のレジストリにPushされたことをApp Serviceに伝える。
  7. App ServiceはContainer RegistryからDockerイメージをPullしてステージングスロットにデプロイ・再起動を行う。
  8. ステージング環境の動作確認を行う。VNET統合されたVNET統合用サブネットにアクセスする。
  9. VNET統合用サブネット経由でOpenLDAPにアクセスする。
  10. VNET統合用サブネット経由でAzure Database for MySQLなどの各種PaaSサービスにアクセスする。この際、サービスエンドポイントによってアクセス制御を行う。
  11. 動作確認の後、問題がなければApp Serviceの設定画面にて、ステージングスロットと本番スロットをスワップする。
  12. 本番環境の動作確認を行う。VNET統合されたVNET統合用サブネットにアクセスする。
  13. VNET統合用サブネット経由でOpenLDAPにアクセスする。
  14. VNET統合用サブネット経由でAzure Database for MySQLなどの各種PaaSサービスにアクセスする。この際、サービスエンドポイントによってアクセス制御を行う。

構築してみよう!!

では構築してみましょう。

App Serviceの作成

まず最初にApp Serviceを構築します。Azureポータルの画面上部の検索テキストボックスに「App Service」と入力して表示される「App Service」をクリックしてください。

 

「作成」をクリックします。

 

「サブスクリプション」と「リソースグループ」はApp Serviceのリソースを作りたい先を選択します。「名前」はApp Serviceを一位に識別する名称であれば何でもOKです。「公開」は今回ShibbolethのDockerイメージからアプリケーションをビルドするので「Dockerコンテナ」を選択します。「オペレーティングシステム」はもちろん「Linux」、「地域」はなんでもOKですが、当方は全て「East US」で試しました。

 

App Serviceプランを作成します。「新規作成」をクリックします。

 

「サイズを変更します」をクリックします。

カスタムドメインその他諸々の必要な機能は「運用」でしか使えないため、この中からプランを選択します。

 

「次: Docker >」をクリックします。

 

取得するDockerイメージは後ほど細かく設定するので、取りあえず以下のデフォルトの設定値で今は問題ありません。「確認および作製」をクリックします。

 

「作成」をクリックします。しばし待つとApp Serviceのリソースが出来上がります。

 

ここまで出来ました。

仮想ネットワークおよびサブネットの作成

仮想ネットワークとVNET統合用サブネットとOpenLDAPを配置するサブネットを作成します。

Azureポータルの画面上部の検索テキストボックスに「仮想ネットワーク」と入力して表示される「仮想ネットワーク」をクリックしてください。

 

「作成」をクリックします。

 

「サブスクリプション」と「リソースグループ」はApp Serviceと同じものを選択します。「名前」は仮想ネットワークを識別する任意のものになります。「地域」はApp Serviceと同じものを選択します。最後に「次:IPアドレス >」をクリックします。

 

「サブネットの追加」をクリックし、VNET統合用のサブネットを作成します。「サブネット名」は任意の名称、「サブネットアドレス範囲」はとりあえず/24くらいとっておけばいいかと思います。もっと少なくてもいいかもしれませんがとりあえずこれで。「追加」をクリックします。

 

同様の手順でOpenLDAPが配置されるサブネットも作成します。「確認および作成」をクリックします。

 

「作成」をクリックします。しばらくすると仮想ネットワークとサブネットが出来上がります。

 

ここまで出来ました。

OpenLDAPの作成

先程作成したOpenLDAP用のサブネットにOpenLDAP用の仮想マシンを作成し、OpenLDAPを構築します。ただ、OpenLDAPの構築方法は本ブログの趣旨とは少々離れるため割愛致します。

ということで、本ブログをお読みの皆様によしなに作成いただいたとして、ここまで出来ました。

Azure Database for MySQLの作成

Azureポータルの画面上部の検索テキストボックスに「mysql」と入力して表示される「Azure Database for MySQL」をクリックしてください。

 

「作成」をクリックします。

 

「単一サーバー」をクリックします。

 

「サブスクリプション」と「リソースグループ」「場所」はApp Serviceと同じものを選びます。「サーバー名」は一意に識別できるもの、「バージョン」は5.7、「コンピューティングとストレージ」はサービスエンドポイントを使うために「汎用目的」以上、「管理者ユーザー名」「パスワード」はMySQLへの接続情報となるのでなにか適当なものを入力します。最後に「確認および作成」をクリックします。

 

「作成」をクリックします。しばらくすると出来上がります。

 

Shibbolethで属性送信情報その他色々な情報を格納するためには以下のスキーマが必要になりますので作成します。

CREATE TABLE `StorageRecords` (
  `context` varchar(255) NOT NULL,
  `id` varchar(255) NOT NULL,
  `expires` bigint(20) DEFAULT NULL,
  `value` longtext NOT NULL,
  `version` bigint(20) NOT NULL,
  PRIMARY KEY (`context`,`id`)
)

 

ここまで出来ました。

Azure Key Vaultの作成

Azureポータルの画面上部の検索テキストボックスに「key」と入力して表示される「キーコンテナー」をクリックしてください。

 

「作成」をクリックします。

 

「サブスクリプション」と「リソースグループ」「場所」はApp Serviceと同じものを選びます。「Key Vault名」はキーコンテナーを一意に識別できるものなら何でもOK、他はデフォルトで問題ありません。最後に「確認および作成」をクリックします。

 

「作成」をクリックします。しばらくすると出来上がります。

 

LDAPのパスワードとMySQLのパスワードのシークレットを作成します。Key Vaultのリソースにアクセスして左部メニューの「シークレット」をクリックして、「生成/インポート」をクリックします。

 

「名前」は「DBPASSWORD」、値はAzure Database for MySQLを作成したときの管理者ユーザー名のパスワードを入力します。その他の値は以下の通りに入力して、「作成」をクリックします。

 

同様にしてLDAPのパスワードも作成します。「名前」は「LDAPBINDPASSWORD」、値はOpenLDAPのBINDパスワード、その他の値は以下の通りに入力して、「作成」をクリックします。

 

ちなみに、後で必要になるのでシークレット識別子をメモしておきます。シークレットの一覧から「DBPASSWORD」を選択します。

 

「現在のバージョン」のところに表示されているシークレットをクリックします。

 

「シークレット識別子」をメモしておきます。

 

LDAPBINDPASSWORDについても同じことを行います。

 

ここまで出来ました。

Application Insightsの作成

まずApplication Insightsで収集されたログを格納するためのLog Analyticsワークスペースを作成します。Azureポータルの画面上部の検索テキストボックスに「log」と入力して表示される「Log Analyticsワークスペース」をクリックしてください。

 

「作成」をクリックします。

 

「サブスクリプション」と「リソースグループ」「地域」はApp Serviceと同じものを選びます。「名前」は一意に識別できるものであれば何でもOKです。最後に「確認および作成」をクリックします。

 

「作成」をクリックします。しばらくすると出来上がります。

 

次にApplication Insightsのリソースを作成します。Azureポータルの画面上部の検索テキストボックスに「insights」と入力して表示される「Application Insights」をクリックしてください。

 

「作成」をクリックします。

 

「サブスクリプション」と「リソースグループ」「地域」はApp Serviceと同じものを選びます。「名前」はこのリソースを一意に識別できるものなら何でもOK、「ワークスペースの詳細」は先ほど作成したLog Analyticsワークスペースを選びます。他の値は以下の画面のようにして、最後に「確認および作成」をクリックします。

 

「作成」をクリックします。しばらくすると出来上がります。

 

Application Insightsのリソースにアクセスして、左部メニューの「概要」をクリックします。「接続文字列」の部分をメモっておきます。後で必要になります。

 

ここまで出来ました。

App ServiceのマネージドID有効化

App ServiceのマネージドIDを有効にします。App Serviceのリソースにアクセスして、左部メニューの「ID」をクリックします。「システム割り当て済み」のタブをクリックして、「オン」をクリックします。最後に「保存」をクリックします。

 

オブジェクトIDはサービスプリンシパルのクライアントIDとなります。後で必要なのでメモします。

Azure Key Vaultへの権限の付与

Azure Key Vaultへのアクセス権限を付与します。マネージドIDに権限を付与して、先程作成したシークレットにアクセス出来るようにします。

Azure Key Vaultのリソースの画面にアクセスして、左部メニューの「アクセスポリシー」→「+アクセスポリシーの追加」の順にクリックします。

 

「キーのアクセス許可」「シークレットのアクセス許可」両方とも「取得」のみでOKです。「プリンシパルの選択」は先程のマネージドIDのオブジェクトIDで検索して、表示されたものを選択します。最後に「追加」をクリックします。

 

「保存」をクリックしないと反映されないので忘れずに!!

App Serviceの構成

App Serviceの構成を変更します。具体的にはApp Serviceに与える環境変数の定義を設定します。App Service側にて、Entity IDやLDAPの接続先などを設定してアプリケーション(Shibboleth)側に環境変数として渡して、Shibboleth側でそれらを展開することにより、Shibbolethから環境固有の設定を削除しようという狙いです。Shibboleth側の設定変更の方法については後述しますので、ここではApp Service側の設定を説明致します。

まず、App Serviceのリソースにアクセスして、「構成」をクリックします。

 

「+アプリケーション設定」をクリックして以下の設定を追加します。追加するときは必ず「デプロイスロットの設定」にチェックをいれてください。これをチェックしないとスワップしたときに値までもひっくり返ってしまいます。

名前 説明
IDP_ENTITY_ID

Shibboleth IdPのEntity ID

 
IDP_METADATA_FILE

idp-metadata.xml

Shibboleth IdPのメタデータのファイル名(/opt/shibboleth-tdp/metadataディレクトリへの配置を想定)
IDP_SIGNIG_CRT_FILE

idp-signing.crt

SAML Responseへの署名を検証するための証明書のファイル名(/opt/shibboleth-tdp/credentialsディレクトリへの配置を想定)
IDP_SIGNIG_KEY_FILE

idp-signing.key

SAML Responseに署名をするための秘密鍵のファイル名(/opt/shibboleth-tdp/credentialsディレクトリへの配置を想定)
IDP_ENCRYPTION_CRT_FILE

idp-encryption.crt

暗号化されたSAML Responseを復号化するための公開鍵を含む証明書のファイル名(/opt/shibboleth-tdp/credentialsディレクトリへの配置を想定)
IDP_ENCRYPTION_KEY_FILE

idp-encryption.key

SAML Responseを暗号化するための秘密鍵のファイル名(/opt/shibboleth-tdp/credentialsディレクトリへの配置を想定)
LDAP_HOST

構築したOpenLDAPのプライベートIPアドレス

 
LDAP_BIND_PASSWORD

@Microsoft.KeyVault(SecretUri=[LDAPBINDPASSWORDのシークレット識別子])

マネージドIDでAzure Key Vaultにアクセスし資格情報を取得
DB_URL

jdbc:mysql://[MySQLのサーバー名]:3306/[DB名]?serverTimezone=UTC

 
DB_USERNAME

MySQLのサーバー管理者ログイン名

 
DB_PASSWORD

@Microsoft.KeyVault(SecretUri=[DBPASSWORDのシークレット識別子])

マネージドIDでAzure Key Vaultにアクセスし資格情報を取得
APPLICATIONINSIGHTS_CONNECTION_STRING

Application Insightsの接続文字列

 

VNet統合の設定

VNET統合の設定をします。App Serviceのリソースにアクセスして、左部メニューの「ネットワーク」をクリックします。次に、「VNet統合」の「構成するにはここをクリック」をクリックします。

 

「VNetの追加」をクリックします。サブネットを選択する画面が表示されるので、先程作成したVNet統合用サブネットを選択して「OK」をクリックします。

サービスエンドポイントの作成

サービスエンドポイントを作成します。

Azure Database for MySQL

Azure Database for MySQLのリソースにアクセスして、左部メニューの「接続のセキュリティ」→「+既存の仮想ネットワークの追加」をクリックします。

 

「名前」には任意の名称を入力して、サブネットについては先に作成したVNet統合用サブネットを選択して、「有効」をクリックします。

 

最後に「保存」をクリックします。

Azure Key Vault

Azure Key Vaultのリソースにアクセスして、左部メニューの「ネットワーク」をクリックします。次に「+既存の仮想ネットワークを追加」をクリックします。

 

先に作成したVNet統合用サブネットを選択して、「有効化」をクリックします。

 

「追加」をクリックします。

 

「保存」をクリックします。

マネージド証明書の作成

App Serviceのリソースにアクセスして、左部メニューの「カスタムドメイン」をクリックします。

 

「カスタムドメイン」の部分にShibbolethのホスト名を入力して「検証」をクリックします。そのときに表示された「カスタムドメインの検証ID」をメモします。

 

以下のTXTレコードをDNSサーバーに追加してください。先のカスタムドメインがiloveshib.siostest02.comだった場合、以下のレコード名はasuid.iloveshib.siostest02.comとなります。

レコード名

asuid.[カスタムドメイン名]

先程メモした「カスタムドメインの検証ID」の値

 

以下の画面に戻ってもう一度「検証」をクリックして、「ホスト名の利用可否」「ドメイン所有権」が以下のように両方緑色のチェックマークが付けばOKです。「カスタムドメインの追加」をクリックします。

 

「TLS/SSLの設定」→「秘密キー証明書(.pfx)」→「+App Serviceマネージド証明書の作成」の順にクリックします。

 

赤の四角で囲ってあるところを見ますと、マネージド証明書を作成するためにはShibbolethのホスト名のCNAMEとして、App Serviceのホスト名を指定しなければいけません。CNAMEレコードを定義しましょう。

 

CNAMEを定義してもう一度先の画面にアクセスすると以下のような表示になるはずです。「作成」をクリックします。

 

「TLS/SSLの設定」→「バインド」→「+TLS/SSLバインディングの追加」の順にクリックします。

 

「カスタムドメイン」は先程追加したカスタムドメインを選択します。「プライベート証明書の捺印」は先程追加したPFX形式の証明書を選択します。「TLS/SSLの種類」は「IPベースのSSL」「SNI SSL」のどちらかを選択します。「IPベースのSSL」はApp Service PlanがStandardかPremiumだとプランあたり1つまで無料で追加できます。「SNI SSL」はいくつ追加しても無料です。最後に「バインディングの追加」をクリックします。

 

ShibbolethのDockerイメージを作成するための準備

ShibbolethのDockerイメージを作成するための設定ファイル(Dockerfileなど)を作成します。構成は以下のとおりです。

.
├── Dockerfile
├── bin
│   └── init.sh
├── lib
│   └── applicationinsights-agent-3.1.0.jar
└── shibboleth-idp

ではそれぞれのファイルについて詳細を説明致します。

Dockerfile

ShibbolethをのDockerイメージをビルドするためのDockerfileです。

# ShibbolethのDockerイメージを使います。ラクチンにShibboleth構築出来ます。
# このDockerイメージの詳細は以下のブログを参照のこと!!
# https://tech-lab.sios.jp/archives/19270
FROM noriyukitakei/dockerized-shibboleth-idp4:latest

# Shibbolethの設定ファイル群を格納しているディレクトリ
# (Shibbolethインストール時のデフォルト設定ファイル格納パス/opt/shibboleth-idpディレクトリ配下にあるファイルに相当)
ADD shibboleth-idp/ /opt/shibboleth-idp/

# Application Insightsでパフォーマンス管理をするために、Application InsightsのAgentを使います。
# Application InsightsのAgentをコンテナにコピーする。
RUN mkdir /opt/applicationinsights
COPY lib/applicationinsights-agent-3.1.0.jar /opt/applicationinsights/

# App Service上に展開するDockerコンテナにSSHでログイン出来るようにsshdを立ち上げます。 
# SSHログインのパスワードを定義する。 
ENV SSH_PASSWD "root:Docker!"

# OpenSSHをインストールする。
RUN apk add openssh && \
    ssh-keygen -A && \
    sed -i -e 's/#Port 22/Port 2222/' /etc/ssh/sshd_config && \
    sed -i -e 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config

# 起動スクリプトをコンテナにコピーする。
COPY bin/init.sh /usr/local/bin/init.sh

RUN chmod +x /usr/local/bin/init.sh

CMD ["/usr/local/bin/init.sh"]

# App Service内にデプロイするコンテナ内にSSHするための規定のポート番号は2222になります。
EXPOSE 2222

bin/init.sh

Shibbolethのコンテナ内のプロセスを起動するためのスクリプトです。sshdとjavaのプロセスを起動します。

#!/bin/sh

# OpenSSHを起動します。
echo "$SSH_PASSWD" | chpasswd
/usr/sbin/sshd &

# Jettyを起動します。
# DockerfileからDockerイメージにコピーしたApplication InsightsのAgentのパスを-javaagent引数に指定します。
$JAVA_HOME/bin/java -javaagent:/opt/applicationinsights/applicationinsights-agent-3.1.0.jar -jar $JETTY_HOME/start.jar $JETTY_JAVA_ARGS

lib/applicationinsights-agent-3.1.0.jar

Application InsightsのAgentになります。以下のURLからダウンロードしてください。

https://docs.microsoft.com/en-us/azure/azure-monitor/app/java-in-process-agent

shibboleth-idp

Shibbolethの設定ファイル群を格納しているディレクトリになります。Shibbolethインストール時のデフォルト設定ファイル格納パス/opt/shibboleth-idpディレクトリ配下にあるファイルに相当します。

そして、これらのファイルのいくつかを今回の環境に合わせて修正しなければなりません。App ServiceにデプロイするShibbolethのDockerイメージは、本番スロットとステージングスロットで当然同じものを使います。しかしながら本番スロットとステージングスロットではEntity IDやLDAPの接続先などが異なります。その差異を吸収するために、App Serviceのアプリケーション設定により、App Serviceの構成でShibbolethに環境変数を注入しました。そしてこれからは、その環境変数をShibbolethの設定ファイル内で受け取るための変更をします。

conf/idp.properties

以下のように変更してください。

# App Serviceのアプリケーション設定で定義したIDP_ENTITY_IDを設定する。
idp.entityID=${IDP_ENTITY_ID}

# App Serviceのアプリケーション設定で定義したIDP_METADATA_FILEを設定する。
idp.entityID.metadataFile=%{idp.home}/metadata/${IDP_METADATA_FILE}

# App Serviceのアプリケーション設定で定義したIDP_SIGNIG_KEY_FILEを設定する。
idp.signing.key=%{idp.home}/credentials/${IDP_SIGNIG_KEY_FILE}

# App Serviceのアプリケーション設定で定義したIDP_SIGNIG_CRT_FILEを設定する。 
idp.signing.cert=%{idp.home}/credentials/${IDP_SIGNIG_CRT_FILE}

# App Serviceのアプリケーション設定で定義したIDP_ENCRYPTION_KEY_FILEを設定する。 
idp.encryption.key=%{idp.home}/credentials/${IDP_ENCRYPTION_KEY_FILE}

# App Serviceのアプリケーション設定で定義したIDP_ENCRYPTION_CRT_FILEを設定する。 
idp.encryption.cert=%{idp.home}/credentials/${IDP_ENCRYPTION_CRT_FILE}

conf/ldap.properties

以下のように変更してください。

# App Serviceのアプリケーション設定で定義したLDAP_HOSTを設定する。
idp.authn.LDAP.ldapURL = ldap://${LDAP_HOST}:389

conf/global.xml

MySQLに接続するための設定を追加します。以下の内容を<beans>〜</beans>の間に追加してください。

<bean id="shibboleth.JPAStorageService"
    class="org.opensaml.storage.impl.JPAStorageService"
    p:cleanupInterval="%{idp.storage.cleanupInterval:PT10M}"
    c:factory-ref="shibboleth.JPAStorageService.EntityManagerFactory" />

<bean id="shibboleth.JPAStorageService.EntityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceUnitName" value="storageservice" />
    <property name="packagesToScan" value="org.opensaml.storage.impl" />
    <property name="dataSource" ref="shibboleth.JPAStorageService.DataSource" />
    <property name="jpaVendorAdapter" ref="shibboleth.JPAStorageService.JPAVendorAdapter" />
    <property name="jpaDialect">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
    </property>
</bean>

<bean id="shibboleth.JPAStorageService.JPAVendorAdapter"
    class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    <property name="database" value="MYSQL" />
</bean>
<bean id="shibboleth.JPAStorageService.DataSource"
    class="com.zaxxer.hikari.HikariDataSource" destroy-method="close" lazy-init="true"
    p:driverClassName="com.mysql.jdbc.Driver"
    p:jdbcUrl="%{DB_URL}"
    p:username="%{DB_USERNAME}"
    p:password="%{DB_PASSWORD}" />

credentials/secret.properties

以下のように変更してください。

# App Serviceのアプリケーション設定で定義したLDAP_BIND_PASSWORDを設定する。
idp.authn.LDAP.bindDNCredential = ${LDAP_BIND_PASSWORD}

metadata/idp-metadata-stg.xml

ステージング用のShibbolethのメタデータファイルを新規に作成してください。

credentials/idp-signing-stg.crt

ステージング用のShibbolethの証明書のファイル(SAML Responseへの署名の検証用)を新規に作成してください。

credentials/idp-signing-stg.key

ステージング用のShibbolethの秘密鍵ファイル(SAML Responseへのデジタル署名用)を新規に作成してください。

credentials/idp-encryption-stg.crt

ステージング用のShibbolethの証明書ファイル(暗号化されたSAML Responseを復号化するための公開鍵を含む証明書)を新規に作成してください。

credentials/idp-encryption-stg.key

ステージング用のShibbolethの秘密鍵ファイル(SAML Responseの暗号化用)を新規に作成してください。

ステージングスロットの作成

ステージングスロットを作成します。App Serviceのリソースにアクセスして、左部メニューの「デプロイスロット」をクリックします。次に「+スロットの追加」をクリックします。

 

「名前」には任意の名称、「次から設定を複製」には複製元のスロットを選択します。これでアプリケーション設定で追加したパラメータがすべてステージングスロットにコピーされます。「追加」をクリックします。

 

ステージングスロット用の以下のリソースを作成します。手順は本番スロットで作成したときと全く同じです。

  • 仮想ネットワークおよびサブネットの作成
  • OpenLDAPの作成
  • Azure Database for MySQLの作成
  • Azure Key Vaultの作成
  • Application Insightsの作成
  • App ServiceのマネージドID有効化
  • Azure Key Vaultへの権限の付与
  • VNet統合の設定
  • サービスエンドポイントの作成
  • マネージド証明書の作成

そして、本番スロットと同様にApp Serviceの「構成」メニューより、アプリケーション設定を行います。以下の値を定義してください。

名前 説明
IDP_ENTITY_ID

ステージング用のShibboleth IdPのEntity ID

 
IDP_METADATA_FILE

idp-metadata-stg.xml

Shibboleth IdPのメタデータのファイル名(/opt/shibboleth-tdp/metadataディレクトリへの配置を想定)
IDP_SIGNIG_CRT_FILE

idp-signing-stg.crt

SAML Responseへの署名を検証するための証明書のファイル名(/opt/shibboleth-tdp/credentialsディレクトリへの配置を想定)
IDP_SIGNIG_KEY_FILE

idp-signing-stg.key

SAML Responseに署名をするための秘密鍵のファイル名(/opt/shibboleth-tdp/credentialsディレクトリへの配置を想定)
IDP_ENCRYPTION_CRT_FILE

idp-encryption-stg.crt

暗号化されたSAML Responseを復号化するための公開鍵を含む証明書のファイル名(/opt/shibboleth-tdp/credentialsディレクトリへの配置を想定)
IDP_ENCRYPTION_KEY_FILE

idp-encryption-stg.key

SAML Responseを暗号化するための秘密鍵のファイル名(/opt/shibboleth-tdp/credentialsディレクトリへの配置を想定)
LDAP_HOST

構築したステージング用OpenLDAPのプライベートIPアドレス

 
LDAP_BIND_PASSWORD

@Microsoft.KeyVault(SecretUri=[ステージング用LDAPBINDPASSWORDのシークレット識別子])

マネージドIDでAzure Key Vaultにアクセスし資格情報を取得
DB_URL

jdbc:mysql://[ステージング用MySQLのサーバー名]:3306/[DB名]?serverTimezone=UTC

 
DB_USERNAME

ステージング用MySQLのサーバー管理者ログイン名

 
DB_PASSWORD

@Microsoft.KeyVault(SecretUri=[ステージング用DBPASSWORDのシークレット識別子])

マネージドIDでAzure Key Vaultにアクセスし資格情報を取得
APPLICATIONINSIGHTS_CONNECTION_STRING

ステージング用Application Insightsの接続文字列

 

 

ここまで出来ました。

Container Registryの作成

Container Registryを作成します。Azureポータルの画面上部の検索テキストボックスに「レジストリ」と入力して表示される「コンテナレジストリー」をクリックしてください。

 

「サブスクリプション」「リソースグループ」「場所」はApp Serviceと同じものにします。「レジストリ名」はAzure全体で一意なものを入力します。最後に「確認および作成」をクリックします。

 

「作成」をクリックします。

 

管理者ユーザーを有効にします。左部メニューの「アクセスキー」をクリックして、「管理者ユーザー」をクリックして「有効」にします。

Azure DevOpsの設定

CI/CDの設定を行います。以下のURLよりAzure DevOpsのポータルにアクセスします。「+New project」をクリックします。

 

任意のプロジェクト名を入力します。VisibilityはとりあえずPrivateにして「Create」をクリックします。

 

Azure ReposのCloneのためのURLをメモします。左部メニューのAzure Reposのアイコン(下から4番目の赤いの)をクリックして「Clone to your conputer」の部分のURLをメモします。

 

先程のURLをもとにAzure Reposのリポジトリをcloneして、ShibbolethのDockerイメージを作成するための準備で作成したファイル群をPushします。

次にAzure PipelinesでCI/CD用のシナリオを作成します。下から3番めの青いロケットのアイコンをクリックして「Create Pipeline」をクリックします。

 

Azure Reposを使うので「Azure Repos Git」を選択します。

 

先程作成したリポジトリを選択します。

 

Dockerイメージをビルドして、Container RegistryにPushするので、「Docker」の「Build and push an image to Azure Container Registry」をクリックします。

 

作成したContainer Registryがあるサブスクリプションを選択します。

 

作成したContainer Registryの情報を入力します。「Container Registry」には作成したレジストリ名を選択します。「Image Name」にはContainer RegistryにPushするときの任意のイメージ名を入力します。「Dockerfile」はそのままでいいです。この記述はAzure Container RegistryにあるリポジトリのトップにあるDockerfileを取得するという意味です。

 

「Show assistant」をクリックします。

 

「Azure Web App for Containers」を選択します。これは指定されたContainer RegistryからApp SerivceにDockerイメージをデプロイしてApp Seeviceを再起動するタスクです。

 

「Azure Subscription」と「App name」を選択して、作成したApp Serivceを指定します。「Deploy to Slot or App Service Environment」にチェックをいれてステージング用のスロットにデプロイするように指定します。「Image name」は先程Container RegistryにPushするときに指定したイメージ名と同じものを指定します。最後に「Add」をクリックします。

azure-pipelines.ymlを少し修正します。containersの部分を以下のように修正しています。$(containerRegistry)と$(imageRepository)は自動で生成された変数で、ビルドされたDockerイメージが格納されたリポジトリのホスト名とイメージ名が入っています。containersは、App ServiceがビルドするDockerイメージを指定する項目になります。当然以下のように指定するのは明らかなのですが、なぜかデフォルトで入っている値がおかしな値でApp Serviceへのデプロイに失敗するので以下のように修正します。

    - task: AzureWebAppContainer@1
      inputs:
        azureSubscription: 'MVP Extended Azure Benefit適用サブスクリプション(0bda4ee2-ff08-44d9-8c7e-63c86b3af316)'
        appName: 'shibapp-prd'
        deployToSlotOrASE: true
        resourceGroupName: 'shibboleth'
        slotName: 'stg'
        containers: '$(containerRegistry)/$(imageRepository):${{ variables.tag }}'

 

作成したazure-pipelines.ymlを保存して動かして見ましょう!!「Save and run」をクリックします。

 

「Save and run」をクリックします。するとビルドが走ります。

Pipelineの一覧画面で最新のPipelineのアイコンが以下のように緑のチェックマークになればいちおうビルド・デプロイは成功です。

 

やっと全てのリソースが出来上がりました。

試してみよう!!

では実際に試してみましょう!!

CI/CDとBlue-Greenデプロイしてみる

処理の流れでも説明した以下の流れ確認してみたいと思います。

  1. Shibboleth構築エンジニアは、Shibbolethの設定ファイルをAzure ReposにPushする。
  2. Azure Reposは、リポジトリにPushされたことをAzure Pipelinesに伝える。
  3. Azure Pipelinesは、Azure ReposからShibbolethの設定ファイルを取得する。
  4. Azure Pipelinesは、設定ファイルに基づきDockerイメージをビルドする。
  5. Azure Pipelinesは、4でビルドしたDockerイメージをContainer RegistryにPushする。
  6. Container Registryは、自身のレジストリにPushされたことをApp Serviceに伝える。
  7. App ServiceはContainer RegistryからDockerイメージをPullしてステージングスロットにデプロイ・再起動を行う。
  8. ステージング環境の動作確認を行う。VNET統合されたVNET統合用サブネットにアクセスする。
  9. VNET統合用サブネット経由でOpenLDAPにアクセスする。
  10. VNET統合用サブネット経由でAzure Database for MySQLなどの各種PaaSサービスにアクセスする。この際、サービスエンドポイントによってアクセス制御を行う。
  11. 動作確認の後、問題がなければApp Serviceの設定画面にて、ステージングスロットと本番スロットをスワップする。
  12. 本番環境の動作確認を行う。VNET統合されたVNET統合用サブネットにアクセスする。
  13. VNET統合用サブネット経由でOpenLDAPにアクセスする。
  14. VNET統合用サブネット経由でAzure Database for MySQLなどの各種PaaSサービスにアクセスする。この際、サービスエンドポイントによってアクセス制御を行う。

 

まず、ログイン画面をちょっと修正して、フッターの部分を「hogehoge」と変更します。

変更してAzure ReposにCommitおよびPushするとPipelineが走り出します。

 

しばらくしてステージングスロットのShibbolethを確認すると、たしかにフッター部分が「hogehoge」に変わっています。

さて、ステージング環境で問題なかったので、ステージングスロットと本番スロットを入れ替えて、本番リリースしましょう。Blue-Greenデプロイですね。App Serviceのリソースにアクセスして、左部メニューの「デプロイスロット」をクリックします。次に「スワップ」をクリックします。

 

「スワップ」をクリックします。

 

本番スロットのShibbolethにアクセスすると、たしかにhogehogeに変わっています!!(変化がわかりにくいですが)

Shibbolethの設定ファイルを変えるだけで、ステージング環境にリリースされ、ステージング環境で十分にテストした上でBlue-Greenデプロイで安全に本番リリースするという夢のようなShibbolethが完成しました!!

APMしてみる

ログとかパフォーマンスを見てみましょう。Application Insightsのリソースにアクセスして、左部メニューの「パフォーマンス」をクリックします。確かにパフォーマンスが取得できています!!

 

アプリケーションマップでも確かにShibbolethがAzure Database for MySQLにアクセスしているのが可視化されています。残念ながらLDAPへのアクセスはApplication InsightsのAgentの仕様で可視化されないのですが。。。

 

ログもApplication Insightsの方で取得出来ています。高度な分析が出来るクエリによって障害解析がはかどりそう!!

スケールアウトしてみる

ではApp Serviceの醍醐味であるスケールアウトしてみましょう。これもGUIで一発です。

しかし、その前にARRアフィニティを有効にする必要があります。これはいわゆるロードバランサーのCookieパーシステンスな機能です。Shibbolethは、画面遷移で使われるSpring Web Flowと言うフレームワークのステートを保存するのにセッションを利用していて、完全にステートレスにならないので、この対応が必要なのです。App Serviceのリソースにアクセスして、左部メニューの「構成」をクリックします。「全般設定」のタブをクリックして、「ARRアフィニティ」を「有効」にチェックします。

 

App Serviceのリソースにアクセスして、左部メニューの「スケールアウト(App Serviceのプラン)」をクリックします。「インスタンス数」を3にしてみます。

 

これも見た目わかりにくいですが、問題なくSSO出来ます!!

まとめ

クラウドネイティブなShibbolethはいかがでしたでしょうか?仮想マシンで構築するのとはまるで異次元な感じで便利になっていますよね。ワタクシ未来を感じました。ドラえもんはすぐそこまで来ているって感じですよね。No CloudNative, No Life!!

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。

コメントを残す

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