こんにちは、亀井です。

ここでは、Windows Active Directory を利用したSSOの実装について書きたいと思います。

Oracle APEX上でActive Directory認証をしたいという内容に対して、調べていたのですが、もともとWebサーバー殆ど触ることが無いためわかりにくく・・。

mod_auth_mellonを利用したSAML2.0での認証の方が現状は主流になるかと思いますが、今回はSPNEGO(Simple and Protected GSSAPI Negotiation Mechanism)での実装を考慮する必要がありましたので、その調査結果を記載します。

ここでの内容は実際に実施した内容ではなく、単純に調査した内容のみとなります。動作についてうまく行かないことがあるかもしれません。内容の理解のためにご参考ください。

弊社ではAzure Active Directoryを利用しており、従来のWindows Activce Directoryの実装はしていないため、何かしらの要望がない限り今後テストすることもないかと思います。

実装する内容について

このあたりが、知っている前提が多くて理解しづらかった部分です。
コマンドなどはOracle Linux想定で記載します(なのでRedHat系も同じかと思います)。

何をするのか

  • SSO(Single Sign On)を実装します。
  • Windows Active Directoryと連携し、パスワード入力なしのドメイン認証を行います。

ログイン後、ユーザー名はREMOTE_USER環境変数に格納され、アプリケーションではREMOTE_USER環境変数(CGI環境変数)を確認して、格納されたユーザー名でのログインがされたとみなします(Oracle APEXではヘッダー認証を利用します)。 つまり、Webサーバ側のみで認証されることになります。

Kerberos認証の利用

Linux上にあるWebサーバー(Apache HTTP Server(以降Apache))とWindows Active Directory(以降AD)を、Kerberos認証によってつなぎます。
Kerberos認証はADの認証のプロトコルに使われており、今回利用する統合Windows認証にも利用されます。統合Windows認証はKerberos認証かNTLM認証のどちらかですが、Kerberos認証が優先して使用されます。

ウィキペディア ケルベロス認証
Microsoft テクニカルドキュメント Windows 認証の概要

Apache HTTP Serverの拡張機能

Apacheの機能を拡張するために、モジュール(外部で作成した機能群)が利用されます。
このモジュールによって、ApacheでKerberos認証を行うことができます。
今回はmod_auth_gssapiというモジュールを利用します。mod_auth_kerbでKerberos直接ではなく、mod_auth_gssapiを利用したほうが良いです(今後のサポートの問題です)
mod_auth_gssapiにより、GSSAPIを介して、Kerberos認証を行い、ADのドメイン認証によるログインを実現します。モジュールのインストールはyum等で行います。

mod_auth_gssapiを利用し、SPNEGOという仕組みで通信します。SPNEGOはGSS-API(Generic Security Service Application Program Interface)上に実装される通信方式です。GSS-APIはKerberos認証を実行します。

SPNEGOについて RFC2478
GSS-APIについて RFC2743

Apacheですが、最初からインストールされている場合、systemctl status httpdで起動状態のステータスが確認できます。

keytabファイルの利用

LinuxからADへKerberos認証をする場合、keytab(KeyTable)ファイルというものを作成する必要があります。ファイル内容は、プリンシパル名、鍵のバージョン、暗号化方式、鍵自身で構成されています。

MIT Kerberos Documentation keytab

プリンシパル名は、どのドメインのどのユーザーかを表したものです。<ユーザー名>@<ドメイン名> で表現されます。ADのドメインユーザー名と紛らわしいですが、今回はプリンシパル名に利用するサービスのプリンシパル名を指定し、ADのドメインユーザーがサービスのプリンシパル名を持つ形になります。

ドメイン認証を行うときに、以下のような流れで認証を行います。

  1. ドメインに参加しているユーザーがAD(ドメインコントローラー)にApache接続用のチケットを発行してもらいます。
  2. ユーザーが発行されたチケットをApacheへ渡します。
  3. ApacheがADに問い合わせて問題がなければログインを許可します。

このとき、Apacheが受け取ったチケットをADに問い合わせするためにKeytabファイルを利用します。

