OpenLDAP で理解するディレクトリサービス入門

ディレクトリサービスディレクトリサービスとは、ディレクトリサービスとは、コンピュータネットワーク上に存在するリソースの情報を収集・記録し、検索できるようにしたサービスです。

ディレクトリサービスの具体例は以下の2つです。

  • ActiveDirectory(Windows), OpenLDAP(Linux)
    • ユーザー名(リソース)から、パスワード(情報)を検索して認証を行います。
    • ユーザー名(リソース)ごとに権限(情報)も記録しており、検索することが可能です。
  • DNS サーバー
関連記事:サーバー

本記事は OpenLdap を利用して、ディレクトリサービスを解説します。

スポンサーリンク

ディレクトリサービスの利点

情報を1箇所に集約可能となります。

例として情報を 1 箇所に集約するメリットを3つ挙げます。

  • 情報の検索が楽!
    • 誰が情報を持っているか意識しなくて良い
    • とりあえずディレクトリサービスに聞けばいい 
  • 情報の修正が楽!
    • ディレクトリに集約されている場合は1箇所(ディレクトリ)だけの修正でよい
    • 100 台のコンピュータが情報を持っている場合、100箇所の修正が必要
  • 情報の保存が効率的!
    • ディレクトリにだけ情報を保存すれば良い。
    • 各コンピュータが全てのホスト名に対する IP アドレスを保存しておくのはディスク容量の無駄
スポンサーリンク

ディレクトリサービスを実現する技術

ディレクトリサービスのディレクトリは DIT(Directory Information Tree) と呼ばれる木構造で構成されています。具体的には下記の通りとなります。下記のディレクトリでは、hogetech_company の development 部署に所属する user01, user02 を表しています。

ディレクトリサービスにアクセスするには、LDAP プロトコルを使用します。
例えば ldap://directory_service.com/ のようにアクセスします。

ディレクトリサービスで使用する用語

ディレクトリサディレクトリサービスで使用される用語で特に重要なものについて説明します。

  • エントリー (オブジェクト): 木構造のノードのことです。
  • 属性: エントリーが持つ情報の種類と値です。
    • 属性は属性タイプと属性値をセットで持ちます。ようするにキーバリュー形式で情報を持ってます。
    • 属性タイプ説明
      dcドメイン構成要素 (Domain Component)
      cn一般名称 (Common Name)
      o組織名 (Organization)
      ou組織名 (Organization)
      o組織単位 (Organization Unit)
      uidユーザーID (User ID)
      gidNumberグループ ID 番号 (Group ID Number)
  • スキーマ (オブジェクトクラス): エントリーで使用する属性一覧を予め定義したものです。
    • 例えば account スキーマでは、uid(ユーザー名)が必須属性として定義されています。スキーマの一覧は下記に列挙されています。
  • A.2 オブジェクトクラス一覧
  • DN (Distinguish Name): エントリーを一意に識別する primary key です [2] 。
  • バインド DN (Login DN): ディレクトリを操作するするユーザーです。
    • ディレクトリの中に存在する「ユーザー」エントリーにバインドします。
  • ベース DN: 検索の起点となるエントリーです。
    • ベース DN の子ノードが検索可能です。親ノードや別のディレクトリは検索不可能です。
  • ルート DN: 根ノードのことです。

それぞれの用語と DIT の関係は下記の通りです。

スポンサーリンク

ディレクトリサービスの実装方法

オープンソースソフトウェアである OpenLDAP を使用してディレクトリサービスの具体的な実装方法を紹介します。OpenLDAP を利用するシナリオとしては、入社・退職する人に対して、社内の Linux へログイン権限を OpenLDAP で管理する状況を想定します。

OpenLDAP の起動

今回は環境に依存する問題を回避するために Docker を使用します。Docker について詳しく知りたい方は、以下の記事をご覧ください。

Docker を使用して OpenLDAP のコンテナを起動

まずは、OpenLDAP の docker コンテナを起動するために docker-compose.yml を作成します。

docker-compose.yml に記載する内容は下記の通りです。

