LoginSignup
14

More than 5 years have passed since last update.

OpenLDAPをマルチドメインでレプリケーション

Last updated at Posted at 2015-07-02

CentOS 7 で OpenLDAP サーバー2台をマルチマスタ構成でセットアップする方法です
example.com と example.net のマルチドメインで構成します
SSL / TLS 対応もさせます

インストール

sudo yum -y install openldap-servers openldap-clients

初期化

やり直すときはここから

sudo systemctl stop slapd
sudo rm -fr /etc/openldap/slapd.d/cn=config/olcDatabase={2}hdb{,.ldif}
sudo rm -fr /etc/openldap/slapd.d/cn=config/olcDatabase={3}hdb{,.ldif}
sudo rm -fr /var/lib/ldap
sudo install -d -o ldap -g ldap -m 0700 /var/lib/ldap
sudo install -d -o ldap -g ldap -m 0700 /var/lib/ldap/example.com
sudo install -d -o ldap -g ldap -m 0700 /var/lib/ldap/example.net
sudo systemctl start slapd

デフォルトの URI を設定

今後の作業を楽にするため、デフォルトの URI を設定します
/etc/openldap/ldap.conf

URL ldapi://

を設定します。この URI でアクセスすれば root ならなんでもできます。

LogLevel 設定

sudo ldapmodify <<__EOD__
dn: cn=config
changetype: modify
replace: olcLogLevel
olcLogLevel: config stats sync conns
__EOD__

systemd でのログ確認は journalctl -aru slapd | less あたりで。

my-domain.com の修正

yum でインストールした場合のサンプル設定が my-domain.com なので example.com に変更

sudo ldapmodify <<__EOD__
dn: olcDatabase={1}monitor,cn=config
changetype: modify
replace: olcAccess
olcAccess: to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external
 ,cn=auth" read by dn.base="cn=Manager,dc=example,dc=com" read by * none
__EOD__

確認

sudo ldapsearch -b cn=config olcDatabase=monitor

スキーマのインポート

sudo ldapadd -f /etc/openldap/schema/cosine.ldif
sudo ldapadd -f /etc/openldap/schema/inetorgperson.ldif

posix アカウントなど必要であればそれ (nis.ldif ?) なども

