暇なので仕事に一息ついたので斜め読みだったSpringBootのリファレンスを少しだけ真面目に読んでみた。そうしたらLDAP認証についての記述があった。ちょうど社内システムの認証方法について検討(わざわざ認証機能作りなくないとも言う)していたこともあり試してみた。
LDAP認証とは何か?
その前に簡単に前提から。認証が必要なのは対象とするアプリケーションを許可した特定の人のみに利用させたいからだ。この特定の人を認証する方法として一般的にはユーザ名とパスワードによる認証方法が取られている。
この認証を実現するためにはユーザ名とパスワードという認証用情報の管理が必要になる。ここで言う管理は情報の保持・登録・変更・削除のことだ。認証対象となるアプリケーションにはユーザ認証機能とユーザ情報管理機能が必要になる。
だがこれらの機能をアプリケーション毎に用意するのは手間だしアプリケーション毎に認証情報を保持するとその管理も大変だ。
そこで認証情報を別のアプリケーションで管理してその認証情報を元にアプリケーションの認証をおこなうことでユーザ情報管理機能の作成は不要となりアプリケーション毎に認証情報を保持する必要もなくなる。
LDAP認証とはこの認証情報をLDAPサーバで管理しアプリケーションはその情報をLDAPサーバに問い合わせることでユーザ認証をおこなう認証方法のことだ。
LDAP(Lightweight Directory Access Protocol)はユーザーやコンピュータの情報を集中管理するディレクトリサービスへのアクセス時に用いられるプロトコルである。ディレクトリサービスという名前の通り情報は階層構造で管理する。
LDAP認証実現の手順
LDAP認証をおこなうためにはざっくりの手順だが以下のような準備が必要になる。
ちょっと長くなりそうなのでここではLDAPサーバの構築について示す。
LDAPサーバの構築
1. インストール
yumでopenldap,openldap-clients,openldap-serversをインストールする。
起動・停止はsystemctrlでおこなう。
その後firewall設定もおこないldapのポート(389)を開ける(検証用のためfirewallは無効化しているため作業はしていないが)。
# yum -y install openldap openldap-clients openldap-servers # systemctl start sladp
……とすんなりいけばいいのだが自分の環境では上手く行かなかった。実行にやたら時間がかかった挙げ句エラーとなってしまう。
エラーとなったURLに直接アクセスしてみたら見事に何もない。どうやらミラーサーバから消えているようだった。ミラーサーバには古いバージョンのものは残っていないらしい。
ということでyumでアクセスするレポジトリを追加してそこから取得した。
# cat /etc/yum.repos.d/CentOS7.4-Vault.repo [C7.4.1708-base] name=CentOS-7.4.1708 - Base baseurl=http://vault.centos.org/7.4.1708/os/$basearch/ gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 enabled=1 [C7.4.1708-updates] name=CentOS-7.4.1708 - Updates baseurl=http://vault.centos.org/7.4.1708/updates/$basearch/ gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 enabled=1 [C7.4.1708-extras] name=CentOS-7.4.1708 - Extras baseurl=http://vault.centos.org/7.4.1708/extras/$basearch/ gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 enabled=1 [C7.4.1708-centosplus] name=CentOS-7.4.1708 - CentOSPlus baseurl=http://vault.centos.org/7.4.1708/centosplus/$basearch/ gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 enabled=1 [C7.4.1708-fasttrack] name=CentOS-7.4.1708 - CentOSPlus baseurl=http://vault.centos.org/7.4.1708/fasttrack/$basearch/ gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 enabled=1 # yum -y --disablerepo=* --enablerepo=C7.4.1708-* install openldap openldap-clients openldap-servers
2. 管理情報の設定
LDAPサーバを立ち上げたら初期セットアップをおこなう。LDAPサーバの設定は設定用のファイルを作成しそれをコマンドで読み込むという形を取る。
初期セットアップはLDAPサーバの管理者パスワード変更・管理者情報の登録・ベースDN情報の登録をおこなう。
まずはLDAPサーバの管理者パスワードを設定する。
# cat rootPW.ldif(※ファイル名は何でもいい) dn: olcDatabase={0}config,cn=config # dn:識別名 changetype: modify # changetype:操作(add,modif,delete) add: olcRootPW # add:対象の属性名 変更時はreplace:olcRootPW olcRootPW: {SSHA}o2Ro2qTviCe5j/DN0Tpu8wFnwDZ6EUbh # 対象の属性名: 属性値 # ldapadd -Y EXTERNAL -H ldapi:// -f rootPW.ldif
LDAPの操作は上記のようなファイルの形式でおこなう。識別名で指定する対象に対して指定した操作を指定した属性に対しておこなう。LDAPでは管理している情報すべてに識別名を持っておりその情報にはそれぞれ属性値を持つ。
olcDatabase={0}config,cn=configがLDAPサーバの管理者パスワードを保持する場所でありolcRootPWがそのパスワード値である。
パスワードはソルト付きSHA(SSHA)で生成している。ソルト付きだと同一のパスワードでも生成するSHA値は毎回異なる。ちなみに上記のパスワードはhogeだが下記のようにパスワードを生成しても結果が異なるだろう。
# slappasswd -s hoge
次に管理者情報の登録をおこなう。便宜上以下のように定義する。
- ベースDN:dc=test,dc=example,dc=jp
- 管理者DN:cn=admin,dc=test,dc=example,dc=jp
- 管理者パスワード:{SSHA}o2Ro2qTviCe5j/DN0Tpu8wFnwDZ6EUbh ※hoge
DN(Distinguished Name)は識別名のことでベースDNとはLDAPサーバに管理させたい情報の先頭識別名のことである。LDAPサーバに管理させる情報はすべてベースDN配下に存在することになる。ディレクトリ構造でいえばrootのことである。
DNはドメイン名みたいなもので後ろに行くほど広域を示す。上記で言うとtest<example<jp。一般的にはベースDNは上記のように複数名をとるらしい(dc=jpのみがベースDNというのは稀)。ベースDNはdc(domain component)で定義する。
管理者DNはベースDN配下の情報を操作するための管理者を示す識別名のことである。ベースDNも管理者DNも初期設定されているためそれを利用環境に応じて変更する必要がある。一つのベースDNに対する管理者DNは一つのみである。
管理者パスワードはベースDN管理者(管理者DNで表す)のものである。ここでいう管理者パスワードとは先に設定したLDAPサーバの管理者のものではない。LDAPサーバ自体の管理者とベースDN配下の情報管理者が同一であるならば同値でも問題はない(通常はこうだろう)。
ベースDNや管理者DNを決定したら管理者情報の登録をおこなう。具体的には管理者DNに対するアクセス権の追加、ベースDNの切替、管理者アカウントのDN変更、管理者パスワードの追加をおこなう。
#cat modifyBase.ldif # 管理者DNのアクセス権を追加 dn: olcDatabase={1}monitor,cn=config changetype: modify replace: olcAccess olcAccess: {0}to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" read by dn.base="cn=admin,dc=test,dc=example,dc=jp" read by * none # ベースDNを置き換える dn: olcDatabase={2}hdb,cn=config changetype: modify replace: olcSuffix olcSuffix: dc=test,dc=example,dc=jp # 管理者アカウントのDN変更 dn: olcDatabase={2}hdb,cn=config changetype: modify replace: olcRootDN olcRootDN: cn=admin,dc=test,dc=example,dc=jp # 管理者パスワードを追加 dn: olcDatabase={2}hdb,cn=config changetype: modify add: olcRootPW olcRootPW: {SSHA}o2Ro2qTviCe5j/DN0Tpu8wFnwDZ6EUbh # ldapmodify -x -D cn=config -w hoge(※LDAP管理者のパスワード) -f modifyBase.ldif
olcDatabase={1}monitor,cn=configに対して属性olcAccessの変更でアクセス権の追加をおこなう。アクセス権はto アクセス先 by 対象 アクセス権の形を取る。dn.baseに管理者DNを指定する。gidNumber=0...はrootユーザのこと。デフォルトではLDAPサーバのrootユーザのみにアクセス権がある。
olcDatabase={2}hdb,cn=configに対して、属性olcSuffixの変更でベースDNの置き換えを、属性olcRootDNの変更で管理者アカウントのDN変更を、olcRootPWの追加で管理者パスワードの追加をおこなう。
最後にベースDN情報の登録をおこなう。この情報登録を行うことでLDAPによる情報管理が可能となる。
# cat baseDN.ldif dn: dc=test,dc=example,dc=jp objectClass: dcObject objectClass: organization dc: test # dnの先頭のものを指定 o: Testman Env. # 組織名(日本語も可) # ldapadd -x -D cn=admin,dc=test,dc=example,dc=jp(※管理者DNを指定) -w hoge(※管理者のパスワード) -f baseDN.ldif
情報の登録はobjectClassを指定して格納する情報を定め、objectClass毎に要求している属性をそれぞれ追加するという形をとる。
このobjectClassはスキーマで定義されている。/etc/openldap/schemaが格納場所。MUSTが必須属性でMAYが任意の属性となる。また属性自体もそれぞれ定義されている。
例えば上記で使用したdcObjectとorganizationは以下のような定義になっている。
olcAttributeTypes: ( 0.9.2342.19200300.100.1.25 NAME ( 'dc' 'domainComponent' ) DESC 'RFC1274/2247: domain component' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) olcObjectClasses: ( 1.3.6.1.4.1.1466.344 NAME 'dcObject' DESC 'RFC2247: domain component object' SUP top AUXILIARY MUST dc ) olcAttributeTypes: ( 2.5.4.10 NAME ( 'o' 'organizationName' ) DESC 'RFC2256: organization this object belongs to' SUP name ) olcObjectClasses: ( 2.5.6.4 NAME 'organization' DESC 'RFC2256: an organization' SUP top STRUCTURAL MUST o MAY ( userPassword $ searchGuide $ seeAlso $ businessCategory $ x121Address $ registeredAddress $ destinationIndicator $ preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $ telephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $ postalAddress $ physicalDeliveryOfficeName $ st $ l $ description ) )
まあ、organizationではoが必須なんだなぐらいがわかれば問題ない。