debian8のベースイメージをベースにopenLDAPコンテナを構築する手順をまとめました。

openLDAP(オープン・エルダップ)とは

LDAP(Lightweight Directory Access Protocol, エルダップ)

  • ディレクトリデータベース(アカウント管理に特化した専用データベース)へアクセスするためのプロトコル


LDAPとは何をするもの? | Think IT(シンクイット)

openLDAP(オープン・エルダップ)

  • オープンソースのLDAP(Lightweight Directory Access Protocol)サーバ
  • SSO(シングルサインオン)の認証基盤となり、redmineやGitLabなどのサービスを一つのアカウントでまとめて管理することができます。
  • ライセンス:OpenLDAP Public License(独自BSDライセンス)

OpenLDAP, Public License for 2.4.45

DockerでopenLDAPコンテナを作成する

ディレクトリ構成

.
├── Dockerfile
├── config
│   └── custom_inetorgperson.ldif
├── docker-compose.yml
└── entrypoint.sh

custom_inetorgperson.ldif

独自スキーマを定義したLDAP設定ファイル

entrypoint.sh

パッケージをインストールするときにコマンド入力が必要となるインタラクティブな項目(管理者パスワード等)を自動設定するためのラッパー・スクリプト

DockerFile

FROM debian:8
ENV DEBIAN_FRONTEND noninteractive
RUN apt-get update && apt-get install -y \
        slapd=2.4.40+dfsg-1+deb8u3 \
        ldap-utils=2.4.40+dfsg-1+deb8u3 && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*
COPY ./config/custom_inetorgperson.ldif /etc/ldap/schema/
COPY ./entrypoint.sh /
ENTRYPOINT ["/entrypoint.sh"]

ENV DEBIAN_FRONTEND noninteractive

ENV DEBIAN_FRONTENDはインストーラのユーザーインターフェースを制御する環境変数です。slapdパッケージはインストールの際にインタラクティブな回答が必要となるため、
DEBIAN_FRONTEND=noninteractiveと設定することで、インタラクティブな設定をしなくなり(=入力待ちでブロックしなくなる)、自動インストールができます。

5.3. ブートパラメータ
DEBIAN_FRONTEND=noninteractive ってなんだ - Qiita

RUN apt-get update && apt-get install

apt-getの記述はDocker公式ドキュメントのベストプラクティスで推奨された記法に従います。

RUN apt-get update && apt-get install -y を使うことで、最新バージョンのパッケージを、追加の記述や手動作業なく利用できます。

バージョンを指定すると、何がキャッシュされているか気にせずに、特定バージョンを取得した上での構築を強制します。このテクニックは、必要なパッケージの予期しない変更によって引き起こされる失敗を減らします。

apt キャッシュをクリーンにし、 /var/lib/apt/lits を削除することで、イメージのサイズを減らします。 RUN 命令は apt-get update から開始されるので、 apt-get install でインストールされるパッケージは、常に新鮮なものです。

Dockerfile のベストプラクティス — Docker-docs-ja 1.9.0b ドキュメント

slapd

OpenLDAPサーバのパッケージ。
Debian -- stretch の slapd パッケージに関する詳細

ldap-utils

ローカルまたはリモートの LDAP サーバにアクセスできます。LDAP サーバへのアクセスに必要なクライアントプログラムのすべてが含まれます。

Debian -- jessie の ldap-utils パッケージに関する詳細

entrypoint.sh


echo "slapd slapd/internal/adminpw password ${LDAP_ADMIN_PASSWORD}" | debconf-set-selections
echo "slapd slapd/internal/generated_adminpw password ${LDAP_ADMIN_PASSWORD}" | debconf-set-selections
echo "slapd slapd/password1 password ${LDAP_ADMIN_PASSWORD}" | debconf-set-selections
echo "slapd slapd/password2 password ${LDAP_ADMIN_PASSWORD}" | debconf-set-selections
echo "slapd slapd/domain string ${LDAP_DOMAIN}" | debconf-set-selections
echo "slapd shared/organization string ${LDAP_ORGANISATION}" | debconf-set-selections
dpkg-reconfigure -f noninteractive slapd
service slapd start

