OpenIGを使ってみよう

 OpenIGは、ForgeRock社が開発しているOSSで、OpenAMと連携して「代理認証」を実現したり、SAML 2.0やOpenID Connectのフェデレーションゲートウェイとして機能することができます。今回はOpenIGを利用した代理認証の動作を確認してみたいと思います。


■ 代理認証とOpenIGとは

 代理認証とは、ユーザーのアプリケーションに対するログインをソフトウェアが代行して、シングルサインオンを実現することを言います。既存アプリケーションの改修が不要(または最小限で済む)な点が、代理認証ソフトウェア導入のメリットといえます。

OpenIGの概要については、以前書いた@ITの記事に簡単なまとめがありますので、こちらも参照してみて下さい。

OSSによるアイデンティティ管理(3): OpenIG、OpenDJと連携したOpenAMの新機能
http://www.atmarkit.co.jp/ait/articles/1402/12/news014.html

 この記事を書いたころの最新バージョンは2.1.0でしたが、現在の最新バージョンは3.1.0で、新たにいくつかの機能を実装しています。

 OpenIGは、代理認証を実現できるだけでなく、フェデレーション標準プロトコルに対応して、以下の役割を担うことができます。

  • SAML 2.0 サービスプロバイダー
  • OAuth 2.0 リソースサーバー
  • OAuth 2.0 クライアント、OpenID Connect 1.0 リライングパーティ

 OpenAMを使ったソリューションを提供する各社でも、自社の代理認証ソフトウェアを持っていると思いますが、フェデレーション標準プロトコルに対応しているものは少ないのではないかと推測します。この点はOpenIG導入のメリットの一つといえます。また、設定だけで実現できない部分はGroovyやJavaでカスタムモジュールを作成して実現できるので、汎用性は比較的高いように感じます。

 ただし、OpenIGのアーキテクチャと各モジュールの動作を理解した上で、アプリケーションに適した設定ファイルを書く必要があるので、使いこなせるようになるまで「慣れ」が必要になります。


■ OpenIGを導入すると…

 以下の図ように、OpenIGを導入していない環境では、アプリケーション毎にログインが要求されることになります。この環境に OpenIGを導入すると、OpenIGはアプリケーションに対するリバースプロキシーサーバーとして動作し、ログインリクエストにIDとパスワードを埋め込んで、ユーザーの代わりにログインを代行できます。

oig1

 ただし、OpenIGだけでは、固定値のIDとパスワードを埋め込むことしかできません。ユーザーがログイン画面で入力したIDとパスワードをログインリクエスト埋め込むためには、OpenAMおよびPolicy Agentと連携する必要があります。

 これらを連携した環境(上の図の3つ目の構成)でのログインシーケンスは、以下のようになります。

① ユーザーがアプリケーションのいずれかにアクセス
② Policy Agentがリクエストをインターセプトして、OpenAMに認証を依頼
③ OpenAMはユーザーにログイン画面を表示。
ユーザーはID/パスワードを入力してログイン。
OpenAMはIDと暗号化したパスワードをセッションに保持し、アプリケーションにリダイレクト。
④ Policy AgentはOpenAMから取得したユーザーのIDと暗号化されたパスワードをリクエストヘッダに設定して、OpenIGに転送。
⑤ OpenIGはアプリにアクセスし、受信したログイン画面にIDと復号化したパスワードをセットして、アプリに送信。
→ ログイン完了


■ 今回構築する環境

 今回は、以下のような環境を実際に構築して、動作確認してみます。検証目的であれば、1台のサーバーにポートを変えて全てをインストールしてもいいと思います。

oig2

warning  なお、今回はDNSの設定については行わないので、OpenIG導入後にサンプルアプリケーションのURLが変わることになります。

 通常は、ユーザーがサンプルアプリケーションのURLにアクセスすると、OpenIGサーバーにリクエストが転送されるようにDNSの設定を見直します。


■ サンプルアプリケーションのインストール

 まずは代理認証の対象となるアプリケーションをホストapp.example.co.jpにインストールします。ダウンロードしてすぐに使えるサンプルアプリケーションが、ForgeRock社のサイトに公開されているので、今回はこれを利用します。もちろん既存のアプリケーションを使ったり、自作しても構いません。

# wget http://maven.forgerock.org/repo/releases/org/forgerock/openig/openig-doc-samples/3.1.0/openig-doc-samples-3.1.0-jar-with-dependencies.jar

ダウンロードしたら、以下のようにjavaコマンドで起動します。

