LDAPサーバによるユーザ認証の検証<サービスインストールからサンプルLDIF追加まで>

#前提
CentOS Linux release 7.5.1804 (Core)

#パッケージのインストール
```bash
yum -y install openldap openldap-clients openldap-servers 
```
#サービス起動

```bash
 systemctl status slapd
● slapd.service - OpenLDAP Server Daemon
   Loaded: loaded (/usr/lib/systemd/system/slapd.service; disabled; vendor preset: disabled)
```
＊初歩的だが、このとき前ステップではopenldapパッケージのインストールだったが、
起動するサービスはslapdであることに注意

#試しに現状のＤＢがどうなってるか確認してみる
まっさらな状態で現在のLDAPデータベースの内容をldapdb.ldifファイルに書き出そうとしてみる。
すると、どうやら初期状態ではなにも存在していないらしい。
/var/lib/ldapという場所にDBが保存されるらしいことはここでわかる。

```bash
 slapcat > ldapdb.ldif
61164aeb The first database does not allow slapcat; using the first available one (2)
61164aeb hdb_db_open: warning - no DB_CONFIG file found in directory /var/lib/ldap: (2).
Expect poor performance for suffix "dc=my-domain,dc=com".
```

#DBの作成
もともとパッケージの中にDB_CONFIG.exampleというDBサンプルが存在するため、これをベースに
DBサンプルを作る。

```
# cd /usr/share/openldap-servers/
# ls -l
total 8
-rw-r--r--. 1 root root  845 Apr 28 09:32 DB_CONFIG.example
-rw-r--r--. 1 root root 3717 Apr 28 09:32 slapd.ldif
# cp -p DB_CONFIG.example /var/lib/ldap/DB_CONFIG
```

コピーしただけだと所有ユーザがコピーしたものだけなので、所有者変更する。

```
# ls -l
total 324
-rw-r--r--. 1 ldap ldap     4096 Aug 13 06:35 alock
-rw-------. 1 ldap ldap   262144 Aug 13 06:35 __db.001
-rw-------. 1 ldap ldap    32768 Aug 13 06:35 __db.002
-rw-------. 1 ldap ldap    49152 Aug 13 06:35 __db.003
-rw-r--r--. 1 root root      845 Apr 28 09:32 DB_CONFIG
-rw-------. 1 ldap ldap     8192 Aug  8 10:26 dn2id.bdb
-rw-------. 1 ldap ldap    32768 Aug  8 10:26 id2entry.bdb
-rw-------. 1 ldap ldap 10485760 Aug  8 10:26 log.0000000001
# chown ldap:ldap DB_CONFIG
# ls -l
total 324
-rw-r--r--. 1 ldap ldap     4096 Aug 13 06:35 alock
-rw-------. 1 ldap ldap   262144 Aug 13 06:35 __db.001
-rw-------. 1 ldap ldap    32768 Aug 13 06:35 __db.002
-rw-------. 1 ldap ldap    49152 Aug 13 06:35 __db.003
-rw-r--r--. 1 ldap ldap      845 Apr 28 09:32 DB_CONFIG
-rw-------. 1 ldap ldap     8192 Aug  8 10:26 dn2id.bdb
-rw-------. 1 ldap ldap    32768 Aug  8 10:26 id2entry.bdb
-rw-------. 1 ldap ldap 10485760 Aug  8 10:26 log.0000000001
```

#DBをいじるための管理者作成
管理ユーザを定義する。
ステップは
⓵slappasswdコマンドで文字列を暗号化
⓶管理者作成用のLDIFファイルの作成
⓷LDIFファイル内にて⓵で出力されたパスワードを記載
⓸ldapaddにてDBに登録

```bash:⓵
# slappasswd
New password:
Re-enter new password:
{SSHA}WyqBCqwPHnBbT+XcsK0LV1O7jHClvYc/ #<-これが暗号化されたパスワード文字列
```

```bash:⓶
# cat rootdn.ldif
dn: olcDatabase={0}config,cn=config #olcDatabaseがconfigのエントリとして以下を登録
changetype: modify
add: olcRootDN #管理者(RootDN)を追加
olcRootDN: cn=admin,cn=config
-
add:olcRootPW #管理者のPWを追加。先ほど出力した暗号化されたPWを入力
olcRootPW:{SSHA}dcP1qo0XUjL272DMezm79cwds8zQLa2b #⓷
```

