macOSで動くFileMaker Server 2024でPHP

FileMaker ServerにはPHPが付属しなくなって久しいのですが、現状、特にFileMaker Serverで動かなくても不便はなく、PHPの処理が欲しい場合は別途サーバを立てるのが最善策なのはいうまでもありません。しかしながら、「どうしても動かしたい」という声が後を絶たず、ここでとりあえず動かす方法をまとめておきます。そもそも、Clarisからの文書として「FileMaker ServerインストーラへのPHP添付の廃止について」があるのですが、内容が非常に込み入っていて、しかも何をしているのかわかりにくいこともあって、PHPを動かすことに躊躇している人も多いかもしれません。この文書は、FileMaker API for PHPという懐かしいライブラリを利用するための方法まで書いてありますが、今時のPHPのバージョンでこのライブラリが無事に動くとは考えづらく、その方法までは不要なのが一般的ではないかと思われます。

以下の方法が成功したのは、macOS Sequoia上のFileMaker Server 2024の場合です。これは対応OSではないのに成功したということです。また、Sequioa上ではFileMaker Server 2023でも成功しました。これも対応OSではありません(2024版はSequioa対応かどうかは公表されていないですね)。一方、macOS Ventura上のFileMaker Server 2023ではエラーになってうまくいきませんでした。これは対応OSであるのに上手くいかなかった組み合わせです。macOS内蔵のApache2に、外部からインストールするphpのモジュールという組み合わせの相性があるようなので、上手くいかない場合もあるという覚悟で進めましょう。

macOSへのFileMaker Serverのインストールはすでにインストールされているものとします。これにHomebrewをインストールします。インストール方法はサイトに記載されているので、それに応じてインストールしてください。インストール後、以下のようなコマンドを叩いて、phpをインストールします。執筆時点では、8.1以降は@をつけるので良いのですが、7.4はタップというサードパーティによるモジュール提供になっているので、コマンドの入れ方が変わります。コマンドラインでphpのバージョンを切り替えるには、unlink/linkを使います。

//最新版のPHPのインストール(執筆時点では8.3)
brew install php
//特定版のインストール
brew install php@8.1
//PHP 7.4のインストール
brew tap shivammathur/php
brew install shivammathur/php/php@7.4
//バージョンの切り替え
brew unlink php
brew link php@7.4

ちなみに、通常は最新版を使うけど、PHPのモジュールとしては古いものを使うということもアリです。以下、自分の使いたいバージョンのパスを確認してください。原則、/opt/homebrew/Cellerというディレクトリ以下にありますが、状況によって違うので、見つからない場合はいろいろ情報を探ってください。

次にコード署名を行います。コード署名とは、秘密鍵/公開鍵を使った暗号化の仕組みの応用で、ざっくり言うと「誰が作ったコードかわかる仕組み」です。iPhoneのアプリケーションでは当初より署名必須となっており、Appleが発行する証明書を利用して署名されていないと、App Storeへの登録ができなくなっています。この手法はmacOSにもかなり浸透してきており、macOSに付属しているApache2のバイナリhttpdも当初よりコード署名がなされています。

PHPはApache2用のモジュール(拡張子.so)を利用します。このモジュールは、httpdと同じプロセス空間で動きます。イメージ的にはhttpdがphpのモジュールを読み込むと思えば良いでしょう。しかし、macOSの制限により、httpdが署名されている場合は、読み込むモジュールも署名されていないといけません。homebrewで得られるモジュールは署名されていないので、そのままではモジュールとして読み込んで実行ができないのです。そこで、brewで得られたphpのモジュールに署名して、httpdが読み込んで利用できるようにする必要があります。

証明書を作る方法はいろいろあるのですが、「キーチェーンアクセス」アプリケーションを利用して、いかような手順で進めて作成される自己署名証明書で大丈夫です。ポイントは、証明書のタイプを「コード署名」にすることと、指定した名前を忘れないようにすることです。この証明書はキーチェーンに入ります。