#add custom schema
ldapadd -Y EXTERNAL -H ldapi:// -f /etc/ldap/schema/custom_inetorgperson.ldif

/bin/bash

entrypoint.shに実行権限を与える

$ chmod +x entrypoint.sh 
$ ls -alt entrypoint.sh 
-rwxrwxr-x. 1 centos centos 710  7月 30 08:36 entrypoint.sh
debconf-set-selections
  • debconf-set-selectionsはパッケージをインストールするときにインタラクティブに設定する項目を事前に設定するコマンドです。
  • debconf-set-selectionsはdebconfパッケージに含まれており、 debconfパッケージはDebianのデフォルトでインストールされています
# dpkg -S /usr/bin/debconf-set-selections
debconf: /usr/bin/debconf-set-selections

docker-compose.yml

version: '3'
services:
  web:
    build: .
    tty: true
    container_name: openldap
    environment:
      - LDAP_ADMIN_PASSWORD=admin
      - LDAP_DOMAIN=example.org
      - LDAP_ORGANISATION=Example Inc.
    networks:
      - ldap_nw
    ports:
      - "389:389"
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    volumes:
      - '/srv/ldap:/var/lib/ldap'
networks:
  ldap_nw:
    external: true

tty

コンテナ内の標準入出力をDockerホストのコンソールにアタッチするための設定です。

logging

ログファイルのサイズと世代管理を指定しないと、ログが溜まりつづけてホスト側のディスクを圧迫する恐れがあるので必ず設定します。

Compose file version 3 reference | Docker Documentation
docker logs で表示されるログの保存場所とローテート方法 - Qiita

networks

以下のコマンドを入力し、OpenLDAPコンテナと連携するその他のコンテナ(phpLDAPadmin, GitLab, redmineなど)をUNIXソケットで通信できるようにするためのコンテナ・ネットワーク(ブリッジ)を作成します。

network create — Docker-docs-ja 1.13.RC ドキュメント

Docker公式によるコマンドラインリファレンス(日本語版)

$ docker network create --driver bridge ldap_nw

コンテナ・ネットワークを作成することで、同一ネットワーク上のコンテナに コンテナ名でアクセスすることができる ようになります。 ネットワーク名は任意ですが、ここではldap_nwとしました。

作成したコンテナ・ネットワークは以下のコマンドで確認できます。

$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
45d08214f6c2        ldap_nw             bridge              local

Compose のネットワーク機能 — Docker-docs-ja 1.13.RC ドキュメント
docker-compose で別の docker-compose.yml で作ったコンテナとリンクする (ネットワークを繋げる) - Qiita

以下のコマンドを入力し、コンテナを生成します。

$ docker-compose up -d

以下のコマンドを入力し、コンテナが起動しているか確認します。

$ docker ps
CONTAINER ID        IMAGE                     COMMAND                  CREATED             STATUS                  PORTS                                                           NAMES
375e6ed11be8        openldap_web              "/entrypoint.sh"         44 minutes ago      Up 44 minutes           0.0.0.0:389->389/tcp                                            openldap

動作確認をする

openldapコンテナにログインします。

$ docker exec -it openldap bash

データベースの中身を確認します。


# slapcat
dn: dc=example,dc=org
objectClass: top
objectClass: dcObject
objectClass: organization
o: Example Inc.
dc: example
structuralObjectClass: organization
entryUUID: 04a6e1de-0918-1037-9448-af08113323d1
creatorsName: cn=admin,dc=example,dc=org
createTimestamp: 20170730021035Z
entryCSN: 20170730021035.599610Z#000000#000#000000
modifiersName: cn=admin,dc=example,dc=org
modifyTimestamp: 20170730021035Z