＊１dn:olcDatabase (OpenLdap Configuration Databaseの略のよう)の値にはconfigのほかにhdbなどが入るみたいだが、今回のように管理者のPWを設定するのはconfigエントリみたいだ。
olcdatabaseの値の例
hdb :ユーザデータなど、様々なデータを追加、管理する
monitor:モニタの管理者DNと管理者パスワード、アクセス制御

＊２ olcDatabase={0}configのような”{数字}”はDB内での順序を示すよう。DBなので順序を意識する必要はほとんどないが、olcDatabaseについては順序が大事なのかもしれない。

```bash:④
ldapmodify -Y EXTERNAL -H ldapi:/// -f /root/ldif/rootdn.ldif
```

-Y はSASL(認証機能を提供するサービス)の中でどれを使うか　引数にKerberos，GSSAPI，DIGEST-MD5などが入る。
今回は認証機能を使わないので、EXTERNALとする。
-HはLDAPサーバをURIで指定する。ローカルサーバの場合,ldapi///と宣言する。
ldapiとは、接続プロトコルのこと。ほかにも以下のように違うプロトコルでの接続も可能

|URL|	Protocol|	Transport|
|---|---|---|
|ldap:///|	LDAP|	TCP port 389|
|ldaps:///	|LDAP over SSL|	TCP port 636|
|ldapi:///|	LDAP|	IPC (Unix-domain socket)|
ソース(https://www.openldap.org/doc/admin24/runningslapd.html)

#設定変更確認

```bash
 ldapsearch -Y EXTERNAL -H ldapi:/// -b "olcDatabase={0}config,cn=config"
SASL/EXTERNAL authentication started
SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
SASL SSF: 0
# extended LDIF
#
# LDAPv3
# base <olcDatabase={0}config,cn=config> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#

# {0}config, config
dn: olcDatabase={0}config,cn=config
objectClass: olcDatabaseConfig
olcDatabase: {0}config
olcAccess: {0}to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external
 ,cn=auth" manage by * none
olcRootDN: cn=admin,cn=config
olcRootPW: {SSHA}dcP1qo0XUjL272DMezm79cwds8zQLa2b

# search result
search: 2
result: 0 Success
```

さきほど設定したolcRootDNとolcRootPWが入っていることを確認できる

#ディレクトリマネージャの作成
先ほどの管理者はあくまでLDAPの設定変更管理者であって実際のDBの管理者ではない。
DBの管理者のことをディレクトリマネージャーと呼ぶ。
ソース(https://qiita.com/y-araki-qiita/items/0c954c3ae25d4ed9dbb8)

まずLDAPデータベースツリーのDN(olcsuffix)やDB作成用のディレクトリマネージャを作成するためのLDIFを作成。

```bash
# cat domain.ldif
dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcSuffix
olcSuffix: dc=example,dc=com #dc=example,dc=comを頂点としたツリーを作る。
-
replace: olcRootDN
olcRootDN: cn=admin,dc=example,dc=com #admin=管理者
-
add: olcRootPW
olcRootPW:{SSHA}dcP1qo0XUjL272DMezm79cwds8zQLa2b #ldappasswdで暗号化したPWを記載。先ほどのPWと同じでも可。

```

objectclass：エントリの必須属性やoptional属性が何かを定義するもの。ユーザというObjectclassであれば「氏名」「年齢」「メールアドレス」などの属性は必須となるなど。


ouとcnとdcがごっちゃになるので、整理。
ou : DITの階層コンポーネントだが、OrganizationUnitということもあり、組織単位を表す場合に使われる。
dc : DITの階層コンポーネントであり、Domain ComponentというだけあってなんでもＯＫ。
cn : Common name。名前なので、人とかモノとかを表すことが多そう。
＊　LDAPではこれ以外にも「C=国／地域」「O=組織」といった属性もあるが、Active Directoryではこれらは利用されない。

どうやらcn=configを頂点としたツリーにおいてはcn=schemaとかcnを使ってるが、
dc=comを頂点としてる場合、dcまたはouを使って、ユーザ名とかだけcnを使ってる気がする。
dcとcnの使い分けが何の意味をなしてるのか今は不明。

```
# 設定変更
ldapmodify -x -W -D cn=admin,cn=config -f /root/ldif/domain.ldif
```

#スキーマの追加
属性やオブジェクトクラスなど定義をセットにしたのがスキーマ。
スキーマがないとオブジェクトクラスや属性をもった管理ができない。
標準的なものは以下。
core.schema	OpenLDAPで必須のスキーマ
cosine.schema	ディレクトリサービスのX.500規格で規定された属性が定義されたスキーマ
inetorgperson.schema	アドレス帳など個人情報を扱うためのスキーマ
nis.schema	UNIX/Linuxのユーザーやグループ情報を扱うためのスキーマ

なので、ひとまずスキーマを使えるようにしておく。

```bash
ldapadd -D cn=admin,cn=config -f /etc/openldap/schema/cosine.ldif -W
Enter LDAP Password:
adding new entry "cn=cosine,cn=schema,cn=config"
```

インストールした中に標準スキーマが準備されてるので、自分のDBに追加しておく。
上記だとcn=cosineが追加されてる。

```bash
ldapsearch -D cn=schema,cn=config -W #追加されたcn=schema,cn=configにはアクセスできない
Enter LDAP Password:
ldap_bind: Invalid credentials (49)

ldapsearch -D cn=admin,cn=config -W #cn=admin,cn=configとした場合、きちんと表示される。
Enter LDAP Password:
# extended LDIF
#
# LDAPv3
# base <> (default) with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#

# search result
search: 2
result: 32 No such object

# numResponses: 1
```

cn=schema,cn=configに実際設定は格納されてるけど、設定管理はcn=admin,cn=configで行うということのよう。

オプションの-DはBindDNと呼ばれ、どの管理者で検索するかを記述する。
LDAPサービスへのログインのことをバインドと呼ぶらしく、それが由来。
どのツリーを調べるかは-b (=baseDN)で定義する。

```bash
ldapsearch -W -D cn=admin,cn=config -b cn=schema,cn=config ObjectClass=*
Enter LDAP Password:
# extended LDIF
#
# LDAPv3
# base <cn=schema,cn=config> with scope subtree
# filter: ObjectClass=*
# requesting: ALL
#

# schema, config
dn: cn=schema,cn=config
objectClass: olcSchemaConfig
cn: schema
olcObjectIdentifier: OLcfg 1.3.6.1.4.1.4203.1.12.2
olcObjectIdentifier: OLcfgAt OLcfg:3
olcObjectIdentifier: OLcfgGlAt OLcfgAt:0
olcObjectIdentifier: OLcfgBkAt OLcfgAt:1
...
```

確かにschemaがあることを確認できる。

#DBの更新

DBを更新するためにinit.ldifを作成する。
init.ldifについては大いにこちら(https://qiita.com/y-araki-qiita/items/6b2dcbf1a39a969d8024)
を参考にしてる。てか丸写しくらい。

```bash
dn: dc=example,dc=com
objectClass: top #topというのはオブジェクトクラスの最上位クラス。すべてのオブジェクトクラスはこれを継承してる。
objectClass: dcObject
objectClass: organization
o: Infra
dc: example

dn: ou=group,dc=example,dc=com
objectclass:organizationalUnit #organizationalUnitというオブジェクトクラスもtopを継承してるが、topを書かなくてもいいよう。
ou:group

dn:ou=user,dc=example,dc=com
objectclass:organizationalUnit
ou:user

dn: cn=infra,ou=group,dc=example,dc=com
objectclass:posixGroup
gidNumber:10001
cn:users
```

init.ldifをDBに追加する。

```bash
# ldapadd -x -W -D cn=admin,dc=example,dc=com -f /root/ldif/init.ldif
Enter LDAP Password:
adding new entry "dc=example,dc=com"

adding new entry "ou=group,dc=example,dc=com"

adding new entry "ou=user,dc=example,dc=com"

adding new entry "cn=infra,ou=group,dc=example,dc=com"
```

確認

```
ldapsearch -W -D cn=admin,dc=example,dc=com -b dc=example,dc=com
```

ということで、とりあえずDBとその管理の手順は一通りできたよう。
次に、LDAPを用いたユーザ認証をするところまで行いたい。