LoginSignup
2
2

More than 1 year has passed since last update.

テストサーバ立ててサブドメイン割り当ててBasic――ではなくSAML認証でSSO(Keycloak)に組み込む

Last updated at Posted at 2021-05-03

概要

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を用意する。

Dockerfile
FROM httpd:latest

RUN apt-get update && \
    apt-get install -y libapache2-mod-auth-mellon && \
    apt-get clean

1-2. Composeファイル

設定ファイルと配信するファイルをマウントして起動するだけのもの。

docker-compose.yml
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 を実行する。

以下はコンテナに入って生成して取り出す場合の手順。

shell
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」の部分に項目と説明が記載されている。

mellon.conf
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. 使用するレルムのクライアント設定の権限を持つユーザーで管理コンソールにログインし、サイドメニューから「クライアント」→右側の「作成」ボタンと進む
  2. 「ファイルを選択」ボタンを押し、ファイル選択ダイアログで手順1-3で作成した <CLIENT_ID>.xml を選択してから「保存」(クライアントIDは変更不可)
  3. 「有効なリダイレクトURI」を * に変更する
  4. 「SAMLエンドポイントの詳細設定」アコーディオン配下の入力欄を全て空にする
  5. 「保存」する

修正後の設定内容
image.png

2-2. メタデータと証明書の取得

  1. 手順2-1を完了したら「インストール」タブに移動する
  2. フォーマット・オプションで「Mod Auth Mellon files」を選択
  3. 出現した「ダウンロード」ボタンを押してzipファイルをダウンロードする
  4. zipに含まれる client-cert.pemidp-metadata.xml をApacheで使用する

3. VirtualHost

サブドメイン(≒VirtualHost)の追加時に必要な作業はこの項の 3-2 のみで、Keycloakを触る必要はない。(アクセス制御のためにマッパーやユーザー情報を弄る必要がある場合は別として)

3-1. mod_auth_mellon共通設定

VirtualHostにおける共通の設定項目。これまでの手順で取得したファイル類のうち、共通で読み込むものの指定。

mellon.conf
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

3-2. 各VirtualHostの設定

3-2-1. mod_auth_mellon用メタデータ

手順1-3で取得したメタデータ <CLIENT_ID>.xml をコピーし、使用するサブドメインに合わせて <SUBDOMAIN> の部分を修正する。

SUBDOMAIN.xml
<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 BasicAuthType Mellon になるだけで、他は普段通りでよい。

SUBDOMAIN.conf
<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 属性をユーザー属性に追加。

image.png

subdomain という属性名で送信するマッパーを作成。

image.png

認証成功時にmod_auth_mellonがこのSAML属性を受け取ると、内容がApache環境変数 MELLON_subdomain に格納される。mod_auth_mellonのディレクティブからは属性名のまま扱える。(環境変数の接頭辞は変更可能)

※mod_auth_mellon側での使用例は後述

4-2. Apache側(conf)

mod_auth_mellonの MellonRequireMelloncond ディレクティブを使用する。

4-2-3. MellonRequireディレクティブ

「特定のSAML属性の値が1個以上の列挙した値のいずれかに一致するかどうか」による判定を行う。

MellonRequire <SAML_ATTR> <VALID_VALUE_1> [<VALID_VALUE_2> ...]
  • MellonRequireは複数記述することができる
    • 複数のMellonRequireはAND条件として処理される
  • 同じ属性名を指定したMellonRequireが複数ある場合は最後に記述されたものが使用される

例えば「属性名 role の値が adminmoderator なら許可する」であれば以下のようになる。

MellonRequire "role" "admin" "moderator"

4-2-2. MellonCondディレクティブ

特定のSAML属性の値と、特定の値ないし正規表現との一致・不一致その他による判定を行う。

MellonCond <SAML_ATTR> <VALUE> [[<OPTION>, ...]]

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用メタデータ内のエンドポイントを合わせる。

参考


  1. 操作で事故ると甚大な被害が生じる可能性がある 

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2