2014-07-19

OpenAMでSSO【Policy Agent編】

以前、OpenAMを使ってSAML2.0でSSOをする記事を書きましたが、SAML2.0を使ったSSOだとWebアプリケーション側が対応してないケースが多く、実装も困難であるため任意のWebアプリケーションに対するSSOの手段としてあまり適用できません。

ということで、今回から「OpenAMを認証プロバイダにして任意のWebアプリに対してシングルサインオン」をする方法に関して書いてみます!

 

SAML以外の方法で任意のWebアプリに対してOpenAMを使ってSSOするには以下のパターンがあります。

  1. Policy Agent
  2. OpenIG+PolicyAgent
  3. OpenID Connect

今回はPolicy Agentを使う方法でやってみます!

この方法はSSO対象のWebアプリケーションをホスティングしているサーバに、Policy Agent(リクエストを監視するソフトウェア)をインストールしてHTTPのアクセスを監視。認証していなければOpenAMへの認証要求(リダイレクト)をする仕組みになっています。

 

フローは以下のようになります。

■同一ドメインの場合(サブドメインが異なる場合も同じ)のフロー

Webアプリにアクセス

→WebアプリへのアクセスをPolicy AgentがインタラプトしてCookieが無効であればOpenAMにリダイレクト

→OpenAM側で認証(OpenAM側でセッション+Cookie発行)してWebアプリケーションにリダイレクト

→Webアプリへアクセス(Cookieが発行されているためAgentが認証済みとして処理)

 

■別ドメインの場合のフロー

Webアプリにアクセス

→WebアプリへのアクセスをPolicy AgentがインタラプトしてCookieが入っていなければOpenAMにリダイレクト

→OpenAM側で認証(OpenAM側でセッション+Cookie発行)してWebアプリにリダイレクト

→リダイレクト先でCookieを発行(cdcservlet)

→Webアプリへのアクセス(Cookieが発行されているためAgentが認証済みとして処理)

 

参考URLは以下のとおり。参考URL見た方がわかりやすい説。

1. OpenAM側の設定

まずトップ画面からアクセス制御を選択

openam-top

対象のレルムを選択

openam-access-ctrl

レルムのトップ画面からエージェントを選択

openam-realm-top

新規のエージェントを作成

openam-agentlist

 

openam-agentnew

名前とパスワードは適当に(2で使います) サーバーURLはOpenAMのURLを入力。 エージェントURLはWebサーバーのURLを入力。

作成した後に詳細な設定ができます。

openam-agent-applyurl

適用されないURLは認証を必要としないURLを入力。 適用されないURLの反転の有効にチェックを付けると上記設定が「適用されるURL」、つまり「認証を必要とするURL」に変更されます。

SSO対象のドメインが異なる場合(サブドメインが異なるのはOK)は、Cookieドメインリストに対象のドメインを入力します。クロスドメインのSSOでこれを設定しないと挙動がおかしくなります。(私の場合は認証無限ループしました)

openam-agent-cookiedomain

認証した証拠となるCookieの名前は以下で設定します。デフォルトはiPlanetDirectoryProです。

openam-agent-cookiename

2. WebアプリのサーバにPolicy Agentをインストール

今回はApacheのPolicy Agentを使います。OSはUbuntuで。

インストールガイド→http://docs.forgerock.org/en/openam-pa/3.1.0-Xpress/agent-install-guide/

 

まずはapacheインストール

$ sudo apt-get install apache2

インストールとともにサービスがstartしていたら

$ sudo service apache2 stop

でstopします。

$ apache2 -v

でバージョンを調べてバージョンに合ったPolicy Agentを以下サイトからダウンロード。

http://forgerock.org/downloads/openam-builds/

 

インストール実行前に以下のコマンドで空のhttpd.confファイルを作成(理由は後述)

$ sudo touch /etc/apache2/httpd.conf

agent用のパスワードファイルも作成

$ echo {1で設定したAgentのパスワード} > ~/.passwd
$ chmod 400 ~/.passwd

zip展開してbinディレクトリで以下を実行して対話型の設定を進める

$ agentadmin --install

設定例は以下のとおり。

apache2の設定フォルダ:/etc/apache2

OpenAMのURL:http://test.example.com:8080/openam

Webアプリ(SSO対象)のURL:http://hoge.fuga.com:80

エージェントの設定の名前(OpenAMで設定したエージェントの名前):ApacheAgent

パスワードファイル:/home/hoge/.passwd

 

インストール時にWebサーバの設定ディレクトリを聞かれますが、設定ディレクトリというよりもhttpd.confが含まれるディレクトリを指定してください。 どうやら指定した設定ディレクトリ内にあるhttpd.confにApache Agentの設定が追記される仕様なので、CentOSとかだとhttpd.confがあるのでそのまま設定ディレクトリを指定してあげればOKですが、Ubuntuとかだとapache2.confがメインの設定ファイルなのでhttpd.confを意図的に作成しないといけません。 ちなみにhttpd.confが無いディレクトリを指定すると「Invalid Apache Server Config directory」って言われます。

ダミーのhttpd.confの最後の行に

include /home/hoge/web_agents/apache24_agent/Agent_001/config/dsame.conf

が書き込まれているので、これを本物の設定ファイルapache2.confの末尾にコピーします。

