2016年5月9日月曜日

OpenLDAPを利用したディレクトリサーバの構築(OLC対応版)


OpenLDAP2.4系がOLC(On-Line Configuration)を利用する設定方式に変わっている。
その仕様にあわせたサーバの構築手順(Red Hat 7、CentOS 7 用)をまとめておく。

-目次-
【設計・簡易構成案】
【インストールから設定】
【試験ユーザの登録】
【TLS(SSL)化】
【その他、設定検討事項項目】



【設計・簡易構成案】
今回、模擬的に作成するツリー構成は以下とする。

dc=jp
    dc=example
        cn=root          ・・・特権アカウント

        ou=managers      ・・・管理アカウントディレクトリ
            cn=readmgr
            cn=writemgr

        ou=users         ・・・ユーザディレクトリ
            cn=user1
            cn=user2

        ou=groups        ・・・グループディレクトリ
            cn=group1
            cn=group2


dc(domain component)としてjp, exampleを上位に配置する。
その配下に全権限をもたせたrootアカウントを作成し、
また各種ou(organization unit)を並行に配置する。

managersでは役割に特化した(権限を絞った)管理者アカウントを作成する。
具体的には、users、groupsディレクトリ配下を参照、または更新ができるアカウントをそれぞれ用意する。

利用者アカウントの管理はusersの下で行う。ここで各ユーザが追加、削除、参照される。
オブジェクトクラスはinetOrgPersonを利用する。

各アカウントが所属する組織はgroups下で作成し、ユーザを各グループへmemberとして登録する。
オブジェクトクラスはgroupOfNamesを利用する。


~補足~
(オブジェクトクラスの決め方)
今回ユーザの管理にinetOrgPersonというオブジェクトクラスを選んだ。
ただし、用途次第で最適なオブジェクトクラスは異なるため慎重に選択するべきである。

例えば今回はユーザの属性の一つとして、メールアドレスを登録させることを想定したため、inetOrgPersonを選んだ。
mail属性をディフォルトの補足属性としてもっているためである。