キーチェーンにコード署名用の証明書を作ったら、コマンド入力を進めます。まず、/opt/homebrew/Celler以下にあるPHPのパスを探って、phpのモジュールを探します。バージョン等でパスの形式が違う場合がありますが、特定バージョンのphpのフォルダには.soファイルは1つだけなので、それをともかく探せば良いでしょう。そして、codesignコマンドで署名を行います。ちなみに、証明書を作ったユーザでログインしてターミナルで作業する範囲では、codesignコマンドはキーチェーンと同一のデータを使うので、コマンドにキーチェーンの場所を指定する必要はありません。コード署名をしたら、署名を読み出してみて、証明書の名前がAuthorityキーの値に見えることを確認しましょう。

モジュールを特定する(以下、「モジュールのパス」と記載)
/opt/homebrew/Cellar/php/8.3.12_1/lib/httpd/modules/libphp.so
コード署名する
codesign -f --options runtime --timestamp -s "msyk-test" モジュールのパス
//コード署名を表示する',
codesign -d -vvv モジュールのパス
//以下、表示結果Authorityの部分が、証明書の名前(作成時に指定したものとなっている)
Executable=/opt/homebrew/Cellar/php@7.4/7.4.33_6/...
Identifier=libphp7
Format=Mach-O thin (arm64)
   :
Authority=msyk-test

続いて、Apache2の設定ファイルを編集します。設定ファイルは、/Library/FileMaker Server/HTTPServer/conf以下にあります。ファイルはhttpd.confなのですが、以下のように、さらに2.4という拡張子が増えているファイルがあります。これを-iをつけたlsコマンドで見ると、1列目にi-nodeが見えています。i-nodeはファイルシステムでのファイルの中身を特定する番号を思ってください。よく見ると、httpd.confとhttpd.conf.2.4は同じi-node番号です。つまり、1つのファイルの実態にファイル名が2つ割り当てられているという状況で、これらは「ハードリンク」という状態であると呼ばれます。つまり、同じ中身なのです。

% ls -l -i
total 424
158411791 drwxrwxr-x  32 fmserver  fmsadmin   1024 10  9 10:19 extra
158417655 -rwxrwxr-x@  1 fmserver  fmsadmin  18175 10  8 13:32 httpd.conf
158417655 -rwxrwxr-x@  2 fmserver  fmsadmin  18144 10  8 11:57 httpd.conf.2.4
158412007 -rwxrwxr-x   2 fmserver  fmsadmin  13077  1 25  2024 magic
158412007 -rwxrwxr-x   2 fmserver  fmsadmin  13077  1 25  2024 magic.2.4

ここで普通にファイルを変更してもいいのですが、2.4が付いた方がバックアップと売れば、httpd.confは別ファイルとして変更したいような気がするので、たとえば次のようにコマンドを入れて、http.confとhttpd.conf.2.4を別物にしてしまうという手はあります。2つ目でhttpd.confは消えるのですが、中身は別のリンクhttpd.conf.2.4から参照できる状態なので、事実上ファイルは消えません。

cd /Library/FileMaker Server/HTTPServer/conf
rm httpd.conf
cp http.conf.2.4 httpd.conf

ということで、設定ファイル/Library/FileMaker Server/HTTPServer/conf/httpd.confを修正します。以下に、元の状態とその範囲の修正結果を示します。ページ内で、phpというキーワードを探せばすぐに元の範囲は特定できます。その前にLoadModule、そしてFileMatchディレクティブの中にSetHeaderを定義します。モジュールのパスや証明書の名前は、もちろん、前に示したものを指定します。

//元の状態',
<FilesMatch "\.php$">
    Require all denied
</FilesMatch>
//修正した状態',
LoadModule php_module モジュールのパス 証明書の名前
<FilesMatch "\.php$">
    SetHandler application/x-httpd-php
</FilesMatch>

なお、ここで、LoadModuleディレクティブは、通常は証明書の名前は設定できません。Apacheのサイトでもこんな引数はありません。おそらく、macOSに搭載されているhttpdは特別にこのような設定を入れることができるようになっていると思われます。

