RedmineでLDAP認証をしてみる-死ぬほど後悔

1. ユーザー管理統合の検討:2つの選択肢

「各ツールごとにユーザー管理をしたくない」という目的を達成するため、AI(Gemini/他)のアドバイスを受けながら検討した2つの方式です。

リバースプロキシ

特徴
Redmineの前段に認証プロキシを置き、ログイン時にKeycloakの画面へリダイレクトさせる(SSO)。

却下理由
Redmineの標準ログイン画面を活かしたかったことと、構築がLDAPに比べて複雑そうに見えたため。

LDAP(AIの提案により採用)

特徴
Redmineの標準機能。Redmineの画面で入力したID/PWを、裏側でKeycloak(LDAPサーバー)に問い合わせる。

採用理由
「こっちのほうが楽そう」というAIの強い提案があり、ログイン画面もRedmineのまま使えるため採用。


2. 最初の罠:4年前で時が止まったイメージ

意気揚々と作成した最初のComposeファイルですが、イメージの選定で失敗しました。

失敗した追加コード

  openldap:
    image: osixia/openldap:latest # 結論:最終更新4年前で動作せず
    container_name: openldap
    logging: *default-logging
    ports:
      - "389:389"
    environment:
      - LDAP_ORGANISATION="HomeLab"
      - LDAP_DOMAIN="local"
      - LDAP_ADMIN_PASSWORD="admin_password" # 現環境でもこれを使用
      - LDAP_CONFIG_PASS="password"
    volumes:
      - /mnt/hdd/data/ldap/db:/var/lib/ldap:z
      - /mnt/hdd/data/ldap/config:/etc/ldap/slapd.d:z

教訓: osixia/openldap は更新が止まっており、最新環境では動きませんでした。結局、メンテナンスされている Bitnami版 に切り替えることで解決しました。


3. 構築手順書

仮想マシンの前提 (Proxmox)

  • ネットワーク: ローカル環境(Class C: 192.168.1.0/24)
  • Keycloak: 192.168.1.11
  • Redmine: 192.168.1.12
  • ファイアウォール: VM内部およびProxmox側ともに OFF

Keycloak:設定-フェデレーション

使いたいレムルを選択します。今回はホームラボを選択してます
※このレムルは事前に作っていた物になります

ユーザーフェデレーションを選択します

新しいプロバイダーから、LDAPを選択します

接続と認証の設定

  • 接続URL: ldap://192.168.1.12:389
  • バインドタイプ: simple
  • バインドDN: cn=admin,dc=local
  • バインドクレデンシャル: admin_password

LDAPの検索と更新

  • 編集モード: WRITABLE
  • ユーザーDN: ou=users,dc=local
  • ユーザー名のLDAP属性: uid
  • RDNのLDAP属性: uid
  • UUID LDAP属性: entryUUID
  • ユーザーオブジェクトクラス: inetOrgPerson, organizationalPerson, person
  • スコープの検索: サブツリー

同期の設定

  • ユーザーのインポート: オン
  • 同期登録: オン
  • 検索中に無効なユーザーを削除する: オフ

cn mapper の個別設定

デフォルトではcn mapperというのがないため、これを追加します。マッパーの追加を押して追加していきます

  • 名前: cn mapper
  • マッパータイプ: user-attribute-ldap-mapper
  • User Model Attribute: username (※ここが username になっている点に注意)
  • LDAP Attribute: cn
  • Force a Default Value: オン

その他のマッパー一覧のLDAP Attributeの値

  • email: mail
  • first name: givenName
  • last name: sn
  • username: uid

Redmine:設定

  • ホスト: openldap(Dockerサービス名)
  • ポート: 389
  • アカウント: cn=admin,dc=local
  • パスワード: admin_password
  • ベースDN: ou=users,dc=local
  • 属性: ログインIDに uid を指定、オンザフライ作成を オン


4. ユーザーの追加と確認

  1. Keycloakの「ユーザー」から test-redmine (パスワード: 12345678) を追加。
  2. 以下のコマンドでLDAPに物理反映されたか確認。
docker exec -i openldap ldapsearch -x -b "dc=local" -D "cn=admin,dc=local" -w admin_password "(uid=test-redmine)"

期待される実行結果:

# test-redmine, users, local
dn: uid=test-redmine,ou=users,dc=local
uid: test-redmine
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
mail: test@test.com
givenName: test
sn: test
cn: test-redmine
userPassword:: MTIzNDU2Nzg=  # 12345678 のハッシュ値

# search result
search: 2
result: 0 Success

5. 最大の後悔:AIも教えてくれなかった仕様の壁

構築完了後、致命的な事実に直面し、猛烈に後悔しました。

「SSO(外部IdP)を使っている既存ユーザーは、LDAP連携ができない」

