Ubuntu 18.04、20.04におけるLDAP認証の考慮点

# はじめに

> 【2021/07/05追記】下記手順は、Ubuntu 20.04 LTSでも適用可能です。

認証基盤としてLDAPサーバーを利用しています。LDAPサーバーは特定のポリシーを強制しない柔軟性に富んだ構成を取る事ができ、Kerberosを継承するMicrosoft社のActive Directory(AD)サーバーで利用されるLDAPと、伝統的なUNIX系システムで利用されるPosix系Schemaを利用するLDAPでは、使い勝手にかなりの差が存在します。

所属組織が運用するメインのLDAPサーバーのDirectoryに加えて、OpenLDAPのMetaモジュールを利用して、自前LDAPサーバーのディレクトリのグループと基幹LDAPサーバーのユーザー情報を透過的に連携させて、メーリングリストの登録情報を活用したグループ認証を独自に追加したディレクトリをUNIX Groupの代わりに利用できるようにしています。

この自前のLDAPサーバーを利用して、Ubuntuクライアントで特定のグループに対するユーザー認証を行なおうとした際に、遭遇した現象についてまとめておきます。

# 環境

* k8s環境下にデプロイされたOpenLDAPサーバー (詳細は後日追記予定)
* クライアント: Ubuntu 18.04 LTS (64bit) on Thinkpad x230, x220, etc.

## OpenLDAPサーバーの構成

                                                 +---------+
                                            +--->| 基幹LDAP |
        +-----------+   +----------------+  |    +---------+
        | GW Server |   | OpenLDAP(meta) |--+    +----------------+
    --->| Port:636  |-->| Port:389       |------>| OpenLdap (mdb) |
        +-----------+   +----------------+       | ou=MailGroup   |
                                                 +----------------+

基本的なou=person,ou=groupは基幹LDAPの情報を利用し、ou=MailGroupではメーリングリストのユーザー情報を独自に登録しています。
Gateway(GW) Serverはstunnel4を利用して、外部に公開しているPort:636へのTLS接続を内部(k8s)のPort:389に振り分けています。

もし内部ネットワークが信用できない場合には、内部ネットワーク内部も自前CAサーバーを準備してTLS化することをお勧めします。

stunnel4の設定ファイルは次のようになっています。

```/etc/stunnel/ldap.conf
cert = /etc/ssl/certs/openldap.example.com.cer
key = /etc/ssl/private/openldap.example.com.nopass.key
CAfile = /etc/ssl/certs/our_ca.cer
sslVersion = TLSv1.2
chroot = /var/run/stunnel4
setuid = stunnel4
setgid = stunnel4
pid = /stunnel.pid
socket = l:TCP_NODELAY=1
socket = r:TCP_NODELAY=1

[openldap]
accept = 636
connect = 10.1.200.159:389
TIMEOUTclose = 0
```

## Ubuntuクライアントでの /etc/ldap.conf の配置

OpenLDAPサーバーとの接続は次のように設定されています。

```bash
$ grep -v '^#' /etc/ldap.conf | sort | uniq

base ou=proxy,dc=example,dc=com
bind_policy soft
bind_timelimit 10
idle_timelimit 300
ldap_version 3
pam_password crypt
timelimit 10
uri ldaps://openldap.example.com/
```

ここでのポイントは、次のとおりです。

* bind_policyをsoftに変更している点
* uriがldapsで始まりTLS接続を利用している点

## クライアントの/etc/nsswitch.confの設定

```bash
$ grep -v '^#' /etc/nsswitch.conf | grep -v '^$'

passwd: compat ldap
group: compat ldap
shadow: compat ldap
gshadow:        files
hosts:          files mdns4_minimal [NOTFOUND=return] dns myhostname
networks:       files
protocols:      db files
services:       db files
ethers:         db files
rpc:            db files
netgroup:       nis
```

デフォルトのままにしていますが、/etc/passwd等によるローカル認証とLDAP認証のみを行なうのであれば、compatである必要はなく、"files ldap"で十分です。

## ansibleなどでバッチ的にlibpam-ldapパッケージを導入していてdebconfが実行されない場合の対処

手動でパッケージを導入し、debconfによって初期設定が行なわれる場合は問題ありません。
自動でパッケージを導入している場合は、/etc/pam.d/ 以下のファイルが上書きされない状況になってしまいます。

pam-auth-updateコマンドを手動で叩いても良いですが、ansibleを使っている私の環境では、各ファイルに次のような設定を入れています。
元設定は、pam-auth-updateコマンドを実行しているホストからコピーしています。