ちなみに、Apache2のエラーログなどを見ると、正しい状態でもこのようなnoticeメッセージが出てきます。パスが違っていたり、署名されていなかったり、名前が違ったりすと別のエラーメッセージになり、Apache自体が起動しません。

[Tue Oct 08 12:44:50.253541 2024] [so:notice] [pid 17129] AH06662: Allowing module loading process to continue for module at /opt/homebrew/opt/php/lib/httpd/modules/libphp.so because module signature matches authority "Developer ID Application: Nii Masayuki (W3WVRUYJRT)" specified in LoadModule directive

ということで、お決まりの、phpinfo()関数の結果は以下のとおりです。Darwin OS内で動いているのが見えます。もう、HomebrewのPHPは、Sequoia対応になっていますが、この時はSonoma対応の段階でしたけど、ちゃんと動いていました。

そういうわけで、FMDataAPIについても動作確認しました。一連の作業は、自分の普段使いのMacで確認したので、HomebrewによるPHPはいろんなものが最初から入っているため一発で動きましたが(というか、その環境でFMDataAPIを開発している)、足りないモジュールがあればbrew install モジュール名 等でインストールしましょう。

一連の作業で面白い動作を発見しました。codesignコマンドなのですが、SSH経由でログインをしてコード署名を行うと、adhocという名前が出てくる、おそらくはちゃんとしてない署名になります。Authorityは設定されないので、ApacheのPHPモジュールに署名しても稼働はしません。つまり、codesignコマンドは、macOSにログインをして、その上でターミナルから起動して署名しないといけないのです。セキュリティ的な理由なのか、あるいはキーチェーンの扱いなんかがあるのかもしれませんが、ともかく、ログイン方法でコマンドの動作が違って場合によっては正く署名されないというのは落とし穴かもしれません。プロバイダにあるMacや、鍵のかかったサーバルームにあるMacなんかの場合はよく注意をしましょう。

そういうわけで、HomebrewがインストールしたPHPのモジュール、macOSに内蔵されているApache2のhttpdを使うということで、コード署名が必要になるということだったのですが、ちなみに、Homebrewのアップデート(brew update/brew upgradeコマンド)を行うと、場合によってはバイナリが置き換わります。上記のような手順の場合、PHP 8.3の新しいマイナーバージョンの登場で古いものは消されるので、つまりはアップデートがあればその都度コード署名をしなければならないという、面倒かつ、実運用では危険な状態でもある点に注意を払う必要があります。

ちなみに、FileMaker Serverは入れておきたいけど、PHPでも開発作業等をしたい場合、つまり、それぞれ別々に利用するのが一般的な場合は、ここにあるような作業をしなくても、phpを-Sオプションをつけて、サーバモードで動かすのが普通です。公開ディレクトリをカレントディレクトリにして、php -S localhost:9000などとコマンドを入れて、ブラウザから「http://localhost:9000」に接続します。ということで、やっぱりFileMaker ServerのApache2上でPHPを動かす必要性って限られるんですよね。

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では認証はおそらくできているとしてもユーザ名が得られないので、認証していないとみなしているというところで、その解決策がわかれば、続きの記事を書くとする。

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

INTER-Mediatorで作るアプリケーションの認証基盤として、KeycloakをSAMLのIdPとして利用した場合の動作を検証する必要が出てきたので、実際に動かしてのチェックを進めてみる。SPはもちろんSimpleSAMLphpであり、これはINTER-Mediatorではcomposerでインストールできている状態になっている。ということで、まずは、Keycloakをきちんと動くようにしたい。(1)は、Keycloakを動かすまでである。

稼働するサーバは、INTER-Mediatorの公開サーバーで、Ubuntu 22で稼働している。そこに、kc.inter-mediator.comというドメイン名を割り当てて、Keycloakを利用することにする。ただ、Keycloak自体はJavaのアプリケーションであり、Apache2と連携しなくても連動するので、ともかく連携しない方法での稼働を目指すが、Let’s Encryptで、kc.inter-mediator.comの証明書を作るので、Apache2側も幾らかの設定を行うことにした。

