nginxが動くFileMaker ServerでPHP

もう、だいぶん前ですが、FileMaker ServerのLinux版は、Webサーバとしてnginxを採用しました。また、もう少し前からになるでしょうか、FileMaker Server自体にPHPがバンドルされていない状態になりました。ということで、nginxでPHPダァ〜!ってなるのかと思ったら、全然情報が見つかりません。まあ、先に結論を言えば、PHPを動かすんだったら、別のサーバー立てるのが何かと安心というのがあるかと思います。しかしですね、「なんとか動かないか」と依頼されることが多々あり、手順をまとめてみました。Ubuntu Server 22.04LTSです。sudo権限を持ったmsykというアカウントでログインして作業をしました。

まず、お決まりのUbuntuのアップデート、そしてFileMaker Serverのアップデートです。Linux版は単にinstallすればアップデートもできて結構便利ですね。FileMaker Serverのインストール方法は、Clarisのドキュメントあるいはサイトをご覧ください。この記事は、FileMaker Serverをインストールした後のお話です。

sudo apt update -y
sudo apt upgrade -y

sudo apt install ./filemaker-server-21.0.2.202-arm64.deb

Apache2は、PHPのモジュール、つまりApache2のプロセスに入り込んで動くバイナリを使っていました。一方で、nginxは、別プロセスで動くPHPに対して、ソケットや通信を使って動きます。ということで、単独のプロセスで動くPHPといえば、PHP-FPM(FastCGI Process Manager)ですね。こちらをセットアップして動くようにする必要があります。モジュール名さえ分かっていればOKですね。以下の1行目で、phpとphp-fpmをインストールしていますが、composerも必要でしょうから一緒に入れておきます。2行目は、systemctlで指定するサービス名は「php8.1-fpm」になります。2行目はphp-fpmを再起動後も起動するように設定しておきます。なお、手元ではApache2も動いてしまっていたので、止めて再起動後にも起動しないようにしておきました。

sudo apt -y install php php-fpm composer
sudo systemctl enable php8.1-fpm
sudo systemctl stop apache2
sudo systemctl disable apache2

続いて、nginxの設定ファイルを変更します。FileMaker Serverは、/opt/FileMaker/FileMaker Server以下にファイルを置きます。スペース入るの嫌ですが、仕方ないですね。その中のNginxServerのさらに下にconfがあり、4つの設定ファイルがあります。fms_nginx.confファイルを修正します。例えば、こんな感じでnanoで編集します。

cd /opt/FileMaker/FileMaker\ Server/NginxServer/conf
sudo nano fms_nginx.conf