# java -jar openig-doc-samples-3.1.0-jar-with-dependencies.jar
9 08, 2015 11:12:16 PM org.forgerock.openig.doc.SampleServer start
INFO: Starting HTTP server on port 8081
9 08, 2015 11:12:17 PM org.glassfish.grizzly.http.server.NetworkListener start
INFO: Started listener bound to [0.0.0.0:8081]
9 08, 2015 11:12:17 PM org.glassfish.grizzly.http.server.HttpServer start
INFO: [HttpServer] Started.
9 08, 2015 11:12:17 PM org.forgerock.openig.doc.SampleServer start
INFO: Press Ctrl+C to stop the server.

http://app.example.co.jp:8081 にアクセスすると、以下のような画面が表示されます。

app81

画面を右クリックしてソースコードを表示すると分かりますが、このアプリケーションは、usernameとpasswordというパラメータをPOSTするformを持ったシンプルなWebアプリケーションです。

loginPageSource

usernameにdemo、passwordにchangeitを入力するとログインに成功し、以下のような画面が表示されます。

loggedin

サンプルアプリケーションは引数にポート番号を指定すると、そのポート番号でリッスンして起動します。デフォルトポートの8081をリッスンするサンプルアプリケーションの他に、8082をリッスンする2つ目のサンプルアプリケーションも以下のコマンドで起動します。

# java -jar openig-doc-samples-3.1.0-jar-with-dependencies.jar 8082
9 08, 2015 11:12:16 PM org.forgerock.openig.doc.SampleServer start
INFO: Starting HTTP server on port 8082
9 08, 2015 11:12:17 PM org.glassfish.grizzly.http.server.NetworkListener start
INFO: Started listener bound to [0.0.0.0:8081]
9 08, 2015 11:12:17 PM org.glassfish.grizzly.http.server.HttpServer start
INFO: [HttpServer] Started.
9 08, 2015 11:12:17 PM org.forgerock.openig.doc.SampleServer start
INFO: Press Ctrl+C to stop the server.

■ Jettyのインストール

 OpenIGを起動するサーブレットコンテナとして、Jettyをホストopenig.example.co.jpにインストールします。現時点(2015年9月15日)のJettyの最新バージョンは9.3ですが、Policy AgentがJetty 7または8にしか対応していないので、バージョン8.1.7をダウンロードします。

# cd /opt
# wget http://download.eclipse.org/jetty/8.1.17.v20150415/dist/jetty-distribution-8.1.17.v20150415.tar.gz

ダウンロードしたら、解凍してJettyサービスを起動できるようにします。

# tar xzvf jetty-distribution-8.1.17.v20150415.tar.gz
# rm jetty-distribution-8.1.17.v20150415.tar.gz
# mv jetty-distribution-8.1.17.v20150415 jetty
# useradd jetty
# chown -R jetty:jetty /opt/jetty
# cp jetty/bin/jetty.sh /etc/init.d/jetty
warning  以下の課題にあるように、Jetty 9にPolicy Agentをインストールすることは現時点ではできません。
https://bugster.forgerock.org/jira/browse/OPENAM-3253

JavaやJettyのホームディレクトリの設定などのため、今回は以下の内容で設定ファイル(/etc/default/jetty)を作成します。

# vi /etc/default/jetty

JAVA_HOME=/usr/java/default
JAVA=$JAVA_HOME/bin/java
JAVA_OPTIONS=" -server -Xms256m -Xmx1024m -XX:+DisableExplicitGC "
JETTY_HOME=/opt/jetty
JETTY_USER=jetty
JETTY_PORT=8080
JETTY_HOST=0.0.0.0
JETTY_LOGS=/opt/jetty/logs/

以上でJettyのインストールは完了です。以下のコマンドをJettyを起動します。

# service jetty start
Starting Jetty: . OK Wed Sep  9 01:59:56 JST 2015

起動したら http://openig.example.co.jp:8080 にアクセスして下さい。以下のような画面が表示されます。

JettyTop


■ OpenIGのデプロイ

 次にJettyにOpenIGをデプロイします。OpenIGは以下からダウンロードできます。

https://backstage.forgerock.com/#!/downloads/OpenIG/Open%20Identity%20Gateway/3.1.0/OpenIG%203.1.0#list

ダウンロードしたwarファイルをroot.warという名前に変更してwebapps以下にコピーすると、ルートコンテキストにOpenIGがデプロイされることになります(つまりopenig.example.co.jp:8080/openigではなく、openig.example.co.jp:8080でOpenIGにアクセス可能になります)。