dn: cn=admin,dc=example,dc=org
objectClass: simpleSecurityObject
objectClass: organizationalRole
cn: admin
description: LDAP administrator
userPassword:: e1NTSEF9K1JCRG1NT0Y3UGEyNWdPZ25hQVR2N3ZmVy9mU0pwN3c=
structuralObjectClass: organizationalRole
entryUUID: 04a8fb72-0918-1037-9449-af08113323d1
creatorsName: cn=admin,dc=example,dc=org
createTimestamp: 20170730021035Z
entryCSN: 20170730021035.613439Z#000000#000#000000
modifiersName: cn=admin,dc=example,dc=org
modifyTimestamp: 20170730021035Z
# ldapsearch -x -D "cn=admin,dc=example,dc=org" -w "admin"
# extended LDIF
#
# LDAPv3
# base <> (default) with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#

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

# numResponses: 1
  • -x SASL の代わりに簡易認証を使う
  • -D LDAP ディレクトリにバインドする識別名 binddn を指定
  • -w 簡易認証のためのパスワードを指定

スキーマの確認


# ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=config dn
dn: cn=config

dn: cn=module{0},cn=config

dn: cn=schema,cn=config

dn: cn={0}core,cn=schema,cn=config

dn: cn={1}cosine,cn=schema,cn=config

dn: cn={2}nis,cn=schema,cn=config

dn: cn={3}inetorgperson,cn=schema,cn=config

dn: cn={4}custom_inetorgperson,cn=schema,cn=config

dn: olcBackend={0}mdb,cn=config

dn: olcDatabase={-1}frontend,cn=config

dn: olcDatabase={0}config,cn=config

dn: olcDatabase={1}mdb,cn=config

付録:Dockerで使用した設定ファイルについての補足

entrypoint.sh

entrypoint.shでdebconf-set-selectionsを使用して設定したslapd slapd/internal/adminpw passwordなどの項目ですが、ウェブで検索してもなかなかヒットしません。設定値を調べるにあたって、以下のコマンドを使用しました。

debconf-get-selections

debconf-get-selectionsはパッケージをインストールするときにインタラクティブに設定する項目を調べるコマンドです。debconf-get-selectionsはdebconf-utilsパッケージに含まれていますが、 debconf-utilsパッケージはDebianのデフォルトではインストールされていない ため、別途インストールする必要があります。

# dpkg -S /usr/bin/debconf-get-selections
debconf-utils: /usr/bin/debconf-get-selections

debconf-get-selectionsコマンドを使うことで、以下のように
slapdをインストールするときにインタラクティブに設定できる項目を調べることができます。今回作成したentrypoint.shではopenLDAPを構築する上で最低限必要となる下記の赤字の項目を設定しました。


# debconf-get-selections | grep slapd
slapd slapd/internal/generated_adminpw  password
slapd slapd/password1 password
slapd slapd/internal/adminpw  password
slapd slapd/password2 password
# Do you want the database to be removed when slapd is purged?
slapd slapd/purge_database  boolean false
slapd slapd/backend select  MDB
slapd slapd/no_configuration  boolean false
slapd slapd/dump_database_destdir string  /var/backups/slapd-VERSION
slapd slapd/dump_database select  when needed
slapd slapd/upgrade_slapcat_failure error 
slapd slapd/domain  string  nodomain
slapd slapd/allow_ldap_v2 boolean false
slapd shared/organization string  nodomain
slapd slapd/invalid_config  boolean true
slapd slapd/password_mismatch note  
# Potentially unsafe slapd access control configuration
slapd slapd/unsafe_selfwrite_acl  note  
slapd slapd/move_old_database boolean true


Debconf-utilsを使ってインストール時の質問に自動で答える - Qiita

custom_inetorgperson.ldif

デフォルトのinetOrgPersonクラスではsn(Sir Name:姓)cn(Common Name:名)のみが必須入力項目に定義されています。そこでinetOrgPersonクラスで既に定義されているuserPasswordmailを新たに必須入力項目とするLDAP設定ファイル(ldifファイル)を作成します。

LDIFとは LDIF(LDAP Data Interchange Format)は、ディレクトリのエントリをテキスト形式で記述するための標準の書式で、RFC2849で定義されています。 リポジトリにエントリを追加したり、リポジトリ内のエントリ情報を変更したりするために使用します。

