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ユーザによる認証ができるようになった。