オブジェクトクラスの属性を調べるにはこのあたりのファイルを開けばよい。
/etc/openldap/schema/*.schema


ユーザ管理であってもサーバのログインアカウントなどでであればposixAccountの方がいいかもしれない。

objectclass ( 1.3.6.1.1.1.2.0 NAME 'posixAccount'
        DESC 'Abstraction of an account with POSIX attributes'
        SUP top AUXILIARY
        MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
        MAY ( userPassword $ loginShell $ gecos $ description ) )


またそれにあわせてグループもposixGroupの方がいいだろう。

objectclass ( 1.3.6.1.1.1.2.2 NAME 'posixGroup'
        DESC 'Abstraction of a group of accounts'
        SUP top STRUCTURAL
        MUST ( cn $ gidNumber )
        MAY ( userPassword $ memberUid $ description ) )

※オブジェクトクラスは継承環境があるため、上位クラス(SUP)の必須、補足属性の定義にも影響される。


(ツリーの構成)
ツリーの設計として、グループの下にユーザディレクトリを掘っていくような案も考えられる。
広さよりも、深さをより優先する設計という意味である。
深さ優先のメリットとしては、検索のベースを下げられ、ロック粒度を下げられる点があるだろう。
しかし、グループ属性が流動的であれば、そことディレクトリを共有し密に紐づくユーザの管理が煩雑になるデメリットもある。



【インストールから設定】
● パッケージのインストール
$ sudo yum install openldap-servers openldap-clients


● 初期設定
DB設定はサンプル例を利用する。
$ sudo cp -iv /usr/share/openldap-servers/DB_CONFIG.example /var/lib/ldap/DB_CONFIG

$ sudo chown -v ldap.ldap /var/lib/ldap/DB_CONFIG


● 起動・自動起動設定
$ sudo systemctl enable slapd

$ sudo systemctl start slapd

$ sudo systemctl status slapd


● スキーマの登録
要・不要の判断を簡略するためさしあたりすべてを登録しておく。
ldap rootアカウントはこの段階で未設定のため、"-Y EXTERNAL"オプションにより、認証をldap環境外の認証(OSローカル)に任せる。
$ find /etc/openldap/schema/*.ldif -exec sudo ldapadd -Y EXTERNAL -H ldapi:/// -f {} \;

スキーマが登録されたことを確認する。
$ sudo slapcat -b cn=config | grep "cn=schema,cn=config"

slapcatコマンドの代わりにldapsearchでconfigを参照しても同じ結果が得られる。
$ sudo ldapsearch -H ldapi:// -LLL -Q -Y EXTERNAL -b "cn=config" dn


● suffixの設定
ldapデータベースのルートとなるディレクトリを指定する。
suffixで指定したノード以下でしかオペレーションはできない、などの制約を発生させることができる。
suffixの設定であり、この段階でツリー構造ができるわけではない。
※コマンドを打った後に、ldifを貼り付け、Ctrl+Dで抜ける。

$ sudo ldapmodify -vv -Y EXTERNAL -H ldapi:///
dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcSuffix
olcSuffix: dc=example,dc=jp

$ sudo slapcat -n 0 | grep -i ^olcSuffix


● パスワード暗号化方式の決定
パスワードのハッシュ化方式を指示する。

{CRYPT}はcrypt(3)を利用することを表す。
$5$はSHA256形式のハッシュ化を、"%.8s"の部分は8文字のランダムなソルトを付与することを意味する。
$6$にするとSHA516形式となる。

$ sudo ldapmodify -Y EXTERNAL -H ldapi:///
dn: cn=config
changetype: modify
replace: olcPasswordHash
olcPasswordHash: {CRYPT}

dn: cn=config
changetype: modify
replace: olcPasswordCryptSaltFormat
olcPasswordCryptSaltFormat: $5$%.8s

$ sudo slapcat -b cn=config -a "(cn=config)" | grep Password


● ldap rootのdnとpwの設定
ldap用rootのパスワードをまず生成する。
schemeとcrypt-saltは先ほどldapコンフィグで指定したものを利用する。
$ slappasswd -h {CRYPT} -c '$5$%.8s' -s "testrootpass"
{CRYPT}$5$0O1V・・・ メモしておく

RootDNはSuffixで指定したノード以下でなければならない。
$ sudo ldapmodify -vv -Y EXTERNAL -H ldapi:///
dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcRootDN
olcRootDN: cn=root,dc=example,dc=jp

dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcRootPW
olcRootPW: {CRYPT}$5$0O1V・・・

$ sudo slapcat -n 0 | grep -i ^olcRoot..


● ツリーの構築
ldap rootアカウントが使えるようになっているはずであるのでここからはこのアカウントを使い操作する。

$ ldapadd -x -vv -H ldap://127.0.0.1 -D cn=writemgr,dc=example,dc=jp -w 'tmp_write_pass'
dn: dc=example,dc=jp
objectClass: top
objectClass: dcObject
objectClass: organization
o: example

dn: cn=root,dc=example,dc=jp
objectClass: organizationalRole
cn: root
description: Directory Manager

dn: ou=users,dc=example,dc=jp
objectClass: organizationalUnit
ou: users

dn: ou=groups,dc=example,dc=jp
objectClass: organizationalUnit
ou: groups

dn: ou=managers,dc=example,dc=jp
objectClass: organizationalUnit
ou: managers


$ ldapsearch -x -vv -H ldap://127.0.0.1 -D cn=root,dc=example,dc=jp -w 'tmp_root_pass' -b "dc=example,dc=jp"



● 管理者アカウント(root以外)の登録
参照用、更新用に専用のアカウントを作成する。
$ ldapadd -x -vv -H ldap://127.0.0.1 -D cn=root,dc=example,dc=jp -w 'tmp_root_pass'
dn: cn=readmgr,ou=managers,dc=example,dc=jp
objectClass: inetOrgPerson
mail: readmgr@example.jp
sn: readmgr

dn: cn=writemgr,ou=managers,dc=example,dc=jp
objectClass: inetOrgPerson
mail: writemgr@example.jp
sn: writemgr

パスワードはコマンドで投入する。
$ ldappasswd -H ldap://127.0.0.1 -D cn=root,dc=example,dc=jp -w 'tmp_root_pass' "cn=readmgr,ou=managers,dc=example,dc=jp" -s 'tmp_read_pass'

$ ldappasswd -H ldap://127.0.0.1 -D cn=root,dc=example,dc=jp -w 'tmp_root_pass' "cn=writemgr,ou=managers,dc=example,dc=jp" -s 'tmp_write_pass'

アクセスリストの設定をまだ行っていないため、この段階では意図した権限にはなっていない。


● アクセスリストの設定
各種DBがあり、それぞれにふさわしいフィルタを設定する必要がある。

$ sudo ls /etc/openldap/slapd.d/cn=config/
~snip~
olcDatabase={0}config.ldif
olcDatabase={1}monitor.ldif
olcDatabase={2}hdb.ldif
~snip~


(olcDatabase={0}config.cn=config、設定管理エントリ)
設定しない。

(olcDatabase={1}monitor、モニタ機能)
設定しない。

The monitor backend to slapd(8) is not an actual database; if enabled, it is automatically generated and dynamically maintained by slapd with information about the running status of the daemon.


(olcDatabase={2}、ユーザデータDB)
$ sudo ldapmodify -vv -Y EXTERNAL -H ldapi:///
dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcAccess
olcAccess: to attrs=userPassword,shadowLastChange
              by dn=cn=root,dc=example,dc=jp write
              by anonymous auth
              by self write
              by * none
olcAccess: to dn.subtree=dc=example,dc=jp
              by dn=cn=readmgr,ou=managers,dc=example,dc=jp read

              by dn=cn=writemgr,ou=managers,dc=example,dc=jp write
olcAccess: to *
              by dn=cn=root,dc=example,dc=jp write

※適用順番には意味がある。変えると意図しない動作になる。

フィルタを削除する場合はldifを以下のようにする。
olcAccess:{数字}は適用するDBの数字ではなく、アクセスリストの番号である。
0番で入れれば、先頭が、設定されている数以上の数字を入れれば最後尾のものが対象となる。

dn: olcDatabase={2}hdb,cn=config
changetype: modify
delete: olcAccess
olcAccess: {0}



【試験ユーザの登録】
● 一般ユーザ、グループの登録
usersツリー配下に試験アカウントを登録する。

rootアカウントは利用せず、書き込み権限のあるwritemgrアカウントを使う。

$ ldapadd -x -vv -H ldap://127.0.0.1 -D cn=writemgr,ou=managers,dc=example,dc=jp -w 'tmp_write_pass'
dn: uid=tmp_user01,ou=users,dc=example,dc=jp
objectClass: inetOrgPerson
mail:tmp_user01@example.jp
uid: tmp_user01
cn: tmp_user01
sn: tmp

dn: uid=tmp_user02,ou=users,dc=example,dc=jp
objectClass: inetOrgPerson
mail:tmp_user02@example.jp
uid: tmp_user02
cn: tmp_user02
sn: tmp

dn: cn=tmp_group01,ou=groups,dc=example,dc=jp
objectClass: groupOfNames
cn: tmp_group01
member: uid=tmp_user01,ou=users,dc=example,dc=jp

dn: cn=tmp_group02,ou=groups,dc=example,dc=jp
objectClass: groupOfNames
cn: tmp_group02
member: uid=tmp_user02,ou=users,dc=example,dc=jp


作成したユーザのパスワード(userPasswordアトリビュート)を追加する。
$ ldappasswd -H ldap://127.0.0.1 -D cn=writemgr,dc=managers,dc=example,dc=jp -w 'tmp_write_pass' uid=tmp_user01,ou=users,dc=example,dc=jp -s 'tmp_user_pass01'

$ ldappasswd -H ldap://127.0.0.1 -D cn=writemgr,dc=managers,dc=example,dc=jp -w 'tmp_write_pass' uid=tmp_user02,ou=users,dc=example,dc=jp -s 'tmp_user_pass02'


readmgrアカウントで適切に追加されているか参照してみる。
$ ldapsearch -x -vv -H "ldap://127.0.0.1" -D "cn=readmgr,ou=managers,dc=example,dc=jp" -w 'tmp_read_pass' -b ou=users,dc=example,dc=jp dn

$ ldapsearch -x -vv -H "ldap://127.0.0.1" -D "cn=readmgr,ou=managers,dc=example,dc=jp" -w 'tmp_read_pass' -b ou=users,dc=example,dc=jp "(&(objectclass=inetOrgPerson)(mail=tmp_user01@example.jp))"



【TLS(SSL)化】
● 自己証明書の作成
暗号化通信目的にTLS(SSL)を使えるよう、CA証明局の構築とCA証明書を作成する(さしあたり自己証明書)。
$ filename="openssl_x509_`date +%F`"

$ openssl genrsa 2048 > ${filename}.key

$ openssl req -new -x509 -out $filename.crt -key $filename.key -days 365 -sha256 -subj "/C=jp/ST=tokyo/L=example-ku/O=example Corp./CN=ldap.example.jp"

$ ls -la $filename.key $filename.crt

$ openssl x509 -text -noout -in $filename.crt

$ sudo cp $filename.key /etc/openldap/certs/server.key

$ sudo cp $filename.crt /etc/openldap/certs/server.crt

$ sudo chown ldap.ldap /etc/openldap/certs/server.key

$ sudo chown ldap.ldap /etc/openldap/certs/server.crt


● 証明書の指定
作成したcrt、keyファイルを設定ファイルに指定する。
$ sudo ldapmodify -Y EXTERNAL -H ldapi:///
dn: cn=config
changetype: modify
replace: olcTLSCertificateFile
olcTLSCertificateFile: /etc/openldap/certs/server.crt

dn: cn=config
changetype: modify
replace: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/openldap/certs/server.key

dn: cn=config
changetype: modify
delete: olcTLSCACertificatePath

$ sudo slapcat -b cn=config -a "(cn=config)" | grep -i TLS


● スキーマの追加
$ sudo vi /etc/sysconfig/slapd
SLAPD_URLS="ldapi:/// ldaps:/// ldap:///"

$ sudo systemctl restart slapd


● ldapクライアントの修正
認証局より認証されていないサーバ証明書であるためクライアントが受け入れを拒否する。
それを許可する指定をする。
$ echo -e "TLS_REQCERT allow" >> /etc/openldap/ldap.conf



【その他、設定検討事項項目】
● ログレベルの設定
$ sudo ldapmodify -vv -Y EXTERNAL -H ldapi:///
dn: cn=config
changetype: modify
replace: olcLogLevel
olcLogLevel: config stats

$ sudo slapcat -n 0 |grep ^olcLogLevel

ログを確認する。
$ sudo journalctl -u slapd -f