```bash
$ grep pam_ldap.so /etc/pam.d/*
/etc/pam.d/common-account:account       [success=1 default=ignore]      pam_ldap.so 
/etc/pam.d/common-auth:auth     [success=1 default=ignore]      pam_ldap.so use_first_pass
/etc/pam.d/common-password:password     [success=1 user_unknown=ignore default=die]     pam_ldap.so use_authtok try_first_pass
/etc/pam.d/common-session:session       optional                        pam_ldap.so 
/etc/pam.d/common-session-noninteractive:session        optional                        pam_ldap.so 
```

# 様々な問題の発生

発生した現象について、まとめていきます。

## 時々ログインできなくなるなど、ユーザー認証周りで問題が発生する

"getent passwd"等によるLDAP情報の参照はできるものの、lightdm等からのログインができなくなる、ログインには成功するもののログアウト等の操作が途中できなくなる、といった現象が発生しました。

OpenLDAPサーバーのメンテナンスなどにより一時的に接続が切れる事が影響しているようでした。
bind_policyをsoftに切り替えたことで、問題は解決しています。

この他、k8s特有の事象ですが、OpenLDAPサーバー(Master)に対してSlaveサーバーを複数準備することで負荷分散に対応しているため、Slave側が正常な応答をしなくなった際に、数回に1回ログインできないといった現象にも遭遇しました。これはk8s側でlivenessProbeを設定することで解決しています。

## /var/log/auth.log に Server is unavailable メッセージが表示される場合

デフォルトの構成では libnss-ldap パッケージが導入されていますが、Debian Wikiによれば、TLSを有効にしたLDAPサーバーではsetuidプログラムが動作しないとされています。

* https://wiki.debian.org/LDAP/NSS

このためTLSを有効にしている環境では、次のようなエラーメッセージが表示されていました。

```text
Jun 26 14:49:10 ubuntu sshd[12422]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=192.168.1.1  user=user01
Jun 26 14:49:10 ubuntu sshd[12422]: Accepted password for user01 from 192.168.1.1 port 38846 ssh2
Jun 26 14:49:10 ubuntu sshd[12422]: pam_unix(sshd:session): session opened for user user01 by (uid=0)
Jun 26 14:49:10 ubuntu systemd-logind: nss_ldap: could not connect to any LDAP server as (null) - Can't contact LDAP server
Jun 26 14:49:10 ubuntu systemd-logind: nss_ldap: failed to bind to LDAP server ldaps://openldap.example.com/: Can't contact LDAP server
Jun 26 14:49:10 ubuntu systemd-logind: nss_ldap: could not search LDAP server - Server is unavailable
Jun 26 14:49:10 ubuntu sshd[12422]: pam_systemd(sshd:session): Failed to create session: No such file or directory
```

解決策としては、別のLDAPモジュール *libnss-ldapd* を導入することで解決しました。

## Translucentモジュールによる基幹LDAPサーバー情報へのOverlay

単純にMetaモジュールで、ou=person,ou=groupの情報だけを基幹LDAPに問い合せしていますが、これだとou=personに電話番号やオフィスアドレスといった追加の情報を登録するといった活用ができません。

外部LDAPサーバーのディレクトリ情報に任意の情報を加えるtranslucentモジュールがOpenLDAPには準備されていますが、これを利用した際には、どういう分けかディレクトリへのアクセスが安定しなくなりました。

原因の特定には至っていませんが、現状ではtranslucentモジュールを利用していない状態で安定しているため外しています。

現状でもApacheのBASIC認証でのグループ認証などでは問題なく利用できています。

## nslcdがエラーを出力している

設定は全て終えているはずなのに、/var/log/syslogを確認すると、nslcdがエラーを出している場合があります。

```bash:ldapに登録されているユーザー情報の検索を試す（user01は実際に存在するID名に変更すること）
$ id user01
```

```text:/var/log/syslogから抜粋
Jun  9 01:36:47 ubuntu nslcd[6754]: [8b4567] <authz="user01"> failed to bind to LDAP server ldap://127.0.0.1/: Can't contact LDAP server: Transport endpoint is not connected
```

通常は/etc/ldap.confの内容をコピーし、正しく設定されるはずですが、導入のタイミングなどによっては/etc/nslcd.confの中で、**uri**、**base**が正しく設定されていない可能性があります。

```Makefile:Makefileに追記したタスク
setup-nslcd:
        ansible all -b -m lineinfile -a 'path=/etc/nslcd.conf regexp="^uri " line="uri ldaps://openldap.example.com/"'
        ansible all -b -m lineinfile -a 'path=/etc/nslcd.conf regexp="^base " line="base ou=proxy,dc=example,dc=com"'
```

nslcdパッケージのremove/installを試みても良いと思いますが、どちらか適当な方法で対応してください。

以上