version: '3'

services:
  ldap-server:
    image: osixia/openldap:latest
    restart: always
    container_name: ldap-host
    environment:
      LDAP_ORGANISATION: "hogetech_company"
      LDAP_DOMAIN: "hogetech.com"
      LDAP_ADMIN_PASSWORD: "hogetech" #cn=admin,dc=hogetech,dc=com のパスワード
      LDAP_CONFIG_PASSWORD: "hogetech" #cn=admin,cn=config のパスワード
      LDAP_TLS_VERIFY_CLIENT: "try"
    networks:
      - webnet
    ports:
      - "389:389"
      - "636:636"

  ldap-admin:
    image: osixia/phpldapadmin:latest
    restart: always
    container_name: ldap-admin
    environment:
      PHPLDAPADMIN_LDAP_HOSTS: "ldap"
      PHPLDAPADMIN_HTTPS: "false"
    ports:
      - "8080:80"
    links:
      - "ldap-server:ldap"
    networks:
      - webnet

networks:
  webnet:

docker-compose.yml で記載した環境変数の詳細は下記の URL に記載されています。

File not found · osixia/docker-openldap
OpenLDAP container image 🐳🌴. Contribute to osixia/docker-openldap development by creating an account on GitHub.

作成した docker-compose.yml を使用して、下記のコマンドで OpenLDAP の docker コンテナを起動します。

起動した OpenLDAP に存在するディレクトリの種類

起動 OpenLDAP は、次の 2 つのディレクトリを持っています。

  • ルート DN が cn=config であるディレクトリ
    • デフォルトで作成されるディレクトリです。Openldap の設定を格納しています。
    • 下記のように「モジュール」・「スキーマ」などが格納されています。
  • dn: cn=module{0},cn=config
    dn:cn=schema,cn=config
  • ルート DN が dc=com であるディレクトリ
    • 独自に作成したディレクトリです。今回は社員に関連する情報を格納していきます。
    • 起動した時点でのディレクトリ構造は下記の通りです。

なお、docker-compose.yml で同時に起動した LdapAdmin (GUIツール) を使っても、ディレクトリの構造を確認したり、操作できます。使用方法は下記のとおりとなります。

  • LdapAdmin へのアクセス URL は 「<サーバーのIPアドレス>:8080」
  • Login DNは「cn=admin,dc=hogetech,dc=com」
  • パスワードは「hogetech」

ディレクトリを構築

dc=com ディレクトリにユーザーを登録し、下記のようなディレクトリを構築します。

ディレクトリにユーザーを登録

OpenLDAP では、ディレクトリにエントリーを登録する際に LDIF ファイルを使用します。LDIF ファイルは下記の 3 つの要素で構成されます。

要素LDIF での書き方
操作するDNdn: から記載を初めます
オブジェクトクラス:objectClass:から記載を初めます
属性<属性タイプ>:から記載を初めます

オブジェクトクラスや属性は複数指定することができます。今回作成する LDIF ファイルは下記の通りです。

vim initAll.ldif
dn: ou=development,dc=hogetech,dc=com
objectClass: organizationalUnit
ou: development

dn: cn=Manager,ou=development,dc=hogetech,dc=com
objectClass: organizationalRole
cn: Manager

dn: ou=People,ou=development,dc=hogetech,dc=com
objectClass: organizationalUnit
ou: People

dn: ou=Group,ou=development,dc=hogetech,dc=com
ObjectClass: organizationalUnit
ou: Group

dn: cn=development,ou=Group,ou=development,dc=hogetech,dc=com
objectClass: posixGroup
cn: development
gidNumber: 3001

dn: uid=user01,ou=People,ou=development,dc=hogetech,dc=com
objectClass: account
objectClass: posixAccount
uid: user01
cn: Test User 01
userPassword: {SSHA}ABCDEFG*******
loginShell: /bin/bash
uidNumber: 3001
gidNumber: 3001
homeDirectory: /home/user01
ou: developer

