📑

OpenLDAP のインストールから memberOf オーバーレイの導入まで (Ubuntu 20.04 LTS)

2022/06/25に公開

GitLab のユーザー同期とグループ同期を試してみたくて、ローカル環境に OpenLDAP を入れることにしました。

ところが、公式サイトは、リファレンス的なドキュメントはがっつり載っているものの、やりたいことからその方法を調べる逆引き的な使い方をしやすいようにはなっていません。
そこで Web を検索するわけですが、Web のどこを探しても、だいたい正しいけれども古い情報や間違った情報が混じっているサイトばかりで、ここだけ見れば完結というところは、公式サイト以外になぜかありません。
公式も含めいろいろなサイトから情報を集め、実際に試して真偽を確かめ、やっと動く環境をつくることができたので、少なくとも現時点では「ここだけ見れば完結」という情報源になるべく、まとめておきます。この記事に載せてある config の ldif はすべて実際に動作しているものです。

現時点っていつよ?

そもそもこれを書いていないサイトが多くて、情報が古いんだかどうなんだか、判断できなかったのも困りました。というわけで、明記しておきます。

Date: 2022-06-24
OS: Ubuntu 20.04 LTS
OpenLDAP: 2.4.49+dfsg-2ubuntu1.9
Apache Directory Studio: 2.0.0.v20210717-M17

インストール

sudo apt update
sudo apt -y install slapd ldap-utils

余談ながら、環境を一度クリーンにするために remove するときはこう。(ldap-util は残しておいても害はないと思う。)

sudo apt --purge remove slapd

なんですが、次の dpkg-reconfigure slapd をやれば、remove まではしなくてもよいと思います。

初期設定

インストール中にも admin パスワードを設定するように言われるんですが、なぜか Base DN のドメイン名 (つまり DCs) を聞かれなくて、dc=nodomain になってしまうので、改めて初期設定を実行します。

dpkg-reconfigure slapd

以下のような画面が出て、インタラクティブに入力を求められるので、入れていきます。


▲ 設定を省略しますか? と訊かれているので、No を選択して Enter。


▲ Base DN に使うドメイン名 (DCs) を入力して Enter。書いてあるように foo.example.org と入れると、Base DN が dc=foo, dc=examle, dc=org になります。
(ここをなぜ internal にしたのかはここ参照。)


▲ Base DN で使う組織名 (O) を入力して Enter。


▲ Admin パスワードを設定。この次の画面で確認のため再入力させられます。

残りの画面の質問は、好きな方を選んでください。私は両方とも Yes にしました。

  • Do you want the database to be removed when slapd is purged? (slapd を purge したときにドメインや設定のデータベースを消すか?)
  • There are still files in /var/lib/ldap ...snip... Move old database? (超要訳:既にあったデータベースはどこか別のところにバックアップしておくか?)

config データベースのパスワードを設定する

上で設定した Admin パスワードは、実はドメインデータベース (OpenLDAP 2.4 の場合 mdb) の Admin のパスワードです。
一方設定データベース (config) は、デフォルトでは SASL EXTERNAL 認証 を使って、Unix ソケット経由で渡される gid と uid で認可するように設定されています。ldapmodify の引数で -H ldapi:/// -Y EXTERNAL とやってるのがそれです。このままだとリモートマシンから、ldapmodify や Apache Directory Studio のような GUI ツールで設定を弄れないので、そうできるようにパスワードを設定します。
ここで一番ハマったので、ハマりどころを書いておきます。

  • olcRootPW を add するのですが、必ず slapdpasswd で生成した文字列を設定すること。
    過去に設定した slapd の slapcat や ldapsearch の出力をコピペしたら invalid credential になりました。
  • olcRootDN はいりません、というかあってはいけません。これも add するように書いてあるサイトが結構あるのですが。
    olcRootDN があると olcRootPW を add するときにエラーになります。(エラーメッセージは下のコードブロックの1行目と同じ。)
    先に olcRootPW を add すれば olcRootDN を add できてしまいますが、slapcat -n 0 がこんなエラーになって異常終了し、dpkg-reconfigure からやり直すしかなくなります。
    62ab5b5c olcRootPW: value #0: <olcRootPW> can only be set when rootdn is under suffix
    62ab5b5c config error processing olcDatabase={0}config,cn=config: <olcRootPW> can only be set when rootdn is under suffix
    slapcat: bad configuration file!
    
  • リモートクライアントからバインドするときの Bind DN は cn=config です。(cn=admin,cn=config や、データベース Admin の cn=admin,dc=internal ではありません。)

前置きが ldif よりも長くなりましたが、下記の ldif を作って、ldapmodify します。

config.ldif:

dn: olcDatabase={0}config,cn=config
changetype: modify
add: olcRootPW
olcRootPW: {SSHA}Vr+KskG9uIWMpY6pb64yFdrFtxB/I47O  # slappasswd で生成した文字列
ldapmodify -H ldapi:/// -Y EXTERNAL -a -c -f config.ldif

memberOf overlay モジュールを有効化する

グループ同期に必要なやつです。
これも olcOverlay=refint も登録しろと書いてないところが多くて、動作するまで少しハマりました。

memberof.ldif:

dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: memberof
-
add: olcModuleLoad
olcModuleLoad: refint

