WebSphere(WAS)でのSAMLによるSSOの構成
SAMLによるSingle-Sign-On(SSO)をWebSphereでサポートしているということで、実際に試してみました。正直、SAMLという言葉を知ったのですらつい最近なので、認識相違等あればご指摘いただきたいです。
基本用語
いろいろな人がいろいろにまとめているので今更感はあるものの、基本用語を自分なりの解釈でまとめておきます。
- SAML
- "Security Assertion Markup Language"の略。"security assertion"とは直訳すればセキュリティ表明。この人(subject)はこういう認証を受けたよ、という情報を書くためのX509署名つきXML文書[^1]。さらにSAML Attributeといわれる属性を持たせることができる(UID等の具体的なユーザ属性)。 [^1]: 正式なドキュメントは[OASIS](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html)文書を参照
- IdP
- "Identity Provider"の略。"identity"すなわち認証を司る。本人認証にはパスワード認証や生体認証、ICカードなどの所持による認証など様々だが、SAML的にはIdPがどんな認証スキームをとろうがどうでもいい。とにかく、本人認証してアサーションを投げる(ID Federationなどともいう)機能を持ったエンティティ(存在、サービス)。
- SP
- "Service Provider"の略。サービスを誰に提供するかといえばもちろんユーザ。IdP・SPはどちらかというとユーザ視点での見え方。IdPからのアサーションを受取り、認証("authentication")されたものとして扱い、認可("authorization")した上でサービスを提供するエンティティ(存在、サービス)[^2]。もちろん、アサーションを検証する(正しい、信頼関係のあるIdPからの情報であることを確認する)というステップが不可欠。 [^2]: 実際に、認証されたユーザを具体的にどのロールにマッピングするかはSPに委ねられます。WASでは「特殊サブジェクト("special-subject")」として、「全員("EVERYONE")」、「全ての認証ユーザ("ALL_AUTHENTICATED_USERS")」、および「信頼されたレルム内で認証されたユーザ("ALL_AUTHENTICATED_USERS_IN_TRUSTED_REALMS")」に対してロールをマッピングできます。
- TAI
- "Trust Association Interceptor"の略。WASが提供する機能で、SAMLアサーション(署名つきXML文書)を解析して検証し、問題がなければ"`javax.security.auth.Subject`"オブジェクトを生成してくれる。`Subject`にはWASのオブジェクトが格納され、その中にSAML属性情報も含まれる。
- ACS
- "Assertion Consumer Service"の略。WASでは、TAIが検証してOKとなったアサーションを消費して(要は実際に使って)ユーザにサービスを提供するエンティティ。WASにもACSアプリケーションが`"/installableApps/WebSphereSamlSP.ear"`として提供されている(詳しくはあとでみていく)。一般的には、TAIの機能もACSに含まれる(つまりアサーションを検証し、認可してユーザにロールを割り当てて実際に使用するサービス)。
- LTPA
- "Lightweight Third Party Authentication"の略。IBM社独自の規格である。WASインスタンスで認証したユーザに対し、特殊なクッキー(その名も"`LTPAToken2`")を発行して別のWASインスタンスでも認証を通す(SSOする)仕組み。
- IdP-initiated SAML
- IdPをリクエストの起点としたSSO。つまり、IdP側でSPへのリンクを持ったポータルサイトなどを作っておいて、ユーザがそのリンクをクリックしたら未認証であれば認証して、SAMLアサーションをつけてSP(ACS)へHTTPリクエストするようなやり方。
- SP-initiated SAML
- IdP-initiatedとは逆に、SP側からSAMLアサーションをIdPに対して要求する。未認証のユーザがSP(ACS)へアクセスしてきたら、正式なSAMLの要求文書(SAMLリクエスト)をIdPに投げて、これをIdP側が処理(ユーザ認証)した上でSPへHTTPリクエスト(アサーション)を投げ返すやり方。
- SP-redirected SAML
- SP-initiatedで「正式なSAMLの要求文書」と書いたのとは対照的に、未認証のユーザがSP(ACS)へアクセスしてきたら、とりあえずIdPのログインページ(たいていform loginのページ)へリダイレクトしてしまえ、その後ここ(SP)に戻ってこられるようにHTTPリクエストのパラメタ(`RelayState`)に自分のURLを書き込んでおく、というやり方。
必要環境
WAS/IHSは構築済みとして、実験にあたって以下の環境が必要になります。
- ユーザリポジトリミドルウェア:具体的にはLDAP、今回は
OpenDJ
を使用 - IdPミドルウェア:OSS(?)の
OpenAM
を使用 - 認証局(CA):証明書発行(特にIdP用とSSL用)、今回は
OpenSSL
を使用
いずれもMacOS上で動作可能なので、IdP機能は全てMacOS上に構築します。もう少し砕いたソフトウェアスタックは以下のようになります。
IdPとSPとでホスト名が一緒だと紛らわしいので、このような構成にしています。IdPはalto.mognet.net
でMacのループバックアドレス127.0.0.1
の別名としてhosts
ファイルに定義します。SP側は構築済みのdocker
コンテナで、これらも実体はループバックアドレスへのポートフォワードでアクセスしますが、ループバックのエイリアスとして10.1.1.1-254
を切っており、docker
も同じホストアドレスのサブネットを切って構築しています。
ホスト情報 | ホスト名 | IPアドレス |
---|---|---|
IdP | alto.mognet.net | 127.0.0.1 |
SP | www.docker.mognet.net | 10.1.1.10 |
ポイントがあるとすれば、SPはWEB(IHS)とAP(WAS)の2サーバで構成しますが、IdPから見たSPのホストがWebサーバになる、ということでしょうか。もし負荷分散装置(ロードバランサ)を導入していれば、そのIPアドレスに対応するホスト名がIdPから見たSPのホスト名になります。あとでWASからSPのメタデータ(IdPに渡すSPの情報)を出力するときに割と効いてくるので念のため。
認証局構築
まずはオレオレ認証局を構成します。格好良くいえば「プライベート認証局」ですね。昔、職場でVerySign
という架空の認証局を、中間CA構成含めたあらゆる属性を真似て作ったことがありますが、このご時世なんとなく怖いので簡単にRoot認証局がEE証明書を発行する形でいきます。
MacOSにもopenssl
は導入されていますが、うまく動かないのでbrew install
で持ってきます。
mac:$ brew update
mac:$ brew install openssl
mac:$ echo 'export PATH=/usr/local/opt/openssl/bin:${PATH}' >> ~/.bash_profile # PATH設定
mac:$ . ~/.bash_profile
mac:$ which openssl
/usr/local/opt/openssl/bin/openssl
mac:$ openssl version
OpenSSL 1.0.2n 7 Dec 2017
mac:$ mkdir ~/mognetCA # CAのルートディレクトリを作成
mac:$ cd ~/mognetCA
mac:$ cp /usr/local/etc/openssl/misc/CA.sh ./ # 構築スクリプト雛形をコピー
mac:$ cp /usr/local/etc/openssl/openssl.cnf ./ # 設定ファイル雛形をコピー
CA構築スクリプトを編集します。あまりこだわるところではないので、コマンドパスを明示、証明書有効期限を約30年に延長し、CAディレクトリを変更している程度です。
61,62c61
< if [ -z "$OPENSSL" ]; then OPENSSL=openssl; fi
<
---
> OPENSSL=openssl
64c63
< CADAYS="-days 1095" # 3 years
---
> CADAYS="-days 10950" # 30 years
70,71c69
<
< if [ -z "$CATOP" ] ; then CATOP=./demoCA ; fi
---
> CATOP=~/mognetCA
スクリプト実行。
mac$ ./CA.sh
設定ファイルを編集します。発行ポリシーをpolicy_anything
(誰でもWelcome)、サブジェクトのデフォルト値を使いやすいように変更、KeyUsage
とnsCertType
をなんでもにしています。特に、サーバ証明書(SSL用)として使用する場合、KeyUsage
にKeyEnciperment
(SSLハンドシェイクの鍵交換時に公開鍵暗号方式でマスターキーを暗号化して交換するため)、nsCertType
にserver
がないとダメだったと思います。
8c8
< HOME = .
---
> HOME = ~/mognetCA
42c42
< dir = ./demoCA # Where everything is kept
---
> dir = ~/mognetCA # Where everything is kept
81c81
< policy = policy_match
---
> policy = policy_anything
129c129
< countryName_default = AU
---
> countryName_default = JP
134c134
< stateOrProvinceName_default = Some-State
---
> stateOrProvinceName_default = Tokyo
136a137
> localitiName_default = Koutou-Ku
139c140
< 0.organizationName_default = Internet Widgits Pty Ltd
---
> 0.organizationName_default = mognet.net
179c180
< # nsCertType = objsign
---
> nsCertType = objsign
185c186
< # nsCertType = client, email, objsign
---
> nsCertType = server, client, email, objsign
188c189
< # keyUsage = nonRepudiation, digitalSignature, keyEncipherment
---
> keyUsage = nonRepudiation, digitalSignature, keyEncipherment
191c192
< nsComment = "OpenSSL Generated Certificate"
---
> #nsComment = "OpenSSL Generated Certificate"
290c291
< # nsCertType = client, email, objsign
---
> nsCertType = client, email, objsign
296c297
< nsComment = "OpenSSL Generated Certificate"
---
> #nsComment = "OpenSSL Generated Certificate"
openssl
コマンドを発行するたびに-config ~/mognetCA/openssl.cnf
を書くのは面倒なので、環境変数にしておきます。コマンドエイリアスまで切ってしまうと、サブコマンドを最初の引数に取るopenssl
コマンドの仕様上厳しいので(wrapperスクリプトを書けばいいんでしょうけど)、引数のみ。
mac:$ echo 'export CA="-config ~/mognetCA/openssl.cnf"' >> ~/.bash_profile
mac:$ . ~/.bash_profile
これで証明書発行時は、openssl ca ${CA} ...
と実行すればOKです。では、一気に今回使用する鍵と証明書を生成していきます。
mac:$ mkdir -p ~/openAM/work; cd ~/openAM/work; pwd # ワークディレクトリ作成して移動
mac:$ openssl genrsa 2048 > opendj.key # LDAPs用鍵生成
mac:$ openssl req ${CA} -new -key opendj.key -out opendj.req # 同証明書発行要求生成(CN=alto.mognet.net)
mac:$ openssl ca ${CA} -in opendj.req -days 3650 -out opendj.crt # 証明書発行(有効期限10年)
mac:$ cat opendj.key opendj.crt | openssl pkcs12 -export -out opendj.p12 # 鍵と証明書をPKCS12形式にエクスポート
これで1セットです。あとはfor
で回します。
mac$: for subject in idp apache ihs; do
openssl genrsa 2040 > ${subject}.key
openssl req ${CA} -new -key ${subject}.key -out ${subject}.req
openssl ca ${CA} -in ${subject}.req -days 3650 -out ${subject}.crt
cat ${subject}.key ${subject}.crt | openssl pkcs12 -export -out ${subject}.p12
done
対話型のところやパスワードは適宜設定します。注意するのはCN(Common Name)
を自分のホスト名にすることくらいです。あと、apache
用の鍵にはパスワードをかけられないことでしょうか(最近apache httpd
を触っていないんですが、さすがにもう鍵パスワードかけられるのかな?)。
Apache httpdインストール
これもMacOSに付属していますが、古そうなのでソースからコンパイル、、、などという面倒な野暮なことはせずにbrew
で持ってきます。
mac:$ brew update
mac:$ brew install httpd
httpd.conf
を修正します。
52c52
< Listen 8080
---
> Listen 127.0.0.1:80
85,88c85,88
< #LoadModule auth_digest_module lib/httpd/modules/mod_auth_digest.so
< #LoadModule allowmethods_module lib/httpd/modules/mod_allowmethods.so
< #LoadModule file_cache_module lib/httpd/modules/mod_file_cache.so
< #LoadModule cache_module lib/httpd/modules/mod_cache.so
---
> LoadModule auth_digest_module lib/httpd/modules/mod_auth_digest.so
> LoadModule allowmethods_module lib/httpd/modules/mod_allowmethods.so
> LoadModule file_cache_module lib/httpd/modules/mod_file_cache.so
> LoadModule cache_module lib/httpd/modules/mod_cache.so
128c128
< #LoadModule proxy_module lib/httpd/modules/mod_proxy.so
---
> LoadModule proxy_module lib/httpd/modules/mod_proxy.so
136c136
< #LoadModule proxy_ajp_module lib/httpd/modules/mod_proxy_ajp.so
---
> LoadModule proxy_ajp_module lib/httpd/modules/mod_proxy_ajp.so
146c146
< #LoadModule ssl_module lib/httpd/modules/mod_ssl.so
---
> LoadModule ssl_module lib/httpd/modules/mod_ssl.so
188,189c188,189
< User _www
< Group _www
---
> User piro
> Group staff
210c210
< ServerAdmin you@example.com
---
> ServerAdmin dummy@mognet.net
219c219
< #ServerName www.example.com:8080
---
> ServerName www.alto.net
519c519
< #Include /usr/local/etc/httpd/extra/httpd-ssl.conf
---
> Include /usr/local/etc/httpd/extra/httpd-ssl.conf
529a530,531
>
> ProxyPass /openam ajp://www.alto.net:8009/openam
先を見越してOpenAM on Tomcat
との連携設定もいれています(proxy_module, proxy_ajp_module, ProxyPass
)。続いてhttpd-ssl.conf
を修正します。
27c27
< #SSLRandomSeed startup file:/dev/urandom 512
---
> SSLRandomSeed startup file:/dev/urandom 512
29c29
< #SSLRandomSeed connect file:/dev/urandom 512
---
> SSLRandomSeed connect file:/dev/urandom 512
36c36
< Listen 8443
---
> Listen 127.0.0.1:443
92,93c92,93
< SSLSessionCache "shmcb:/usr/local/var/run/httpd/ssl_scache(512000)"
< SSLSessionCacheTimeout 300
---
> #SSLSessionCache "shmcb:/usr/local/var/run/httpd/ssl_scache(512000)"
> #SSLSessionCacheTimeout 300
121c121
< <VirtualHost _default_:8443>
---
> <VirtualHost _default_:443>
124,128c124
< DocumentRoot "/usr/local/var/www"
< ServerName www.example.com:8443
< ServerAdmin you@example.com
< ErrorLog "/usr/local/var/log/httpd/error_log"
< TransferLog "/usr/local/var/log/httpd/access_log"
---
> ServerName www.alto.net:443
144,146c140,142
< SSLCertificateFile "/usr/local/etc/httpd/server.crt"
< #SSLCertificateFile "/usr/local/etc/httpd/server-dsa.crt"
< #SSLCertificateFile "/usr/local/etc/httpd/server-ecc.crt"
---
> SSLCertificateFile /Users/piro/openAM/work/apache.crt
> SSLCertificateKeyFile /Users/piro/openAM/work/apache.key
> SSLCertificateChainFile /Users/piro/mognetCA/cacert.cer
154c150
< SSLCertificateKeyFile "/usr/local/etc/httpd/server.key"
---
> #SSLCertificateKeyFile "/usr/local/etc/httpd/server.key"
特別な設定はいりません。さっき作った鍵と証明書のパスを指定してやるくらいです(ポートは443を使います)。
Apache Tomcatインストール
おとなしく公式サイトからダウンロードします。解凍したディレクトリごと、~/openAM/tomcat
として移動します。特にすることはありませんが、1点だけJava
のバージョンが1.8
系であることだけ確認しておきます。
mac:$ java -version
java version "1.8.0_151"
Java(TM) SE Runtime Environment (build 1.8.0_151-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)
OpenDJインストール
ForgeRock社の公式サイトからOpenDJをダウンロードします。リンク先からTRY NOW
を辿ってダウンロードします(要ユーザ登録@無料)。ZIPを解凍したら、ディレクトリごと~/openAM/opendj
として移動します。setup
コマンドを実行して対話型セットアップを実行します。セットアップスクリプトが存在するディレクトリをベースにして、データディレクトリ等が作成されるので、ご自分の環境に合ったディレクトリに配置してください。
mac:$ cd ~/openAM/opendj; pwd
mac:$ ./setup
〜省略〜
Do you accept the License Agreement? (yes / no) [no]: yes
Select the type of server to set up:
1) Directory Server
2) Proxy Server
3) Replication Server
Enter choice [1]: 1
〜省略〜
Provide the directory root user DN [cn=Directory Manager]: cn=admin
〜省略〜
Provide the server's fully qualified host name [alto.local]: www.alto.net
〜省略〜
Options to secure server connections:
1) 自己署名付き証明書を生成します (テスト目的のみでの使用を推奨)
2) PKCS#12 鍵ストアに存在する既存の証明書を使用します
3) JCEKS 鍵ストアに存在する既存の証明書を使用します
4) Java 鍵ストア (JKS) に存在する既存の証明書を使用します
5) PKCS#11 トークン上の既存の証明書を使用します
Enter choice [1]: 2
PKCS#12 鍵ストアのパス: /Users/piro/openAM/work/opendj.p12
鍵ストアの PIN:
〜省略〜
Enable LDAP? (yes / no) [yes]:
LDAP port [1389]: 38900
Enable StartTLS on LDAP port? (yes / no) [yes]:
Enable LDAPS? (yes / no) [yes]: 38901
Invalid response. Please enter "yes" or "no"
Enable LDAPS? (yes / no) [yes]:
LDAPS port [1636]: 38901
〜省略〜
ディレクトリデータのベース DN を指定してください: [dc=example,dc=com]: dc=mognet,dc=net
〜省略〜
All Server Parameters
---------------------------------------------------------------------
Server instance path /Users/piro/openAM/opendj
ルートユーザー DN: cn=admin
Root user password ******
Hardened configuration for production 無効
Server's fully qualified host name www.alto.net
管理コネクタポート: 6444
Start server after setup 有効
Server security Use existing PKCS#12 keystore
Keystore file /Users/piro/openAM/work/opendj.p12
Keystore password ******
Certificate nickname(s) to use opendj
LDAP (cleartext) Listening on port 38900
Start TLS (secure) for LDAP 有効
LDAPS (secure) Listening on port 38901
HTTP (cleartext) Listening on port 18080
HTTPS (secure) Listening on port 18081
Data storage option Generate 6000 sample entries
Base DN to create dc=mognet,dc=net
Data storage (backend) type JE Backend
〜省略〜
サンプルデータを6000エントリ生成するオプションをつけましたがどちらでもよいです。ちなみにサンプルエントリは、オブジェクトクラスinetOrgPerson
として生成されます。
ただし、実際のユースケースでは、組織コードや社員番号等、LDAP標準スキーマをそのまま使って運用することは少ないと思います(というかそうしている例を知らない)。そこで、今回は一つのオブジェクトクラスに一つの属性を持ったスキーマを追加で作成します。
セットアップが正常に完了すれば、ディレクトリサーバが起動していますので、まずは以下のようにスキーマ定義ファイルを作成します。
1: # "mognetid"という名前で、文字列(UTF-8)を値に持つ属性(attributeTypes)を追加する
2: dn: cn=schema
3: changetype: modify
4: add: attributeTypes
5: attributeTypes: (
6: 1.1.2.1.1-myOwnSchema
7: NAME 'mognetid'
8: DESC 'mognet.netextended attribute'
9: EQUALITY caseIgnoreOrderingMatch
a: ORDERING caseIgnoreMatch
b: SUBSTR caseIgnoreSubstringsMatch
c: SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 USAGE userApplications )
d:
e: # "mognetobject"という名前で、inetOrgPersonオブジェクトクラスを拡張した、
f: # 属性"mognetid"を必須値に持つオブジェクトクラスを追加する
g: dn: cn=schema
h: changetype: modify
i: add: objectclasses
j: objectclasses: (
k: 1.1.2.2.1-myOwnSchema
l: NAME 'mognetobject'
m: DESC 'mognet extended objectclass'
n: SUP inetOrgPersono
o: STRUCTURAL
p: MUST mognetid )
q:
説明の都合で行番号(1~q:)を記載しています(ていうかこれ、マークダウンインタープリタで自動でやってくれないかなぁ。。。)。一応解説しておくと、まず2〜c行目までで一つの属性(属性名:mognetid
)を追加しています(RDBでいうところのカラムに近いです)。6行目は当該属性のOID(Object IDentifier)1を記載します。本来であればIETFあたりが管理していますが(だったかな?)、検証用の独自属性なので適当なIDをいれておきます。9〜b行目は照合規則等の指定です。c行目でシンタックス、つまりこの属性の構文(データ型)を指定しています(RDBでいうところの、カラムの型に近いです)。この場合は、SYNTAX
のOID
が1.3.6.1.4.1.1466.115.121.1.15
なので、Directory String syntax
になります(UTF-8の文字列だと思えばよいです)。
g行目からがオブジェクトクラスの追加です。既存の標準オブジェクトクラスに、追加した独自属性を持たせることも物理的には可能ですが、移植性等の問題からあまりというかほとんど禁じ手に近いです。普通は、このように別のオブジェクトクラスを作成します。今回はn行目にある通り、inetOrgPerson
オブジェクトクラスを拡張し(Javaのクラスの拡張(extend
)同様、子のオブジェクトクラスは暗黙的に親オブジェクトクラスの属性を継承します。is A
の関係です)、p行目でさきほど追加したmognetid
属性をMUST
で持つ、l行目mognetobject
というオブジェクトクラスを追加します。
なお、LDIF形式はディレクトリサーバとの通信で使用するディレクトリ操作言語(SQLのような規格)ですが、スペースや空行の扱いが結構厳格です。基本形はaaa: bbb
ですが、ここでaaa
が属性、bbb
が値になります。値を複数行で書く場合は、2行目以降の先頭にスペース文字が一つ必要です。さらに、attributeTypes
属性の構文として、たとえば(
とOID
の間には一つのスペースがなくていはいけない、などのルールもあります。なので、6〜c行目とk〜p行目ではあえて行頭に2個の半角スペースをいれています。
ひと昔まえによくldif
を触っていましたが、そのときは各エントリ間は\n-\n
で区切らなければいけなかった気がします。なぜかそれをいれたら弾かれたのでいれていません。もしかしたら他の構文エラーがあって弾かれたのを勘違いしただけかもしれません。上の表記でうまくいかない場合は、d行目の後ろに-
だけの行ともう一つ空行をいれてください。さらにいうと、LDAPサーバには基本的に1操作単位でしかトランザクションの概念がありません(一つの変更に対してはほとんどの実装でATOMIC性があります)。このLDIFの場合、一つのエントリ(dn: cn=schema
エントリ)を2回modify
しますので、2トランザクションになります。一件ずつコミットされますので、同じLDIFを複数回実行することはできません(同じattributeTypes
をadd
することはできません)。
mac:$ ./bin/ldapmodify -p 38900 -D cn=admin -w password -f ./addschema.ldif
無事に正常終了すればスキーマ拡張は完了です。このオブジェクトクラスを持つエントリー(ユーザ)を以下のldif
で定義して同じように追加します。
dn: uid=alto,ou=People,dc=mognet,dc=net
objectClass: mognetobject
mail: alto@mognet.net
departmentNumber: 0
carLicense: n/a
employeeNumber: 100
telephoneNumber: 0120123456
givenName: Alto
mognetid: alto1
displayName: alto the sweet
sn: Benjiamin
cn: Jiang Benjiamin Alto of Mountfield
businessCategory: sheep dog
description: lovely dog
description: hi! i'm alto the border collie
userPassword: password
l: Tokyo
ディレクトリに登録します。
mac:$ ./bin/ldapmodify -p 38900 -D cn=admin -w password -f ./alto.ldif
mac:$ ./bin/ldapsearch -p 38900 -D cn=admin -w password -b dc=mognet,dc=net objectclass=mognetobject
これでエントリの検索に一件ヒットすればOKです。今作ったユーザが、実際にシングルサインオンシナリオで使用するユーザなので、特にuid
とuserPassword
の値は手元に控えておきます。ついでに、検索した結果のuserPassword
属性の値がハッシュ値になっていることも確認できます。多くのディレクトリサーバの実装では、設定値passwordStorageScheme
に則って、平文ではなくハッシュ値を格納します(逆にいえばこのスキームをtext-plain
などとすれば、平文で格納することができる実装もあります)。また、登録時にはmognetobject
オブジェクトクラスしか指定していなかったのに、検索すると親オブジェクトクラスであるinetOrgPerson
なども追加されているのがわかります。
OpenAMインストール
OpenDJ
と同様に、ForgeRock社の公式サイトからOpenAMをダウンロードします。
"Item..."の後ろにS
のマークがついているものはサブスクリプション(有償)版です。今回は13.0.0
を選択します。Tomcatにデプロイするので、war
形式のファイルをダウンロードします。ダウンロードしたら、名前をopenam.war
に変更して、先にインストールしたTomcatのドロップインディレクトリ(~/openAM/tomcat/webapps
)に移動またはコピーします。
IdP環境構築
さて、ここからはIdPに必要なサービスを起動して、初期設定をやっていきます。openDJ
、httpd
とtomcat
を一つずつ落としあげするのは面倒なので、以下のようなコントロールスクリプトを作っておきます(エラー処理等省略)。
#!/bin/sh
OPENAM_HOME=/Users/piro/openAM
export CATALINA_HOME=${OPENAM_HOME}/tomcat
case "${1}" in
start)
${OPENAM_HOME}/opendj/bin/start-ds
${CATALINA_HOME}/bin/startup.sh
sudo /usr/local/bin/apachectl start
;;
stop)
sudo /usr/local/bin/apachectl stop
${CATALINA_HOME}/bin/shutdown.sh
${OPENAM_HOME}/opendj/bin/stop-ds
;;
*)
echo "Usage: ${0} start|stop"
;;
esac
パスワードなしでsudo
できるようsudo visudo
でpiro ALL=NOPASSWD: /sbin/ifconfig,/usr/bin/vi,/usr/local/bin/apachectl
のように/usr/local/bin/apachectl
をパスワードなしでsudo
実行できるようにしておきます。それでは、IdPを起動します。
mac:$ openamctl start
起動できたらブラウザを立ち上げ、https://www.alto.net/openam
にアクセスします。初期設定ウィザード画面になりますので、カスタム設定(スクショ撮り忘れているので項目名違うかも)で新規作成していきます。
設定項目 | 設定値 | 備考 |
---|---|---|
adminユーザ名 | amadmin | |
adminパスワード | p@ssw0rd | |
サーバURL | https://www.alto.net/ | |
Cookieドメイン | www.alto.net | Tomcat 8以降のバージョンでは、. (ドット)から始まるCookieドメイン名は使用不可 |
プラットフォームロケール | ja_JP | |
設定ディレクトリ | /Users/piro/openAM/config | 事前に作成不要。存在する場合は空ディレクトリでないとNG |
設定データストア | OpenAM | OpenAM自体の情報を格納するディレクトリサーバ |
ポート | 52389 | デフォルト |
管理者ポート | 7444 | デフォルト |
JMXポート | 3689 | デフォルト |
暗号化キー | [ランダム文字列] | |
ルートサフィックス | dc=mognet,dc=net | ユーザリポジトリLDAPサーバのルートDN |
デフォルトポリシーエージェント(UrlAccessAgent) | password | adminパスワードと同じ文字列は不可 |
初期設定ができたらログイン画面が表示されます。ここから具体的な設定に入ります。amadmin
ユーザでログインしてください。
LDAP属性設定
まずはOpenAMに今回使用するユーザリポジトリのスキーマ情報を定義します(初期設定ではinetOrgPerson
の一部属性しか認識しません)。"Top Level Realm"に入ります(Realmとは抽象的な概念ですが、認証が通る境界内の名前空間といったニュアンスです。認証ドメインといってみてもいいかもしれません)。
「Data Sources」に入ります。
「OpenDJ」に入ります。
「ユーザー設定」のセクションに、「LDAP ユーザーオブジェクトクラス」にmognetobject
オブジェクトクラスを追加します。また、同様に「LDAP ユーザー属性」に、以下の属性が登録されている状態にします。
userPassword
telephoneNumber
mail
displayName
givenName
mognetid
description
dn
cn
l
carLicense
employeeNumber
uid
departmentNumber
businessCategory
sn
IdP鍵と証明書の設定
割と前に作った鍵と証明書をOpenAMに登録します。PKCS12
キーストアフォーマットには対応していないようなので、jre
のkeytool
コマンドを使って、まずはJKS
タイプのキーストアを作成します(keytool
のインポートコマンドでPKCS12
→JKS
に変換)。
mac:$ cd ~/openAM/config/openam; pwd
mac:$ mkdir private
mac:$ cd private; pwd
mac:$ keytool -importkeystore -srckeystore ~/openAM/work/idp.p12 \
> -srcstoretype PKCS12 -destkeystore keystore.jks -deststoretype JKS \
> -storepass changeit # パスワードは適宜で設定
mac:$ keytool -changealias -alias 1 -keystore ./keystore.jsk -storepass changeit # キーストア内のエイリアス名を"idp"に変更
mac:$ keytool -list -v -keystore ./keystore.jks -storepass changeit # 一覧出力
キーストアと秘密鍵にはパスワードがかかっているので、このままではOpenAM
から鍵を使えません。そこで、同じディレクトリに暗号化したパスワード情報を格納しておきます。パスワードの暗号化は以下のURLで行います(amadmin
ユーザでログイン済みでないとページを開けません)。
https://www.alto.net/openam/encode.jsp
フォームにパスワード平文を入力して送信すれば、暗号化されたパスワードが画面に出力されます。
ちなみに、暗号化キーはセットアップ時にランダムで設定されます。
ファイルに書き出します。末尾に改行が入らないよう、echo
ではなくprintf
をおすすめします。ファイル名は任意ですが、以降は記載のファイル名で作成した前提で進めます(今更ですが)。
mac:$ printf [出力されたキーストアパスワード暗号化文字列] > .storepass
mac:$ printf [出力された秘密鍵パスワード暗号化文字列] > .keypass
Webコンソールに戻ります(セッションが切れている場合は再ログインします)。画面上部のナビメニューから「CONFIGURATION」に入ります。「サーバーおよびサイト」に入ります。「デフォルトのサーバー設定値」に入ります。「セキュリティー」に入ります。「キーストア」セクションにて、前の手順で作成したキーストアの情報を設定します。
設定項目 | 設定値 |
---|---|
キーストアファイル | %BASE_DIR%/%SERVER_URI%/private/keystore.jks |
キーストアパスワードファイル | %BASE_DIR%/%SERVER_URI%/private/.storepass |
非公開鍵パスワードファイル | %BASE_DIR%/%SERVER_URI%/private/.keypass |
証明書エイリアス | idp |
ページトップの「保存」を押します。
IdPの作成
Webコンソールから、「Top Level Realm」に入ります。「Create SAMLv2 Providers」に入ります。「Create Hosted Identity Provider」に入ります。「このプロバイダのメタデータがありますか?」が「いいえ」になっていることを確認しつつ値を入れます。変更が要るのは「署名鍵:keytool
で指定したエイリアス名」と「新しいトラストサークル」くらいです。トラストサークル2は任意の名前でOKです。ここではmognettrustcircle
としています。
入力できたら「設定」を押します。
SAMLの設定
IdPがSPに渡すSAML文書に載せるユーザ属性(SAML属性、"SAML Attribute")を設定します。コンソールトップから「FEDERATION」に入ります。エンティティプロバイダのIdPのURLを押します。「表明処理」に入ります。「属性マッパー」セクションの「属性マップ」を以下のように設定してください。なお、画面内に注記されている通り、SAML属性名=LDAP属性名
という形式でマッピングしていきます。ここを設定しないとSAMLのアサーションに属性が載りません。
CommonName=cn
MognetId=mognetid
SirName=sn
BusinessCategory=businessCategory
CarLicense=carLicense
DepartmentNumber=departmentNumber
Description=description
DisplayName=displayName
EmployeeNumber=employeeNumber
GivenName=givenName
Locality=l
MailAddress=mail
TelephoneNumber=telephoneNumber
UserId=uid
UserPassword=userPassword
入力できたら「保存」します。ここで、敢えてuserPassword
属性もSAML属性「UserPassword」として送ることにしていますが、実際はこんなことはしないでしょう。そもそもLDAP上はハッシュ値しか持っていませんし。今回は実験なので、このようにしていますが、本来SSOシナリオにおいてユーザの認証に必要なデータ(パスワードの情報)をSPに送るのはアンチパターンですね。
これでIdPとしての設定は完了です。次回はSP環境を構築していきます。
参考文献
- IBM Knowledge Center
- WebSphere SAML構成ガイド
- Understanding the SAML trust association interceptor for the WebSphere Application Server