Redmine を Azure AD で SAML SSO してみた

# はじめに

Microsoft365 の AzureAD を使って業務関連のシステムの SSO 化を進めており、redmine もサクッと行けるだろうと思ったら結構ハマったので、自分のメモ & 誰かの役に立てば、と書いてみました。

なお今回の検証にあたっては @kaihei777 さんの[こちらの記事](https://qiita.com/kaihei777/items/1e69b677d57789fabf6c) にとても助けていただいたので、お礼を兼ねてタイトルも真似させていただきました。大変助かりました m(__)m。

2024/1/4 追記：
コメント欄に書きましたが Redmine 5.0 以降は chrodriguez/redmine_omniauth_saml を fork した [davintoo/redmine_omniauth_saml](https://github.com/davintoo/redmine_omniauth_saml) を使う必要があるようです。とりあえず検証環境では動いた、という程度で私も未だ十分に確認できていませんが…


# 検証したプラグイン

「Redmine SSO AzureAD」などで検索して見つかった以下の 2つのプラグインを検証しました。

1. [Gucin/redmine_omniauth_azure](https://github.com/Gucin/redmine_omniauth_azure)
1. [chrodriguez/redmine_omniauth_saml](https://github.com/chrodriguez/redmine_omniauth_saml)

## 1. redmine_omniauth_azure

こちらのプラグインは OAuth2.0 による SSO なので SAML では無く、結果的に採用しなかったのですが、導入・設定が分かりやすかったので参考までに記載しておきます。

導入手順の概要は以下のとおりです。

```bash
$ cd redmine/plugins
$ wget https://github.com/Gucin2/redmine_omniauth_azure/archive/master.zip
$ unzip master.zip
$ mv redmine_omniauth_azure-master redmine_omniauth_azure
$ cd ..
$ bundle install
$ bundle exec rake redmine:plugins:migrate RAILS_ENV=production
$ sudo /sbin/service httpd restart
```
AzureAD の設定は https://blog.tsuchinokometal.com/2020/01/06/post-362/ を参考にさせてもらいました。前述の通り設定も分かりやすくて良かったのですが、Redmine のログイン画面にアクセスすると

- 通常のログイン用フォーム（アカウント、パスワードの入力欄）
- AzureAD アクセス用ボタン

の2つが表示され、**明示的に AzureAD アクセス用ボタンを押す必要がある** のが気になりました。検証中は良いのですが、正式運用の際には Redmine にアクセスしただけで SSO の処理が走って欲しいのです。プラグインを弄ってリダイレクトしようかとも思ったのですが

- プラグインのバージョンアップの都度、パッチを当てるのは手間なこと
- redmine_omniauth_saml プラグインはログイン画面を非表示にできること

から、このプラグインの採用は見送りました。

## 2. redmine_omniauth_saml

こちらのプラグインは名前の通り SAML による SSO が実装できます。が、「適当にやれば設定できるだろう」と思ったら全く上手く動かず、ちょっと途方に暮れてしまうくらいでした…

導入手順は redmine_omniauth_azure プラグインとほぼ同じです。

```bash
$ cd redmine/plugins
$ wget https://github.com/chrodriguez/redmine_omniauth_saml/archive/master.zip
$ unzip master.zip
$ mv redmine_omniauth_saml-master redmine_omniauth_saml
$ cd ..
$ bundle install
$ bundle exec rake redmine:plugins:migrate RAILS_ENV=production
$ sudo /sbin/service httpd restart
```

そして設定については以下の記事を参考にしました。

- [RedmineをG SuiteでSAML SSOしてみた](https://qiita.com/kaihei777/items/1e69b677d57789fabf6c)
- [Azure AD Config #33](https://github.com/chrodriguez/redmine_omniauth_saml/issues/33)

ハマったのは @kaihei777 さんと同じ ```config/initializer/saml.rb``` です。全く上手く行かないのでプラグインのコードを弄ってデータをファイルに書き出してみたりしましたが検討もつかず… ふと思い立って Github の Issue を見てみたら [Azure AD Config #33](https://github.com/chrodriguez/redmine_omniauth_saml/issues/33) を見つけた次第です。

まず ```config/initializer/saml.rb``` の ```:attribute_mapping``` を以下のように記載します。

```ruby
Redmine::OmniAuthSAML::Base.configure do |config|
  config.saml = {
（省略）
    :attribute_mapping              => {
    # How will we map attributes from SSO to redmine attributes
      :login => 'extra raw_info http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name',
      :mail => 'extra raw_info http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name',
      :firstname => 'extra raw_info http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname',
      :lastname => 'extra raw_info http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname'
    }
  }
（省略）
end
```

そして ```lib/redmine_omniauth_saml.rb``` の 64行目を以下のように修正します。

```ruby
--- redmine_omniauth_saml-master/lib/redmine_omniauth_saml.rb	2019-09-13 22:05:29.000000000 +0900
+++ redmine/4.1.1/plugins/redmine_omniauth_saml/lib/redmine_omniauth_saml.rb	2020-10-31 16:51:45.796554571 +0900
@@ -64,7 +64,7 @@
         HashWithIndifferentAccess.new.tap do |h|
           required_attribute_mapping.each do |symbol|
             key = configured_saml[:attribute_mapping][symbol]
-            h[symbol] = key.split('.')                # Get an array with nested keys: name.first will return [name, first]
+            h[symbol] = key.split(' ')                # Get an array with nested keys: name.first will return [name, first]
               .map {|x| [:[], x]}                     # Create pair elements being :[] symbol and the key
               .inject(omniauth) do |hash, params|     # For each key, apply method :[] with key as parameter
                 hash.send(*params)
```

```sudo /sbin/service httpd restart``` してから redmine_omniauth_saml プラグインの設定画面で以下のように設定し、希望通りの挙動になりました。

- Enable SAML authentication ... チェック
- Login page text ... Microsoft365 に認証
- Replace Redmine login page ... チェック
- Create users automatically? ... 未チェック

※ Login page text は一応、設定してありますが Replace Redmine login page にチェックをすると表示されないため、未設定でも構わないと思います。

# まとめ

これでまた一つ、Azure AD を使った SSO を実現することができました。グループ設定もあるのでアカウントの自動作成は使いませんが、アカウント管理を一元化できるのはとても便利です。次は Samba をやろうかな…