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.conf
で TLS_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.conf
の TLS_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 の設定をしてみます。
<?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 に書かないとダメらしいですが、外部からのアクセスしかないのでこれだけで良いでしょう。