数カ月ぶりにRedmineのプラグインを開発した阿部です。
「Redmine Plugin Auth Remote User」を開発しました。
これはREMOTE_USER
(X-Forwarded-User
)をRedmineのログインIDと見なして自動ログインするプラグインです。
いわゆるシングルサインオン(SSO: Single Sign On)ができるようになるプラグインです。
本記事ではそのプラグインの紹介と実装のポイントを説明します。
本題の前に注意
「Redmine Plugin Auth Remote User」はリバースプロキシ + Redmineの構成での利用を想定しています。
リバースプロキシで設定されたREMOTE_USER
環境変数が X-Forwarded-User
ヘッダとしてRedmineに渡されるような構成です。
このプラグインではRedmineで受け取ったX-Forwarded-User
ヘッダの値を、RedmineのログインIDと見なして自動ログインをします。
Redmineに不正なX-Forwarded-User
ヘッダがリクエストされる環境で利用すると、セキュリティリスクがあることをご承知おきください。
Redmine Plugin Auth Remote User の紹介
機能説明
「本題の前に注意」でほぼ説明していまいましたが、X-Forwarded-User
ヘッダの値をRedmineのログインIDと見なして自動ログインをします。
プラグインをインストールするとその機能が有効になり、X-Forwarded-User
ヘッダの値をRedmineのログインIDが一致していれば自動でログインするようになります。
インストールコマンド:
cd redmine
git clone https://gitlab.com/redmine-plugin-auth-remote-user/redmine-plugin-auth-remote-user.git plugins/auth_remote_user
設定できること
X-Forwarded-User
ヘッダの値をRedmineのログインIDと見なして自動ログインをしますが、RedmineのログインIDと一致しないこともあります。
例えばActive DirectoryのID(domain\username
)で考えると、RedmineのログインIDとして採用されるのは username
の部分のみのことが多いはずです。
username
のみがログインIDの場合は、domain\username
でRedmineにログインを試みても当然失敗します。
上述のケースにも対応できるようにX-Forwarded-User
ヘッダの一部を変更できる機能も搭載しています。
具体的には次の2つの機能があります。
- Active DirectoryのIDである
domain\username
からdomain\
の部分のみ削除 - 正規表現を使って置換
「本題の前に注意」と同様の注意ですが、X-Forwarded-User
ヘッダの値を変更することで、予期せぬログインが発生するリスクがあることを理解してご利用ください。
設定画面のキャプチャ:
また、プラグインの設定画面でプラグインの有効・無効を設定することもできます。
1. domain\username
からdomain\
の部分のみ削除
機能名の通りdomain\username
からdomain\
の部分のみ削除をします。
設定画面でチェックを入れると有効になります。
Active DirectoryのIDの場合はこの機能を利用するのがオススメです。
2. 正規表現を使って置換
RubyのString#gsub
で置換します。
設定画面で「REMOTE_USERの一部を置換する」を有効にしてから設定します。
上述のdomain\username
の場合は以下の設定で同様の置換ができます。
- 置き換える文字列の正規表現:
\Adomain\\
- 置き換え後文字列: (空欄)
この設定で置換が行われるとdomain\username
のdomain\
の部分が削除(空文字に置換)され、username
のみがログインIDとして使用されます。
設定を誤ると予期せぬログインが発生するリスクがありますので、機能を理解した上、入念な検証をした後にご利用ください。
補足: プラグインの有効・無効
シンプルにこのプラグイン(= 自動ログイン)の有効・無効が設定画面で設定できます。
このプラグイン(= 自動ログイン)が有効だとX-Forwarded-User
の情報で自動ログインをするため、例えば「検証用の別アカウントを使った検証」などが実施しにくくなります。そういった場合に一時的に無効にして検証する、という使い方もできます。
実装のポイント
実装方針
RedmineのApplicationController
を確認するとtry_to_autologin
というメソッドがあります。
メソッド名から想像するに、今回やりたい自動ログインをしてそうです。
コードを見るとCookieの値で自動ログインを試みるメソッドでした。
app/controllers/application_controller.rb
:
def try_to_autologin
if cookies[autologin_cookie_name] && Setting.autologin?
# auto-login feature starts a new session
user = User.try_to_autologin(cookies[autologin_cookie_name])
if user
reset_session
start_user_session(user)
end
user
end
end
Cookieの値を使うか、X-Forwarded-User
の値を使うか、の違いだけで同様の処理なので try_to_autologin
を拡張する方針で修正することにしました。
実装の説明
肝となるauto_loginable.rb
のコードを掲示して説明します。
コード中に解説コメントを入れる形で説明します。(このコメントはコミットしてあるコードには入っていません、あくまでククログでの説明用です。)
lib/auth_remote_user/auto_loginable.rb
:
module AuthRemoteUser
module AutoLoginable
def try_to_autologin
# 次の2行で「Cookieの値で自動ログイン」を処理しています。
user = super
return user if user
# `X-Forwarded-User`の値をチェック
remote_user = request.env["HTTP_X_FORWARDED_USER"]
return nil unless remote_user
# 本記事での説明は割愛しますが、`AuthRemoteUser::Normalizer` が設定を元に
# `X-Forwarded-User`の値を置換します。
normalizer = Normalizer.new(Setting.plugin_auth_remote_user)
# 設定の通りに置換された`X-Forwarded-User`の値で有効なユーザがいるか探します。
user = User.active.find_by_login(
normalizer.normalize(remote_user))
return nil unless user
# 有効なユーザが見つかればログイン処理
reset_session
start_user_session(user)
user.update_last_login_on!
user
end
end
end
# `ApplicationController`に`prepend`して`try_to_autologin`を拡張
ApplicationController.prepend(AuthRemoteUser::AutoLoginable)
まとめ
REMOTE_USER
(X-Forwarded-User
)をRedmineのログインIDと見なして自動ログインするプラグインの紹介と実装ポイントの説明でした。
リバースプロキシ + Redmineの構成を想定していると書きましたが、IISでリバースプロキシをする場合はひと手間が必要です。 IIS(リバースプロキシ) + Redmineの構成で利用する方法は別記事で紹介します! (追記: 紹介しました。)
なお、この機能はクリアコードが提供しているRedmineサポート契約の中で開発しました。 クリアコードが提供するRedmineサポートではお客さんが必要としている機能を理解した上で 汎用的なプラグインとして対応することが多いです。 その際、プラグインは自由なソフトウェアとして開発し広く公開します。 汎用的な機能のため、お客さんの機密情報などが含まれることがなく、 お客さんにとってデメリットはありません。 また他のお客さんでも使うことになったら、メンテナンス費用を 按分できるコストメリットもあります。
プラグインではなくRedmine本体を改良した方がよさそうな場合もあります。 その時はお客さんが使っているRedmineにパッチを当ててもらうのではなく、 Redmine本体に機能を提案してRedmine本体に取り込んでもらえるようにします。 自分たちでパッチを当てるよりもRedmine本体に入っていた方がアップデートが楽になり、 メンテナンスコストが下がるからです。 ただ、Redmine本体の開発をコントロールすることはできないので、 一時的にプラグインで対処したりパッチを当ててもらうということはあります。
そのようなRedmineサポートがよい方はお問い合わせフォームよりご連絡ください。