docker-composeでGUI管理できるLDAPサーバをサクッと立てる

# 概要
株式会社Link-Uでエンジニアを務めている堀内です。
Linuxサーバの認証統合を実現する手段として、LDAPを用いることが一般的です。
docker-composeを用いて、GUIで管理できるLDAPサーバの構築手順を解説します。

# 検証環境
- Ubuntu Server 20.04 LTS
- Docker 19.03.11 (snap版)
- 使用イメージ
    - [osixia/openldap](https://github.com/osixia/docker-openldap)
    - [ldapaccountmanager/lam](https://hub.docker.com/r/ldapaccountmanager/lam)
    - [jwilder/nginx-proxy](https://hub.docker.com/r/jwilder/nginx-proxy)

# サーバ構成
- LDAPデーモン: OpenLDAP
    - 管理用アカウント・読取専用アカウントを作成、匿名アクセスは不可
    - LDAPS (636/TCP) でのみ外部からアクセス可能にする
    - Linuxのユーザ認証向けの設定を行う
    - SSH公開鍵を格納できるようにする (sshPublicKey)
- LDAP管理ツール: Ldap Account Manager
    - LDAPのユーザ登録・編集用
    - HTTPSでアクセスを可能にする(リバースプロキシコンテナ経由)
- ホストとの共有ディレクトリ
    - ./data/certs ... SSL証明書
    - ./data/ldap_config ... OpenLDAP設定
    - ./data/ldap_db ... OpenLDAPデータベース
- 公開ポート
    - 80/tcp (HTTP) ... 管理ツール用、HTTPSに転送
    - 443/tcp (HTTPS) ... 管理ツール用
    - 636/tcp (LDAPS) ... LDAPサービス用

# docker-compose設定
## Dockerの導入

下記コマンドにてsnap版のdockerを導入します。

```console
$ sudo snap install docker
```

## docker-compose設定の記述
root権限に移行して、必要なディレクトリを準備します。

```console
$ sudo -s
# cd ~
# mkdir ldap
# cd ldap
# mkdir data
# mkdir data/certs
# mkdir data/ldap_config
# mkdir data/ldap_db
# vi docker-compose.yml
```

docker-compose.yml を記述します。
コメント文のある箇所は環境により適宜修正してください。

```yaml
version: '3.8'
services:
  slapd: # OpenLDAPサーバ
    image: osixia/openldap:1.4.0
    container_name: slapd
    restart: on-failure:3
    hostname: ldap.example.com                              # サーバのホスト名
    ports:
      - 636:636
    environment:
      LDAP_ORGANISATION: "Example Inc."                     # 組織名
      LDAP_DOMAIN: "example.com"                            # 組織のドメイン
      LDAP_ADMIN_PASSWORD: "admin_password"                 # 管理者パスワード
      LDAP_READONLY_USER: "true"                            # 読取専用ユーザ有効
      LDAP_READONLY_USER_USERNAME: "readonly"               # 読取専用ユーザ名
      LDAP_READONLY_USER_PASSWORD: "readonly_password"      # 読取専用ユーザパスワード
      LDAP_TLS_VERIFY_CLIENT: "never"                       # SSLクライアント認証の無効化
      # デフォルトでは自己署名証明書を自動生成、本番環境では下記コメントを外して証明書を指定
      # LDAP_TLS_CRT_FILENAME: "ldap.crt"    # SSL証明書
      # LDAP_TLS_KEY_FILENAME: "ldap.key"    # SSL秘密鍵
      # LDAP_TLS_CA_CRT_FILENAME: "ca.crt"   # クライアント認証用CA証明書、使用しなくても指定は必須
    volumes:
      - ./data/ldap_db:/var/lib/ldap
      - ./data/ldap_config:/etc/ldap/slapd.d
      - ./data/certs:/container/service/slapd/assets/certs
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
  lam:  # Ldap Account Manager (LDAP管理ツール）
    image: ldapaccountmanager/lam
    container_name: lam
    restart: on-failure:3
    environment:
      LDAP_DOMAIN: "example.com"                           # 組織のドメイン、OpenLDAPの設定に合わせる
      LDAP_BASE_DN: "dc=example,dc=com"                    # LDAPのルートDN、ドメインをDN形式で表記する
      LDAP_USERS_DN: "ou=people,dc=example,dc=com"         # ユーザOU、ドメイン部分だけ変更する
      LDAP_GROUPS_DN: "ou=groups,dc=example,dc=com"        # グループOU、ドメイン部分だけ変更する
      LDAP_SERVER: "ldap://slapd:389"
      LAM_LANG: "ja_JP"
      LAM_PASSWORD: "lam_password"                         # LdapAccountManagerの 初期設定用パスワード
      VIRTUAL_HOST: "lam"
      CERT_NAME: "ldap"                                    # SSL証明書名、.crt, .key のファイル名部分を指定。
    depends_on:
      - slapd
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
  proxy: # LDAP Account Manager 用のリバースプロキシ
    image: jwilder/nginx-proxy
    container_name: proxy
    restart: on-failure:3
    environment:
      DEFAULT_HOST: "lam"
    ports:
      - 80:80 
      - 443:443
    volumes:
      - ./data/certs:/etc/nginx/certs
      - /var/run/docker.sock:/tmp/docker.sock:ro
    depends_on:
      - slapd
      - lam
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
```

## サービスの起動
サービスを起動し、全コンテナが正常に動作していることを確認します。
初回起動時はリバースプロキシが証明書を読み込めていない場合があるため、手動でproxyコンテナの再起動をかけてください。

```console
# docker-compose up -d
# docker-compose ps 
Name               Command                  State                        Ports                  
------------------------------------------------------------------------------------------------
lam     /usr/bin/dumb-init -- /usr ...   Up (healthy)   80/tcp                                  
proxy   /app/docker-entrypoint.sh  ...   Up             0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp
slapd   /container/tool/run              Up             389/tcp, 0.0.0.0:636->636/tcp 
# docker-compose restart proxy
```

# LDAP Account Manager設定

## 初期設定

サービスを起動したサーバにブラウザでアクセスします。
正式な証明書を入れていない場合、下記のように警告が出ますが、無視して続行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912129/99611dec-317e-e653-1df9-9c7c15830f4a.png)

右上の "LAM構成設定" を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912129/867cba4e-1587-7c5c-469d-05c6fbfdbd5f.png)

"サーバープロファイルの編集" を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912129/c213ce79-fe98-c336-fed8-0ccd79c8645a.png)