ブラウザの設定

ネゴシエートによる認証(チケットを利用したパスワード入力なしの認証)をするために、接続するWebサーバー(URL)がイントラネット内であることをブラウザ側で設定する必要があります。(おそらくチケットを渡す相手を判定するためだと思いますが、詳細までは見ていません)

実装の流れ

  1. AD操作 ApacheからADへチケットの有効性を問い合わせるためのドメインユーザーの作成
  2. AD操作 問い合わせ用のkeytabファイルの作成(作成したドメインユーザーがADへアクセスするため)
  3. Linux操作 Kerberos認証を利用するためのKerberosの設定
  4. Linux操作 mod_auth_gssapiモジュールを利用するためのApacheの設定
  5. クライアント操作 ブラウザのローカルイントラネット設定

実装

実際の作業はしていないので、ここでは詳細は書きません。

AD操作 ドメインユーザーの作成

ADにてドメインユーザーを作成してください。

Kerberos認証の通信が、デフォルトだとRC4になるようです。現在のセキュリティ事情を考えれば、可能であればAES256とAES128での通信の許可をしたほうがよいです。
ポリシーまたは、ユーザーごとの設定で変更できるかと思います。

Microsoft テクニカルドキュメントネットワーク セキュリティ: Kerberos で許可する暗号化の種類を構成する

AD操作 Keytabの作成

ADサーバーでKeytabを作成します。

ktpass -princ HTTP/<アクセスするWebのURL>@<ドメイン名> -mapuser <ドメインユーザー名> -pass <ドメインユーザーパスワード> -crypto All -ptype KRB5_NT_PRINCIPAL -out <keytabファイル名>

実際の例:

ktpass -princ HTTP/web.example.co.jp@EXAMPLE.COM -mapuser examplecojpuser@EXAMPLE.COM -pass xxxxxx -crypto All -ptype KRB5_NT_PRINCIPAL -out httpd.keytab
変数・オプション説明
アクセスするWebのURL実際のURLに利用されるドメイン名です。小文字になるかと思います。https://<この部分>/ords
ドメイン名ユーザーが参加するドメイン名です。
ドメインユーザー名作成したドメインユーザー名です。
ドメインユーザーパスワード作成したドメインユーザーのパスワードです。
princHTTPサービスプリンシパル名(SPN)を指定します。 HTTP/<アクセスするWebのURL>@<ドメイン名> の形式で登録してください。小文字と大文字が区別されるため、ドメイン名は大文字で指定してください。
mapuserここに指定するドメインユーザーにサービスプリンシパル名がひも付きます
ptypeKRB5_NT_PRINCIPAL が一般値であり、推奨値となっています。形式の違いかと思いますが、他の値(KRB5_NT_SRV_INST/KRB5_NT_SRV_HST)については未調査です。
crypto暗号化方式をAllで全ての暗号方式で通信可能としています。強固なもののみにしたい場合は、AES256-SHA1を指定してください。
out出力するkeytabファイル名。ファイル名は何でも大丈夫です。後でLinuxサーバーへ転送し、Apacheの設定でファイル名を指定します。

ktpassのマニュアルに、複数のサービスインスタンスをマップすることが出来ませんと注意書きがあります。サービスインスタンスの一意の識別子がサービス プリンシパル名とあるので、別のサーバーに同じユーザーを使うことが出来ないということかと思います。

Microsoft テクニカルドキュメント ktpass
Microsoft テクニカルドキュメント Kerberos とサービス プリンシパル名 (SPN)

Linux操作 Kerberosの設定

Kerberosの設定を行います。

vi /etc/krb5.conf
[logging]
default = FILE:/var/log/krb5libs.log
kdc = FILE:/var/log/krb5kdc.log
admin_server = FILE:/var/log/kadmind.log

[libdefaults]
default_realm = EXAMPLE.COM

# The following krb5.conf variables are only for MIT Kerberos.
kdc_timesync = 1
ccache_type = 4
forwardable = true
proxiable = true

fcc-mit-ticketflags = true

