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

スポンサーリンク

ディレクトリサービスとは

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

具体例

具体的には次の 2 つが挙げられます。

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

利点

情報を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 について説明しています。

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 に記載されています。

Build software better, together
GitHub is where people build software. More than 83 million people use GitHub to discover, fork, and contribute to over 200 million projects.

作成した 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とnslcd+nscdの比較 | 外道父の匠

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 へのログイン権限を管理できます。