# cp OpenIG-3.1.0.war /opt/jetty/webapps/root.war
# service jetty restart

起動したら再度 http://openig.example.co.jp:8080 にアクセスして下さい。以下のような画面が表示されるはずです。

OpenIGTop


■ Jetty Agentのプロファイルの作成

 OpenAM管理コンソールで、Jetty Agent(Jetty用のPolicy Agent)のプロファイルを作成します。アクセス制御 > / (トップレベルレルム) > エージェント > J2EE をクリックし、J2EEエージェントの一覧画面に遷移し、「新規」ボタンをクリックして下さい。

agentProf

以下を入力して、「作成」ボタンをクリックして下さい。

  • 名前、パスワード:任意値
  • 設定:集中
  • サーバー URL:http://openam.example.co.jp:8080/openam
  • エージェント URL:http://openig.example.co.jp:8080/agentapp

エージェントプロファイルを作成したら、一部の情報を更新します。「グローバル」のタブをクリックし、「一般」のセクションにある「エージェントフィルターモード」の現在の値「ALL」を削除し、「SSO_ONLY」を追加します。
agentProfile2

さらに「アプリケーション」タブをクリックし、「セッション属性処理」のセクションにある「セッション属性フェッチモード」を「なし」から「HTTP_HEADER」に変更します。

また、「セッション属性マッピング」に以下を追加します。

  • UserToken=username
  • sunIdentityUserPassword=password

agentProf3

以上でエージェントプロファイルの作成・設定は完了です。


パスワードキャプチャー/リプレイの設定

 OpenAMのログイン画面で入力したIDとパスワードを暗号化してセッションに保持し、Jetty Agentがそれを取得できるようにする設定(パスワードキャプチャー/リプレイの設定)を行います。

OpenAMコンソールで、アクセス制御 > / (トップレベルレルム) > 認証 の「すべてのコアの設定」をクリックし、ポスト認証プロセスクラスに「com.sun.identity.authentication.spi.ReplayPasswd」を追加します。

PAP

OpenAMのcom.sun.identity.common.DESGenKeyコマンドを実行し、次に、OpenAM認証プラグインのとOpenIGのための共有キーを生成します

# java -classpath /usr/share/tomcat6/webapps/openam/WEB-INF/lib/forgerock-util-1.3.5.jar:/usr/share/tomcat6/webapps/openam/WEB-INF/lib/openam-core-12.0.0.jar:/usr/share/tomcat6/webapps/openam/WEB-INF/lib/openam-shared-12.0.0.jar com.sun.identity.common.DESGenKey
Key ==> ivHLQGsEjD0=

OpenAMコンソールで、設定 > (サーバー名) > 高度 をクリックし、共有キープロパティを追加します。

  • キー:com.sun.am.replaypasswd.key
  • 値:生成した共有キー(上の例では、「ivHLQGsEjD0=」)

adv

以上でパスワードキャプチャー/リプレイの設定は完了です。


■ Jetty Agentのインストール

 エージェントのプロファイルを作成したら、エージェントの実体(Jetty Agent)をインストールします。
インストールするには、Jettyを停止しておく必要があります。

# service jetty stop

また、OpenAMの管理コンソールと同一のパスワードが書き込まれたパスワードファイルを作成しておいて下さい。

# echo password > /tmp/pwd.txt
# chmod 400 /tmp/pwd.txt

Jetty Agentは以下からダウンロードできます。

https://backstage.forgerock.com/#!/downloads/OpenAM/J2EE%20Policy%20Agents/3.5.0/Jetty%207%20and%208/zip#list

ダウンロードしたら解凍して、ディレクトリの所有者をjettyに変更します。

# cd /home/jetty/
# unzip Jetty-v7-Agent_3.5.0.zip
# chown -R jetty:jetty /home/jetty/

以下のagentadminコマンドでインストールを行って下さい。

# j2ee_agents/jetty_v7_agent/bin/agentadmin --install --acceptLicense


************************************************************************
Welcome to the OpenAM Policy Agent for Jetty 7.x server

************************************************************************

Enter the complete path to the directory which is used by Jetty Server to store
its configuration Files. This directory uniquely identifies the Jetty
Server instance that is secured by this Agent.
[ ? : Help, ! : Exit ]
Enter the Jetty Server Config Directory Path [/opt/jetty-7.6.11/etc]: /opt/jetty/etc


This is the root of the Jetty installation.
[ ? : Help, < : Back, ! : Exit ]
Enter the Jetty home directory path: /opt/jetty