なお、インストール方法は、ITC Enginnering Blog:Ubuntu 22にKeycloak 22をインストールして、Identity providers=Azure ADでSAMLを参考にさせていただいた。基本、概ね同じである。

利用するドメインの証明書を取得するまで

INTER-Mediatorの公開サーバーは、Sakura VPSである。すでにWeb等が稼働しているため、当然ながらのポートは開いているが、Keycloak用に、TCP 8443とTCP 9000のポートを開いた。9000はとりあえずは使わない。サーバ側にFirewallを設定する方式ではなく、コントロールパネルから設定するパケットフィルタを設定しているので、そこで8443/9000ポートを開いておいた。

そして、まずは、OSのアップデートを行い、kc.inter-mediator.comサイトを作る。サイトのルートは、/var/www/kcとした。そして、Apache2の設定ファイルをsites-availableディレクトリに作る。80番ポートと433番ポートは同じディレクトリを公開するように設定しているが、80側にはリダイレクトの設定があり、httpでアクセスがあると自動的にhttpsにしている。なお、certbotが成功するまでは、SSLCertificateFileとSSLCertificateKeyFileの設定は頭に#を入れてコメントにしておく必要がある。ここまで準備できれば、a2ensiteでサイトの設定ファイルをアクティブにして、systemctlでApache2を再起動し、certbotコマンドで証明書を作成する。証明書が正しく作成できれば、006-kc.confファイルのSSLCertificateFileとSSLCertificateKeyFileの前にある#を消しておき、改めてApache2をsystemctlで再起動する。もちろん、http://kc.inter-mediator.comにブラウザで接続して、証明書が正しく存在することは確認しよう。ここまでは、Keycloakとは関係のない、単にサーバ証明書を取得するための手順である。

sudo apt update
sudo apt upgrade
cd /var/www
mkdir kc
sudo chown www-data kc
sudo nano /etc/apache2/sites-available/006-kc.conf
# 006-kc.conf ファイルの内容をここで修正(証明書読み込み部分はコメント)
sudo a2ensite 006-kcsudo
systemctl restart apache2
sudo certbot certonly --webroot -w /var/www/kc -d kc.inter-mediator.com
sudo nano /etc/apache2/sites-available/006-kc.conf
# 006-kc.conf ファイルの内容をここで修正(証明書の読み込み部分を活かす)
systemctl restart apache2

以下は最終的な006-kc.confファイルの内容である。もちろん、これは一例であって、皆さんはご自分のスタイルがあるだろうから、それに従って作ろう。ともかく、Let’s Encryptで証明書を作ってもらうためだけのサイトなのである。

<VirtualHost *:80>
    ServerName kc.inter-mediator.com
    ServerAdmin info@inter-mediator.org
    DocumentRoot /var/www/kc

    ErrorLog ${APACHE_LOG_DIR}/kc-error.log
    CustomLog ${APACHE_LOG_DIR}/kc-access.log combined

    RewriteEngine on
    RewriteCond %{SERVER_NAME} =kc.inter-mediator.com
    RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>
<VirtualHost *:443>
    ServerName kc.inter-mediator.com
    ServerAdmin info@inter-mediator.org
    DocumentRoot /var/www/kc

    ErrorLog ${APACHE_LOG_DIR}/kc-error.log
    CustomLog ${APACHE_LOG_DIR}/kc-access.log combined

    SSLCertificateFile /etc/letsencrypt/live/kc.inter-mediator.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/kc.inter-mediator.com/privkey.pem
</VirtualHost>

PostgreSQLのインストールと準備