修正箇所を以下に示しますが、ファイルのほとんど末尾です。ファイル全体は、大きく分けて、ポート80に対する設定と、443に対する設定があり、後者の { } 内に以下のコードの赤字部分を追加します。最後にコメントになっている箇所があるので、その直前でいいかと思います。このままコピペして大丈夫だと思いますが、一応、設定のポイントを次に説明します。

    location ^~ /fmi/ {
      proxy_set_header X-Forwarded-Proto https;   # MWPE need ...
           :
      proxy_cookie_path /fmi "/; Secure; HttpOnly; Max-Age=43200";
    }

    location ~ \.php$ {
        fastcgi_pass  unix:/var/run/php/php-fpm.sock;
        fastcgi_index index.php;
        include       /etc/nginx/fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

#   location / {
     # First attempt to serve request as file, then
     # as directory, then fall back to displaying a 404.
         :

まず、locationは正規表現を伴っていますが、これは、拡張子が.phpのファイルに対するリクエストが来たらこの後の定義に従うということです。fastcgi_passは、ソケットあるいはURLを指定しますが、ここではソケットを指定しています。php-fpmはソケットあるいはTCP/IPのポートが開きますが、ソケットについてはこの後のphp-fpmの設定ファイルwww.confの中に記載されているので、それと合わせる必要があります。

fastcgi_indexは、ディレクトリへのリクエストの場合、index.phpファイルを探して、そのファイルへのリクエストとして処理するという意味です。

includeの後に、ファイルのパスを指定しますが、ここで指定したファイルはFileMaker以下ではなく、/etc/nginx以下、つまり一般的なnginx側のファイルです。よほど、複製して使う方がいいかと思いましたが、書き換えないので、標準nginxのファイルをそのまま使います。このファイルや、次のfastcgi_paramでは、CGI呼び出しのパラメータを指定します。PHP的にいえば、$_SERVER変数で得られる値を定義します。もちろん、キーと値を指定します。fastcgi_paramsファイルには一般的なCGIのパラメータがほぼ記載されているのですが、$_SERVER[‘SCRIPT_FILENAME’]の値については、モジュールで動かすPHPと若干違っているので、その設定がファイルに加わっています。変数$fastcgi_script_nameは、例えば、「/info.php」のような、リクエストで指定したパスです。変数$document_rootは、nginxのドキュメントルートで、root “…”で指定すると勝手に変数が定義されます。これらの変数をつなげることで、つまりは、稼働しているスクリプトへの絶対パスがパラメータとして伝達されるということです。

続いて、php-fpmの設定を行います。モジュール版の場合、Ubuntsuではwww-dataという名前およびグループのプロセスとしてApache2が稼働しているので、それらのユーザが前提となります。一方、FileMaker Serverは、nginxも、fmsuser/fmsadminというユーザ・グループで稼働します。それに合わせるため、設定を変更します。例えば、次のようなコマンドで編集作業を行います。

cd /etc/php/8.1/fpm/pool.d/
sudo nano www.conf

修正箇所は2箇所4行ですが、他の部分を入れ混ぜるとちょっと冗長なので、修正部分だけを以下に抜き出します。つまり、user, group, listen-*をそれぞれFileMaker Server向けに書き換えます。

    :
user = fmserver
group = fmsadmin
    :
listen.owner = fmserver
listen.group = fmsadmin
    :

設定はこれだけです。ということで、プロセスを再起動して…と言いたいのですが、FileMaker ServerはApache2だけの頃からも再起動が怪しかったですが、nginxでもやっぱりダメです。こちらのブログ「FileMakerServer での nginx チューニングをするための前準備」で細かく検討されていますが、ここではサーバを再起動させることにします。

再起動後、ドキュメントのルート、/opt/FileMaker/FileMaker\ Server/NginxServer/htdocs/httpsRootに、ファイル名info.php、中身は「<?php phpinfo();」というお決まりのテストファイルを作成します。はい、無事に出ました。

なお、ドキュメントルートは、例えば以下のコマンドで、自分のアカウントに書き込み権限があるようにしておくと良いでしょう。実行については読み出し権限がグループのfmsadminに設定されているので問題ありません。

cd /opt/FileMaker/FileMaker\ Server/NginxServer/
sudo chown -R msyk htdocs
cd htdocs/httpsRoot
echo "<?php phpinfo();" > info.php

ちなみに、FileMaker Serverでnginxをセットアップしているので、NginxServer/logsにログがあるのではと思うところで、実際にアクセスログなどはあります。ですが、設定ファイルのエラー等は、/var/log/nginx/error.logに書かれます。設定ファイルの間違い探しでは、こちらのファイルを参照しなければなりません。なお、PHPはそのままにしているので、/var/log/php8.1-fpm.logに書かれます。iniファイルは、/etc/php/8.1/fpm/php.iniですので、必要ならそのファイルを修正しましょう。

さて、FileMakerでPHPとなると、普通はFMDataAPIクラスを使うと思いますが、FMDataAPIも順調にバージョンアップして、Ver.32になっています。なお、Ver.32以降はPHP 8.1が最低条件ですので、PHP 7の方は前のバージョンを使ってください。Ubuntu 22はかろうじてPHP 8.1ですので最新版を使えます。例えば、次のようにコマンドを入れて、サンプルを動かしてみましょう。サンプルのデータベースは、FMDataAPIのページを参考にして、FileMaker Serverにセットアップしておいてください。

cd /opt/FileMaker/FileMaker\ Server/NginxServer/htdocs/httpsRoot
git clone https://github.com/msyk/FMDataAPI
cd FMDataAPI
composer update

これで必要なライブラリがセットアップされる…と思ったら、Problemとして2つ項目が出ます。

Composer is operating significantly slower than normal because you do not have the PHP curl extension enabled.
Loading composer repositories with package information
Updating dependencies                                 
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Root composer.json requires PHP extension ext-curl * but it is missing from your system. Install or enable PHP\'s curl extension.
  Problem 2
    - phpunit/phpunit[3.7.0, ..., 3.7.38, 4.0.0, ..., 4.8.36, 5.0.0, ..., 5.2.7, 8.5.12, ..., 8.5.40, 9.3.0, ..., 9.6.21, 10.0.0, ..., 10.5.36] require ext-dom * -> it is missing from your system. Install or enable PHP\'s dom extension.
    - phpunit/phpunit 4.8.20 requires php ~5.3.3|~5.4|~5.5|~5.6 -> your php version (8.1.2) does not satisfy that requirement.
    - phpunit/phpunit[5.2.8, ..., 5.7.27] require php ^5.6 || ^7.0 -> your php version (8.1.2) does not satisfy that requirement.

色々書いてありますが、要するにcurlとdomの機能が入っていません。これらは、モジュール版と同様に、aptコマンドでインストーすることができます。インストール後、念のためphp-fpmを再起動します。前の続きなら、途中のcdコマンドは不要ですが、カレントディレクトリを明示するために書いておきます。

sudo apt install -y php-curl php-dom
sudo systemctl restart php8.1-fpm
cd /opt/FileMaker/FileMaker\ Server/NginxServer/htdocs/httpsRoot/FMDataAPI
composer update

これで、ブラウザからサンプルコードの.phpファイル(例えば、https://localhost/FMDataAPI/samples/FMDataAPI_Sample.php)を開くと、個別の通信が示されて、途中にはデータベースからデータを取り出す部分などが参照できます。

ちなみに、Ubuntsu上での動いているプロセスを見てみます。例えば、以下のようなコマンドで見てください。綺麗にレイアウトされないので、ここには結果は出しません。

ps aux

ここでは、プロセス名php-fpmで稼働ののち、FileMaker Serverがnginxプロセスを起動して、その後にFileMaker Serverのさまざまなプロセスが稼働しているのがよ句わかります。php-fpmはfmsuserがプロセスのオーナーになっていますし、nginxも同様です。

ということで、FileMaker Server 2024 on Ubuntsu 22において、PHP 8.1が起動しました。ここにはいちいち書きませんが、INTER-Mediatorも動いてサンプルが稼働しています(MySQLでテストしましたが〜笑)。

Webページでのテキストフィールド移動

同じことで何度も悩んでしまったので、ちゃんとまとめて書いておこう。タイトルだけでは何の意味かわからないですが、具体的には、ログインパネルのユーザ名とパスワードと思ってください。通常、ユーザ名を入力してEnterキーを押すと、パスワードのフィールドに自動的に移動したいですよね。ということで、ユーザ名のテキストフィールドをuserBox、パスワードのテキストフィールドをpasswordBoxで参照しているとして、まあ、例えば、こんなプログラムを組みます。イベント設定はいろんな方法があるのでそこは置いといて、keydownイベントが来て、押されたキーがEnterあるいはNumpadEnterなら、パスワードボックスに飛びます。

userBox.onkeydown = function (event) {
  if (event.code === 'Enter' || event.code === 'NumpadEnter') {
    passwordBox.focus()
  }
}

しかし、これだと、Macではおかしな動作が発生します。かな漢字変換途中の文字列でEnterを押すと、確定した文字列がユーザ名だけでなく、パスワード側にも入るのです。つまり、Enterによる確定でユーザ名に確定した文字列が入るのですが、同時に変換途中の状態でパスワード側にフォーカスが移動して、そこでもやっぱり確定されて、両方のフィールドに文字が入ってしまうようなのです。Windowsでは、どうなっているのかというのはちょっと説明しづらいですが、あまりいい感じではありません。

そこで、いろいろ調べると、EventクラスにisComposingというプロパティがあって、変換途中ならtrueになることがわかりました。ということで、結論としてはこのようにisComposingを考慮すればいいということになります。つまり、Enterキーを押しても、変換途中ならばパスワードフィールドへの自動移動をしないということです。

userBox.onkeydown = function (event) {
  if ((event.code === 'Enter' || event.code === 'NumpadEnter')
      && !event.isComposing) {
    passwordBox.focus()
  }
}

ちなみに、caniuse.comによると、isComposingはモダンなブラウザならどれもサポートしているので安心して使えます。

KeycloakをIdpに使ってSAML認証(3)

この記事は、KeycloakをIdpに使ってSAML認証(1)およびKeycloakをIdpに使ってSAML認証(2)に続く記事である。(1)はKeyCloakのインストール、(2)はKeyCloakをSAMLのIdPとして、INTER-Mediatorで作ったアプリケーションに認証処理を提供する方法を記述したが、認証が通ったことはわかるものの、SAML属性にユーザ名が入っていないということで、INTER-Mediator側では想定した状態になっていなかった。そして、その点は解決したので、ここに記載しておく。

KeyCloak をIdPとし、SimpleSAMLphpをSPに設定している場合で、SP側の管理ページに入り、そこで認証をしてみる。そこでは、アプリケーション側で得られてるSAMLの属性がわかるので、その部分の確認と、ユーザ名を入れる拡張を進めてみた。まず、単にKeyCloakのClientとしてSAML SPを登録した状態では次のようになる。管理ぺ時のTestのところにSPが見えている(ここでは「default-sp」)。そのSPをクリックする。

すると、KeyCloakの黒い背景の認証ページに移動する。ここで、既存のkcuser1で認証をした場合、次のように、得られる属性が見えている。(2)で説明した通り、ここでも同様にRoleという属性しか見えていない。

しかしながら、以下のようにKeyCloak側を設定すると、次の図のように、kcuser1でログインした結果、そのユーザの姓名とメールアドレスに加えて、ユーザ名も取得することができた。

INTER-Mediator側で、取得した属性をチェックすると、このような感じになる。以前は、Roleだけだったのが、ユーザ名や氏名、メールアドレスも、ともかく取得できている。

ここで、INTER-Mediatorのparams.phpファイルでは、このように取得される属性から必要なデータを得られるように、$samlAttrRules変数の配列を定義しておく必要がある。この配列のキーは、ユーザテーブル(authuser)のフィールド名である。配列のキーに対する値は、SAMLの属性から取り出す時のキーと、|で挟んで配列の要素番号を指定している。メールや氏名については、前述の属性のダンプにあるように、複雑な記号がキーになっているので、それをコピペするなどして、きちんと配列を定義しなければならない。

いずれにしても、これで、INTER-Mediatorのアプリケーションで、KeyCloakをIdPとして認証ができることが確認できた。

どうすれば属性を増やすことができるのか

ということで、タイトル通りの疑問がここで沸く。言い換えれば、KeyCloakに設定したユーザでの認証については、「何かしなければRoleしか得られない」ということだ。

まず、左側で、Client Scopesを見てみよう。ここをみると、SAMLプロトコルに対しては、role_listという名前のものしか定義されておらず、他はみんなOpenIDのものである。以下の画像には1ページ目しか見えていないが、もう1項目だけ次のページにあり、それはOpenID向けのものである。ここを見て、「属性にRoleしかない」という状況と、Client scopesにSAMLのものはrole_listしかないという結果に大きな関連性があると着目して、ここに設定を加えれば良いということがわかる。

このリストで、Create client scopeボタンをクリックする。そうすると、以下のような画面になる。ここで、Nameを指定するが、この名前は、このリスト上の名前であり、すでに存在する名前はつけられない。ここでは、snとした。そして、もう1つProtocolからSAMを選択しておく。とりあえず、この2つで良いようだ。Protocolは後から変更できないので忘れないようにする。そしてSaveボタンをクリックする。

Saveボタンをクリックすると、Clinet scopeの1項目を編集する画面に入るのだが、上部のタブのMappersをクリックする。最初は次の図のように、No mappersとなっている。ある属性を得るには、マッパーを利用して、既存のどこかの値を持ってくるという設定を記述しないといけないようである。

ここでまず、Add predefined mapperボタンをクリックして、つまりは既定義のマッパーを探して設定する。ちなみに、既定義のマッパーが一覧されるので、該当する項目にチェックを入れて、Addボタンをクリックする。ただ、これをよくみると、既定義のマッパーでは、sureName、givenName、emailの3つしかない。ここでは、snに対するものとして、X500 surenameにチェックを入れて、Addをセットする。ちなみに、すでに設定したものはこのリストでは表示されなくなる。

この設定を、sn、gn、mailというClient scopeに対して行った。

では、usernameはどうすればいいか?まずは、usernameに対応するためのClient scopeの1項目を作る。もちろん、ProtocolをSAMLにするのを忘れないようにする。そしてマッパーを登録すが、usernameのマッパーは既定義には存在しないのでConfigure a new mapperをクリックする。すると、次のような選択画面が表示される。何を選択するか迷うが、とりあえず、User Attribute、User Propertyのいずれも成功している。ここではUser Attributeを選択したとする。

すると、次のようなマッパーを定義する画面に入るので、ここで、Name以下4つの項目で全部、usernameと入力する。なお、ここでは最後の項目にあるように、SAML属性上でのキー名も入力できるので、属性上の値の解釈も分かりやすくなる。入力後、Saveボタンをクリックして保存しておく。

こうして、4つのClient scopeを追加した。Client scopeの一覧で、SAML Protocolに絞って一覧を示すとこのような感じである。username以外は、既定義のマッパーを使っている。

これでOKかと思ったのだが、まだRoleしか属性に入っていない。さらに続いて、次のように設定を行う。Clientsを選択すると、クライアントのリストが表示される。ここで、追加したSAML SPの項目のリンクをクリックする。

そして、Clientの設定画面が出てくるので、上部のタブで、Client scopesを選択する。規定値では、role_listが見えているはずだが、いずれにしても、Client scopesに登録し、さらにここで追加する必要があるということで、ここで、さらに選択を加えないといけない。

Add client scopeボタンをクリックすると、次のように、Client scopesで定義したProtocolがSAMLの項目が一覧される。とりあえず、ここでは全ての項目にチェックを入れて、Addボタンをクリックする。さらに選択するポップアップが出てくるので、Defaultを選択しておく。

SAML SPのClient scopesの設定が次のようになったらOKである。

ここまで設定を行うことで、最初に示したように、SAML属性にusernameや氏名、メールアドレスを含めることができ、INTER-Mediatorでの利用が可能になった。

KeycloakをIdpに使ってSAML認証(2)

この記事は、KeycloakをIdpに使ってSAML認証(1)に続く記事である。(1)では、ともかくKeyCloakを稼働するところまで進めた。そして、KeyCloakをIdPとし、SimpleSAMLphpを使ったINTER-Mediatorのアプリケーションまで稼働する。なお、SP側は、SimpleSAMLphp Ver.2を使ってみる(3)の設定が終わっているものとする。SPは、IdPもSPもSimpleSAMLphpを利用していて、default-spがとりあえず、SimpleSAMLphpのIdPに接続されている。

KeyCloak側にRealmを1つ定義する

ここでまず、KeyCloak側にRealmを1つ定義する。KeyCloakの管理画面にadminユーザでログインをして、左側のナビゲーションバーの一番上にあるドロップダウンリストにある「Create realm」ボタンをクリックする。

すると、次のようなフォームが表示される。ここではとりあえず、Realm Nameだけを入力して、Createボタンをクリックすれば良い。以下は、IM_Testと入力をした。

左側の上の方に、作ったRealmのIM_Testが見えているはずだが、ここでRealmを切り替えて利用できる。以後はここで作成したIM_Testが選択された状態で作業を進めることにする。

KeyCloak側にユーザを定義する

続いてユーザを定義する。左側でUsersを選択すると、ユーザの一覧が表示されるが、その中にあるAdd Userボタンをクリックする。すると、以下のようなフォームになるので、とりあえず、Usernameだけは入力しておく。ここではkcuser1とした。Emailも入力されている。そして、Createボタンをクリックすると、ユーザは作られた。

次に作ったユーザにパスワードを与える。管理画面の左側でUsersを選択すると、作ったユーザkcuser1が見えているはずだ。そのユーザ名のリンクをクリックすると。そのユーザの設定変更ができるフォームが見えている。ここで、右側上部のタブにあるCredentialsをクリックして、パスワードを設定する。

Credentialsをクリックすると、次のようなパネルが表示される。ここで、PasswordとPassword Confirmationに同じパスワードをキータイプする。TemporaryはOnだとログイン時にパスワードの変更が必要になる。ここではテストなので、Offにしておくのが良いだろう。そして、Savaボタンで、パスワードが設定される。

IdP(KeyCloak)のメタデータをSPに登録

ここからは、IdPとSPのメタデータの交換である。まずは、IdPのメタデータをSPに登録する。別記事の手順で作られたSPだが、SP側の設定はほぼそのままにして、IdPのメタデータだけを入れ替えることにする。

IdPのメタデータは、左側のReaml settingsをクリックして、Generalのタブの一番下にあるEndpointsというところにある「SAML 2.0 Identity Provider Metadata」というリンクをクリックする。

すると、次のように新たなウインドウにIdPのメタデータが表示される。このXMLを利用するのだが、ブラウザでXMLのコピペをするとしても、ブラウザが余計なことをしてしまうことになりそうなので、ここでは、URLをコピーしておく。そして、作業用のコンピュータ側でいいので、「curl -O メタデータのURL」とコマンドを入れて確実に中身の変更が行われない状態で、ファイルに納める。descriptorというファイルが作られるが、それをエディタで開いて、中身にコピーしておくと良い。

このメタデータをSP側に登録するのだが、SPはSimpelSAMLphpであるので、メタデータはXML形式ではなくPHPのプログラムにしなければならない。そこで、SP側の管理ページを開く。URLを示す意味があるかは若干疑問だが、別記事の通りにセットアップしてれば、「https://demo.inter-mediator.com/saml-trial/lib/src/INTER-Mediator/vendor/simplesamlphp/simplesamlphp/public/admin」で管理ページへのログインができる。ログイン後、上部で「連携」を選択する。ここにある「ツール」の「XMLをSimleSAMLphpメタデータに変換」の部分をクリックする。

すると、メタデータパーサと書かれたページが出てくるので、XMLメタデータ部分にペーストする。つまり、IdPのメタデータそのものを、ここに貼り付けて、「パース」ボタンをクリックする。

すると、次のように、PHPのコードに変換されたものが「変換されたメタデータ」の部分に見えるようになる。このPHPのコードを慎重にコピーして取り込んでおく。

SPの設定を変更する。設定ファイルのsaml20-idp-remote.phpを修正する。テキストエディタで開いて、末尾に変換されたIdPのメタデータをペーストすれば良い。従来のIdPのメタデータはコメントにしておくのが良いだろう。

SPのメタデータをIdP(KeyCloak)に登録

次は、SPのメタデータをIdPに登録する。SPのメタデータは、SPの管理画面で「連携」のところのdefault-spの下にあるVの部分をクリックすると、表示される。XMLとPHPの両方で表示されるが、ここで必要なのはXMLの方である。ここではコピペは必要なく、SAML Metadataの直下「You can get the metadata XML on a dedicated URL:」のすぐ下にある1行のURLの部分の右端にクリック可能なボックスが出てくる。これをクリックすると、メタデータのXMLがファイルとしてダウンロードされる。ファイル名は、default-sp.xmlという名前になる。

KeyCloakの管理画面に戻る。IM_TestのRealmが選択されていることを確認した上で、左側でClientsをクリックする。以下のような画面になるので、リストの上にあるImport clientの部分をクリックする。

インポートの画面に遷移するが、ここでは、Browseボタンをクリックして、先ほどダウンロードしたSPのメタデータファイル(default-sp.xml)を選択する。すると、Resource fileの部分にメタデータファイルの中身が展開される。ここまででSaveボタンを押して保存をしておく。

アプリケーションを動かしてみるが…

では、実際にSAMLに対応したアプリケーションを動かしてみる。認証が必要な場面で、KeyCloakの認証画面に移動し、登録したユーザkcuser1での認証は通った。なお、名前を入力していなかったので、このように入力を促すパネルが出るが、認証自体は通っているようである。

しかしながら、その後には、次のようなメッセージが出てきた。というか、これはINTER-Mediatorが出すメッセージである。INTER-Mediatorは、認証時に得られる属性の配列から、ユーザ名や氏名、メールアドレス等を取り出すようにしており、ユーザ名の取得は必須にしてある。$samlAttrRules変数は設定ファイルに記述する属性の何を取り出すのかを指定する配列であるが、ここでは既定の状態でユーザ名の取得が正しく取れていないことを意味する。

SAMLのやり取りをみるとRoleしか来てない

デバッグメッセージを表示してもう少し詳しく見てみよう。右側、中央あたりに見えている6要素の配列が、IdPから得られたデータである。これをよくみると、Roleというキーがあるので、Roleが得られていることはわかるのだが、それ以外のデータが全くない。つまり、ログインは成功したもののIdPからRole以外のデータが全く来ていないということになり、その結果、どのユーザでログインしたのかは、アプリケーション側は全く検知できない状態になってしまっているということである。

つまり、KeyCloakを既定の状態で使うままではどうやらINTER-Mediatorでの認証はできない。ユーザ名を「気にしない」アプリケーションなら良いのかもしれないが、ユーザ名を取得できることを必須としているINTER-Mediatorでは対策が必要である。

ちなみに、SAML-tracerでチェックしてみた。ユーザ名とパスワードを入力した直後のSAMLのやり取り行をみてみると、SAML2.0 Responseはあるのだが、ここでやり取りされるデータは非常に少ない。

ちなみに、INTER-Mediatorを使って稼働している別のシステムではこのようなデータが見えている。なお、IdP, SPともにSimpleSAMLphpを使い、IdPはADに対してLDAPで接続して、ADユーザでのログインを可能にしている。ログイン直後に、SAML2.0 AttirubuteStatementの部分には、たくさんのデータが見えているが、事実上、これはADのユーザデータである。見てすぐにわかるように、cnでユーザ名、snとgivenNameで氏名が得られるので、そのようにINTER-Mediatorをセットアップして利用している。

ということで、KeyCloakとSimpleSAMLphpのSPは、接続等のセットアップはできるが、そのままではRoleしか得られないので、INTER-Mediatorでは認証はおそらくできているとしてもユーザ名が得られないので、認証していないとみなしているというところで、その解決策がわかれば、続きの記事を書くとする。

Active DirectoryのLDAPを使ってSAML認証

SAML認証についての解説記事のうち、以下の3つはSimpleSAMLphp v2についての記事で、まだまだ、書いたことはそのまま使える(というか、そうなるように時々アップデートしている)。(1)(2)がIdP、(3)がSPの構築だ。

これらの情報をベースにして、タイトルの通り、Active DirectoryをLDAPサーバとして利用して、SAML認証が行うことに成功したので、やり方を書いておく。以下の手順時は、上記の流れでIdPとSPが稼働できるようにした上での追加の作業について記載する。

まず、SimpleSAMLphpでLDAPの機能を動かすためには、PHPのLDAP機能拡張の追加と、SimpleSAMLphpのLDAPモジュールの追加が必要である。Ubuntu Server 24.0.2 LTSでは、例えば、IdPをセットアップしたディレクトリ(/var/www/simplesaml-idp)で、以下のようにコマンドを入力する。

sudo apt install -y php-ldap
cd /var/www/simplesaml-idp 
composer require simplesamlphp/simplesamlphp-module-ldap

続いて、設定ファイルを変更する。前述の記事の通りにIdPのセットアップがなされているとして、IdP側のconfig.phpに以下の設定を追加する。もちろん、最後のldapのエントリーを追加する。

'module.enable' => [
  'exampleauth' => true,
  'core' => true,
  'admin' => true,
  'saml' => true,
  'ldap' => true,
],

そして、さらに、IdPのauthsource.phpファイルを修正する。ここでは以下のように、LDAPに関する設定を有効にする。すでに、コメントされた箇所があるので、そのコメントをはずして、実際に使用する値にすればよい。以下は、実質的な設定の行だけを残したものであるが、実際には、ファイルに記載されている説明のためのコメントを読みながら設定をすることになるだろう。LDAPについての知識があれば、そんなに大変ではないと思われるが、AD自体の状況も加味しないといけない。設定変更した箇所は、赤字で示したが、「最低限」となるとこれくらいだろう。なお、ドメインは架空であり、実際とはちょっと違う。

    'example-ldap' => [
        'ldap:Ldap',
        'connection_string' => 'ldap://ad.msyk.net/',
        'encryption' => 'none',
        'version' => 3,
        'ldap.debug' => false,
        'options' => [
            'referrals' => 0x00,
            'network_timeout' => 3,
        ],
        'connector' => '\SimpleSAML\Module\ldap\Connector\Ldap',
        'attributes' => null,
        'attributes.binary' => [
            'jpegPhoto',
            'objectGUID',
            'objectSid',
            'mS-DS-ConsistencyGuid'
        ],
        'dnpattern' => 'CN=%username%,CN=Users,DC=msyk,DC=net',
        'search.enable' => false,
        'search.base' => [
            'CN=Users,DC=msyk,dc=net',
        ],
        'search.scope' => 'sub',
        'search.attributes' => ['uid', 'mail'],
        'search.filter' => '(&(objectClass=Person))',
        'search.username' => null,
        'search.password' => null,
    ],

設定の最後の3分の1程は、serch.とプレフィックスがあるようにユーザ検索を行うときの設定のようである。username等をnullにしているので、「検索はしない」という設定にしておいた。認証だけなら、この設定は不要である。なので、search.baseも設定する必要はないとは思われる。

ということで、実際に使用したのは、connection_string、つまり、LDAPサーバへのURLと、dnpatternつまりユーザレコードのDistingished Name(DN)のパターンだけである。AD側は、シンプルに(シンプル過ぎるがww)Users以下にだけユーザを作ってあるので、CN=UsersがDNパスに入るだけである。ユーザ名はCNで得られる。

さらにIdPのmetadataディレクトリに入れるsaml20-idp-hosted.phpファイルも修正をする。URLやキーファイル名はすでに入っているとして、以下のように、authキーの値をauthsource.phpで指定したLDAP設定のキー値に置き換える。もちろん、「example」は実際の運用ではちゃんとした名前に置き換えよう。ちなみに、最初、LDAPの設定をしたのに、example-userpassのテストユーザでないと認証ができないというところでグルグルしてしまったのだが、ちゃんと認証ソースの設定があるということであった。

$metadata['https://idp.topse.jp/'] = [
    'host' => '__DEFAULT__',
    'privatekey' => 'idp.pem',
    'certificate' => 'idp.crt',
    'auth' => 'example-ldap',
:

これでIdP側の管理ページにログインして、表示内容を確認しよう。まず、「設定」のタブのところでは、Modulesにldapが追加されてチェックが入っている。

Testのところを見ると、authsource.phpに設定した、example-ldapが見えている。これをクリックして、このソースでの認証を確かめてみる。

いつも通りのIdPの認証パネルが見える。ここで、LDAPに存在するユーザを入力して認証を確認する。

認証できた。認証した結果の属性が見えている。この属性は、SimpleSAMLphpのSimpleクラスのインスタンスに対して、getAttributes()メソッドで得られるものだ。実際には、例えば、cnキーに対して配列が得られて、0番目の要素として取り出すなどするが、ともかく、この属性値から、アプリケーションに必要な値を取り出す必要がある。とは言え、ざっと見たところ、ユーザ名はcn、フルネームはsnとgivenName、メールアドレスはmailというあたりが実際に欲しい情報かと思われる。

IdPはセットアップできた。SPは、前述の別記事のままで良い。SP側のTestでは、default-spが見えているはずで、これが上記のIdPと連携が取れていると思われるが、そのままdefault-spで認証テストをすると、上記のように、LDAPのユーザ情報が同じように得られる。

INTER-Mediatorでの設定は、params.phpファイルに仕込む。$isSAMLをtrueにして、SPの名前を入れ、キャッシュした情報での認証時間を1800秒とする。ここまでは普通のSAMLの設定だが、$samlAttrRulesに次のような配列を入れて、SAML属性からの値の取り出しを指定している。この変数は、キーがauthuserテーブルのフィールドを示し、値がSAML属性からの取り出しパスを指定している。例えば、属性のcnキーの値から0番目の要素を取ってきて、それをusernameフィールドに入れるということだ。なお、名前が姓と名で分離されているので、その場合は、値を配列にすれば、それぞれの要素をキーとして取り出した値を結合して、そのフィールドの値とする。ここではrealnameフィールドの値は「新居 雅行」となることを期待している(というか、この仕組み、さっき急いで実装したりした)。

$isSAML = true; # The default value of isSAML is false.
$samlAuthSource = 'default-sp';
$samlExpiringSeconds = 1800;
//$samlWithBuiltInAuth = true;
$samlAttrRules = [
    'username' => 'cn|0',
    'realname' => ['sn|0','givenName|0'],
    'email' => 'mail|0',
];

これで、SAMLベースでのLDAPユーザによる認証ができるようになった。

Ubuntu Server 24.04 LTSでINTER-Mediatorを稼働する

Ubuntu 24が正式にリリースされました。この上で、INTER-Mediatorを動かす方法をまとめておきます。期待通り、以前よりも追加モジュールは少なくて済むようになりました。セットアップ作業後、管理者アカウントで、以下のようにコマンドを入力します。

sudo apt -y -U upgrade
sudo apt install -y apache2 php mysql-server
sudo apt install -y nodejs
sudo apt install -y composer
sudo apt install -y php-xml php-gd
sudo apt install -y mysql-client php-pdo-mysql
sudo apt install -y postgresql php-pgsql
sudo apt install -y sqlite3 php-sqlite3

cd /var/www/html
sudo chown -R www-data:adm /var/www
sudo chmod -R g+rw /var/www
sudo systemctl restart apache2

git clone https://github.com/INTER-Mediator/INTER-Mediator.git
cd INTER-Mediator/
composer update

最初の塊は、必要なソフトウエアのインストールです。Linux自体のアップデートは、1つのコマンドでできるようになったので、早速使います。MySQLだけでなく、PostgreSQLやSQLiteについても記述しました。PHPのモジュールで足りないのは、xmlとgdだけでした。これだけのインストールで、最後のcomposer updateで必要なライブラリのダウンロードが成功します。これらでインストールされたソフトウエアのバージョンをまとめておきます。なお、Nodeについては、INTER-Mediatorのcomposer.jsonでも記述があり、そちらは、Ver.20をダウンロードして利用します。

  • Apache2:2.4.58
  • PHP:8.3.6
  • MySQL:8.0.36
  • Node:18.19.1
  • PostgreSQL:16.2
  • SQLite:3.45.1

2つ目の塊では、Apache2が稼働しているユーザwww-dataが、自分自身のホーム以下に書き込み権限があるようにしています。念の為、Apache2を再起動しています。この設定は、INTER-Mediatorの一部の機能では必要になります。

3つ目は、INTER-Mediatorのインストールです。なお、サンプルのデータベースを入れる必要があるので、続いて以下のようにコマンドを入れれば、3つのデータベースにサンプルデータベースのtest_dbがセットアップされます。最初にカレントディレクトリがINTER-Mediatorであることを前提にしています。特定のデータベースだけでいいのなら、他のデータベースについてのセットアップは無視してOKです。

cd dist-docs/
# MySQLのサンプルデータベース登録
sudo mysql -uroot < sample_schema_mysql.sql 

# PostgreSQLのサンプルデータベース登録
sudo -u postgres psql -c 'create database test_db;'
sudo -u postgres psql -f sample_schema_pgsql.sql test_db

# SQLiteのサンプルデータベース登録
sudo mkdir -p /var/db/im
sudo sqlite3 /var/db/im/sample.sq3 < sample_schema_sqlite.sql
sudo chown -R www-data:adm  /var/db/im

これで、「http://(サーバのIPアドレス)/INTER-Mediator/samples/」に接続すれば、INTER-Mediatorのサンプルの稼働が確認できます。

simplesamlphpのIdPでGoogle OAuth2認証を実行する

PHPでのSAML認証ライブラリであるsimplesamlphpは、PHPでSAML認証を行う場合にはほぼ一択となる選択肢です。これを利用してIdPを構築する方法は、https://blog.msyk.net/?p=1566 で紹介しました。本項目は、別掲の記事で紹介したIdPが存在する状態で、Google OAuth2、すなわちGoogleのアカウントでSAML認証を行えるようになるまでを紹介します。

とりあえずセットアップ方法とテスト方法は残しておかないといけないと思いつつ、まずは記事を書いたのです。INTER-MediatorでのGoogleアカウントでの認証は成功してはいませんでした。そして1日経過し、ふと疑問に思った事があります。simplesamlphpの追加モジュールとしてcirrusidentity/simplesamlphp-module-authoauth2を使うことにしています。このモジュールは、OAuth2の汎用モジュールなので、Google向けということではありません。さて、このモジュールは、IdPに必要なのか、SPに必要なのか、どちらなのでしょうか? このことは、ライブラリのインストールのページにも書いていません。当たり前のことなのかな?

以下の方法で、IdPにまずはインストールして設定を追加して、リダイレクトURLはIdP側にしたのですが、認証されている状態になりませんでした。そこで、全く同じようにSP側にもモジュールを追加し、リダイレクトURLをSP側にしましたが、なんと、全く同じ結果になりました。どちらでもいいのかもしれません。どちらでやっても同じ結果になったので、どっちが正しいのかという結論はとりあえずは出ていません。しかし、OAuth2の認証サーバがすでにあるという前提を考えれば、SPだけを用意してそれを利用する方が明らかに作業は少なくて済みます。ということは、SP側にモジュールを追加して運用するのが正しいような気がします。IdPに登録したユーザで認証する結果をみていると、一旦IdPに行ってログインパネルを出して、そこから、SP内部へリダイレクトして認証結果が伝わってくるように思えるので、やはりそのこともあって、SPへモジュールを追加するのが正しいように思います。

Googleのプロジェクトから認証情報を得る

SimpleSAMLphpの設定の前に、GoogleからクライアントIDとシークレットを取得します。その方法はあちらこちらのサイトで記載されていますので、例えば、Shinonome Tech BlogのOAuth 2.0 を使用してGoogle API にアクセスする方法 に記載された「1.Google API ConsoleでOAuth2.0の認証情報を取得」の作業を行なってクライアントIDとシークレットを取得します。以下、それぞれ、MYCLIENTID、MYCLIENTSECRETと記述します。つまり、これらのキーワードが登場したら、Google Developer Consoleで取得した実際の値に置き換えて設定等を行なってください。なお、「承認済みのリダイレクトURI」には、以下のURLを指定します。module.php以下のパスは常に同じですが、それ以前は実際にSPのサイトをどのようにセットアップしたかに依存します。

https://demo.inter-mediator.com/simplesaml/module.php/authoauth2/linkback.php

なお、実際のSPは「アプリケーション内のsimplesamlphp」でもあるので、エイリアスを使わないパスだとこのようになります。いずれにしても、Google側への登録と、後で説明する認証画面を出すためのURLの両方に、同一のリダイレクトURLを指定する必要があります。

https://demo.inter-mediator.com/saml-trial/lib/src/INTER-Mediator/vendor/simplesamlphp/simplesamlphp/public/module.php/authoauth2/linkback.php

SPにモジュールを追加する

SPで使用するモジュールは、https://github.com/cirrusidentity/simplesamlphp-module-authoauth2.git です。simplesamlphp自体にはGoogle OAuth2に対応した処理を実行できる素材はないので、外部から追加します。composer.jsonに組み込むか、あるいは次ようなコマンドで追加でインストールを行います。

composer require cirrusidentity/simplesamlphp-module-authoauth2

続いて、SP側のsimplesamlphpのconfig/config.phpファイルを修正します。まず、この先で、SAML認証結果を利用するアプリケーションのドメインを追加します。既定値では[]になっていますが、OAuth2のモジュールはこの設定を見ているようです。

'trusted.url.domains' => ['demo.inter-mediator.com'],

そして、OAuth2モジュールを利用可能にします。IdPとしてセットアップするときに値を変更した配列に、1行加えます。

'module.enable' => [
    'exampleauth' => true,
    'core' => true,
    'admin' => true,
    'saml' => true,
    'authoauth2' => true,
],

次に、config/authsources.phpに次の指定を加えます。’default-sp’などと同じ階層にキーとして’google’を指定し、値として配列を指定します。MYCLIENTID、MYCLIENTSECRETは実際に取得した値にもちろん置き換えて指定します。

'google' => [
   'authoauth2:OAuth2',
   'template' => 'GoogleOIDC',
   'clientId' => 'MYCLIENTID',
   'clientSecret' => 'MYCLIENTSECRET'
],

SP側の設定はとりあえずこれでOKのようです。SPの管理画面に、authoauth2モジュールが稼働していることが見えています。

なお、Testのところに「google」というリンクが出ており、クリックするとGoogleのページを表示しますが、認証処理までには至りません。リダイレクトのURLが設定されていないというようなメッセージが見えます。

認証プロセスを通して見る

ここで実際に使用しているアプリケーションのURLは「https://demo.inter-mediator.com/saml-trial/chat.html」です。このURLと最初の方でGoogle Consoleで指定したリダイレクトURL「https://demo.inter-mediator.com/simplesaml/module.php/authoauth2/linkback.php」がこの後必要になります。これらのURLに加えて、MYCLIENTIDを含め、以下のURLの文字列をエディタ上などで整えます。URLEncodingをしてもいいのですが、問題ある文字列はないので、そのままこの文字列に置き換えて利用してもいいでしょう。そして、このURLをブラウザのアドレス欄に貼り付けて、Enterキーを押し、GoogleのOAuth2の認証プロセスに入ります。なお、URLの細かい点では別の機会に改めてきちんと処理すると言うことにして、まずは動く状態にしたいと思います。

https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=MYCLIENTID&scope=openid%20email&redirect_uri=リダイレクトURL&state=authoauth2|security_token%3D333344445555%26url%3DアプリケーションURL&nonce=0394852-3190485-2490358&hd=gmail.com

stateはちょっと適当にしていますが、「authoauth2|」で始まっていないと行けないのは、このモジュールでの実装です。また、stateの中に「url=アプリケーションURL」と言う記述があって、そこが後から効いてきます。

ちなみに、OAuth2に対応したサイト等では、認証がなされていない場合に、GoogleやFacebookの認証プロセスに入るボタンを提供するかと思います。その場合、「Google」ボタンをクリックすると、上記のURLにリンクするような作りになっているはずです。FacebookならFacebookの認証システムへのURLにパラメータを設定してジャンプするようになっています。

URLをブラウザのアドレス欄に入れると、Googleの認証の画面になります。状況は人によって違うのでしょうけど、いずれにしても、Googleの認証の画面になって、ログインしていない場合はユーザ名とパスワードの入力を行ないます。

Googleの認証ページから、アプリケーションURLに指定したURLにリダイレクトされます。

実のところ「よしOK!」とは行きません。INTER-MediatorのSAML対応では、このままでは認証を継続させる事ができないので、これからチェックしないと行けないのですが、アプリケーションURLに対しては、それよりも先にリダイレクトURLにアクセスし、さらにそこからアプリケーションURLへリダイレクトされているようで、GETメソッドで何のパラメータもありません。SAMLのクッキーがHTTP Onlyで乗ってくるだけのようです。ちなみに、SAML認証が成功すると、SimpleSAMLとSimpleSAMLAuthTokenという2つのクッキーが入るのですが、現状、IdP/SPいずれの側を利用しても、前者のクッキーしか入らず、後者は入ってこないのです。

(続報:2024-3-9)その後、色々やってみたのですが、認証が成功した状態のクッキーになりません。と言うことで、コードを読みました。simplesamlphp-module-authoauth2には認証後に返ってくるURL(linkback.phpで終わるもの)が記載されており、実際にそのリダイレクトURIが呼び出されています。そこをチェックすると、前述の「https://accounts.google.com/o/oauth2/v2/auth」に対するstateパラメータの値は、次のようになるはずです。色々試しているうちに、セッションデータが落ちてきたのでわかったことです。

state=authoauth2|_796bed184adf107c10c0faf60c658c7da54d2b0972:アプリケーションURL

最初は「authoauth2|」である必要があります。リダイレクトされた最初の段階でチェックしています。続いて、|より後の文字列について、:で区切って、最初をid、最初の:以降をマージしてurlとしています。エラーがあると、そのurlにリダイレクトがします。セッションのデータが何かのきっかけで落ちてきた時に、そこからデータが取り出せるとしたら、|と最初の:の間は上記のコードか、もしくは「_eff7a14ba6adc55b6cd2f08b0399b747ed3ac9844e」である必要があります。このコード、ランダムに充てられている可能性もありますが、このデータで数回セッションデータが得られた後、セッションデータはほぼ空っぽのものばかりになってしまっていました。それで、SPの管理ページのログインテストを利用して、一度認証した状態にしておいて、アプリケーションを動かすと、今度は、セッションデータは何か大量に乗っかってきています。SimpleSAMLAuthTokenクッキーも来ました。しかし、その時のstateの|より後にくるべきコードは違うものでした。やはりランダムでした。このstateは最初にaccounts.google.comを呼び出すときに引数に指定しないといけないと言うことは、開発者が指定しないといけないと考えられますが、それを得る方法はわかりません。あるいは指定したコードが乗ってくるということなのかな?もうちょっとコードを追ってみようと思います。と言うことで、ここまでは行けました。ちょっとだけ進歩したかな。そうこうしているうちに、SimpleSAMLphpのVer.2.2.0が出ましたね。セッションのなかに、「アップデートしろー!」って情報がどっさり。

SimpleSAMLphp Ver.2を使ってみる(3)

(1)はIdPの起動、(2)はIdPの管理画面のチェックと進みました。ということで続いてSPです。ここでのSPはINTER-Mediatorで稼働しているという前提で話をします。状況としては次のようなものです。

  • 証明書を発行済みのドメインdemo.inter-mediator.com内で稼働する。DocumentRootは/var/www/demo_im_com
  • DocumentRootにsaml-trialディレクトリを作り、そこに、ページファイルchat.html、定義ファイルchat.phpを定義した
  • INTER-Mediatorは、saml-trial/lib/srcにgit cloneでインストールして、composerで必要なライブラリをインストール
  • 結果的に、SimpleSAMLphpのレポジトリのルートは、DocumenRoot以下、saml-trial/lib/src/INTER-Mediator/vendor/simplesamlphp/simplesamlphp となる
  • 設定ファイルのparams.phpは、saml-trial/lib/src/params.phpとする
  • demo.inter-mediator.comをホストしているApache2のsiteファイルでは、以下のように、/simplesamlへのエイリアスを作成する(Aliasの行は1行で記述)
<VirtualHost *:443>
    ServerAdmin info@inter-mediator.org
    DocumentRoot /var/www/demo_im_com
    ServerName demo.inter-mediator.com
Alias /simplesaml "/var/www/demo_im_com/saml-trial/lib/src/INTER-Mediator/vendor/simplesamlphp/simplesamlphp/public"

いきなり動くかなと確かめてみたら、ダメでした。composerの扱いをちゃんとやらないといけません。ここでは、composer.jsonのsimplesamlphp/simplesamlphpの値を”2.0.4″とバージョンをしっかり入れるようにしてみました。INTER-Mediatorの場合、composer clearnでライブラリを消して、composer update, composer installの順でコマンドを入れれば良いでしょう。

simplesamlphpの管理ページは、前回にも紹介したように、赤いヘッダなどがついたもので、CSSやスタイルシート、画像などが提供されています。ブラウザでパスを見る限りは、/simplesaml/assets/base…となっているので、レポジトリのpulic/assetsを見るのですが、空です。どうやら、assets以下の内容は、simplesamlphp/simplesamlphp-assets-baseという別のパッケージにあるようで、これが読み込まれていません。この別パッケージをassets以下に展開するには、composer installが必要なようで、結果的にupdateとinstallは両方行う必要があるようです。

設定ファイルの記述

これまでのセットアップを行うと、SimpleSAMLphpのSP自体は、パスがちょっと長いですが、/var/www/demo_im_com/saml-trial/lib/src/INTER-Mediator/vendor/simplesamlphp/simplesamlphpに存在することになります。以下のこのパスを「SPのルート」と記載します。このディレクトリの、configに設定ファイル、metadataにメタデータファイル、certに証明書類を入れるのが基本です。以下、参考にコマンドを記述しますが、INTER-Mediatorではもう少し手軽にする方法を用意していて、近々、これをSimpleSAMLphp Ver.2向けに更新する予定です。

まず、通信暗号化のための証明書を作ります。この証明書はサイトのTLSのための証明書を使ってもよく、実際には案件ではそのようにしましたが、SimpleSAMLphpのサイトの説明では、10年期限の自己署名証明書を作っています。サイトの証明書はつまり「自己署名だとダメかも」と思って使っていたわけですが、本家の説明がいきなり自己署名なので、単に暗号化のためだけに使っているということですね。opensslコマンドの後に属性などを入力しますが、(1)のIdPのところと同様適当に入れればいいかと思います。-outと-keyoutの後のファイル名も適当に指定します。

cd cert
openssl req -newkey rsa:3072 -new -x509 -days 3652 -nodes -out sp.crt -keyout sp.pem

SPのルート以下、configディレクトリには、元からあるconfig.php.distからコピーしたconfig.phpを用意します。そして、その内容を変更します。ポイントは以下の点です。baseurlpathは、SPのルートのpublicを参照するようにします。以前はwwwを参照していましたが、Ver.2で変わっています。残り3つの設定は、IdPと同様ですので、(1)の記事を参照してください。

'baseurlpath' => 'saml-trial/lib/src/INTER-Mediator/vendor/simplesamlphp/simplesamlphp/public/',
'technicalcontact_email' => 'your_email',
'secretsalt' => 'your_salt',
'auth.adminpassword' => 'your_admin_pass',

SPのルート以下、configディレクトリには、元からあるauthsources.php.distからコピーしたauthsources.phpを用意します。以下のように、default-spキーの配列の要素に、certificateとprivatekeyのエントリーを用意して、ここで作成したキーファイルと証明書ファイルを指定します。そして、entityIDをサイトのドメインに設定しておきます。

'default-sp' => [
  'saml:SP',
  'certificate' => 'sp.crt',
  'privatekey' => 'sp.pem',

   // The entity ID of this SP.
   'entityID' => 'https://demo.inter-mediator.com/',
   :

SPのルート以下、metadataディレクトリには、元からあるsaml20-idp-remote.php.distからコピーしたsaml20-idp-remote.phpを用意します。このファイルの最後(とはいえ、中身は短いコメントがあるのみ)に、IdPの管理ページからコピーした配列をコピーしておきます。

SPの管理ページからメタデータを取得

ということで、インストールに少しハマってしまいましたが、なんとか動きました。一応のルートは、https://demo.inter-mediator.com/simplesaml ですが、こちらは「ようこそ」と出るだけです。SPの管理ページに行くには、このURLの後にadminをつけた、https://demo.inter-mediator.com/simplesaml/admin にアクセスします。そして、config.phpで指定したパスワードを入力して、管理者として認証します。

設定のページは諸々確認できますが、ModulesのところでIdPとしては稼働していないことなどが分かります。

Testのタブでdefault-spのリンクをクリックすると、次のような画面が見えており、登録したIdPを認識していることが分かります。ただ、ここで「選択」をクリックするとエラーになるので、まだ何か問題なのかもしれません。

連携のところで、「V」の部分をクリックすると、メタデータが表示されます。このメタデータを、IdPに登録します。IdPがSimpleSAMLphpなら、metadata/saml20-sp-remote.phpファイルに追記することになります。

認証できています

それでは実際にIdPで認証したユーザで、INTER-Mediatorのアプリケーションを使ってみます。通常、ログインパネルが出てくるとこが、IdPというか、SPの画面に行きます。ここでは、まず、IdPを選択します。

すると、ログインパネルが出てきます。こちらはドメインを見ればわかるように、IdP側に切り替わっています。ここでは、テストユーザのuser01でログんを試みます。

無事にログインができ、メッセージが見えています。

ちなみに、SAML-tracerを使って追っかけてみました。チャットのアプリケーションのURL(https://demo.inter-mediator.com/saml-trial/chat.html)をブラウザに入れると、何度かリダイレクトされて、IdPの側の認証ダイアログが表示されます。そこまでのトレースは以下の通りです。

続いて、正しいユーザとパスワードを入力して、IdPにポストしますが、その後、アプリケーションのURLにリダイレクトされています。この時は、認証が通っているので、アプリケーション側でも、認証が通った後の処理をして、ページが構築されています。

ということで、SimpleSAMLphp Ver.2.0.4でも動くことを確認しましたが、途中ちょっとハマった理由は、すでにVer.3の作業に入っていることに気づかず、dev-masterで作業したら、色々思った通りに動かなかったのでした。Packagistのサイトを見て、あ、Ver.3.0.0になっていると気づき、Ver.2.0.4で通るようにやり直して稼働を確認できたという次第です。ちゃんとチェックしようねってことですね。

SimpleSAMLphp Ver.2を使ってみる(2)

前の記事では、テスト用のIdPを起動するところまでを説明しました。Ver.2ではIdPの管理画面も新しくなっているので、続いてその管理画面に何が出ているかを確認しましょう。

まず、画面上部のタブ「設定」のページです。最初にSimpleSAMLphpのインストール場所やバージョンが見えています。正しく、Ver.2.0.4がインストールされていると判断できるでしょう。そして、インストールされているモジュールや動作チェックなどがあります。Ver.2になった変更点として、プラグイン的に必要な機能は追加するようになったと記載があり、必要な素材が全部入っている状態ではありません。必要な機能があるのなどはこの画面などでのチェックも必要かもしれません。

前の画面のDetailsにある「Information on your PHP installation」のリンクは、phpinfo()関数を動かした結果を表示します。「ホストネームやポート、プロトコルを診断」は次のような画面を表示します。サーバがきちんと動くようなら、特に確認は不要かもしれません。

「Test」のタブでは、admin、default-sp、example-userpassの3つのリンクがあります。まず、adminは次のように、管理者ログインに関する情報が見えています。

「default-sp」をクリックしても「No identity providers found. Cannot continue.」と出るだけです。これは正しい状態なのか、追々調べます。

example-userpassをクリックすると、次のようにログインパネルが出て、ログインの検証が可能です。ここで、config/authsource.phpで定義したユーザとパスワードを入れてログインをしてみます。

正しいユーザ名とパスワードを入れれば認証が行われて、その時に得られる属性についても表示されます。

ページ上部の「連携」をクリックすると、次のような表示が見えます。SPが2つになってしまっていますが、idpのドメイン名を設定した側を利用するものとして想定します。ここでは、中央付近に見えているボックスの下部にある「V」部分をクリックします。

V部分をクリックすると、表示が開いて、IdPのメタデータが表示されます。上部が一般的なXMLによる記述で、下部がSimpleSAMLphpで利用できるPHPの配列形式のメタデータです。ともかく、SPとの連携の時のデータは取り出しができるようです。

以上のように、IdPの管理画面としては、以前より少しは機能が増えたものの、SPの登録などはないようなので、やはり基本は設定ファイルを修正するということになるでしょう?認証可能かどうかやインストール状態などの動作チェック等にはある程度は利用できそうです。

SimpleSAMLphp Ver.2を使ってみる(1)

PHPでSAML認証を実現するライブラリ、SimpleSAMLphpが、2023年からVer.2となりました。SAML 2.0に対応するのは以前から、つまり、SimpleSAMLphp Ver.1でもSAML 2.0に対応していましたが、どちらのバージョンも「2」になったということです。バージョン記述がややこしいですが、まあ、これを読んでいる方は慣れているかと思いますので、先に進みます。

この記事は2023 7/1に最初に記述しましたが、状況が変わりつつあるのとノウハウが少し溜まったこともあって、2024/3/2までに追記を何度か行なっています。

INTER-MediatorはSimpleSAMLphpベースでSAML対応しています(勉強会での発表ビデオはこちらです)。SAMLというか、Shibboleth認証の案件を実際に行ったこともあります。ということで、SimpleSAMLphp Ver.2は早めにチェックしようと思いつつ、今になってしまいました。

SimpleSAMLphp Ver.2になっての違いはこちらのページに記載されています。かいつまんで説明すると、Shibboleth 1.3、SAML 1.1にはもう対応しないということで、SAML 2.0のみ対応となっています。ということは、Shibboleth案件は、Ver.1.19.xあたりで作業する必要があるということになります。設定ファイル名は変わっていないものの、「作り直したほうがいい」となっていますので、手順を含めて、引き続いてそのあたりは説明したいと思います。それから、いくつかの重要なパスも変わっています。これも説明で紹介します。

INTER-MediatorのSAMLのテストは、SimpleSAMLphpによるIdPと、SimpleSAMLphpによるSPを使って行うようにセットアップをしてあるのですが、改めて、この環境を構築し直しを始めました。その記録をブログにつけていこうと思います。IdPには、テスト用のアカウントをいくつか記録する程度で、そこから別の認証サービスを使うまではとりあえずは考えていません。

テスト環境ですが、Ubuntu Server 22.0.4 LTSです。よって、PHPは8.1です。普通に、Apache2、PHPとモジュールをインストールしました。INTER-Mediatorをインストールする以外には、PHPのSOAPモジュールを追加するだけで大丈夫でした。 テスト用のアプリケーションも当然ながらINTER-Mediatorで作ってあるのですが、SimpleSAMLphpのVer.1とVer.2の相互運用も考えないといけないのかなとも考えられます。

さて、数年前に一生懸命検証をした時の1つの結論は、「ちゃんとドメインを切って、正しい証明書をセットアップしたサイト」にするということです。その時の設定はまだあって、IdP用にidp.inter-mediator.com、アプリケーションとSPはdemo.inter-mediator.com/saml-trialにしました。いずれも、Let’s Encryptではありますが、それぞれ有効な証明書が動き、通信はすべてHTTPSで動くという状態になっています。

IdPサイトの構築

IdPのサイトは、SimpleSAMLphpのコードをそのまま使って構築します。Ubuntuなので、/var/www以下に、例えば、以下ようなコマンドで、コードを取り出します。バージョンごとにタグがあるので、Ver.2系列の最新版である2.1.4をインストールすることにします。そして、composerを動かして、必要なライブラリのインストールを行います。

cd /var/www
git clone https://github.com/simplesamlphp/simplesamlphp simplesaml-idp
cd simplesaml-idp
git checkout v2.1.4
composer update

/var/www以下は、ログインしたユーザであれば書き込みできるという前提で説明をします。また、ログインしたユーザはsudoコマンド可能であって、root権限が必要な処理はsudoを利用するという方針でコマンドを示します。また、ログインユーザはadminsグループにも登録してあるものとします。

前述のコマンドで、/var/www/simplesaml-idpというディレクトリができ、そこにレポジトリの内容が展開されました。このディレクトリを公開するのかというと、そうではなくて、この中のpublicを公開します。以前はwwwというディレクトリでしたが、Ver.2でpublicという名前に変えたそうです。ということで、Apache2のidp.inter-mediator.comのサイト設定ファイルは、大体以下のような記述つまり、DocumentRootがある感じです(実際には証明書の設定などもあってもっとややこしい)。/simplesamlはIdPの設定ファイルに書かれているbaseurlpathの値でもあるので、とりあえずAliasを定義しておきます。

<VirtualHost *:443>
    ServerAdmin info@inter-mediator.org
    DocumentRoot /var/www/simplesaml-idp/public
    ServerName idp.inter-mediator.com
    Alias /simplesaml "/var/www/simplesaml-idp/public"
:

さて、サーバを見てみましょう!という感じで開くと、次の通りです。当然、セットアップを何もしていないので、そのような表示が出るだけです。ちゃんと、設定ファイルがないとメッセージが出ています。

IdPが使う証明書を用意する

SAMLでは通信の暗号化のために証明書を使います。IdPで使用する証明書は、opensslコマンドを使って作成しますが、レポジトリのcertディレクトリに作るのが一番手軽です。このディレクトリに作った証明書関連のファイルは、フルパスを指定する必要がありません。例えば、以下のようなコマンドで作成できます。

cd /var/www/simplesaml-idp/cert
openssl req -newkey rsa:3072 -new -x509 -days 3652 -nodes \
    -out idp.inter-mediator.com.crt -keyout idp.inter-mediator.com.pem

コマンド例ではカレントディレクトリを明示するためにcdコマンドを随所で書くようにしますが、もちろん、コマンドの理解がある方は自分の状況に応じてコマンドを入れてください。そして、opensslコマンドの-outと-keyoutの2つのパラメータは実際に保存されるファイル名になるので、自分のドメイン等に変えるか、server.cert、privatekey.pemみたいな名前にするのが良いでしょう。

乱数生成などの後、入力を促されます。要するに大雑把な住所と組織などを入力します。以下は私が入力した例ですが、もちろん、ご自分の状況に合わせてください。Common Nameについては、FQDNを入れるのが良いと思われます。

Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Saitama
Locality Name (eg, city) []:Midori-ward
Organization Name (eg, company) [Internet Widgits Pty Ltd]:INTER-Mediator
Organizational Unit Name (eg, section) []:Authentication Support
Common Name (e.g. server FQDN or YOUR name) []:idp.inter-mediator.com
Email Address []:nii@msyk.net

なお、生成されたキーファイルは、ownerだけが読み書きできて、gropuやeveryoneに対する読み出し権限すらありません。Apache2のプロセスのユーザ(Ubuntuではwww-data)が読み出し権限があるようにしなければなりません。しかしながら、アクセス権は、レポジトリの内容全体に設定した方が手軽でしょうから、アクセス権の設定は最後にまとめて行います。

configディレクトリの設定を行う

それでは、設定を進めましょう。まず、レポジトリのルートにあるconfigディレクトリの中身です。このファイルには3つの設定ファイルを作りますが、そのうち、config.phpとauthsources.phpの2つのファイルを用意します。このファイルはスクラッチから作るのではなく、ファイル名に.distが付いたテンプレートのファイルがあるので、それをコピーして用意します。まず、ファイルをコピーします。

cd /var/www/simplesaml-idp/config
cp authsources.php.dist authsources.php
cp config.php.dist config.php

config.phpファイルは、以下のポイントを修正します。 そのためにvimやnanoなどのエディタで開くことになりますが、その前に、以下のコマンドを入れて、secretsaltキーの値を生成しておきます。このことはファイルのコメントにも書かれてあり、以下のコマンドで生成して、出力結果をコピーしておきます。

LC_ALL=C tr -c -d '0123456789abcdefghijklmnopqrstuvwxyz' </dev/urandom | dd bs=32 count=1 2>/dev/null;echo

そして、config.phpファイルを編集します。まず、technicalcontact*は、このサーバの管理者です。基本的には自分を指定すれば良いでしょう。secretsaltはファイルを開く前にコピーしたものを指定すればよく、文字列の中身を消してペーストします。auth.adminpasswordは、IdPのログインする管理者のパスワードです。

:
    'technicalcontact_name' => 'Administrator',
    'technicalcontact_email' => 'msyk@msyk.net',
:
    'secretsalt' => 'whr5p645s3ig7nm9wxibfckllmjfvjl6',
:
    'auth.adminpassword' => 'samltest5682',
:
    'enable.saml20-idp' => true,
    'enable.adfs-idp' => false,
:
    'module.enable' => [
        'exampleauth' => true,
        'core' => true,
        'admin' => true,
        'saml' => true
    ],

enable.saml20-idpは、文字通り、IdPの機能をアクティブにします。module.enableは、exampleauthの値をtrueにしますが、これは、設定ファイルで認証ユーザを提供する仕組みをオンにします。もちろん、簡易的にテストができるようにということです。

続いて、config/authsources.phpの修正です。まず、default-sp以下の配列において、entityIDを変更します。そして、この配列内に、privatekeyとcerificateというキーで、それぞれ秘密鍵と証明書のファイル名を指定しておきます。もちろん、ここでは、前の手順でopensslで生成したファイルを指定します。さらに、テスト用のユーザとして、example-userpassの部分のコメントを外して、その中に定義します。以下の例では、user01というユーザとuser02というユーザが定義されており、それぞれ、パスワードはuser01pass、user02passです。キーになっている’user01:user01pass’の部分でユーザ名とパスワードを表現しており、対応する配列は応答する情報を記載します。ちなみに、大学のディレクトリなどでは、eduPersonAffiliationといった属性が入ってきて、それに応じて大学生か、職員かを判断するようなロジックを求められることはよくあるようです。

:
    'default-sp' => [
        'saml:SP',

        // The entity ID of this SP.
        'entityID' => 'https://idp.inter-mediator.com/',
:
        'proxymode.passAuthnContextClassRef' => false,

        'privatekey' => 'idp.inter-mediator.com.pem',
        'certificate' => 'idp.inter-mediator.com.crt',
:
    'example-userpass' => [
        'exampleauth:UserPass',
:
        'user01:user01pass' => [
            'uid' => ['user01'],
            'eduPersonAffiliation' => ['member', 'student'],
        ],
        'user02:user02pass' => [
            'uid' => ['user02'],
            'eduPersonAffiliation' => ['member', 'employee'],
        ],
    ],

metadataディレクトリの設定を行う

続いて、レポジトリルートにあるmetadataディレクトリの設定を行います。このディレクトリも設定ファイルはないものの、拡張子が.distとなっているそれぞれのファイルのテンプレートがあるので、それをコピーして変更して利用します。3つのファイルがありますが、利用するのは2つだけです。コピーしないsaml20-idp-remote.phpファイルは、SPで利用するものです。

cd /var/www/simplesaml-idp/metadata
cp saml20-idp-hosted.php.dist saml20-idp-hosted.php
cp saml20-sp-remote.php.dist saml20-sp-remote.php

ちなみに、ファイル名がややこしいと思われるかもしれませんが、それぞれ、IdPの設定、SPの設定を行います。IdP自分自身についてはhostedの方で設定します。そして、SPの設定は自分ではないので、remoteであるということです。ファイル名にはきちんと意味があると思えば、少しは見通しよく見えるのではないでしょうか。

metadata/saml20-idp-hosted.phpについては、以下を修正します。まず、$metadata配列のキーについてはキーの値を既定値から変更して設定します。ここでは、とりあえず、IdPのドメインにしました。ちなみに、このキーを既定値のままにすると、動作がおかしかったので、これを切り替えるのが必要ではないかと思われます。そして、privatekeyとcertificateキーのファイル名を、生成したファイルのものに切り替えておきます。

$metadata['https://idp.inter-mediator.com/'] = [
    /*
     * The hostname of the server (VHOST) that will use this SAML entity.
     *
     * Can be '__DEFAULT__', to use this entry by default.
     */
    'host' => '__DEFAULT__',

    // X.509 key and certificate. Relative to the cert directory.
    'privatekey' => 'idp.inter-mediator.com.pem',
    'certificate' => 'idp.inter-mediator.com.crt',

実際にはもっといろいろ変更は必要なのでしょうけど、ここまでの設定だと、証明書やキーのファイルの整合、IdPを稼働、テストユーザの登録程度のことです。

全てのファイルの所有者とグループを揃える

必要なファイルをすべて揃えたので、simplesamlphpのファイルの所有者を、Webサーバのwww-dataに変更しておくのがいいように思います。例えば、次のようなコマンドです。

sudo chown -R www-data:admins /var/www/simplesaml-idp
sudo chmod -R g+w /var/www/simplesaml-idp

こうすれば、simplesaml-idp以下のすべてのファイルやフォルダは、所有者がWebサーバのプロセスのユーザであるwww-dataになり、グループはadminsになります。そして、所有者はrwあるいはrwxになりますが、グループも同様なアクセス権になることを期待します。通常ログインするユーザをadminsグループに入れておけば、そのユーザでのファイルの読み書き権限もあり、Webユーザの読み書き権限も確保していると言うことになります。simplesamlphpのIdPでは、ファイルの書き込み権限がWebサーバに対して必要なのかという問題はありますが、とりあえずはメンテナンスしやすい状態にしていると考えてください。

キャッシュのディレクトリを用意する

ここで、https://idp.inter-mediator.com/ つまり、Webのルートにアクセスすると次のような画面が出てきます。Ver.2.0.xではこのような画面は出てこなかったのですが、Ver.2.1.xでは出るようになったようです。

このエラーはよく読むと、意味がわかります。どうやら、既定値では、/var/cache/simplesamlphp以下のキャッシュファイルを作るようで、そのディレクトリが必要ということに加えて、アクセス権も設定が必要なようです。例えば、次のようなコマンドで対処できます。

sudo mkdir -p /var/cache/simplesamlphp
sudo chown -R www-data:admins /var/cache/simplesamlphp

キャッシュとして、かなりたくさんのファイルが作られます。

なお、simplesamlphp自体をgitを使って更新した後などは、場合によってはキャッシュをクリアしておかないと起動時にエラーになる場合もあります。エラーにならない時もあるのですが、いずれにしてもソースコードの変更によってキャッシュの整理は場合によっては自分でやらないといけない模様です。謎のエラーが出た場合には、/var/cache/simplesamlphp以下を消してみてください。

管理ツールを稼働する

ここで、https://idp.inter-mediator.com/ つまり、Webのルートにアクセスすると次のような画面が出てきます。ちゃんと動いている模様ですが、肝心の管理作業ができません。

管理作業をするには、https://idp.inter-mediator.com/admin にアクセスします。いろいろリダイレクトしますが、認証画面が出てきます。ここでは、ユーザ名はadmin、パスワードは、config.phpファイルに指定したパスワードを入力して認証します。

最初は、以下のようにTestというタブのページになります。ここから先は次の記事で説明ます。