WordPressのdocker imageをアップデートした時に遭遇したLDAPのトラブルと対処方法

こんにちは、システムエンジニアの kouki です。

この記事では WordPress のアップデートをした時に遭遇した LDAP のトラブルとその対処法について紹介します。今回は端的にまとめていますので、調査経緯に興味がある方は気づくまでに至った「調査ログ」も見ていただけると幸いです。

トラブルに遭遇した環境

上記のプラグインに限定されず、PHP の LDAP extension を利用しているコードが存在するならば今回のトラブルに遭遇するはずです。

経緯

  • bitnami/wordpress docker image を 5.9.3 から 6.0.0 にアップデートした
  • アップデートを行った時に、WordPress から OpenLDAP に接続できない事象が発症した
    • ログには Can't contact LDAP server というログが出力されていた

原因と対処

bitnami/wordpress の 5.9.3 では存在していた /etc/ldap/ldap.conf というファイルが 6.0.0 では削除されていました。そのため、PHP LDAP extension において「TLS_CACERT」のファイルパスが見つからず、証明書の検証に失敗し、TLS 接続時にエラーが出ていることが分かりました。

対処として wordpress container 起動時に環境変数として TLS_CACERT=/etc/ssl/certs/ca-certificates.crt を指定することで PHP LDAP extension が LDAP サーバに接続できるようになりました。

2022/09/08 時点の 5.9.3 image でも /etc/ldap/ldap.conf が存在しないようです。(社内の bitnami/wordpress:5.9.3 をカスタムしたイメージには含まれていました) 5.9.2 のイメージでは /etc/ldap/ldap.conf があることが確認できますので、確認してみたい方は 5.9.2 でお試しいただけるかと思います。

調査ログ

ここからはデバッグを行った調査ログを残しておきます。

LDAP サーバに openssl コマンドで接続するも、問題無し (ドメインは example.com で置換済)

# echo | openssl s_client -connect example.com:636 | openssl x509 -noout -text
depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = R3
verify return:1
depth=0 CN = *.example.com
verify return:1
DONE
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = US, O = Let's Encrypt, CN = R3
        Validity
            Not Before: May 31 14:55:53 2022 GMT
            Not After : Aug 29 14:55:52 2022 GMT
        Subject: CN = *.example.com

WordPress plugin から通信があった際の LDAP サーバ側のログは下記の通り

62cfd66c conn=3662245 fd=26 ACCEPT from IP=XXX.XXX.XXX.XXX:33692 (IP=0.0.0.0:636)
62cfd66c conn=3662245 fd=26 TLS established tls_ssf=256 ssf=256
62cfd66c conn=3662245 fd=26 closed (connection lost)

connection lost というログしか出ないため、「接続時に何らかのトラブルが起きているんだろう」ということしかわからず。

ここで一旦調査に行き詰まる。

LDAP Login for Intranet Sites plugin のソースコードを改変して、デバッグ作業の開始

/opt/bitnami/wordpress/wp-content/plugins/ldap-login-for-intranet-sites/class-mo-ldap-config.php (GitHub へのリンク) のファイルに下記のコードを仕込み、container のログを確認する

# stdout に変数の内容を出力するコード ($err, $error_no は ldap_bind 関数から取得したもの
$fp = fopen('php://stdout', 'w');
fwrite($fp, $err . "\n");
fwrite($fp, $error_no . "\n");
fclose($fp);

ldap_bind 関数にて Can't contact LDAP server というメッセージが出力されていることを確認

再度調査が行き詰まる

その後、ldap_set_option という関数があることが分かり、そのコードを仕込む

ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7);

コード適用後、下記のようなログが出力される

ldap_ndelay_off: 12
ldap_pvt_connect: 0
TLS: peer cert untrusted or revoked (0x42)
TLS: can't connect: (unknown error code).

「証明書周りで問題がありそうだ」というところまで特定

putenv('LDAPTLS_REQCERT=never') というコードを plugin に書き加えて、接続ができることを確認 (あくまで仮の対処法)

しかし、LDAPTLS_REQCERT=never という workaround は筋が悪い。セキュリティ的にも問題がある

その後、調査を進めると ssl - Can not connect to server via ldaps using Let's Encrypt certificates - Stack Overflow という記事を他のメンバーが発見

bitnami/wordpress 5.9.3 時点では /etc/ldap/ldap.conf ファイルが存在し、6.0.0 では存在しないことが分かる

### 5.9.3 image
$ docker run -it --rm bitnami/wordpress:5.9.3 --entrypoint /bin/bash -c 'cat /etc/ldap/ldap.conf'
#
# LDAP Defaults
#

# See ldap.conf(5) for details
# This file should be world readable but not world writable.

#BASE   dc=example,dc=com
#URI    ldap://ldap.example.com ldap://ldap-master.example.com:666

#SIZELIMIT      12
#TIMELIMIT      15
#DEREF          never

# TLS certificates (needed for GnuTLS)
TLS_CACERT      /etc/ssl/certs/ca-certificates.crt

### 6.0.0 image
$ docker run -it --rm --entrypoint /bin/bash bitnami/wordpress:6.0.0 -c 'cat /etc/ldap/ldap.conf'
cat: /etc/ldap/ldap.conf: No such file or directory

image 起動時の環境変数に TLS_CACERT=/etc/ssl/certs/ca-certificates.crt を指定

上記の LDAPTLS_REQCERT=never は削除し、TLS_CACERT にて対応完了

最後に

今回は他の docker image でも応用が利きそうなトラブルシューティングを紹介しました。

NextCloud なども PHP で作られているのでこの問題に該当しているならば、同じような対処を行うことで意図した挙動になると思います。