KeyCloakは、データをPostgreSQLに溜め込むので、そのための準備が必要である。PostgreSQLをセットアップして、さらにデータベース側のユーザとデータベースを定義しておく必要がある。インストールは特に難しくはなく、aptで行う。その後に、システムに作られたpostgresユーザへのログインができるように、そのユーザにパスワードを設定する。ここでは書かないが、自分のパスワードはどこかにメモると良いだろう。その後、suでpostgresユーザに切り替わり、そしてpsqlコマンドでデータベースへのコマンド入力ができる状態にする。そして、CREATE USERとCREATE DATABASEのコマンドを入れる。PostgreSQLユーザのPASSWORDももちろん、独自に設定して自分で管理してもらいたい。

sudo apt install postgresql postgresql-contrib
sudo systemctl enable postgresql
sudo passwd postgres
# パスワードはきちんと把握しておく
su - postgres
psql
CREATE USER keycloak WITH PASSWORD 'PASSWORD' CREATEDB;
CREATE DATABASE keycloak OWNER keycloak;

Keycloakのインストール

やっと、Keycloakをインストールする段階にきた。インストールは簡単だが、aptでインストールはできない。そして、まずはJavaのインストールも必要である。コンポーネントとしては若干複雑な構成のようだが、JavaとKeycloakのインストールがともかく必要になる。

sudo apt install openjdk-21-jdk
java -version
cd /var/www
wget https://github.com/keycloak/keycloak/releases/download/25.0.2/keycloak-25.0.2.tar.gz
tar zfxv keycloak-25.0.2.tar.gz
sudo nano keycloak-25.0.2/conf/keycloak.conf

Javaのインストールは良いとして、Keycloak自体は/var/wwwにインストールした。もちろん、好みの場所で良いだろうが、使っているサーバはインストールしたものが概ね/var/wwwにあるのでこのようにした。wgetの後のURLは、KeyCloakのWebサイトのDownloadのページにあるtar.gzファイルのリンクをそのまま利用した。そして、そのまま展開している。よって、KeyCloak本体は、/var/www/keycloak-25.0.2というディレクトリに展開された。

続いて、KeyCloakの設定ファイル「conf/keycloak.conf」を編集する。実際のファイルにはコメントが丁寧に細かく書かれているので、それを読みながら作業するのが良いと思われる。以下コメントとともにファイルの内容を記載する。

# Basic settings for running in production. Change accordingly before deploying the server.

# Database

# The database vendor.
db=postgres

# The username of the database user.
db-username=keycloak

# The password of the database user.
db-password=PASSWORD

# The full database JDBC URL. If not provided, a default URL is set based on the selected database vendor.
db-url=jdbc:postgresql://localhost/keycloak

# Observability

# If the server should expose healthcheck endpoints.
health-enabled=true

# If the server should expose metrics endpoints.
metrics-enabled=true

# HTTP

# The file path to a server certificate or certificate chain in PEM format.
https-certificate-file=${kc.home.dir}/conf/fullchain.pem

# The file path to a private key in PEM format.
https-certificate-key-file=${kc.home.dir}/conf/privkey.pem

# The proxy address forwarding mode if the server is behind a reverse proxy.
#proxy=reencrypt

# Do not attach route to cookies and rely on the session affinity capabilities from reverse proxy
#spi-sticky-session-encoder-infinispan-should-attach-route=false

# Hostname for the Keycloak server.
hostname=kc.inter-mediator.com

最初のdbはこのまま、次のdb-username、db-passwordは、psqlコマンドで打ち込んだCREATE USERの時の情報をここに記載する。db-urlはこのままでOKである。HTMLセクションの最初の2つは、サーバ証明書である。ここにLet’s Encriptのディレクトリをそのまま書きたいところだが、そこはrootしかアクセス権がなく、KeyCloak自体をそのためのユーザで稼働することを考えているためその方法は使えない。そこで、証明書をKeyCloakの配下に以下のようにコマンドを入れてコピーすることにした。3ヶ月後はどうするかはまた考えるとして、とにかくこのようにした。なお、privkey.pemはユーザしか読み出しできないので、グループや全員に読み出し許可を与えている。