dn: uid=user02,ou=People,ou=development,dc=hogetech,dc=com
objectClass: account
objectClass: posixAccount
uid: user02
cn: Test User 02
userPassword: {SSHA}ABCDEFG*******
loginShell: /bin/bash
uidNumber: 3002
gidNumber: 3001
homeDirectory: /home/user02
ou: developer

userPassword 属性タイプは、Linux へのログインパスワードを指定します。平文だと抵抗があるので、下記のコマンドで暗号化しておきましょう。この記事の例では、パスワードに "hogetech" を使用します。

slappasswd
New password:<ログインで使用するパスワードを入力>
Re-enter new password:<ログインで使用するパスワードを入力>
{SSHA}ABCDEFG*******

LDIF ファイルが完成したので、 ldapsearch コマンドを使用して、ディレクトリにエントリーを追加します。

ldapadd -D "cn=admin,dc=hogetech,dc=com" -W -f initAll.ldif

上記のコマンドはバインド DN ("cn=admin,dc=hogetech,dc=com")で initAll.ldif ファイルのエントリーをディレクトリに追加します。パスワードは docker-compose.yml で指定した "hogetech" です。

完成したディレクトリは下記のとおりです。

登録したユーザーをディレクトリから検索

ディレクトリにユーザーが登録できているか確認するために、ディレクトリを検索してみます。ディレクトリの検索には ldapsearch コマンドを使用します。

ldapsearch -LLL -D "cn=admin,dc=hogetech,dc=com" -W -b "uid=user01,ou=People,ou=development,dc=hogetech,dc=com"
dn: uid=user01,ou=People,ou=development,dc=hogetech,dc=com
objectClass: account
objectClass: posixAccount
uid: user01
cn: Test User 01
userPassword:: ******
loginShell: /bin/bash
uidNumber: 3001
gidNumber: 3001
homeDirectory: /home/user01
ou: development

user01 という社員を正しく登録できてることが確認できました。

ディレクトリのアクセス制御を変更

デフォルトのアクセス制御では、バインド DN ("cn=admin,dc=hogetech,dc=com") 以外で、他のエントリーを参照出来ません。アクセス制御は DN (olcDatabase={1}mdb,cn=config) の olcAccess 属性タイプを変更することで設定可能です。

olcAccess を変更する LDIF ファイルの書式は次のとおりです。

olcAccess: to attrs=<属性タイプ>
  by <アクセスを許可する対象> <アクセス許可レベル>

<アクセスを許可する対象> の一覧は次のとおりです。

設定対象
*すべて
anonymous匿名ユーザー
users認証したユーザー
selfユーザー自身
dn=<正規表現>正規表現に一致したユーザー

<アクセス許可レベル> の一覧は次のとおりです。

レベル説明
noneアクセス権無し
authバインド可能(ログインで使用可能)
compare比較可能
search検索フィルタリング実行可能
read検索結果参照可能
write更新可能
discloseエラー情報の開示可能

一例として、バインド DN (dn=uid=user01,ou=People,ou=development,dc=hogetech,dc=com) 『以降 バインド DN user01 と表記』で userPassword 属性タイプ以外の、全ての属性タイプを読み取れる olcAcsess を設定する LDIF ファイルを作成します。

vim access.ldif
dn: olcDatabase={1}mdb,cn=config
changetype: modify
replace: olcAccess
olcAccess: to attrs=userPassword
  by anonymous auth
  by dn=cn=admin,dc=hogetech,dc=com read
  by * none
olcAccess: to *
  by self read
  by dn=uid=user01,ou=People,ou=development,dc=hogetech,dc=com read
  by dn=cn=admin,dc=hogetech,dc=com read
  by * none

■アクセス制御を変更する前

ldapsearch -LLL -D "uid=user01,ou=People,ou=development,dc=hogetech,dc=com" -W -b "uid=user02,ou=People,ou=development,dc=hogetech,dc=com"
Enter LDAP Password:
No such object (32)

バインド DN user01 で user02 の情報を検索することができません。

次に、先程作成した access.ldif ファイルを cn=config ディレクトリに適用し、アクセス制御を変更します。(パスワードは"hogetech"です。)