4.2.2.1 LDIFとは

スキーマ定義を作成する

任意のディレクトリにcustom_inetorgperson.schemaを作成します。ここではinetOrgPersonを継承し、userPasswordとmailを新たに入力が必須な項目として設定しています。

/etc/ldap/schema/custom_inetorgperson.schema

objectclass     ( 1.1.2.2.2
    NAME 'customInetOrgPerson'
        DESC 'Custom Internet Organizational Person'
    SUP ( inetOrgPerson )
    STRUCTURAL
        MUST (
                givenName $ userPassword $ mail )

任意のディレクトリにinclude.confを作成します。

/etc/ldap/include.conf

include /etc/ldap/schema/core.schema
include /etc/ldap/schema/cosine.schema
include /etc/ldap/schema/nis.schema
include /etc/ldap/schema/inetorgperson.schema
include /etc/ldap/schema/custom_inetorgperson.schema

以下のコマンドを入力し、ldifファイルを作成します。

etc/ldap# slaptest -f /etc/ldap/include.conf -F /etc/ldap/slapd.d
config file testing succeeded

成功していれば、以下のように新たにldifファイルが作成されているはずです。


etc/ldap/slapd.d# tree
.
|-- cn=config
|   |-- cn=module{0}.ldif
|   |-- cn=schema
|   |   |-- cn={0}core.ldif
|   |   |-- cn={1}cosine.ldif
|   |   |-- cn={2}nis.ldif
|   |   |-- cn={3}inetorgperson.ldif
|   |   `-- cn={4}custom_inetorgperson.ldif
|   |-- cn=schema.ldif
|   |-- olcBackend={0}mdb.ldif
|   |-- olcDatabase={-1}frontend.ldif
|   |-- olcDatabase={0}config.ldif
|   `-- olcDatabase={1}mdb.ldif
`-- cn=config.ldif

cn={4}custom_inetorgperson.ldifを以下のように書き換えます。

cn={4}custom_inetorgperson.ldif(修正後)

# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
# CRC32 29881081
dn: cn=custom_inetorgperson,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: custom_inetorgperson
olcObjectClasses: {0}( 1.1.2.2.2 NAME 'customInetOrgPerson' DESC 'Custom Int
 ernet Organizational Person' SUP inetOrgPerson STRUCTURAL MUST ( givenName
 $ userPassword $ mail ) )

cn={4}custom_inetorgperson.ldif.origin(修正前)とcn={4}custom_inetorgperson.ldif(修正後)の差分

# diff -u cn\=\{4\}custom_inetorgperson.ldif.origin cn\=\{4\}custom_inetorgperson.ldif
--- cn={4}custom_inetorgperson.ldif.origin  2017-07-30 10:08:05.024987637 +0000
+++ cn={4}custom_inetorgperson.ldif 2017-07-30 10:10:16.848343049 +0000
@@ -1,15 +1,8 @@
 # AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.
 # CRC32 29881081
-dn: cn={4}custom_inetorgperson
+dn: cn=custom_inetorgperson,cn=schema,cn=config
 objectClass: olcSchemaConfig
-cn: {4}custom_inetorgperson
+cn: custom_inetorgperson
 olcObjectClasses: {0}( 1.1.2.2.2 NAME 'customInetOrgPerson' DESC 'Custom Int
  ernet Organizational Person' SUP inetOrgPerson STRUCTURAL MUST ( givenName 
  $ userPassword $ mail ) )
-structuralObjectClass: olcSchemaConfig
-entryUUID: 8a096328-095a-1037-9504-4b6d37bb3025
-creatorsName: cn=config
-createTimestamp: 20170730100646Z
-entryCSN: 20170730100646.165408Z#000000#000#000000
-modifiersName: cn=config
-modifyTimestamp: 20170730100646Z

上記の手順で作成したcn={4}custom_inetorgperson.ldifをcustom_inetorgperson.ldifとしてDockerでコンテナを作成する際に使用しました。

参考文献