[realms]
EXAMPLE.COM = {
  kdc = domaincontroler.example.com.:88
  admin_server = domaincontroler.example.com
  default_domain = example.com
}

[domain_realm]
.example.com = EXAMPLE.COM
example.com = EXAMPLE.COM

レルム(realm)はKDC(Key Distribution Center)でサービスを提供している論理ネットワークです。ここではADが提供しているEXAMPLE.COMという論理ネットワークということです。これは、プリンシパル名の@の後ろと一致します。

Red Hat ドキュメント 11.3. Kerberos クライアントの設定

Linux操作 Apache設定

ADサーバーで作成したkeytabファイルを/etc/httpd.keytabへ配置します。SELinuxでetc配下のファイルが読み込めないなど気をつけてください。その場合、Apacheのプロセスをps -eZ | grep httpdなどで確認し、Apacheプロセスのコンテキストをファイルに付与してください。
Apacheにmod_auth_gssapiモジュールの設定を行います。
Apacheの設定ファイルの詳細は別途ご確認ください。

# keytabは作成したドメインユーザーで接続するための鍵があるため、権限400でapacheのみ見れるようにしておきます
chmod 400 /etc/httpd.keytab
chown apache:apache /etc/httpd.keytab
vi /etc/httpd/conf/httpd.conf
ServerName web.example.co.jp:80

<VirtualHost *:80>
    ...省略...

    <Location /adsso-test>
      AuthType GSSAPI
      AuthName "GSSAPI Single Sign On Login"
      GssapiAllowedMech krb5
      GssapiBasicAuth Off
      GssapiNegotiateOnce On
      GssapiCredStore keytab:/etc/httpd.keytab
      Require valid-user

      AddHandler cgi-script .cgi
      Options ExecCGI
    </Location>

    ErrorLog /opt/log/error.log
    CustomLog /opt/log/access.log combined
    LogLevel Debug

    ...省略...
</VirtualHost>

adsso-test配下に認証が必要なように構成しています。

    <Location /adsso-test>

テスト用にCGIの実行権限と

      AddHandler cgi-script .cgi
      Options ExecCGI

デバッグログの設定を付けています。

    ErrorLog /opt/log/error.log
    CustomLog /opt/log/access.log combined
    LogLevel Debug

詳細なオプションはGSS-APIのREADMEを確認してください。
例えば、GssapiBasicAuthをOnにすればに接続していないユーザーが、Kerberos経由で基本認証によるログインが出来ます。

GitHub mod_auth_gssapi/README

クライアント操作 ブラウザの設定

インターネットオプション > セキュリティ > ローカル イントラネット でURLを登録することでEdge/Chromeは動くかと思います。Firefoxは別の設定です。
このあたりは情報が多いので、ここでは詳しく書きません。

接続確認

環境変数の確認

以下の記事を参考に、CGI環境変数を取得するスクリプトを準備します。
ログインなしで画面が表示され、REMOTE_USER環境変数にユーザー名が入っていれば成功です。

Qiita 社内gitサーバで、kerberosベースのシングルサインオンを実現する @tohosaku@github

vi /var/www/adsso-test/env.cgi
#!/usr/bin/perl

print "Content-type: text/html\n\n";
print "<table>\n";

for $key (sort(keys(%ENV))) {
    print "<tr><td>$key</td><td>$ENV{$key}</td></tr>\n";
}

print "</table>\n";
exit;

http://web.example.co.jp/adsso-test/env.cgiに接続し、REMOTE_USERを確認してください。

Oracle APEX に認証させるための後続作業について

リバースプロキシによるhttpの転送か、TomcatにORDS(Oracle REST Data Service)をデプロイしてAJP (Apache JServ Protocol)を利用した連携のどちらかとなると思います。
Oracle APEX でもSAML2.0の対応を予定しており、徐々に機能がついているようなのでそろそろ実装されそうですが、なかなか出てきませんね(2022/02/06現在)。

おわりに

最初に記載させていただきましたが、ここでの内容は実際に実施した内容ではありません。
うまく動作しない場合は大変申し訳無いですが、内容の理解のための記事とご理解ください。