ldapmodify -x -D "cn=admin,cn=config" -W -f access.ldif

■アクセス制御を変更した後

ldapsearch -LLL -D "uid=user01,ou=People,ou=development,dc=hogetech,dc=com" -W -b "uid=user02,ou=People,ou=development,dc=hogetech,dc=com"
Enter LDAP Password:
dn: uid=user02,ou=People,ou=development,dc=hogetech,dc=com
objectClass: account
objectClass: posixAccount
uid: user02
cn: Test User 02
loginShell: /bin/bash
uidNumber: 3002
gidNumber: 3001
homeDirectory: /home/user02
ou: development

バインド DN user01 で user02 の情報を検索できるようになりました。また、バインド DN user01 では userPassword 属性タイプが検索されないことも確認できます。

OpenLDAP を使用して Linux のログインを認証

Linux のログイン認証に OpenLDAP を利用するには、次の 2 つの認証システムのうち、どちらか 1 つを使用します。

  • nslcd (Name Service LDAP Connection Daemon)
  • sssd (System Security Services Daemon)

nslcd と sssd の比較は下記の記事が参考になります。

sssd のほうが良さげなので、今回は sssd を使用することにします。

Linux ログインの認証方法に sssd を使用するように設定

sssd を yum でインストールします。

yum install sssd sssd-client sssd-ldap

sssd の認証では、デフォルトで file(/etc/shadow) を使用して認証します。sssd の認証に OpenLDAP を使用するように、authconfig コマンドを実行します。

authconfig --update --enablesssd --enablesssdauth

authconfig で書き換えるファイルは次の 2 つです。うまく修正できてない場合は、手動で変更しても構いません。

  • /etc/nsswitch (ネームサービススイッチ)
    • パスワードの参照先を決定します。
      • デフォルトでは、パスワードの参照先に file(/etc/shadow)を使用しますが、sssd も利用するように設定します。
  • /etc/pam.d/system-auth PAM(Pluggable Authentication Module)
    • パスワードの認証方法を設定します。
      • デフォルトでは、認証ライブラリに pam_unix.so を使用しますが、pam_sss.so も利用するように設定します。

sssd で LDAP を利用して認証するように設定

sssd の設定ファイル /etc/sssd/sssd.conf を変更して認証に LDAP を利用するようにする。

vim /etc/sssd/sssd.conf

sssd.conf で下記の内容が不足している場合は追記してください。(デフォルトで記載されている内容は消さないでください。)

[sssd]
domains = LDAP

[domain/LDAP]
id_provider = ldap
auth_provider = ldap
ldap_schema = rfc2307
ldap_uri = ldap://127.0.0.1
ldap_search_base = dc=hogetech,dc=com
ldap_default_bind_dn = cn=admin,dc=hogetech,dc=com
ldap_tls_reqcert = never
ldap_default_authtok_type = obfuscated_password
ldap_default_authtok = ******

ldap_default_authtok はバインド DN のパスワードです。下記のコマンドを実行することで、難読化したパスワードが /etc/sssd/sssd.conf に自動で書き込まれます。

sudo sss_obfuscate -d LDAP
Enter password:
Re-enter password:

設定が完了したら、sssd サービスを起動します。

sudo systemctl start sssd

OpenLDAP を使用して user01 にログイン

user01 でログインする前に user01 のホームディレクトリを作成します。

sudo mkdir /home/user01

次のコマンドで user01 にログイン可能なことを確認します。パスワードは initAll.ldif の userPassword 属性で指定した値です。この記事のパスワードは "hogetech" です。

su - user01
パスワード:
最終ログイン: 2020/06/15 (月) 00:57:09 UTC日時 pts/1
-bash-4.2$

OpenLDAP に登録した user01 で Linux にログインできました!

このように、OpenLDAP に認証情報を集約することができました。これにより、社員の入社・退職に合わせて OpenLDAP のエントリーを追加・削除するだけで、Linux へのログイン権限を管理できます。

関連記事

関連記事:サーバー