AI(Geminiなど)に相談しながら進めてきましたが、この仕様については最後まで教えてくれませんでした(知らなかったのか、不明ですが)。

  • LDAP: 新規ユーザー作成には対応するが、既にKeycloakでSSO(GitHubやGoogle等)を紐付けている既存ユーザーは連携不可。
  • 結論: 既にSSOを使っている場合は、Redmine側もリバースプロキシ構成にするしか道はありませんでした。LDAP方式を選択したのは、既存ユーザーにとっては「意味がない」という結果に終わりました。

最終的なyml

x-logging: &default-logging
  driver: "json-file"
  options:
    max-size: "10m"
    max-file: "3"

services:
  # -----------------------------------------------------
  # 1. 権限修正用コンテナ 
  # -----------------------------------------------------
  fix-permissions:
    image: busybox
    container_name: fix-permissions
    command: chown -R 999:999 /usr/src/redmine/log
    volumes:
      - ./data/log:/usr/src/redmine/log

  # -----------------------------------------------------
  # 2. テーマインストーラー
  # -----------------------------------------------------
  theme-installer:
    image: alpine/git
    container_name: theme-installer
    volumes:
      - ./data/themes:/themes
    entrypoint: ["/bin/sh", "-c"]
    command: >
      "if [ ! -d /themes/bleuclair ]; then
        echo 'Starting download...';
        git clone https://github.com/farend/redmine_theme_farend_bleuclair.git /themes/bleuclair;
        cd /themes/bleuclair;
        echo 'Cleaning up junk files...';
        rm -rf .git .github .husky src storybook node_modules;
        rm -f package.json package-lock.json webpack.config.js .gitignore LICENSE README.md README.ja.md remove_development_files;
        chmod -R 755 /themes/bleuclair;
        echo 'Install Complete';
      else
        echo 'Folder already exists. Skipping download.';
      fi"

  # -----------------------------------------------------
  # 3. Redmine 本体
  # -----------------------------------------------------
  redmine:
    image: redmine:latest
    container_name: redmine-app
    ports:
      - "3000:3000"
    environment:
      - REDMINE_DB_MYSQL=db
      - REDMINE_DB_PASSWORD=secret
      - REDMINE_DB_USERNAME=redmine
      - REDMINE_DB_DATABASE=redmine
      - REDMINE_SECRET_KEY_BASE=secret_key_base
      - TZ=Asia/Tokyo
      - RAILS_LOG_TO_STDOUT=
      - REDMINE_LOG_LEVEL=info
    volumes:
      - ./data/files:/usr/src/redmine/files:z
      - ./data/log:/usr/src/redmine/log:z
      - ./data/themes:/usr/src/redmine/themes:z
    depends_on:
      db:
        condition: service_started
      fix-permissions:
        condition: service_completed_successfully
    restart: always

  # -----------------------------------------------------
  # 4. データベース
  # -----------------------------------------------------
  db:
    image: mysql:8.4
    container_name: redmine-mysql
    command: 
      - --mysql-native-password=ON
      - --innodb_use_native_aio=0
    environment:
      - MYSQL_ROOT_PASSWORD=secret
      - MYSQL_DATABASE=redmine
      - MYSQL_USER=redmine
      - MYSQL_PASSWORD=secret
      - TZ=Asia/Tokyo
    volumes:
      - db-data:/var/lib/mysql
    restart: always

  # -----------------------------------------------------
  # 5. OpenLDAP
  # -----------------------------------------------------
  openldap:
    image: bitnamilegacy/openldap:2.6
    container_name: openldap
    depends_on:
      fix-permissions:
        condition: service_completed_successfully
    logging: *default-logging
    ports:
      - "389:389"
    environment:
      - LDAP_PORT_NUMBER=389
      - LDAP_ADMIN_USERNAME=admin
      - LDAP_ADMIN_PASSWORD=admin_password
      - LDAP_ROOT=dc=local
      - LDAP_CONFIG_ADMIN_ENABLED=yes
      - LDAP_CONFIG_ADMIN_PASSWORD=config_password
      - LDAP_SKIP_DEFAULT_TREE=no
      - TZ=Asia/Tokyo
    volumes:
      - /mnt/hdd/data/ldap/db:/bitnami/openldap/data:z
    restart: always

volumes:
  db-data:

6. 公式ドキュメントリンク


この記事に関する技術サポート・ご相談

「手順通りにいかない」「自社環境への構築を代行してほしい」など、
インフラ・サーバー周りでハマった際はお気軽にご相談ください。

相談・問い合わせフォームを開く

※ Googleフォームへ移動します(初回相談無料)

個人支援・寄付について

サイトラボでは個人支援・寄付を受けております。ご協力いただける方はお願いいたします。当サイトではビットコインで受け付けております。

  • ビットコイン:3LHnADwZwUbic2L45EnVJEykiG6KfbqrwS