これは /etc/openldap/slapd.d/cn=config/cn=schema/ に保存されるので /var/lib/ldap/* の削除では消えません

example.com, example.net 用のデータベース作成

sudo ldapadd <<__EOD__
dn: olcDatabase={2}hdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcHdbConfig
olcDatabase: {2}hdb
olcDbDirectory: /var/lib/ldap/example.com
olcSuffix: dc=example,dc=com
olcRootDN: cn=Manager,dc=example,dc=com
olcAccess: to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external
 ,cn=auth" manage by * none
olcAccess: to dn.subtree="" by * read
__EOD__
sudo ldapadd <<__EOD__
dn: olcDatabase={3}hdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcHdbConfig
olcDatabase: {3}hdb
olcDbDirectory: /var/lib/ldap/example.net
olcSuffix: dc=example,dc=net
olcRootDN: cn=Manager,dc=example,dc=net
olcAccess: to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external
 ,cn=auth" manage by * none
olcAccess: to dn.subtree="" by * read
__EOD__

olcDatabase={2}hdb, olcDatabase={3}hdb と数字部分を明示するところとドメインごとに別のディレクトリ (olcDbDirectory) を指定するところがポイント

データベースのチューニング

値はなんとなくです

sudo ldapmodify <<__EOD__
dn: olcDatabase={2}hdb,cn=config
changetype: modify
add: olcDbConfig
olcDbConfig: set_cachesize 0 67108864 1
olcDbConfig: set_lg_dir .
olcDbConfig: set_lg_bsize 33554432
olcDbConfig: set_lk_max_objects 3000
olcDbConfig: set_lk_max_locks 3000
olcDbConfig: set_lk_max_lockers 3000
olcDbConfig: set_flags DB_LOG_AUTOREMOVE
__EOD__
sudo ldapmodify <<__EOD__
dn: olcDatabase={3}hdb,cn=config
changetype: modify
add: olcDbConfig
olcDbConfig: set_cachesize 0 67108864 1
olcDbConfig: set_lg_dir .
olcDbConfig: set_lg_bsize 33554432
olcDbConfig: set_lk_max_objects 3000
olcDbConfig: set_lk_max_locks 3000
olcDbConfig: set_lk_max_lockers 3000
olcDbConfig: set_flags DB_LOG_AUTOREMOVE
__EOD__

ドメイン作成と RootDN アカウント作成

この例のパスワードは 2h6R&9QE-6

sudo ldapadd <<__EOD__
dn: dc=example,dc=com
dc: example
o: "Example, Inc."
objectClass: dcObject
objectClass: organization

dn: cn=Manager,dc=example,dc=com
objectClass: organizationalRole
objectClass: simpleSecurityObject
cn: Manager
userPassword: {SSHA}T6fIJME1FHwKEuS9MG7BBhYU6TYLGRnX
__EOD__
sudo ldapadd <<__EOD__
dn: dc=example,dc=net
dc: example
o: "Example, ltd."
objectClass: dcObject
objectClass: organization

dn: cn=Manager,dc=example,dc=net
objectClass: organizationalRole
objectClass: simpleSecurityObject
cn: Manager
userPassword: {SSHA}T6fIJME1FHwKEuS9MG7BBhYU6TYLGRnX
__EOD__

{SSHA} で始まるパスワードのハッシュ値は slappasswd コマンドで生成できます

$ slappasswd
New password: パスワード
Re-enter new password: パスワード
{SSHA}T6fIJME1FHwKEuS9MG7BBhYU6TYLGRnX

OU 作成

とりあえず People と Group を作成します。(People に対応するのは Groups じゃないのか?と思いつつ)

sudo ldapadd <<__EOD__
dn: ou=People,dc=example,dc=com
ou: People
objectClass: organizationalUnit

dn: ou=Group,dc=example,dc=com
ou: Group
objectClass: organizationalUnit

dn: ou=People,dc=example,dc=net
ou: People
objectClass: organizationalUnit

dn: ou=Group,dc=example,dc=net
ou: Group
objectClass: organizationalUnit
__EOD__

ACL 設定

sudo ldapmodify <<__EOD__
dn: olcDatabase={2}hdb,cn=config
replace: olcAccess
olcAccess: to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external
 ,cn=auth" manage by * break
olcAccess: to attrs=userPassword
           by anonymous auth
           by self write
           by dn.exact="cn=Replication,dc=example,dc=com" read
           by dn.regex="uid=(user001|user002|user003),ou=People,dc=example,dc=com" write
           by group/groupOfUniqueNames/uniqueMember.exact="cn=Administrators,ou=Groups,dc=example,dc=com" write
           by * none
olcAccess: to *
           by dn.regex="uid=(user001|user002|user003),ou=People,dc=example,dc=com" write
           by group/groupOfUniqueNames/uniqueMember.exact="cn=Administrators,ou=Groups,dc=example,dc=com" write
           by * read
__EOD__
sudo ldapmodify <<__EOD__
dn: olcDatabase={3}hdb,cn=config
replace: olcAccess
olcAccess: to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external
 ,cn=auth" manage by * break
olcAccess: to attrs=userPassword
           by anonymous auth
           by self write
           by dn.exact="cn=Replication,dc=example,dc=net" read
           by dn.regex="uid=(user001|user002|user003),ou=People,dc=example,dc=net" write
           by group/groupOfUniqueNames/uniqueMember.exact="cn=Administrators,ou=Groups,dc=example,dc=net" write
           by * none
olcAccess: to *
           by dn.regex="uid=(user001|user002|user003),ou=People,dc=example,dc=net" write
           by group/groupOfUniqueNames/uniqueMember.exact="cn=Administrators,ou=Groups,dc=example,dc=net" write
           by * read
__EOD__
  • user001, user002, user003 もしくは Administrators グループに所属するメンバーは各エントリの書き換えや新規エントリの追加が可能
  • パスワード以外の参照であれば誰でも可能
  • パスワードは認証での利用か、本人が認証後に変更することは可能
  • Replication (後で設定する) 用ユーザーはパスワードを参照可能

認証テスト

ここらで認証のテストをしてみます

ldapsearch -x -D "cn=Manager,dc=example,dc=com" -H ldap://localhost/ -W
ldapsearch -x -D "cn=Manager,dc=example,dc=net" -H ldap://localhost/ -W

どちらでも認証できたでしょうか

一意性制約

メールドレスや uid の重複登録を許容しないように Unique 制約をつけます

sudo ldapadd <<__EOD__
dn: cn=module,cn=config
cn: module
objectClass: olcModuleList
olcModulePath: /usr/lib64/openldap/
olcModuleLoad: unique.la
__EOD__

モジュールの定義は /etc/openldap/slapd.d/cn=config/cn=module{N}.ldif に書かれるので /var/lib/ldap/* を消しても残ります

sudo ldapadd <<__EOD__
dn: olcOverlay=unique,olcDatabase={2}hdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcUniqueConfig
olcOverlay: unique
olcUniqueUri: ldap:///ou=People,dc=example,dc=com?uid?sub
olcUniqueUri: ldap:///ou=People,dc=example,dc=com?mail?sub
__EOD__
sudo ldapadd <<__EOD__
dn: olcOverlay=unique,olcDatabase={3}hdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcUniqueConfig
olcOverlay: unique
olcUniqueUri: ldap:///ou=People,dc=example,dc=net?uid?sub
olcUniqueUri: ldap:///ou=People,dc=example,dc=net?mail?sub
__EOD__

インデックス作成

性能向上のためインデックスを作成します。レプリケーション時にも使われます。

sudo ldapmodify <<__EOD__
dn: olcDatabase={2}hdb,cn=config
changetype: modify
add: olcDbIndex
olcDbIndex: uid pres,eq,sub
-
add: olcDbIndex
olcDbIndex: ou pres,eq,sub
-
add: olcDbIndex
olcDbIndex: mail pres,eq,sub
-
add: olcDbIndex
olcDbIndex: objectClass eq
-
add: olcDbIndex
olcDbIndex: entryCSN eq
-
add: olcDbIndex
olcDbIndex: entryUUID eq
__EOD__

sudo ldapmodify <<__EOD__
dn: olcDatabase={3}hdb,cn=config
changetype: modify
add: olcDbIndex
olcDbIndex: uid pres,eq,sub
-
add: olcDbIndex
olcDbIndex: ou pres,eq,sub
-
add: olcDbIndex
olcDbIndex: mail pres,eq,sub
-
add: olcDbIndex
olcDbIndex: objectClass eq
-
add: olcDbIndex
olcDbIndex: entryCSN eq
-
add: olcDbIndex
olcDbIndex: entryUUID eq
__EOD__

SSL / TLS 設定

証明書設定

Mozilla の Network Security Services (NSS) ツールを使う例があったりするのですが、手持ちの PEM を使う方法がよくわからなかったため、PEM ファイルをそのまま使います。

certutil で作られているファイルを削除

sudo rm /etc/openldap/certs/*

手持ちの証明書がなければ自己署名の証明書を作成

sudo openssl req -x509 -days 3650 -out /etc/openldap/certs/server.crt \
   -newkey rsa:2048 -keyout /etc/openldap/certs/server.key -nodes \
   -subj "/C=JP/ST=Tokyo/L=Minato-ku/O=Example, Inc./CN=ldap.example.com/emailAddress=test@example.com"

/etc/openldap/certs/ca.crt に中間証明書を /etc/openldap/certs/server.crt にサーバー証明書を /etc/openldap/certs/server.key に秘密鍵を置きます。
秘密鍵は ldap ユーザーだけが読めれば良いので

sudo chown ldap:ldap /etc/openldap/certs/server.key
sudo chmod 400 /etc/openldap/certs/server.key
sudo ldapmodify <<__EOD__
dn: cn=config
changetype: modify
delete: olcTLSCACertificatePath
-
replace: olcTLSCertificateFile
olcTLSCertificateFile: /etc/openldap/certs/server.crt
-
replace: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/openldap/certs/server.key
-
replace: olcTLSProtocolMin
olcTLSProtocolMin: 3.1
__EOD__

中間証明書が必要な場合はこちらも

sudo ldapmodify <<__EOD__
dn: cn=config
changetype: modify
replace: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/openldap/certs/ca.crt
__EOD__

確認

sudo ldapsearch -b cn=config objectClass=olcGlobal

PEM ファイルを更新したら slapd の再起動が必要です

が、その前に ldaps (636) ポートを Listen するように /etc/sysconfig/slapd を書き換えます
(SLAP_URLS に ldaps:/// を追加)

sudo sed -i -e 's#SLAPD_URLS="ldapi:/// ldap:///"#SLAPD_URLS="ldapi:/// ldap:/// ldaps:///"#' /etc/sysconfig/slapd

slapd の再起動

sudo systemctl restart slapd

クライアント側の設定となりますが /etc/openldap/ldap.confTLS_REQCERT allow と指定しない場合、ldapsearch コマンドなどでいわゆるオレオレ証明書(自己署名)サイトなどにアクセスできなくなります。
TLS_CACERT /etc/pki/tls/certs/ca-bundle.crt としておくと一般的な証明機関で発行されたサーバーへの接続が可能となります。

テスト

ldapsearch -x -H ldaps://localhost/ -W -D cn=Manager,dc=example,dc=com -b dc=example,dc=com cn=Manager
openssl s_client -connect localhost:636

openssl コマンドで接続できるのに ldapsearch で接続できない場合は ldap.confTLS_REQCERT allow を確認

レプリケーション設定

ServerID 設定

ldap1 と ldap2 というサーバーでレプリケーションするとします
olcServerID をサーバーごとに一意に設定します

ldap1

sudo ldapmodify <<__EOD__
dn: cn=config
changetype: modify
replace: olcServerID
olcServerID: 1
__EOD__

ldap2

sudo ldapmodify <<__EOD__
dn: cn=config
changetype: modify
replace: olcServerID
olcServerID: 2
__EOD__

を実行します

レプリケーション用ユーザー作成

ACL 設定で既に出ていますが Replication というユーザー(?)を作成します
レプリケーションは参照さえできれば良いため RootDN (Manager) とは別にユーザーを作成します

この例のパスワードは YB)gNF!Q6)

sudo ldapadd <<__EOD__
dn: cn=Replication,dc=example,dc=com
objectClass: organizationalRole
objectClass: simpleSecurityObject
cn: Replication
userPassword: {SSHA}bSU9ueMZ93IIlXB7KdiiLby9S0bF0C+J
__EOD__

sudo ldapadd <<__EOD__
dn: cn=Replication,dc=example,dc=net
objectClass: organizationalRole
objectClass: simpleSecurityObject
cn: Replication
userPassword: {SSHA}bSU9ueMZ93IIlXB7KdiiLby9S0bF0C+J
__EOD__

パスワード変更は次のようにして行えます (-S はパスワード入力プロンプト表示の指定)

sudo ldappasswd -S cn=Replication,dc=example,dc=com

レプリケーションモジュール登録

sudo ldapadd <<__EOD__
dn: cn=module,cn=config
objectClass: olcModuleList
cn: module
olcmodulePath: /usr/lib64/openldap
olcModuleLoad: syncprov.la
__EOD__

一意性(Unique)モジュールと同様に /etc/openldap/slapd.d/cn=config/cn=module{N}.ldif に書かれます

プロバイダ側設定

sudo ldapadd <<__EOD__
dn: olcOverlay=syncprov,olcDatabase={2}hdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov
olcSpSessionLog: 1000
__EOD__

sudo ldapadd <<__EOD__
dn: olcOverlay=syncprov,olcDatabase={3}hdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov
olcSpSessionLog: 1000
__EOD__

コンシューマ側設定

双方のサーバーでもう一方のサーバーと同期するように設定する
双方向のため(?) oldMirrorMode を有効にします

sudo ldapmodify <<__EOD__
dn: olcDatabase={2}hdb,cn=config
changetype: modify
add: olcSyncRepl
olcSyncRepl: rid=001
  provider=ldap://{相方サーバー}/
  bindmethod=simple
  binddn="cn=Replication,dc=example,dc=com"
  credentials="レプリケーションユーザーのパスワード"
  type=refreshAndPersist
  interval=00:00:05:00
  searchbase="dc=example,dc=com"
  scope=sub
  retry="5 10 30 +
__EOD__

sudo ldapmodify <<__EOD__
dn: olcDatabase={3}hdb,cn=config
changetype: modify
add: olcSyncRepl
olcSyncRepl: rid=002
  provider=ldap://{相方サーバー}/
  bindmethod=simple
  binddn="cn=Replication,dc=example,dc=net"
  credentials="レプリケーションユーザーのパスワード"
  type=refreshAndPersist
  interval=00:00:05:00
  searchbase="dc=example,dc=net"
  scope=sub
  retry="5 10 30 +
__EOD__

sudo ldapmodify <<__EOD__
dn: olcDatabase={2}hdb,cn=config
changetype: modify
add: olcMirrorMode
olcMirrorMode: TRUE
__EOD__

sudo ldapmodify <<__EOD__
dn: olcDatabase={3}hdb,cn=config
changetype: modify
add: olcMirrorMode
olcMirrorMode: TRUE
__EOD__

ユーザー、グループを登録してみる

sudo ldapadd <<__EOD__
dn: uid=user01,ou=People,dc=example,dc=com
objectClass: person
objectClass: inetorgperson
objectClass: organizationalperson
objectClass: top
userPassword: {SSHA}OLoVlITmijyBebCFt8f9UfC+w3ikhDTK
mail: user01@example.com
givenName: Taro
uid: user01
cn: Taro Yamada
sn: Yamada

dn: cn=group01,ou=Group,dc=example,dc=com
objectClass: groupOfUniqueNames
objectClass: top
description:: dGVzdCBncm91cAo=
cn: group01
uniqueMember: uid=user01,ou=People,dc=example,dc=com
__EOD__

2台のサーバーで検索してレプリケーションがうまく動作しているかを確認する

ldapsearch -b dc=example,dc=com uid=user01
ldapsearch -b dc=example,dc=com cn=group01

レプリケーションがうまく動作していなかったら firewalld の設定を確認してみましょう

sudo firewall-cmd --list-all

services に ldap, ldaps が入っていなかったら追加します

sudo firewall-cmd --add-service=ldap
sudo firewall-cmd --add-service=ldap --permanent
sudo firewall-cmd --add-service=ldaps
sudo firewall-cmd --add-service=ldaps --permanent

ログも確認

sudo journalctl -aru slapd | less

サイズ制限

変える必要はないかもしれないが、検索で全件表示させようと思っても制限にかかって一部しか出力されないので制限を緩和する

sudo ldapmodify <<__EOD__
dn: cn=config
changetype: modify
replace: olcSizeLimit
olcSizeLimit: 3000
__EOD__

phpLDAPadmin の設定

phpLDAPadmin は EPEL リポジトリからインストールできます

sudo yum -y install epel-release
sudo yum -y install phpldapadmin

設定ファイルが /etc/phpldapadmin/config.php にあります

base 指定

マルチドメイン環境で利用する場合それぞれの base を指定する必要があります

$servers->setValue('server','base',array('dc=example,dc=com', 'dc=example,dc=net'));

不要な警告の抑制

期待するスキーマが無いと警告が表示されてちょっとうるさいので表示しないようにします

$config->custom->appearance['hide_template_warning'] = true;

ログインID設定

uid だけでログインするには次のようにしますが、両方のドメインに存在する uid の場合にどちらかのドメインでしかログインできません

$servers->setValue('login','attr','uid');
$servers->setValue('login','base',array('ou=people,dc=example,dc=com', 'ou=people,dc=example,dc=net'));

そのため dn で ID 指定する必要があります
ID に uid=user01,ou=people,dc=example,dc=com と入力します

$servers->setValue('login','attr','dn');

一意性制約

OpenLDAP とは別に Unique 制約の設定があります。デフォルトで mail, uid, uidNumber に制約がかかっていますが、ドメインをまたいでの制約となっており、uid=user01,ou=people,dc=example,dc=com が存在すると uid=user01,ou=people,dc=example,dc=net が登録できません。そこでこの設定を mail だけに変更します。

$servers->setValue('unique','attrs',array('mail'));

アクセス制限

yum で phpldapadmin をインストールした場合、デフォルトでは localhost からしかアクセスできないようになっているため /etc/httpd/conf.d/phpldapadmin.conf を編集する必要があります

おまけ

OpenDJ という LDAP サーバーを非特権ユーザーで使っていたため、既存サーバーは 1389/tcp と 1636/tcp を Listen しています。
これをスムーズに移行させるため iptables (firewalld) に redirect の設定をしてみます。

/etc/firewalld/direct.xml
<?xml version="1.0" encoding="utf-8"?>
<direct>
    <rule ipv="ipv4" table="nat" chain="PREROUTING" priority="0">-p tcp --dport 1389 -j REDIRECT --to-port 389</rule>
    <rule ipv="ipv4" table="nat" chain="PREROUTING" priority="0">-p tcp --dport 1636 -j REDIRECT --to-port 636</rule>
</direct>

localhost からのアクセスでは OUTPUT に書かないとダメらしいですが、外部からのアクセスしかないのでこれだけで良いでしょう。

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
14