概要
Apacheのmod_auth_mellonモジュールとKeycloakを使用し、静的WebサイトやCMSテンプレート等の開発でありがちな「サブドメインを割り当てた案件/用途別テストサーバ」をSAML認証でSSOに組み込む。
本稿ではサブドメイン毎に個別のSP(サービスプロバイダ、Keycloakにおけるクライアント)に分割せず、複数のサブドメインを一つのSPで扱う。これにより運用フェーズにおいてKeycloakのクライアント設定の操作が不要になり1、設定手順をBasic認証並に簡略化できる。
- アクセス許可の条件は「アカウントを持っているかどうか」からSAML属性と正規表現を使った複雑なものまで柔軟に設定可能
- 条件はApacheのLocationディレクティブ毎に指定できる
- サブドメインの追加はApache側の設定だけで完結
- SAML属性はApacheの環境変数に入る
作業環境
- RHEL8互換
- Keycloak (12.0.4)
- 稼働中のもの
- Apache (2.4.46)
- Docker公式イメージ(Debianベース)、非SSL(リバースプロキシでSSLオフロードする構成のため)
- libapache2-mod-auth-mellon (0.17.0-1)
- Docker (20.10.6), docker-compose (1.29.1)
ファイル構成
あくまで一例として。confや配信ファイルの配置はお好みで。カッコ内は対応する手順を示す。
httpd
|-- Dockerfile (1-1)
|-- docker-compose.yml (1-2)
`-- mount
|-- httpd.conf
|-- conf.d
| `-- mellon.conf (1-4)
|-- mellon
| |-- <CLIENT_ID>.cert (1-3)
| |-- <CLIENT_ID>.key (1-3)
| |-- <CLIENT_ID>.xml (1-3)
| |-- client-cert.pem (2-2)
| `-- idp-metadata.xml (2-2)
`-- vhost.d
|-- include
| `-- mellon.conf (3-1)
|-- metadata
| |-- subdomain1.xml (3-2-1)
| |-- subdomain2.xml (3-2-1)
| `-- subdomain3.xml (3-2-1)
|-- subdomain1.conf (3-2-2)
|-- subdomain2.conf (3-2-2)
`-- subdomain3.conf (3-2-2)
1. Apache
mod_auth_mellonモジュールが動けばなんでもよい。
1-1. Dockerイメージ
Apacheの公式イメージには mod_auth_mellon
モジュールが入っていないため、latestタグ(Debianベース)を元にモジュールをインストールするだけのDockerfileを用意する。
FROM httpd:latest
RUN apt-get update && \
apt-get install -y libapache2-mod-auth-mellon && \
apt-get clean
1-2. Composeファイル
設定ファイルと配信するファイルをマウントして起動するだけのもの。
version: '3.8'
services:
httpd:
image: <DOMAIN>/httpd:<VERSION>
build:
context: .
container_name: httpd
hostname: httpd-server
environment:
- TZ=Asia/Tokyo
volumes:
- ./mount/httpd.conf:/usr/local/apache2/conf/httpd.conf
- ./mount/conf.d:/usr/local/apache2/conf.d
- ./mount/vhost.d:/usr/local/apache2/vhost.d
- ./mount/mellon:/usr/local/apache2/mellon
- /var/www/html:/var/www/html
expose:
- '80'
restart: always
networks:
- container-link
logging:
options:
max-size: "10m"
max-file: "3"
networks:
default:
external:
name: bridge
container-link:
name: docker.internal
-
<DOMAIN>
は自家製イメージだとわかる文字列を適当に設定すればよい -
<VERSION>
もなんでも構わないが、その時点のApacheのバージョンを入れておくと更新の確認に便利 - コンテナ間リンク上のリバースプロキシでSSLオフロードしているため非SSLかつ外向きには閉じている
- 単独動作時は
expose
を適宜ports
に読み替えのこと
- 単独動作時は
1-3. 証明書・鍵・メタデータ
Apacheのコンテナ上(または依存関係をインストールした環境)でmod_auth_mellon公式が用意している生成用シェルスクリプト mellon_create_metadata.sh
を実行する。
以下はコンテナに入って生成して取り出す場合の手順。
docker exec -it httpd /bin/bash
# ここからApacheコンテナ内
apt-get install -y wget
cd /tmp
wget https://raw.githubusercontent.com/latchset/mod_auth_mellon/master/mellon_create_metadata.sh
chmod +x mellon_create_metadata.sh
./mellon_create_metadata.sh <CLIENT_ID> https://*.<DOMAIN>/mellon
exit
# Apacheコンテナ内ここまで
docker cp httpd:/tmp/<CLIENT_ID>.key /path/to/httpd/mount/mellon
docker cp httpd:/tmp/<CLIENT_ID>.cert /path/to/httpd/mount/mellon
docker cp httpd:/tmp/<CLIENT_ID>.xml /path/to/httpd/mount/mellon
-
<CLIENT_ID>
はSP名(Keycloakにおける「クライアントID」)-
/
や:
などが含まれる場合、ファイル名ではアンダースコアに変換される(mellon_create_metadata.sh
の出力するログを参照のこと)
-
-
<DOMAIN>
は使用するドメイン
生成されるファイルは以下の通り。
-
<CLIENT_ID>.key
: SP鍵,<CLIENT_ID>.cert
: SP証明書- mod_auth_mellonが使用する
-
<CLIENT_ID>.xml
: SPメタデータ- Keycloakのクライアント設定にインポートする
- サブドメイン毎にコピーし一部を書き換えたものをmod_auth_mellonが使用する
1-4. mod_auth_mellonグローバル設定
LoadModule
でモジュールを読み込み、サーバ共通のグローバル設定を行う。
モジュール公式リポジトリのReadmeで大半を占めているサンプル設定の記述の内、上の方にある「Global configuration」の部分に項目と説明が記載されている。
LoadModule auth_mellon_module /usr/lib/apache2/modules/mod_auth_mellon.so
<IfModule auth_mellon_module>
# MellonCacheSize 100
# MellonCacheEntrySize 196608
# MellonLockFile "/var/run/mod_auth_mellon.lock"
# MellonPostDirectory "/var/cache/mod_auth_mellon_postdata" # Default: None
# MellonPostTTL 900
# MellonPostSize 1048576
# MellonPostCount 100
# MellonDiagnosticsFile logs/mellon_diagnostics
# MellonDiagnosticsEnable Off
</IfModule>
- 本稿では
httpd.conf
でimportする想定(各自のポリシーに沿って適宜読み替えのこと) - サーバ共通の設定であり、ディレクティブの外に記述する
- 特にデフォルト値から変更する必要はないと思われる
- 利用人数が多い場合に
MellonCacheSize
の調整が必要になる程度?
- 利用人数が多い場合に
- 今回の導入方法ではDiagnostics関連の設定項目は存在しているだけでエラーを吐く
- 依存関係がインストールされていないため
2. Keycloak
Keycloakの公式ドキュメントにmad_auth_mellonの導入ガイドがあるので参考に。
2-1. クライアント設定
- 使用するレルムのクライアント設定の権限を持つユーザーで管理コンソールにログインし、サイドメニューから「クライアント」→右側の「作成」ボタンと進む
- 「ファイルを選択」ボタンを押し、ファイル選択ダイアログで手順1-3で作成した
<CLIENT_ID>.xml
を選択してから「保存」(クライアントIDは変更不可) - 「有効なリダイレクトURI」を
*
に変更する - 「SAMLエンドポイントの詳細設定」アコーディオン配下の入力欄を全て空にする
- 「保存」する
修正後の設定内容
2-2. メタデータと証明書の取得
- 手順2-1を完了したら「インストール」タブに移動する
- フォーマット・オプションで「Mod Auth Mellon files」を選択
- 出現した「ダウンロード」ボタンを押してzipファイルをダウンロードする
- zipに含まれる
client-cert.pem
とidp-metadata.xml
をApacheで使用する
3. VirtualHost
サブドメイン(≒VirtualHost)の追加時に必要な作業はこの項の 3-2
のみで、Keycloakを触る必要はない。(アクセス制御のためにマッパーやユーザー情報を弄る必要がある場合は別として)
3-1. mod_auth_mellon共通設定
VirtualHostにおける共通の設定項目。これまでの手順で取得したファイル類のうち、共通で読み込むものの指定。
MellonSPPrivateKeyFile /usr/local/apache2/mellon/<CLIENT_ID>.key
MellonSPCertFile /usr/local/apache2/mellon/<CLIENT_ID>.cert
MellonIdPMetadataFile /usr/local/apache2/mellon/idp-metadata.xml
MellonIdPCAFile /usr/local/apache2/mellon/client-cert.pem
- 本稿では各VirtualHostのconfでimportする想定(各自のポリシーに沿って適宜読み替えのこと)
- 詳細はモジュール公式リポジトリのReadmeを参照のこと
3-2. 各VirtualHostの設定
3-2-1. mod_auth_mellon用メタデータ
手順1-3で取得したメタデータ <CLIENT_ID>.xml
をコピーし、使用するサブドメインに合わせて <SUBDOMAIN>
の部分を修正する。
<EntityDescriptor entityID="<CLIENT_ID>" xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol" AuthnRequestsSigned="true">
<KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>MIID......</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</KeyDescriptor>
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://<SUBDOMAIN>.<DOMAIN>/mellon/logout"/>
<AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://<SUBDOMAIN>.<DOMAIN>/mellon/postResponse" index="0"/>
</SPSSODescriptor>
</EntityDescriptor>
3-2-2. conf
概ね AuthType Basic
が AuthType Mellon
になるだけで、他は普段通りでよい。
<VirtualHost *:80>
DocumentRoot /var/www/html/<SUBDOMAIN>
ServerName https://<SUBDOMAIN>.<DOMAIN>
<Location />
AllowOverride none
# ここからmod_auth_moellonの設定
Include vhost.d/include/mellon.conf
MellonSPMetadataFile vhost.d/metadata/<SUBDOMAIN>.xml
AuthType Mellon
MellonEnable auth
Require valid-user
</Location>
</VirtualHost>
- この設定ではルート配下が全てアクセス制御の対象となり、Keycloak上にアカウントが存在していれば閲覧できる
-
ServerName
には実際にアクセスする時のプロトコルを含める- プロトコルを省略するとmod_auth_mellonがリダイレクトURLを生成する際にApacheの設定を元に補完されるが、本稿の構成ではApacheは非SSLのため、SSLの表側と一致せずエラーになる
- 設定項目の詳細はモジュール公式リポジトリのReadmeを参照のこと
4. アクセス制御
4-1. Keycloak側(マッパー)
アクセス制御に使用するKeycloakのユーザー情報(プロパティ・属性・グループ・ロール等)は、Keycloakのクライアント設定でマッパーを作成して受け渡す。
SAMLとKeycloakの仕様の範囲内であればなんでも渡せて、JavaScriptマッパーで加工もできるため、mod_auth_mellon側の制限や機能も勘案しつつ使いやすい形でフォーマットを整える。
例
「閲覧可能なサブドメインをカンマ区切りテキストで指定する」という運用ルールを想定した subdomain
属性をユーザー属性に追加。
subdomain
という属性名で送信するマッパーを作成。
認証成功時にmod_auth_mellonがこのSAML属性を受け取ると、内容がApache環境変数 MELLON_subdomain
に格納される。mod_auth_mellonのディレクティブからは属性名のまま扱える。(環境変数の接頭辞は変更可能)
※mod_auth_mellon側での使用例は後述
4-2. Apache側(conf)
mod_auth_mellonの MellonRequire
と Melloncond
ディレクティブを使用する。
4-2-3. MellonRequireディレクティブ
「特定のSAML属性の値が1個以上の列挙した値のいずれかに一致するかどうか」による判定を行う。
MellonRequire <SAML_ATTR> <VALID_VALUE_1> [<VALID_VALUE_2> ...]
- MellonRequireは複数記述することができる
- 複数のMellonRequireはAND条件として処理される
- 同じ属性名を指定したMellonRequireが複数ある場合は最後に記述されたものが使用される
例えば「属性名 role
の値が admin
か moderator
なら許可する」であれば以下のようになる。
MellonRequire "role" "admin" "moderator"
4-2-2. MellonCondディレクティブ
特定のSAML属性の値と、特定の値ないし正規表現との一致・不一致その他による判定を行う。
MellonCond <SAML_ATTR> <VALUE> [[<OPTION>, ...]]
- MellonCondは複数記述することができる
- オプションは大括弧で囲う
- 詳しくはモジュール公式リポジトリのReadmeを参照のこと
VALUEで展開できる変数
- %n, %{num}
- 正規表現使用時のマッチ結果の参照、1桁は
%n
、2桁以上は%{num}
- %{ENV:env_name}
- Apache環境変数の展開
%
をエスケープしたい場合は %%
とする
オプション
- OR
- そのMellonCondの評価結果がfalseだった場合、次のMellonCondを評価する
- NOT
- 評価結果を反転する(値と一致しない場合にtrueを返す)
- SUB
- 一致条件を部分一致にする
- REG
- VALUEを正規表現として扱い評価する(正規表現はApacheの
LocationMatch
互換?) - NC
- 大文字・小文字を区別しない
- MAP
- MellonSetEnvによってリマップされた属性名を探して判定する?(詳細不明)
- REF
- REGと同時に使用し、次のMellonCondにおけるマッチ結果の参照に今回のマッチ結果を利用する?(詳細不明)
例
手順4の例で設定したカンマ区切りテキストを格納しているSAML属性 subdomain
に、 asdf-preview
に一致するフィールドが含まれていたらアクセスを許可する。
MellonCond "subdomain" "(^|,)asdf-preview(,|$)" [REG]
Tips
Apache側の認証用エンドポイント
標準では /mellon/*
が各種エンドポイントとして使用されるため、/mellon
とその配下に実体ファイルを置くことはできない。
変更する場合は MellonEndpointPath
ディレクティブを使用し、手順3-2-1のmod_auth_mellon用メタデータ内のエンドポイントを合わせる。
参考
-
One Service Provider and multiple Apache VirtualHosts?
- 単一SPマルチサブドメイン行けそうかも、と思った発端
- mod_auth_mellon を使ってみた
- 【Keycloak】Apache の VirtualHost で分けられた複数のサイトをまとめてシングルサインオンしよう
-
操作で事故ると甚大な被害が生じる可能性がある ↩