こんにちは、滝澤です。
Apache HTTP ServerをOpenID Connect Relying Partyにするmod_auth_openidcというモジュールを使ってみる機会がありましたので、本記事で情報共有します。
なお、記事が長くなったので本編と設定例である3編に分けました。
本記事では、複数のOP(OpenID Provider)として、Google(G Suite)とAzure Active Directory(以降、Azure ADと略す)を利用する場合の設定例を紹介します。
設定例
ウェブサイトURI
まず、ウェブサイトURIとリダイレクトURIを決めます。
ウェブサイトURIには、利用するウェブサイトのURIを指定します。このとき、パス名を付けない形式にしてください。
リダイレクトURIには、リダイレクトURI(redirect_uri)として使用されるURIを指定します。コンテンツが存在するURIに被らないものにしてください。このリダイレクトURI経由でリクエストのやりとりが行われます。
ウェブサイトURI | https://example.com |
リダイレクトURI | https://example.com/redirect_uri |
アプリケーションの登録とクライアントIDとクライアントシークレットの取得
Google (G Suite)の場合
次のヘルプのページに従って、アプリケーションの登録とクライアントIDとクライアントシークレットを取得してください。
- OAuth 2.0 の設定 (Google)
このとき、以下のような値を指定します。
アプリケーションの種類 | ウェブアプリケーション |
承認済みの JavaScript 生成元 | 先の「ウェブサイトURI」で指定した値(例: https://example.com) |
承認済みのリダイレクト URI | 先の「リダイレクトURI」で指定した値(例: https://example.com/redirect_uri) |
Azure ADの場合
次のヘルプのページに従って、アプリケーションの登録を行います。
- AD テナントへのアプリケーションの登録 (Microsoft)
このとき、以下のような値を指定します。
アプリケーションの種類 | Web アプリ/API |
サインオン URL | 先の「ウェブサイトURI」で指定した値(例: https://example.com) |
応答 URL | 先の「リダイレクトURI」で指定した値(例: https://example.com/redirect_uri) |
ここで、「プロパティ」のブレードで表示される「アプリケーション ID」がクライアントIDになります。
さらに、「APIアクセス」の「キー」をクリックすると、「キー」のブレードが表示されるので、ここで「説明」と「有効期限」を入力して保存すると値が表示されます。これがクライアントシークレットになります。
設定ファイルの編集
設定ファイル /etc/httpd/conf.d/auth_openidc.conf を編集して、各ディレクティブを設定します。
以降、ディレクティブ毎に説明します。ただし、デフォルト値で特に問題ないと思われるものについては説明はしません。
OIDCRedirectURI
リダイレクトURI(request_uri)として指定した値を記述します。
OIDCRedirectURI https://example.com/redirect_uri
OIDCCryptoPassphrase
cookieやキャッシュエントリーなどの暗号化に利用するパスフレーズの文字列を指定します。
OIDCCryptoPassphrase XXXXXXXXXXXXXXXXXXXXXXXX
OIDCMetadataDir
OIDCMetadataDirにメタデータ用のディレクトリを指定します。このディレクトリはapacheの実行権限でファイルの読み書きができる必要があります。
OIDCMetadataDir /var/cache/httpd/mod_auth_openidc/metadata
OIDCProviderMetadataURL
OIDCProviderMetadataURLの指定があったら、コメントアウトして無効にします。
OIDCAuthRequestParams
OP毎に異なることがあるので、コメントアウトされていなければコメントアウトしてください。
OIDCSessionInactivityTimeout
無操作タイムアウトの秒数を指定します。デフォルトは300秒です。
OIDCSessionInactivityTimeout 300
OIDCCacheType
キャッシュのストレージのタイプを指定します。
本記事の動作確認ではRedisを使うので、"redis"を指定します。
OIDCCacheType redis
さらに、関連するディレクティブの設定も行ってください。
例えば、redisを指定した場合は下記のようにOIDCRedisCacheServerとOIDCRedisCachePasswordの指定も行う必要があります。
OIDCRedisCacheServer 127.0.0.1 #OIDCRedisCachePassword 8dTjsdkngM4Y2okg3XjYgWRX
OIDCHTMLErrorTemplate
エラーページのテンプレートファイルのパスを指定します。
OIDCHTMLErrorTemplate /etc/httpd/conf.d/auth_openidc.error.html
上記の設定を行った場合は、ファイル"/etc/httpd/conf.d/auth_openidc.error.html"に次のような内容の記述を行ってください。1つ目の%sにはエラーメッセージが、2つ目の%sにはエラーの説明が埋め込まれます。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"/> <title>OpenID Connect: Error</title> </head> <body> <h1>OpenID Connect: Error</h1> <p>Message: %s</p> <p>Description: %s</p> <p><a href="/">Home</a></p> </body> </html>
OIDCDefaultLoggedOutURL
ログアウト後に遷移するURIを指定します。
OIDCDefaultLoggedOutURL https://example.com/loggedout.html
上記のように設定した場合は、次のよう内容のファイルloggedout.htmlをドキュメントルートに配置します。 再びログインできるようにトップページへにリンクを用意します。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"/> <title>OpenID Connect: Logged out</title> </head> <body> <h1>OpenID Connect: Logged out</h1> <p><a href="/">Login</a></p> </body> </html>
OIDCClaimPrefix
claimを環境変数やHTTPヘッダーフィールドにする際に付与するプレフィックスを指定します。
OIDCClaimPrefix OIDC_CLAIM_
例えば、claimの一つ"name"は環境変数あるいはHTTPヘッダーフィールドの名前としては"OIDC_CLAIM_name"になります。
リバースプロキシーとして動かしている場合は、バックエンド側ウェブサーバー側でHTTPヘッダーを環境変数に変換する際の"-"と"_"の曖昧性を排除するために、次のように"_"を使わないようにした方がよいと思われます。
OIDCClaimPrefix OIDC-CLAIM-
OIDCRemoteUserClaim
apacheの認証されたユーザーIDを示す変数REMOTE_USERとして設定するclaimの名前を指定します。
デフォルトでは「sub@iss」の形式になります。 subはエンドユーザーの識別子で、issはIssuerから"https://"を除いたものになります。
複数のOPで共通で利用でき、ユーザーを識別可能なclaimを指定してください。
emailが使えれば次のようにしてもよいでしょう。
OIDCRemoteUserClaim email
共通で利用できるclaimがなければ、コメントアウトしてデフォルトの形式(sub@issの形式)に任せてもよいと思います。
なお、GoogleとAzure ADでは共通で利用できるclaimにはsub, name, family_name, given_nameくらいしかなく、ユーザーを識別可能なものはsubかnameしかありません。同姓同名が存在しなければnameは使えますが、そうでなければコメントアウトしてデフォルトの形式にしてもよいでしょう。
OIDCPassClaimsAs
claimをアプリケーション環境にどのようにして渡すかを指定します。
- none
- environment (環境変数)
- headers (HTTPヘッダー)
- both (両方) (default)
OIDCPassClaimsAs both
OIDCAuthNHeader
リバースプロキシーとして動かしている場合は、REMOTE_USERの情報をバックエンドに送信するHTTPヘッダーに追加するヘッダーフィールド名を指定します。
OIDCAuthNHeader X-Remote-User
OP毎のメタデータと設定ファイルの配置
OIDCMetadataDirディレクティブで指定したディレクトリ(先の例では"/var/cache/httpd/modauthopenidc/metadata")に、OP毎に次のようなファイル名の3つのファイルを用意します。
- ISSUER.provider (メタデータ)
- ISSUER.client (クライアントIDとクライアントシークレット)
- ISSUER.conf (設定)
ここで、ISSUERはissuerの値から「https://」と最後の「/」を取り除いた文字列をURLエンコードしたものになります。
issuerはメタデータURIから取得できるので、次のように取得して、「https://」と最後の「/」を取り除きます。
Googleの場合:
$ curl https://accounts.google.com/.well-known/openid-configuration 2>/dev/null | jq .issuer | sed 's|^"||;s|"$||;s|https://||;s|/$||' accounts.google.com
Azure ADの場合:
$ curl https://login.microsoftonline.com/{tenant}/.well-known/openid-configuration 2>/dev/null | jq .issuer | sed 's|^"||;s|"$||;s|https://||;s|/$||' sts.windows.net/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
※{tenant}をテナント名に置き換えてください。
次に、この文字列をURLエンコードします。
Googleの場合:
$ echo -n 'accounts.google.com' | jq -s -R -r @uri accounts.google.com
Azure Adの場合:
$ echo -n 'sts.windows.net/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' | jq -s -R -r @uri sts.windows.net%2FXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
これにより、3つのファイル名が次のように確定します。
Googleの場合:
- accounts.google.com.provider
- accounts.google.com.client
- accounts.google.com.conf
Azure ADの場合:
- sts.windows.net%2FXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.provider
- sts.windows.net%2FXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.client
- sts.windows.net%2FXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.conf
ISSUER.providerファイル
次のようにメタデータURIのファイルを取得してそのまま保存します。 保存後にapacheのユーザー権限で書き込みできるようにパーミッションを変更してください。
Googleの場合:
$ sudo curl -o /var/cache/httpd/mod_auth_openidc/metadata/accounts.google.com.provider https://accounts.google.com/.well-known/openid-configuration $ sudo chown apache:apache /var/cache/httpd/mod_auth_openidc/metadata/accounts.google.com.provider
Azure ADの場合:
$ sudo curl -o /var/cache/httpd/mod_auth_openidc/metadata/sts.windows.net%2FXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.provider https://login.microsoftonline.com/{tenant}/.well-known/openid-configuration $ sudo chown apache:apache /var/cache/httpd/mod_auth_openidc/metadata/sts.windows.net%2FXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX.provider
※{tenant}をテナント名に置き換えてください。ファイル名も先に決まったものに置き換えてください。
ISSUER.clientファイル
次のようにクライアントIDとクライアントシークレットをJSONで記述します。
Googleの場合:
{ "client_id" : "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.apps.googleusercontent.com", "client_secret" : "XXXXXXXXXXXXXXXXXXXXXXXX" }
Azure ADの場合:
{ "client_id": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "client_secret": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" }
ISSUER.confファイル
OP毎の設定をJSONで記述します。共通の設定と異なるものがあれば、ここに記述することになります。
上書きできる設定は次のページを参考にしてください。
- OP/Client Configuration (GitHub)
Googleの場合:
{ "scope": "openid email profile", "response_type": "code", "pkce_method": "S256", "session_max_duration": "28800", "auth_request_params": "hd=example.com" }
Azure ADの場合:
{ "scope": "openid", "response_type": "code", "pkce_method": "S256", "session_max_duration": "28800" }
動作確認用コンテンツの配置
動作確認として取得した変数を表示するために、次の内容のSSIを使ったページindex.shtmlをドキュメントルートに配置してみます。
REMOTE_USER
はユーザーIDに、OIDC_CLAIM_name
はユーザーのフルネームに置き換わります。
明示的にログアウトできるように、ログアウトするパス「/redirect_uri?session=logout」へのリンクを用意します。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"/> <title>OpenID Connect</title> </head> <body> <h1>OpenID Connect</h1> <p> <!--#echo var="OIDC_CLAIM_name" --> (REMOTE_USER=<!--#echo var="REMOTE_USER" -->) </p> <p><a href="/redirect_uri?session=logout">Logout</a></p> </body> </html>
apacheの設定
apacheの設定は以下のようになります。
<Location /> # OpenID Connectによる認証・認可を要求する。 AuthType openid-connect # G Suiteの認可設定: # claimのhdがexample.comである場合のみ認可する。 Require claim hd:example.com # Azure AD用の認可設定: # claimのissが下記の値(https://sts.windows.net/〜)の場合のみ認可する。 # issuerはテナント毎にユニークであるため、組織内のユーザーに制限できる。 Require claim iss:https://sts.windows.net/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/ # 動作確認のためのSSI用の設定を行う。 Options +Includes DirectoryIndex index.shtml index.html </Location> # ログアウト後に遷移するページでは認証されていないユーザーにもアクセスを許可する。 <Location /loggedout.html> Require all granted </Location>
設定の反映
設定が完了したら、設定を反映するために、apacheを再起動してみましょう。
$ sudo apachectl configtest $ sudo systemctl reload httpd.service
実際にアクセスしてみて何か問題があれば、エラーログに出力されるので確認しましょう。
$ sudo less /var/log/httpd/error_log
動作確認
G SuiteとAzure ADの両方用の設定を行った場合は、動作確認用のページにアクセスすると次のようなOPを選択する画面が表示されます。
ここで、それぞれのOPのリンクをクリックすると、それぞれのOPで認証・認可が行われます。
ログインが完了すると次のようなページが表示されます。
氏名とユーザーIDが表示されていれば成功です。
※ この例のユーザーIDについて、OIDCRemoteUserClaimディレクティブの設定をコメントアウトしてデフォルトの「sub@iss」の形式にした場合はこのようになります。