cd /var/www/keycloak-25.0.2/conf
sudo cp /etc/letsencrypt/live/kc.inter-mediator.com/privkey.pem .
sudo chmod a+r privkey.pem
sudo cp /etc/letsencrypt/live/kc.inter-mediator.com/fullchain.pem .

KeyCloakを動かす

KeyCloakの起動スクリプトは、ディレクトリのbinにあるkc.shである。これをまずは起動してみる。とりあえず、サブコマンドにbuildを与えて、そしてサブコマンドstartで実際に起動する。出力結果を見ていてERRORがなければ大丈夫。そして、最後の方に、8443と9000ポートが開くメッセージがあるのでわかる。

cd  /var/www/keycloak-25.0.2/bin
./kc.sh build
./kc.sh start

そして、 https://kc.inter-mediator.com:8443 を開いてみる。すると以下のようなメッセージが見える。現状では管理者としてログインができないということを示している。

ここで、Ctrl+Cで一度止める。そして、記載の通り、環境変数に管理者のユーザ名、パスワードを指定して再度起動する。コマンドとしては次の通り。PASSWORDはもちろん、何か適当なパスワードを指定して、自分で記録管理しておく。

cd  /var/www/keycloak-25.0.2/bin #カレントはKeyCloakの中のbinとする
export KEYCLOAK_ADMIN=admin
export KEYCLOAK_ADMIN_PASSWORD=PASSWORD
printenv|grep KEY #一応確認
./kc.sh --verbose start

すると、次のように、ログインパネルが表示され、ここで、admin/PASSWORDでログインができるはずである。ここまでで、基本的な動作確認ができた。

なお、環境変数にパスワードがある状態になるが、おそらくこの後にサービス化するので、再起動して後付けの環境変数は消えるはずである。このKEYCLOAK_*という2つの環境変数がある状態で、まだ管理者がない状態であれば、この環境変数の値を使って管理者ユーザを作成する。つまり、その瞬間にだけ環境変数があれば良いようで、その後は環境変数になくても管理者は存在し、ログインができるのである。

KeyCloakのサービス化

コマンドラインで起動した状態で使うというのはちょっと現実的ではないので、SystemCTL配下のサービスとして、バックグランドで自動起動するようにしたい。そのために、以下のようにコマンドを入れた(ほとんど前述のブログ記事の通り)。

sudo groupadd -r keycloak
sudo useradd -m -d /var/lib/keycloak -s /sbin/nologin \
     -r -g keycloak keycloak
sudo nano /etc/systemd/system/keycloak.service
# ここで、新しいファイルが作られるので、以下のように記述する
sudo chown -R keycloak /var/www/keycloak-25.0.2
sudo systemctl enable keycloak.service
sudo systemctl start keycloak.service
systemctl status keycloak.service

ファイル「keycloak.service」については、以下のように作成した。おそらく調整ポイントは、WorkingDirectoryとExecStartのパスだけだろう。

[Unit]
Description=Keycloak Application Server
After=syslog.target network.target

[Service]
Type=simple
TimeoutStopSec=0
KillSignal=SIGTERM
KillMode=process
SuccessExitStatus=143
LimitMEMLOCK=infinity
SendSIGKILL=no
WorkingDirectory=/var/www/keycloak-25.0.2/
User=keycloak
Group=keycloak
LimitNOFILE=102642
ExecStart=/var/www/keycloak-25.0.2/bin/kc.sh start --log=console,file

[Install]
WantedBy=multi-user.target

コマンド入力について説明しておく。最初のgroupaddとuseraddコマンドでは、ユーザkeycloak、グループkeycloakを作成している。もちろん、keycloakユーザはkeycloakグループに所属させる。このユーザで、デーモンを動かすということである。ここでのKeyCloakが存在するディレクトリ/var/www/keycloak-25.0.2以下についても、keycloakユーザが所有者となるように、chownコマンドで設定している。useraddについては、-dでホームディレクトリを設定しているが、ここに何かを仕掛ける予定は特にない-mによりホームは自動的に作られる。シェルをnologinにしてあるので、ログインをしないことを想定している。-rはシステムアカウントとしてkeycloakを作る。実際にはIDは997で作られた。

