2024年11月19日 星期二

Linux 開發筆記 - OpenLDAP StartTLS 憑證驗證與故障排除 @ Ubuntu 20.04


負責 DevOps 同事休假,當初 LDAP 架設沒涉入,用 AI 輔助來練一下功,盡量讓同事好好休假不用 oncall 解題,就把這過程筆記一下。

現在有 AI 輔助服務,做事的心態變了不少,包括:
  1. 面對不是自己從頭到尾參與的計劃時,該怎樣建立自己了解系統思維或是達成想做的任務
  2. AI 的答案也不一定是對的,該如何驗證(如同管理或跨團隊合作上,請夥伴做事,但不一定做對,該怎樣驗正)
事件起因是一些小型內部服務,因 LDAP 憑證失效而無法完成登入(已登入不受影響),我的追法:
  1. 請 AI 給我指令或程式,檢驗快速檢驗 ldap 連線過程,著重在憑證檢查,例如檢查 https 憑證最常就是靠 openssl 指令或是 browser 瀏覽檢視憑證
  2. 有時 AI 會回的很搞剛,開始回寫 code 的 python 解法,這時因為自己有經驗 openssl 指令就能搞定,所以不斷提醒 AI 直接給指令解
  3. 對於 OpenLDAP 不熟的我,開始追問 AI 憑證相關的方向,才順勢了解 StartTLS ,才知道有 ldapsearch, slaptest 指令可用
  4. 對於管理線上服務踩過雷,知道不能隨便 service restart,多問一下 AI 該怎樣先檢驗設定檔,避免機器從 running -> stop -> 設定有誤 -> 服務停止營運
使用 openssl 指令檢查 SSL 憑證,假設網域是 ldap-dmz.changyy.org

```
$ openssl s_client -connect ldap-dmz.changyy.org:443 
...
---
SSL handshake has read xxxx bytes and written xxx bytes
Verification: OK
---
...
```

使用 openssl 指令檢查 LDAP + StartTLS 憑證,假設網域是 ldap-dmz.changyy.org

```
$ openssl s_client -connect ldap-dmz.changyy.org:389 -starttls ldap
...
---
SSL handshake has read xxxx bytes and written xxx bytes
Verification error: certificate has expired
---
...
```

得知 LDAP + StartTLS 使用流程中的憑證過期了,且此例剛好 https 憑證已正常工作,僅 LDAP + StartTLS 的部分失敗,所以很輕鬆的可以把 https 憑證複製一份到 ldap 使用即可。先追蹤設定檔位置:

```
$ sudo cat /etc/ldap/slapd.d/cn\=config.ldif | grep -i TLS
olcTLSCACertificateFile: /etc/ssl/ldap/wildcard.*.ca-bundle
olcTLSCertificateFile: /etc/ssl/ldap/wildcard.*.crt
olcTLSCertificateKeyFile: /etc/ssl/ldap/wildcard.*.key
$ sudo tree -L 1 /etc/ssl/
/etc/ssl/
├── certs
├── ldap
├── nginx
├── openssl.cnf
└── private
```

速速解:

```
$ sudo cp -r /etc/ssl/ldap /etc/ssl/ldap-bak
$ sudo cp /etc/ssl/nginx/wildcard.* /etc/ssl/ldap/
```

檢驗設定檔和重啟服務:

```
$ sudo slaptest
config file testing succeeded
$ sudo service slapd restart
```

再次驗證:

```
$ openssl s_client -connect ldap-dmz.changyy.org:389 -starttls ldap
...
---
SSL handshake has read xxxx bytes and written xxx bytes
Verification: OK
---
...
```

此外,追蹤 LDAP 和使用他的服務時,意外發現不同的服務整合過程,也順手筆記一下,例如 Apache2 設定檔:

僅 LDAP 非加密:

<Location />
    Order allow,deny
    Allow from all
    AuthType basic
    AuthName "service.changyy.org"
    AuthBasicProvider ldap
    AuthLDAPUrl "ldap://ldap-dmz.changyy.org/dc=changyy,dc=org?uid"
    Require valid-user
</Location>

LDAP + StartTLS:

<Location />
    Order allow,deny
    Allow from all
    AuthType basic
    AuthName "service.changyy.org"
    AuthBasicProvider ldap
    AuthLDAPUrl "ldap://ldap-dmz.changyy.org/dc=changyy,dc=org?uid" TLS
    Require valid-user
</Location>

對應的其他服務也會有類似架構,舉例來說 Mantis 也有 mantisbt.org/docs/master/en-US/Admin_Guide/html/admin.config.auth.ldap.html

預設是啟用 LDAP + StartTLS

//
// Determines whether the connection will attempt an opportunistic upgrade to a TLS connection (STARTTLS).
//
// Defaults to ON.
//
// $g_ldap_use_starttls = ON

需要測試 LDAP 健康情況時,可以把它關閉

// $g_ldap_use_starttls = OFF

補充檢查 ldap service 健康情況時,最常是直接使用 ldapsearch 指令:

```
$ sudo apt install ldap-utils
$ ldapsearch -x -H 'ldap://ldap-dmz.changyy.org' -b 'dc=changyy,dc=org'
...
# search result
search: #
result: 0 Success
...
# numResponses: ###
# numEntries: ###
```

測試 LDAP + StartTLS:

```
$ ldapsearch -x -H 'ldap://ldap-dmz.changyy.org' -b 'dc=changyy,dc=org' -Z
...
ldap_start_tls: Connect error (-11)
additional info: (unknown error code)
ldap_result: Can't contact LDAP server (-1)
```

測試 LDAP + StartTLS 需要更多 debug 資訊:

```
$ ldapsearch -x -H 'ldap://ldap-dmz.changyy.org' -b 'dc=changyy,dc=org' -Z -d 1
...
TLS: peer cert untrusted or revoked (0x402)
TLS: can't connect: (unknown error code).
...
ldap_start_tls: Connect error (-11)
...
```

用 PHP Code 嘗試建立連線,先確認 php.ini 套件:

```
php -i | grep ldap
/etc/php/7.4/cli/conf.d/20-ldap.ini,
Protocols => dict, file, ftp, ftps, gopher, http, https, imap, imaps, ldap, ldaps, pop3, pop3s, rtmp, rtsp, scp, sftp, smb, smbs, smtp, smtps, telnet, tftp
ldap
...
```

運行:

```
$ cat /tmp/t.php 
<?php
$ldapconn = ldap_connect("ldap://ldap-dmz.changyy.org");
if ($ldapconn) {
    ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);
    ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0);
    // 關閉 TLS
    putenv('LDAPTLS_REQCERT=never');
    
    $bind = ldap_bind($ldapconn);
    if ($bind) {
        echo "LDAP bind successful...\n";
    } else {
        echo "LDAP bind failed...\n";
    }
}
$ php /tmp/t.php 
LDAP bind successful...
```

收工!

沒有留言:

張貼留言