Enter the URL where the OpenAM server is running. Please include the
deployment URI also as shown below:
(http://openam.sample.com:58080/openam)
[ ? : Help, < : Back, ! : Exit ]
OpenAM server URL: http://openam.example.co.jp:8080/openam


Enter the Agent URL. Please include the deployment URI also as shown below:
(http://agent1.sample.com:1234/agentapp)
[ ? : Help, < : Back, ! : Exit ]
Agent URL: http://openig.example.co.jp:8080/agentapp


Enter the Agent profile name
[ ? : Help, < : Back, ! : Exit ]
Enter the Agent Profile name: jettyagent


Enter the path to a file that contains the password to be used for identifying
the Agent.
[ ? : Help, < : Back, ! : Exit ]
Enter the path to the password file: /tmp/pwd.txt


-----------------------------------------------
SUMMARY OF YOUR RESPONSES
-----------------------------------------------
Jetty Server Config Directory : /opt/jetty/etc
Jetty installation directory. : /opt/jetty
OpenAM server URL : http://openam.example.co.jp:8080/openam
Agent URL : http://openig.example.co.jp:8080/agentapp
Agent Profile name : jettyagent
Agent Profile Password file name : /tmp/pwd.txt

Verify your settings above and decide from the choices below.
1. Continue with Installation
2. Back to the last interaction
3. Start Over
4. Exit
Please make your selection [1]:

Creating directory layout and configuring Agent file for Agent_001
instance ...DONE.

Reading data from file /tmp/pwd.txt and encrypting it ...DONE.

Generating audit log file name ...DONE.

Creating tag swapped OpenSSOAgentBootstrap.properties file for instance
Agent_001 ...DONE.

Creating a backup for file /opt/jetty/start.jar ...DONE.

Creating a backup for file /opt/jetty/start.ini ...DONE.

Updating the jetty start.conf with the Agent classpath ...DONE.

Copying Login configuration files...DONE.

Deploying Agent app...DONE.


SUMMARY OF AGENT INSTALLATION
-----------------------------
Agent instance name: Agent_001
Agent Bootstrap file location:
/home/jetty/j2ee_agents/jetty_v7_agent/Agent_001/config/OpenSSOAgentBootstrap.properties
Agent Configuration file location
/home/jetty/j2ee_agents/jetty_v7_agent/Agent_001/config/OpenSSOAgentConfiguration.properties
Agent Audit directory location:
/home/jetty/j2ee_agents/jetty_v7_agent/Agent_001/logs/audit
Agent Debug directory location:
/home/jetty/j2ee_agents/jetty_v7_agent/Agent_001/logs/debug


Install log file location:
/home/jetty/j2ee_agents/jetty_v7_agent/installer-logs/audit/install.log

Thank you for using OpenAM Policy Agent

問題なくインストールができたらwebdefault.xmlに、OpenIGへのリクエストをインターセプトするためのフィルターの定義を追加します。

# view /opt/jetty/etc/webdefault.xml

<web-app>

 ...

 <filter>
  <filter-name>Agent</filter-name>
  <display-name>Agent</display-name>
  <description>OpenAM Policy Agent Filter</description>
 <filter-class>com.sun.identity.agents.filter.AmAgentFilter</filter-class>
 </filter>
 <filter-mapping>
  <filter-name>Agent</filter-name>
  <url-pattern>/*</url-pattern>
  <dispatcher>REQUEST</dispatcher>
  <dispatcher>INCLUDE</dispatcher>
  <dispatcher>FORWARD</dispatcher>
  <dispatcher>ERROR</dispatcher>
 </filter-mapping>

</web-app>

以上でJett Agentのインストールは完了です。


OpenIGの設定

 OpenIGの設定をします。リバースプロキシーサーバーとしての基本的な動作を行うための、設定ファイル(config.json)を$HOME/.openig/config以下に作成します。

# mkdir -p /home/jetty/.openig/config/routes/
# vi /home/jetty/.openig/config/config.json
{
    "handler": {
        "type": "Router",
        "audit": "global",
        "capture": "all"
    },
    "heap": [
        {
            "name": "LogSink",
            "type": "ConsoleLogSink",
            "config": {
                "level": "DEBUG"
            }
        },
        {
            "name": "JwtSession",
            "type": "JwtSession"
        },
        {
            "name": "ClientHandler",
            "type": "ClientHandler"
        },
        {
            "name": "capture",
            "type": "CaptureDecorator",
            "config": {
                "captureEntity": true,
                "_captureExchange": true
            }
        }
    ],
    "baseURI": "http://app.example.co.jp:8081/"
}

次にサンプルアプリケーション1に代理認証を実現する設定ファイル(01-app1.json)を/home/jetty/.openig/config/routes以下に作成します。

# vi /home/jetty/.openig/config/routes/01-app1.json
{
    "handler": {
        "type": "Chain",
        "config": {
            "filters": [
                {
                    "type": "CryptoHeaderFilter",
                    "config": {
                        "messageType": "REQUEST",
                        "operation": "DECRYPT",
                        "algorithm": "DES/ECB/NoPadding",
                        "key": "ivHLQGsEjD0=",
                        "keyType": "DES",
                        "charSet": "utf-8",
                        "headers": [
                            "password"
                        ]
                    }
                },
                {
                    "type": "StaticRequestFilter",
                    "config": {
                        "method": "POST",
                        "uri": "http://app.example.co.jp:8081",
                        "form": {
                            "username": [
                                "${exchange.request.headers['username'][0]}"
                            ],
                            "password": [
                                "${exchange.request.headers['password'][0]}"
                            ]
                        }
                    }
                },
                {
                    "type": "HeaderFilter",
                    "config": {
                        "messageType": "REQUEST",
                        "remove": [
                            "password",
                            "username"
                        ]
                    }
                }
            ],
            "handler": "ClientHandler"
        }
    },
    "condition": "${matches(exchange.request.uri.path, '^/app1')}"
}

同様にサンプルアプリケーション2に代理認証を実現する設定ファイル(01-app2.json)も作成します。

# vi /home/jetty/.openig/config/routes/01-app2.json
{
    "handler": {
        "type": "Chain",
        "config": {
            "filters": [
                {
                    "type": "CryptoHeaderFilter",
                    "config": {
                        "messageType": "REQUEST",
                        "operation": "DECRYPT",
                        "algorithm": "DES/ECB/NoPadding",
                        "key": "ivHLQGsEjD0=",
                        "keyType": "DES",
                        "charSet": "utf-8",
                        "headers": [
                            "password"
                        ]
                    }
                },
                {
                    "type": "StaticRequestFilter",
                    "config": {
                        "method": "POST",
                        "uri": "http://app.example.co.jp:8082",
                        "form": {
                            "username": [
                                "${exchange.request.headers['username'][0]}"
                            ],
                            "password": [
                                "${exchange.request.headers['password'][0]}"
                            ]
                        }
                    }
                },
                {
                    "type": "HeaderFilter",
                    "config": {
                        "messageType": "REQUEST",
                        "remove": [
                            "password",
                            "username"
                        ]
                    }
                }
            ],
            "handler": "ClientHandler"
        }
    },
    "condition": "${matches(exchange.request.uri.path, '^/app2')}"
}

これらのファイルは以下のようにHTTPリクエストのヘッダーに設定されたパスワードを復号化して、IDとともにアプリケーションのログイン画面のFormに値を設定して、アプリケーションへと送信します。

oig3

以上で、OpenIGの設定は完了です。


動作確認

 それでは実際に動作を確認してみましょう。Jettyを再起動したら http://openig.example.co.jp:8080/app1 にアクセスして下さい。OpenAMのログイン画面が表示されるので、デフォルトで登録されているユーザーであるdemo(パスワードはchangeit)でログインします。

login

これでログインできるはずなのですが、OpenAM 12.0.0を新規にインストールした場合は以下のような画面が表示されてしまいます。

authZError

実はこれは次のバグで、XUIを有効にしていると発生します。
https://bugster.forgerock.org/jira/browse/OPENAM-3253

以下のページを参考にしてXUIを無効化して下さい。

https://t246osslab.wordpress.com/2014/10/26/openam%E3%81%AExui%E3%81%AE%E7%84%A1%E5%8A%B9%E5%8C%96/

再度、OpenAMからログアウトして、http://openig.example.co.jp:8080/app1 にアクセスします。

classiclogin

うまくいけば、以下の画面のようにサンプルアプリケーション1のログイン画面を表示することなく、サンプルアプリケーション1にログインできるはずです。

loggedin2

さらにhttp://openig.example.co.jp:8080/app2 にもアクセスします。今度はOpenAMのログイン画面もサンプルアプリケーション2のログイン画面もスキップして、サンプルアプリケーション2にシングルサインオンできるはずです。

OpenIGを使ってみよう」への1件のフィードバック

  1. ピンバック: OpenAMによるリバースプロキシ型シングルサインオン | OpenAM最新情報 | かもめエンジニアリング

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中