dn: olcOverlay=memberof,olcDatabase={1}mdb,cn=config
changetype: add
objectClass: olcConfig
objectClass: olcMemberOf
objectClass: olcOverlayConfig
objectClass: top
olcOverlay: memberof
olcMemberOfRefInt: TRUE
olcMemberOfDangling: ignore
olcMemberOfGroupOC: groupOfNames
olcMemberOfMemberAD: member
olcMemberOfMemberOfAD: memberOf

dn: olcOverlay=refint,olcDatabase={1}mdb,cn=config
changetype: add
objectClass: olcConfig
objectClass: olcOverlayConfig
objectClass: olcRefintConfig
objectClass: top
olcOverlay: refint
olcRefintAttribute: memberof member manager owner
ldapmodify -H ldapi:/// -Y EXTERNAL -a -c -f memberof.ldif

なお、Apache Directory Studio では、Fetch operational attributes while browsing しておかないと memberOf が表示されないので注意。

ディレクトリ

とりあえず、動作検証用にこんな感じのを入れてます。あくまで例です。
なお、memberof は、前述の memberOf オーバーレイによって member から自動生成されるので、ここでは設定していません。
なお、この ldif は、実際に動作しているものから、手で抜粋・改変しています。このままちゃんと動作するかどうかは検証していませんのでご注意ください。

addMembers.ldif:

dn: ou=people,dc=internal
objectClass: organizationalUnit
ou: people

dn: ou=groups,dc=internal
objectClass: organizationalUnit
ou: groups

dn: cn=ldap_user_01,ou=people,dc=internal
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
cn: ldap_user_01
sn: Ldap
givenName: User01
mail: ldap01@internal
uid: LDAP01
userPassword:: {SSHA}crBim/AU3yqdhtBjGZ/ehHBGm5M0yBTd  # slappasswd で生成した文字列

dn: cn=ldap_user_02,ou=people,dc=internal
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
cn: ldap_user_02
sn: Ldap
givenName: User02
mail: ldap02@internal
uid: LDAP02
userPassword:: {SSHA}crBim/AU3yqdhtBjGZ/ehHBGm5M0yBTd  # slappasswd で生成した文字列

dn: cn=ldap_user_03,ou=people,dc=internal
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
cn: ldap_user_03
sn: Ldap
givenName: User03
mail: ldap03@internal
uid: LDAP03
userPassword:: {SSHA}crBim/AU3yqdhtBjGZ/ehHBGm5M0yBTd  # slappasswd で生成した文字列

dn: ou=gitlab_groups,ou=groups,dc=internal
objectClass: organizationalUnit
ou: gitlab_groups

dn: cn=gitlab_admins,ou=gitlab_groups,ou=groups,dc=internal
objectClass: groupOfNames
objectClass: top
cn: gitlab_admins
member: cn=ldap_user_03,ou=people,dc=internal

dn: cn=ldap_group_01,ou=gitlab_groups,ou=groups,dc=internal
objectClass: groupOfNames
objectClass: top
cn: ldap_group_01
member: cn=ldap_user_01,ou=people,dc=internal
member: cn=ldap_user_03,ou=people,dc=internal

dn: cn=ldap_group_02,ou=gitlab_groups,ou=groups,dc=internal
objectClass: groupOfNames
objectClass: top
cn: ldap_group_02
member: cn=ldap_user_02,ou=people,dc=internal

おまけ1: よく使うコマンド

config データベースを変更する

ldapmodify -H ldapi:/// -Y EXTERNAL -a -c -f foobar.ldif
  • -H ldapi:///: Unix ソケットで接続
  • -Y EXTERNAL: SASL の EXTERNAL 認証
  • -a: changetype: を書いていないときには add になる (ldapadd と同じ)
  • -c: エラーがあっても中断せず残りのエントリを処理する

ディレクトリデータベース (mdb) を変更する

ldapmodify -H ldapi:/// -D "cn=admin,dc=internal" -W -a -c -f foobar.ldif
  • -D: Bind DN
  • -W: パスワードをプロンプト
  • -H,-a,-c: 同上

ディレクトリデータベースの検索

ldapsearch -H ldap://ldap.internal -W -x -D cn=admin,dc=internal -b dc=internal filter
  • -H ldap://...: リモート接続
  • -x: シンプル認証
  • -b: 検索ベース DN
  • -W,-D: 同上

データベースを ldif でダンプ

slapcat -n 0
  • -n: データベース番号。指定しないと 1。普通は config が 0、mdb が 1

おまけ2: ldapmodify にくべる ldif

1行目に dn、2行目に changetype を書きます。changetype を省略すると、-a を指定しない場合は modify、指定した場合は add になります。

changetype: add

changetype: modify
add: ほげほげ

とが紛らわしいのですが、前者は dn をまるごと追加する場合、後者は既存の dn に attribute を追加する場合です。

changetype: add

dn をまるごと新しく追加します。

例:

dn: cn=John Doe,ou=people,dc=internal
changetype: add
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top
cn: John Doe
sn: Doe
givenName: John
mail: john_doe@internal
uid: LDAP04

changetype: modify

既存の dn の attribute を追加したり (add)、書き換えたり (replace)、削除したり (delete) します。
- だけの行で区切って複数のコマンドを併記できます。

例:

dn: cn=ldap_group_01,ou=groups,dc=internal
changetype: modify
delete: member
member: cn=ldap_user_02,ou=people,dc=internal
-
add: member
member: cn=ldap_user_03,ou=people,dc=internal
-
replace: description
description: reflected Jun 2022 new members

changetype: delete

既存の dn をまるごと削除します。

例:

dn: cn=ldap_user_02,ou=people,dc=internal
changetype: delete

Discussion