WebSphereでのSAML SSOシナリオにおけるSPの構築
前回はIdPを構築しましたので、今回はSPを作ります。
実は全3回分を一気に書き上げて保存しておいたのを、あまりに長すぎるので三分割したんですが、残り3分の2を消してしまいました。。。心を痛めつつ、なるべく新鮮さを失わないように書いていきます。
SP環境構築
IHSのSSL有効化
SAMLのSSOシナリオにおいては、やはり通信経路は暗号化(SSL)するのがグッドプラクティス(というかほぼMUST)ですので、IHSとの通信はHTTPSとします。鍵と証明書は作成済みなので、これをIHSに食わせてやります(IHSからWASの通信はIHSプラグインの設定で、リクエストがHTTPであればHTTP、HTTPSであればHTTPSになります。IHSでSSLをほどいてssl-accelerator
として動作させることも可能です)。ついでにクライアント認証できるように、CAの証明書も持たせてやります。前の手順で一度PKCS12
キーストアを作っていますが、作り直します。
mac:$ openssl pkcs12 -inkey ~/openAM/work/ihs.key -name www.docker.mognet.net -caname mognetca \
-in ihs.crt -export -out www.docker.mognet.net.p12 -CAfile ~/mognetCA/cacert.pem -chain
mac:$ cp www.docker.mognet.net.p12 /opt/IBM/HTTPServer/conf
httpd.conf
を修正して、SSLを有効化します。
849,855c849,860
< #LoadModule ibm_ssl_module modules/mod_ibm_ssl.so
< #Listen 443
< #SSLCheckCertificateExpiration 30
< #<VirtualHost *:443>
< #SSLEnable
< #Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
< #</VirtualHost>
---
> LoadModule ibm_ssl_module modules/mod_ibm_ssl.so
> Listen 443
> SSLCheckCertificateExpiration 30
> <VirtualHost *:443>
> ServerName www.docker.mognet.net
> SSLEnable
> KeyFile /opt/IBM/HTTPServer/conf/www.docker.mognet.net.p12
> SSLServerCert www.docker.mognet.net
> SSLClientAuth Optional
> SSLProtocolEnable TLSv10 TLSv11 TLSv12
> Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
> </VirtualHost>
857c862
< #SSLDisable
---
> SSLDisable
コンテナにログインし、PKCS12
キーストアのパスワードファイルを生成して、IHSを再起動します。
mac:$ docker exec -it ihs bash
root:# /opt/IBM/HTTPServer/bin/sslstash -c /opt/IBM/HTTPServer/conf/www.docker.mognet.net.sth crypto password
root:# /opt/IBM/HTTPServer/bin/apachectl restart
ブラウザを起動してHTTPS接続できることを確認します。ついでにWAS連携できていることも確認、、、の前に、IHSとWAS間の鍵交換がまだできていなかったので、WASのWebコンソールからHTTPSトランスポートチェーンの設定を完成させます。
「サーバー」ー「サーバー・タイプ」ー「Webサーバー」を開き、「webserver1」に入ります。「プラグイン・プロパティー」に入ります。「Webサーバー鍵ストア・ディレクトリーへコピー」を押します。「Webサーバー」に戻ります。「webserver1」にチェックして、「停止」→「開始」で再起動です。
無事にsnoop
サーブレットまでSSL通信できました。
SAML TAIの構成
WASが提供する重要な機能であるTAI(Trust Association Interceptor)を構成します。Interceptor
の名の通り、この機能はリクエストをインターセプトして、言い換えればSAMLアサーションを捕まえて、内容(署名や宛先など)を確認したうえで、javax.security.auth.Subject
オブジェクトを作成してくれます。オブジェクトはサーブレットコンテクストから、getCallerSubject()
メソッドで取得できます。SAML処理の重要というか要の部分を製品がやってくれるというわけですから、これは助かりますね。
Webコンソールから設定していきます。「セキュリティー」ー「グローバル・セキュリティー」を開き、「アプリケーション・セキュリティーを使用可能にする」を有効にして「適用」します。同じく「グローバル・セキュリティー」から、「WebおよびSIPセキュリティー」を開き、「トラスト・アソシエーション」に入ります。「トラスト・アソシエーションを使用可能にする」を有効にして「適用」します。再度「トラスト・アソシエーション」に入り、今度は「インターセプター」に入ります。「新規作成」を押します。
下表のとおり入力し、「適用」します。
入力項目 | 入力値 |
---|---|
インターセプター・クラス名 | com.ibm.ws.security.web.saml.ACSTrustAssociationInterceptor |
カスタム・プロパティー名 | sso_1.sp.acsUrl |
カスタム・プロパティー値 | https://www.docker.mognet.net/samlsps/acs |
カスタム・プロパティー名 | sso_1.sp.idMap |
カスタム・プロパティー値 | idAssertion |
注目すべき点は、acsUrl
の値です。ここにはIdP(ユーザ)から見たSPへのエントリポイントのURLが入ります。URLホスト部はWebサーバのホスト名になります。後ろの/samlsps/acs
はWASが提供するサーブレットで、後でデプロイします。コンテクストルート/samlsps
は固定(初期値)で、/samlsps/*
が全て同じサーブレットにマッピングされてます。なので、acs
の部分はどんな文字列でも構いません。このサーブレット自体は、単純にリクエストの中に設定されたRelayState
パラメタのURLにリダイレクトさせる機能を持っているだけです。Subject
の生成などは全てTAIの機能です。
続けて、「グローバル・セキュリティー」の「カスタム・プロパティー」に入ります。定義済みの「com.ibm.websphere.security.DeferTAItoSSO」を開きます。プロパティ値に"com.ibm.ws.security.web.saml.ACSTrustAssociationInterceptor"
を入力して「OK」します。さらに、カスタム・プロパティーを「新規作成」します。プロパティー名に"com.ibm.websphere.security.InvokeTaibeforeSSO"
、プロパティー値に"com.ibm.ws.security.web.saml.ACSTrustAssociationInterceptor"
をそれぞれ入力して「OK」します。
これらのプロパティはそれぞれ、SSO前後に起動(Invoke
)するInterceptorクラスを指定します。ここでは、SAML TAI
を使用するため、全てcom.ibm.ws.security.web.saml.ACSTrustAssociationInterceptor
を指定します。
さらに、「グローバル・セキュリティー」から「ユーザー・アカウント・リボジトリー」の「構成...」を押します。「関連項目」の「トラステッド認証レルム・インバウンド」に入ります。「外部レルムの追加...」を押します。レルム名にIdPのURL(正確にはEntityID
)である"https://www.alto.net/openam"
を入力して「OK」します。構成を「保存」して、サーバーを再起動します。
先ほどのTAIのカスタム・プロパティーの設定で、sso_1.sp.idMap
の値をidAssertion
にしました。他に設定可能な値は、localRealm
です。localRealm
を指定すると、JavaEEサーバでJDBCレルム等で管理しているユーザとSAMLアサーションのユーザ情報をマッピングし、管理しているユーザ属性を取ってきてSubject
を生成してくれます。今回はユーザリポジトリをWASに持たせていないので、idAssertion
を指定して、SAMLに乗ってきた属性をそのままSubject
にしてもらいます。なお、localRealmThenidAssertion
という選択肢も取れて、これにするとまずはWASのリポジトリからユーザを検索し、いなかったらSAMLの属性をそのまま使うという動きになります。
改めて、ここではSAMLアサーションを発行したEntityID
を信頼する(トラステッドにする)ということをしました。抽象的でわかりづらいですが、具体的に言えばSAMLアサーション中の、EntityID
属性の値がhttps://www.alto.net/openam
となっているものは認証済みとして信頼しますよ、ということです(もちろん、実際にはさらに署名の検証等もしますので、この設定だけをもって全てのトラステッドレルムからのSAMLアサーションを受け入れる訳ではありません)。
ACSアプリケーションのデプロイ
続けて管理コンソールからWASが提供するACSアプリケーションをデプロイします。
「アプリケーション」ー「新規アプリケーション」を開き、「新規エンタープライズ・アプリケーション」に入ります。「リモートファイルシステム」にチェックし、/opt/IBM/WebSphere/AppServer/installableApps/WebSphereSamlSP.ear
を選択して「次へ」いきます。デプロイ自体は「ファストパス」でも可能ですが、見ておきたい部分があるので、「詳細」を選んで「次へ」いきます。ほとんどデフォルトのまま「次へ」進んでいきますが、「モジュールのマッピング」だけはモジュールを選択した(チェックした)状態で、「クラスターおよびサーバー」からAPサーバとWebサーバー両方を選んで「適用」してから、「次へ」進みます。Webサーバーも含めることで、プラグインの生成と伝搬が自動で行われます。さらにデフォルトのまま進んでいくと、「ユーザーまたはグループへのセキュリティー・ロールのマップ」画面になります。これが非常に重要で、ACSアプリケーションの必須要件として、宣言型のセキュリティ制約をかけ、適当なロールを作り、特殊サブジェクトである「トラステッド・レルム内で認証済みすべて」がこのロールに割り当てられる必要があります。
これは言い換えれば、どのようなアプリケーション(サーブレット)でもセキュリティ制約をかけて特殊サブジェクトをロールに割り当てるという実装をしていれば、ACSとして振舞うことができることを意味します。WASが提供するACSサーブレットは、単純にリクエストをリダイレクトするだけなので、自作サーブレットが直接業務リクエストを受けられてSAML属性を取得できるのであれば、その方がクライアントとの通信回数が1回分減るので、若干効率的です。特にサービスのエントリーポイントが単一なのであれば、わざわざこのアプリケーションを使う必要はないと思います。複数ある場合、たとえばメインメニューに入る場合と、商品ページに行く場合等でも、同一URLで受けてリクエストをディスパッチするようなサーブレット実装にすれば、わざわざ一度クライアントにレスポンスを返す必要はなさそうです(そういう風に実装する場合もあるでしょうけれど)。特にアクセスの多いサイトでは、一往復分のやりとりを減らしたい、ということも多いかもしれませんので。
残りの項目もデフォルトのまま「次へ」いき、「終了」して「保存」します。
これでSPとしての機能は実装できました。実際に提供するサービスとして、今回の検証ではサンプルアプリケーションのsnoop
を使います。
次回はIdPとSPとのメタデータを交換した上で、実際にSAMLによるシングルサインオンを実践してみます。