`docker-compose.yml` で指定したパスワードでログインします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912129/1607ce04-43ee-4d13-016e-7b9514a15e1c.png)

タイムゾーンの設定を `Asia/Tokyo` に変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912129/0a8dbb0f-d9a3-0227-e558-2e11c78b0e0a.png)

"モジュール"タブを開き、ユーザのモジュールから "shadow"を削除、"SSH公開鍵"を追加して図のような状態にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912129/d42a87f7-01b7-3806-01ee-6c66a2d6a83a.png)

設定を保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912129/c85b4c6b-1d99-da83-112c-bc027dd70154.png)

## LDAPユーザ追加
初期設定の完了後、LDAPの管理者ユーザのパスワードを入力してログインします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912129/c97e779e-6790-936e-dfdd-a1d63bf95459.png)

"作成" を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912129/3bcd834c-e95c-e7a7-5c53-c165ed5e0493.png)

テスト用ユーザのプライマリグループを追加します。"グループ"タブの"新しいグループ"を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912129/fff0109b-e403-f909-ce8e-0a7dcaedcd09.png)

グループ名を入力して保存します。ここでは"testuser"を作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912129/c2b66a06-ca16-4028-e846-c464a01c90ec.png)

グループ追加の完了を確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912129/5c0f4158-40ec-2e83-30a5-f9641aaea840.png)

"ユーザ" タブに移動し、"新しいユーザ" を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912129/958f64dd-8d84-83fd-e6f7-b5497c5d51de.png)

"RDN識別子"は"uid"を選択、姓名を入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912129/367088dd-937f-1585-1c50-f33b7cc2da2c.png)

"unix"タブに移り、ユーザ名とcommon nameを入力します。プライマリグループとして先ほど登録したグループが表示されていることを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912129/da0b7b1b-ac95-81fa-b20d-25eae011a555.png)

"パスワード設定"を選択し、ログインパスワードを設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912129/43add2ed-6ab3-9a5c-3ccd-ecb4812cb4eb.png)

"SSH公開鍵"タブの"SSH公開鍵拡張を追加" を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912129/5cfbf781-6420-bcf8-2a44-ac72f89063f5.png)

SSH公開鍵を入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912129/feb7cda9-f144-260e-2404-0c0701e108ee.png)

設定を保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912129/0cbfcec3-77aa-f115-8334-68997f0d701c.png)

# 動作確認
Apache Directory Studio 等のLDAPブラウザでLDAPサーバに接続し、作成したユーザとグループの情報が読み取れることを確認します。
接続の際の認証情報は下記のように設定します。

プロトコル: `LDAPS`
ポート: `636`
ユーザDN: `cn=admin,dc=example,dc=com` ※cn=admin以降は組織のドメインに合わせて変更
パスワード: (docker-compose.ymlで設定した管理者パスワード)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912129/2729a5e7-b3bf-6f78-1acd-78f2e1a8bdf3.png)

# まとめ
docker-composeを用いたLDAPサーバの構築方法を解説しました。
LinuxサーバにSSSDを導入して本LDAPサーバと連携させれば、
パスワード認証 or SSH鍵認証を行ってログインすることが可能になります。