実際のログファイルは、/var/www/keycloak-25.0.2/data/log/keycloak.logとなる。つまり、KeyCloakのディレクトリ以下、data/logの場所に作られる。この内容は、kc.shで起動した時に画面で見えるのと同じである。なお、systemctlコマンドではログの一部はアクセス権がないということで出てこないが、ログの場所がわかっていれば問題ないだろう。

ということで、無事にKeyCloakが起動した。

(1)はここまでとする。

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のサンプルの稼働が確認できます。

スクラップ&ビルドしろってことでしょうね

日本時間だと2024-5-7の深夜に公開されたAppleの新しいイベント映像は、iPadの新しいラインナップだった。その中で、既存の様々なものがぺちゃんこに潰され、そこには新しいそして薄っぺらいiPad Proが残っているという映像が流された。これに嫌悪感を感じる人も日本人には多いようであるが、まあ、それもAppleの計算のうちだったのでしょう。この映像は、単に「ぺちゃんこにして詰め込んだ」ということを言いたいのではないと思う。この映像から受け取れるメッセージは、「新しいものの創造には、スクラップ&ビルドが必要」ということではないだろうか。もちろん、嬉々として、あるいは楽しんでスクラップするということもあるのだろうけど、心苦しくもスクラップしないといけないということも創造にはつきものだと思われる。なんでもそうだと思われるが、進歩には何かしら過去の成果を凌駕することから生まれる。他人の成果はもちろんだが、自分の過去の成果を否定して進まなければならないときもあるものだ。ノスタルジーやあるいはなんらかの感情でそれをやめた瞬間、創造物は劣化する。痛みを感じても前に進まないといけないのである。それを「わかりやすい映像」でAppleは示したのである。

ところで、今回のAppleのイベントは、確かにiPadのテレビショッピング的ではあったのだが、前述のぺちゃんこにされるいろんな機材の中に、「実はMacもありまして…」というオチがあるのじゃないかとふと思った。多分、アップライトピアノがあったように思うが、その後ろにこっそりと、iMacがあって、iMacも破壊したのじゃないかと想像してしまう。M4はiPad向けではあるものの、Macシリーズより進んだプロセッサの登場がある意味象徴的だ。「今後Macは無くなる」と散々言われて来ているものの、iPadの進化がついにそれを現実のものにするのではないかと思ってしまう。つまりはMacのスクラップだ。そして、Macは一部の用途にだけ残るのか、あるいはすっかりなくなってしまうのかはわからないけども、ともかく現状はソフトウエア開発環境以外はほぼiPadで代替できてしまうというのが多くの皆さんのMacの使い方になってしまって来ていないだろうか? Appleとしては「準備はもうできている」と認識しているだろう。プレス機でのインパクトのある映像は、「そのうちMacもスクラップするよー」というメッセージに聞こえてしまうのである。

FileMakerのクイック検索とデフォルト言語

FileMaker 2023 (Ver.20.3.2.201) で不可解な動作がありました。なぜか、見え見えの検索条件を指定しても、クイック検索で検索できないのです。原因は、そのフィールドのオプション設定にあるデフォルト言語をUnicodeにしていたからです。通常「日本語」なので、何か設定をしてしまったのでしょうけど、「日本語」だとクイック検索できて、「Unicode」だと検索できないってなんか変な気がします。

ビデオを見ていただくとわかるように(外を歩く子供達の声が聞こえるけど無視して〜)、最初の検索で、「日本」では検索できるけど「大学」では検索ができないです。「病院」も同様に検索できません。検索できる文字とできない文字があるようです。このフィールドのデフォルト言語をUnicodeにしてありますが、それを「日本語」に変えるとちゃんと検索できています。

まあ、Unicodeにするなということだと思いますけど、デフォルト言語を「デフォルト」にしても、Unicodeと同様に、「病院」などの検索ができなくなりますね。これ、バグなんだろうか? あるいは有名な話?