で、stopしていたapacheをstart

$ sudo service apache2 start

3. テストアプリをWebアプリケーションとして設置。

OpenAMから発行されたCookieを元に認証を行うアプリを作成します。

今回はPython/Flaskで。

# -*- coding: utf-8 -*-
from flask import Flask, request, render_template
import urllib.request
import json

BASE_URL = "http://test.example.com:8080"

#値の検証とuidの取得
def validate(token):
    req = urllib.request.Request(
        url=BASE_URL + "/openam/json/sessions/" + token + "?_action=validate",
        headers={"Content-Type" : "application/json"},
        data="".encode()
    )
    res = urllib.request.urlopen(req)
    return json.loads(res.read().decode())

app = Flask(__name__)

@app.route("/")
def index():
    #Cookie値を取得
    token = request.cookies.get("iPlanetDirectoryPro")
    if token == None:
        return "none"
    
    #tokenの検証
    validate_res = validate(token)
    if not validate_res["valid"]:
        return "invalid"
    
    #ユーザ属性の取得
    req = urllib.request.Request(
        url=BASE_URL + "/openam/json/users/" + validate_res["uid"],
        headers={"iplanetDirectoryPro" : token}
    )

    res = urllib.request.urlopen(req)
    return res.read().decode()

app.debug = True
app.run()

OpenAMの設定で認証CookieキーをiPlanetDirectoryProとしているので、そのCookie値(token)を取得。あとは、そのtoken使ってOpenAMのREST APIを叩いてtokenの検証をした後にユーザ属性情報を取得します。

validateのレスポンスはこんな感じ。

{
    "valid": true,
    "uid": "amAdmin",
    "realm": "/"
}

ユーザ属性はこんな感じ。

{
    "username": "amAdmin",
    "realm": "dc=openam,dc=forgerock,dc=org",
    "mail": [],
    "sunIdentityMSISDNNumber": [],
    "sn": [
        "amAdmin"
    ],
    "givenName": [
        "amAdmin"
    ],
    "telephoneNumber": [],
    "universalid": [
        "id=amAdmin,ou=user,dc=openam,dc=forgerock,dc=org"
    ],
    "employeeNumber": [],
    "postalAddress": [],
    "cn": [
        "amAdmin"
    ],
    "iplanet-am-user-success-url": [],
    "roles": [],
    "iplanet-am-user-failure-url": [],
    "inetUserStatus": [
        "Active"
    ],
    "dn": [
        "uid=amAdmin,ou=people,dc=openam,dc=forgerock,dc=org"
    ],
    "iplanet-am-user-alias-list": []
}

あとはOpenAMのユーザ情報をWeb側のユーザ情報とマッピングしSession張るなりすればOK。

apache→flaskのリバースプロキシをしたいので、以下の一文をapache2.confに記述するのもお忘れなく。

ProxyPass / http://localhost:5000/

4. 動作確認

ネットワークレベルで動作確認をしてみます。以下はサブドメイン間のSSOをトラッキングしたものです。モザイクかけすぎて何が何だかわかんなくなっているのはご了承w

まずは何も認証してない状態でWebサイトにアクセスするとOpenAMへのリダイレクト処理が走ります。

openam-first-get-to-web

次にOpenAM内でUI/login ->XUI/#loginへのリダイレクト処理が走ります。

openam-ui-login

そうするとログイン画面が出てくるので認証します。

openam-login

ログインリクエストこんな感じ。

openam-auth

あとはJSでごにょごにょやってCookieセットして、OpenAM→Webサイトへリダイレクトします。この時点で有効なCookieが発行されているためWebサイト側で認証扱いになります。

openam-landingweb

ちなみに、クロスドメインSSOの場合はcdcservletというモジュールを介すため、ややシーケンスが増えます。

ハマりどころ

■作成したAgentをディレクトリ(例えばAgent_001)を削除してもう一度作成しようとすると

*** ERROR: Installation failed due to the following error - (Agent Agent_001 has already been configured for this Web Server instance.).

と言われてAgentがインストールできなくなります。

 

これは/etc/.amAgentLocatorにエージェントの設定ファイルの場所が書かれているためで、一度作成した設定を削除するにはこのファイルも削除しないとダメ。 というか、設定の削除は以下のコマンドでやるべきですね。

agentadmin --uninstall

 

■libamapc22.so: undefined symbol: ap_log_rerror

→undefined symbolと言われたらPolicy AgentとApacheのバージョンが合っているかを見直してください。

 

■権限系のエラー

→Webサーバの設定ディレクトリを指定するときには設定ファイルに対して書き込み権限が必要になりますし、パスワードファイルも読み込み権限が必要になります。この2つのファイルの権限を見直してください。

 

Policy Agentは設定もインストールもさほど難しくないので手軽では有りますが、OpenAMのCookieを使った認証になるため、プログラム側の改修が必要になります。つまり「認証されたらCookieにtokenを残す」「Cookieが存在すれば認証状態とする」ことはOpenAMとPolicy Agentが自動的に行ってくれますが、どのユーザでログインしているかどうかを判別する処理はアプリケーション側で実装しなければなりません。

ただ、Cookie認証の実装やOpenAMの設定がそんなに難しくないため、要件次第ではSSO実現の強力な方法になりそうです。

このエントリーをはてなブックマークに追加