---
# System prepended metadata

title: ldapsearch 指令
tags: [Base DN, CN, LDAP, Bind DN, DN, ldapsearch]

---

ldapsearch 指令
===
###### tags: `LDAP`
###### tags: `LDAP`, `ldapsearch`, `DN`, `CN`, `Base DN`, `Bind DN`

<br>

[TOC]

<br>

## Usage
```
usage: ldapsearch [options] [filter [attributes...]]
where:
  filter	RFC 4515 compliant LDAP search filter
  attributes	whitespace-separated list of attribute descriptions
    which may include:
      1.1   no attributes
      *     all user attributes
      +     all operational attributes
Search options:
  -a deref   one of never (default), always, search, or find
  -A         retrieve attribute names only (no values)
  -b basedn  base dn for search
  -c         continuous operation mode (do not stop on errors)
  -E [!]<ext>[=<extparam>] search extensions (! indicates criticality)
             [!]accountUsability         (NetScape Account usability)
             [!]domainScope              (domain scope)
             !dontUseCopy                (Don't Use Copy)
             [!]mv=<filter>              (RFC 3876 matched values filter)
             [!]pr=<size>[/prompt|noprompt] (RFC 2696 paged results/prompt)
             [!]ps=<changetypes>/<changesonly>/<echg> (draft persistent search)
             [!]sss=[-]<attr[:OID]>[/[-]<attr[:OID]>...]
                                         (RFC 2891 server side sorting)
             [!]subentries[=true|false]  (RFC 3672 subentries)
             [!]sync=ro[/<cookie>]       (RFC 4533 LDAP Sync refreshOnly)
                     rp[/<cookie>][/<slimit>] (refreshAndPersist)
             [!]vlv=<before>/<after>(/<offset>/<count>|:<value>)
                                         (ldapv3-vlv-09 virtual list views)
             [!]deref=derefAttr:attr[,...][;derefAttr:attr[,...][;...]]
             !dirSync=<flags>/<maxAttrCount>[/<cookie>]
                                         (MS AD DirSync)
             [!]extendedDn=<flag>        (MS AD Extended DN
             [!]showDeleted              (MS AD Show Deleted)
             [!]serverNotif              (MS AD Server Notification)
             [!]<oid>[=:<value>|::<b64value>] (generic control; no response handling)
  -f file    read operations from `file'
  -F prefix  URL prefix for files (default: file:///tmp/)
  -l limit   time limit (in seconds, or "none" or "max") for search
  -L         print responses in LDIFv1 format
  -LL        print responses in LDIF format without comments
  -LLL       print responses in LDIF format without comments
             and version
  -M         enable Manage DSA IT control (-MM to make critical)
  -P version protocol version (default: 3)
  -s scope   one of base, one, sub or children (search scope)
  -S attr    sort the results by attribute `attr'
  -t         write binary values to files in temporary directory
  -tt        write all values to files in temporary directory
  -T path    write files to directory specified by path (default: /tmp)
  -u         include User Friendly entry names in the output
  -z limit   size limit (in entries, or "none" or "max") for search
Common options:
  -d level   set LDAP debugging level to `level'
  -D binddn  bind DN
  -e [!]<ext>[=<extparam>] general extensions (! indicates criticality)
             [!]assert=<filter>     (RFC 4528; a RFC 4515 Filter string)
             [!]authzid=<authzid>   (RFC 4370; "dn:<dn>" or "u:<user>")
             [!]bauthzid            (RFC 3829)
             [!]chaining[=<resolveBehavior>[/<continuationBehavior>]]
                     one of "chainingPreferred", "chainingRequired",
                     "referralsPreferred", "referralsRequired"
             [!]manageDSAit         (RFC 3296)
             [!]noop
             ppolicy
             [!]postread[=<attrs>]  (RFC 4527; comma-separated attr list)
             [!]preread[=<attrs>]   (RFC 4527; comma-separated attr list)
             [!]relax
             [!]sessiontracking[=<username>]
             abandon, cancel, ignore (SIGINT sends abandon/cancel,
             or ignores response; if critical, doesn't wait for SIGINT.
             not really controls)
  -H URI     LDAP Uniform Resource Identifier(s)
  -I         use SASL Interactive mode
  -n         show what would be done but don't actually do it
  -N         do not use reverse DNS to canonicalize SASL host name
  -O props   SASL security properties
  -o <opt>[=<optparam>] any libldap ldap.conf options, plus
             ldif_wrap=<width> (in columns, or "no" for no wrapping)
             nettimeout=<timeout> (in seconds, or "none" or "max")
  -Q         use SASL Quiet mode
  -R realm   SASL realm
  -U authcid SASL authentication identity
  -v         run in verbose mode (diagnostics to standard output)
  -V         print version info (-VV only)
  -w passwd  bind password (for simple authentication)
  -W         prompt for bind password
  -x         Simple authentication
  -X authzid SASL authorization identity ("dn:<dn>" or "u:<user>")
  -y file    Read password from file
  -Y mech    SASL mechanism
  -Z         Start TLS request (-ZZ to require successful response)
```

<br>

## 常用參數
### `-x`, Simple authentication
- 使用「簡單驗證（simple bind）」，避免走 SASL。

<br>

### `-H URI`, LDAP Uniform Resource Identifier(s)
- 指定 LDAP 伺服器位址與埠（URI）。
    - **URI**: Uniform Resource Identifier, 統一資源識別碼
- `ldap://…:389` 為一般 LDAP；若要加密，**再加 `-ZZ` 做 StartTLS**。
    - `-ZZ`：在 389 上**升級成 TLS（StartTLS）**。若伺服器強制 TLS，沒加會被拒。
- `ldaps://…:636` 走 **LDAPS**（連上即 TLS）。

<br>

### `-D binddn`, bind DN
> 講單講，就是輸入**登入帳號**的地方：`-D "你的帳號"`
- `-D "..."`：Bind DN（登入身分）。
    - **必須是完整 DN**，例如 `cn=admin,dc=www,dc=google,dc=com`。
    - **DN**：**Distinguished Name**, 辨別名稱（完整地址）
        - 輔助記憶：ID Name (唯一識別碼名稱)

<br>

### `-w passwd`, bind password (for simple authentication)
> 講單講，就是輸入**登入密碼**的地方：`-w "你的密碼"` (把密碼寫在命令列)
- `-w`：把密碼寫在命令列。
- `-W`：互動式輸入密碼（較安全，不把密碼寫在命令列）。

<br>

### `-W`, prompt for bind password
> 講單講，就是輸入**登入密碼**的地方：`-W` (互動式輸入)
- `-w`：把密碼寫在命令列。
- `-W`：互動式輸入密碼（較安全，不把密碼寫在命令列）。

<br>

### `-y file`, Read password from file
> 講單講，就是透過檔案導入**登入密碼**

<br>

### `-Y mech`, SASL mechanism
> 講單講，透過外部機制(本機管理, TLS憑證)登入，免帳密

```
# 本機免帳密 (在同一台機器上做管理)
ldapsearch -Y EXTERNAL -H ldapi:/// -b cn=config dn
# 遠端免帳密 + TLS憑證登入
ldapwhoami -Y EXTERNAL -H ldaps://ldap.example.com
```

<br>

### `-b basedn`,  base dn for search
> 講單講，就是從這個**樹根**開始找
- Base DN（搜尋起點）

<br>

### filter
> 講單講，就是：搜尋過濾器
- 設定方式
    - 單一條件：`"(uid=tj_tsai)"`
    - 可用 AND/OR，例如：`"(&(objectClass=person)(uid=tj_tsai))"`。

<br>

### none vs `-L` vs `-LL` vs `-LLL`
> - `-L`  ,  print responses in LDIFv1 format
> - `-LL` ,  print responses in LDIF format without comments
> - `-LLL`,  print responses in LDIF format without comments and version

- ### none -> `-L`
    > 移除 search result ，添加版本資訊
    ```
    --- none
    +++ -L
    -# extended LDIF
    +version: 1

    # search result
    -search: 2
    -result: 0 Success
    ```
- ### `-L` -> `-LL`
    > 移除註解
    ```
    --- -L
    +++ -LL
    -#
    -# LDAPv3
    -# base <uid=tj_tsai,ou=sec1,ou=dep2,ou=div3,ou=ocis,dc=www,dc=asus,dc=com> with scope subtree
    -# filter: (objectclass=*)
    -# requesting: ALL
    -#

    -# search result
    -# numResponses: 2
    -# numEntries: 1
    ```
- ### `-LL` -> `-LLL`
    > 移除版本資訊
    ```
    --- -LL
    +++ -LLL
    -version: 1
    ```

<br>

### 其他參數
- `-s {base|one|sub}`：**搜尋範圍**，預設 `sub`（整棵子樹）。
- `-d 1`（或更高）：輸出 **debug** 訊息，有助排錯。
- 在尾端加屬性名（如 `dn uid cn mail`）可**限制回傳欄位**；不加則回傳伺服器預設屬性集。

---

<br>


## ldapsearch 指令測試環境

### 使用 K8s pod (`bitnami/openldap:latest` 已經下架)
> 改用下一小節所介紹

```
$ kubectl run ldap-test --rm -it --image=bitnami/openldap:latest -- bash
```
- `--rm`：Pod 結束後自動刪除。
- `-it`：互動式終端機。
- `--image=bitnami/openldap:latest`：使用 Bitnami 的 OpenLDAP 工具映像檔。
- **可用指令**
    - `ldapadd`
    - `ldapcompare`
    - `ldapdelete`
    - `ldapexop`
    - `ldapmodify`
    - `ldapmodrdn`
    - `ldappasswd`
    - `ldapsearch`
    - `ldapurl`
    - `ldapvc`
    - `ldapwhoami`
- ### ImagePullBackOff
    ```
    $ kubectl -n slurm run ldap-test -it --image=bitnami/openldap:latest

    Events:
      Type     Reason     Age                 From               Message
      ----     ------     ----                ----               -------
      Normal   Scheduled  2m9s                default-scheduler  Successfully assigned slurm/ldap-test to ntuh-dev-master01
      Normal   Pulling    23s (x4 over 2m7s)  kubelet            Pulling image "bitnami/openldap:latest"
      Warning  Failed     21s (x4 over 2m2s)  kubelet            Failed to pull image "bitnami/openldap:latest": rpc error: code = NotFound desc = failed to pull and unpack image "docker.io/bitnami/openldap:latest": failed to unpack image on snapshotter overlayfs: unexpected media type text/html for sha256:fec5d90e6a5d44014ec596bc6bb03e29f4f66ad9c7417e1bc388d81fb7901420: not found
      Warning  Failed     21s (x4 over 2m2s)  kubelet            Error: ErrImagePull
      Normal   BackOff    6s (x6 over 2m1s)   kubelet            Back-off pulling image "bitnami/openldap:latest"
      Warning  Failed     6s (x6 over 2m1s)   kubelet            Error: ImagePullBackOff
    ```

<br>

### 使用 K8s pod (使用 `--image=ubuntu:24.04`)
- ### 建立測試的 pod
    ```
    $ kubectl -n slurm run ldap-test --image=ubuntu:24.04 -- sleep infinity
    $ kubectl -n slurm exec -it ldap-test -- bash
        $ apt update; apt install dnsutils ldap-utils -y
    ```
- ### 確認登錄帳密
    ```
    $ ldapwhoami -x \
        -H ldap://openldap.openldap:389\
        -D "cn=admin,dc=www,dc=google,dc=com" \
        -w "YourSecurePassword"
    anonymous
    ```
- ### 查詢資料：全部的資料
    ```
    $ ldapsearch -x \
        -H ldap://openldap.openldap:389 \
        -D "cn=admin,dc=www,dc=google,dc=com" \
        -w "YourSecurePassword" \
        -b "ou=users,dc=www,dc=google,dc=com" 
    ```
- ### 查詢資料：查詢 tj (LDAP 世界不區分大小寫，可以使用萬用字元`*`)
    ```
    $ ldapsearch -x \
        -H ldap://openldap.openldap:389 \
        -D "cn=admin,dc=www,dc=google,dc=com" \
        -w "YourSecurePassword" \
        -b "ou=users,dc=www,dc=google,dc=com" \
        "(uid=tj*)"
    ```

<br>

### 目錄伺服器的 Port： 389 (LDAP) vs 389 (LDAP + StartTLS) vs 636 (LDAPS)
- ### 用 Port 389 + 明文傳輸
    ```bash
    ldapsearch -x -H ldap://server.example.com:389 \
      -b "dc=example,dc=com" "(objectClass=*)"
    ```
- ### 用 Port 389 並啟用加密（StartTLS）**
    > - 連線一開始是一般 LDAP；**用 `StartTLS` 指令升級成 TLS**。
    > - 若**沒執行 StartTLS**，就是明文（不安全）。
    
    ```bash
    ldapsearch -x -H ldap://server.example.com:389 \
      -ZZ -b "dc=example,dc=com" "(objectClass=*)"
    ```
    - `-ZZ` = 要求 StartTLS。沒有 `-ZZ` 就是明文，請避免。
        - 先明文握手，再發 **StartTLS** 升級
    - 失敗訊息
        `ldap_start_tls: Connect error (-11)
	additional info: error:0A000086:SSL routines::certificate verify failed (self-signed certificate)`
- ### 用 636（LDAPS）
    > - 一連上就進入 **TLS**（俗稱「LDAP over SSL/TLS」）
    > - **不需要再下 StartTLS**。
    
    ```bash
    ldapsearch -x -H ldaps://server.example.com:636 \
      -b "dc=example,dc=com" "(objectClass=*)"
    ```
- ### 差異總表
    | 項目 | 389（LDAP 明文） | 389（LDAP + StartTLS） | 636（LDAPS） |
    | ---- | -------------- | --------------------- | ------------ |
    | **預設是否加密** | 否（明文） | 否 → 透過 StartTLS 升級為加密 | 是（連上即 TLS） |
    | **加密啟動方式** | 無 | 先明文握手，再發 **StartTLS** 升級 | 一連線即進入 **TLS** |
    | **客戶端額外參數** | 不需要 | 需要（例：`-ZZ`） | 不需要 |
    | **主要風險** | 密碼/資料明文外洩 | 客戶端若忘記升級→風險 | 一律加密、較直覺 |
    | **防火牆/埠口** | 只用 389 | 只用 389（明文/加密同埠） | 需另開 636 |
    | **URL Scheme** | `ldap://` | `ldap://` + StartTLS | `ldaps://` |
    | **伺服器端管控** | 可讀但高風險，不建議啟用 simple bind | 可**強制要求 StartTLS**、拒絕明文 bind | 天生強制加密 |
    | **相容性與標準傾向** | 僅測試或封閉網段 | IETF/現代實務常見 | 歷史悠久、相容性普遍 |

<br>

---

<br>

## 指令 - `ldapwhoami` (連線測試、帳號綁定測試)

### 匿名連線（只測通不通）
> 如果有啟用匿名功能

```bash
$ ldapwhoami -x -H ldap://openldap.openldap:389
```
- **參數說明**
    - `ldapwhoami`：向伺服器詢問「我（此連線）是誰？」成功就代表能連上，且（若未提供帳密）是匿名身分。
    - `-x`：使用「簡單驗證（simple bind）」，避免走 SASL。對大多數測試最直覺。
    - `-H ldap://openldap.openldap:389`：LDAP 伺服器位址（URI）。
        - `ldap://` 走 389（明文，若要加密用 `-ZZ` 或改用 `ldaps://`）。
        - `openldap.openldap` 是 DNS 名稱，請換成你的 Service 或主機名。

- **輸出**
    ```
    # 成功 (代表連得通，但沒有帳密驗證)
    anonymous

    # 失敗
    ldap_bind: Inappropriate authentication (48)
        additional info: anonymous bind disallowed
    ```

<br>

---

### 帶管理者帳密驗證（最常用）
```bash
$ ldapwhoami -x \
  -H ldap://openldap.openldap:389 \
  -D "cn=admin,dc=www,dc=google,dc=com" \
  -w "YourSecurePassword"
  # 把密碼寫在命令列，會留在 shell 歷史，建議使用 -W 互動輸入密碼
```
```bash
$ ldapwhoami -x \
  -H ldap://openldap.openldap:389 \
  -D "cn=admin,dc=www,dc=google,dc=com" \
  -W
  # -W 互動式輸入密碼
```
- **參數說明**
    - `-D "..."`：Bind DN（登入身分）。**必須是完整 DN**，例如 `cn=admin,dc=www,dc=google,dc=com`。
    - `-w`：把密碼寫在命令列
    - `-W`：互動式輸入密碼（較安全，不把密碼寫在命令列）。
        - **對照** `-w YourSecurePassword`：直接在命令列帶密碼（**不建議**，會留在 shell 歷史）。
        - **更安全** `-y /path/to/passfile`：密碼存在檔案，指向檔案路徑。

- **範例輸出**
    ```
    # 成功 (代表帳密正確、成功綁定)
    dn: cn=admin,dc=www,dc=google,dc=com 

    # 失敗
    ldap_bind: Invalid credentials (49)
    ```
- **使用 curl 指令**
    ```
    $ curl -u 'cn=admin,dc=www,dc=google,dc=com :YourSecurePassword' \
        ldap://openldap.openldap:389/dc=www,dc=google,dc=com
    
    DN: dc=www,dc=google,dc=com
        objectClass: top
        objectClass: dcObject
        objectClass: organization

        o: Google

        dc: google
    ```

<br>

---

### 帶使用者帳密驗證
```bash
$ ldapwhoami -x -H ldap://openldap.openldap \
  -D "uid=tj_tsai,ou=sec1,ou=dep2,ou=div3,ou=ocis,dc=www,dc=google,dc=com" \
  -w '123456'
  # 把密碼寫在命令列，會留在 shell 歷史，建議使用 -W 互動輸入密碼
```
- **範例輸出**
    ```bash
    # 成功 (代表帳密正確、成功綁定)
    dn:uid=tj_tsai,ou=sec1,ou=dep2,ou=div3,ou=ocis,dc=www,dc=asus,dc=com

    # 失敗
    ldap_bind: Invalid credentials (49)
    ```
- **相似用法1**
    ```bash
    $ ldapsearch -x -H ldap://openldap.openldap \
      -D "uid=tj_tsai,ou=sec1,ou=dep2,ou=div3,ou=ocis,dc=www,dc=google,dc=com" \
      -w '123456' \
      -LLL
    
    # 成功：
    No such object (32)

    # 失敗：
    ldap_bind: Invalid credentials (49)
    ```
- **相似用法2**
    ```bash
    $ ldapsearch -x -H ldap://openldap.openldap \
      -D "uid=tj_tsai,ou=sec1,ou=dep2,ou=div3,ou=ocis,dc=www,dc=google,dc=com" \
      -w '123456' \
      -b "" -s base namingContexts -LLL

    # 成功：
    namingContexts: dc=www,dc=asus,dc=com

    # 失敗：
    ldap_bind: Invalid credentials (49)
    ```
    - `-b ""` 可省略
    - `-s` 預設是 `sub`
    - 如果回傳 `No such object (32)`，
        表示 **連 RootDSE/namingContexts** 都看不到（ACL 太緊）。

<br>

---

### 安全連線 / StartTLS（在 389 埠升級成加密）
> （伺服器有啟用 TLS/LDAPS 才能用）

```bash
$ ldapwhoami -x -ZZ \
  -H ldap://openldap.openldap:389 \
  -D "cn=admin,dc=www,dc=google,dc=com" \
  -W
```
- **參數說明**
    - `-ZZ`：要求使用 StartTLS（**強制**）。伺服器若沒開 StartTLS 會回錯誤。
        - （補充：`-Z` 為「嘗試」，`-ZZ` 為「必須」。實務上建議 `-ZZ`。）
- **輸出**
    ```
    # 失敗 (未啟用 StartTLS)
    ldap_start_tls: Connect error (-11)
        additional info: error:0A000086:SSL routines::certificate verify failed (self-signed certificate)
    ```

<br>

---

### 安全連線 / 直接 LDAPS（636）
> （伺服器有啟用 TLS/LDAPS 才能用）

```bash
$ ldapwhoami -x \
  -H ldaps://openldap.openldap:636 \
  -D "cn=admin,dc=www,dc=google,dc=com" \
  -W
```
- **參數說明**
    - `ldaps://...:636`：以 LDAPS 加密通道直連。伺服器需正確安裝憑證。
        > 若遇到 TLS 憑證問題：請確認客戶端有對應的 CA 憑證；**不要**直接關閉驗證，除非純測試環境且明白風險。
- **輸出**
    ```
    # 失敗 (未啟用 TLS/LDAPS)
    ldap_sasl_bind(SIMPLE): Can't contact LDAP server (-1)
    ```

<br>

---

<br>

## 指令 - `ldapsearch` (連線測試)
### 帶帳密驗證（最常用）
> 同前面 `ldapwhoami` 用法

```bash
$ ldapsearch -x \
  -H ldap://openldap.openldap:389 \
  -D "cn=admin,dc=www,dc=google,dc=com" \
  -w "YourSecurePassword"
  # 把密碼寫在命令列
```
```bash
$ ldapsearch -x \
  -H ldap://openldap.openldap:389 \
  -D "cn=admin,dc=www,dc=google,dc=com" \
  -W
  # 互動式輸入密碼
```

- **輸出**
    - **成功**
        ```ldif
        # extended LDIF
        #
        # LDAPv3
        # base <> (default) with scope subtree
        # filter: (objectclass=*)
        # requesting: ALL
        #

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

        # numResponses: 1
        ```
    - **失敗**
        ```
        ldap_bind: Invalid credentials (49)
        ```

<br>

### 帶帳密驗證（請求 namingContexts）
```
$ ldapsearch -x \
    -H ldap://openldap.openldap:389 \
    -D "cn=admin,dc=www,dc=google,dc=com" \
    -w "YourSecurePassword" \
    -s base namingContexts -LLL
```
- **輸出**
    - **成功**
        ```
        dn:
        namingContexts: dc=www,dc=google,dc=com
        ```
    - **失敗**
        ```
        ldap_bind: Invalid credentials (49)
        ```

<br>

### 使用其他帳密驗證
帳號：一個dn
密碼：
```
$ ldapsearch -x \
    -H ldap://openldap.openldap:389 \
    -D "uid=tj_tsai,ou=sec1,ou=dep2,ou=div3,ou=ocis,dc=www,dc=google,dc=com" \
    -w "YourSecurePassword" \
    -b "dc=www,dc=google,dc=com" uid=tj_tsai -LLL
    
dn: uid=tj_tsai,ou=sec1,ou=dep2,ou=div3,ou=ocis,dc=www,dc=google,dc=com
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
cn: tj_tsai
sn: tsai
givenName: tj
displayName: tj_tsai
mail: tj_tsai@gmail.com
telephoneNumber: 39981
uid: tj_tsai
uidNumber: 10001
gidNumber: 10001
homeDirectory: /home/tj_tsai
loginShell: /bin/bash
gecos: tj_tsai
userPassword:: e1NTSEF9Yi9EY1B0RDFDWDgzNkhjRjJ5TmZRbCtmd2NCZGI0R0Y=
```
- `userPassword:: e1NTSEF9Yi9EY1B0RDFDWDgzNkhjRjJ5TmZRbCtmd2NCZGI0R0Y=`
    ```
    echo e1NTSEF9Yi9EY1B0RDFDWDgzNkhjRjJ5TmZRbCtmd2NCZGI0R0Y= | base64 -d
    {SSHA}b/DcPtD1CX836HcF2yNfQl+fwcBdb4GF
    ```

<br>

---

<br>


## 指令 - `ldapsearch` (查詢測試)

### 測試有讀到任何東西
> 同前面章節的連線測試

```bash
ldapsearch -x \
  -H ldap://openldap.openldap:389 \
  -D "cn=admin,dc=www,dc=google,dc=com" \
  -w "YourSecurePassword"
```
- **執行範例**
    ```ldif
    # extended LDIF
    #
    # LDAPv3
    # base <> (default) with scope subtree
    # filter: (objectclass=*)
    # requesting: ALL
    #

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

    # numResponses: 1
    ```

<br>

### 測試有讀到任何東西 (從指定的搜尋起點開始)
```bash
ldapsearch -x \
  -H ldap://openldap.openldap:389 \
  -D "cn=admin,dc=www,dc=google,dc=com" \
  -w "YourSecurePassword" \
  -b "dc=www,dc=google,dc=com" \
  "(objectClass=*)" -LLL -z 1
```
- **新增參數說明**
    - `ldapsearch`：做查詢（搜尋目錄樹）。
    - `-b "..."`：Base DN（搜尋起點）。
    - `"(objectClass=*)"`：搜尋條件（過濾器）。這是「匹配全部物件」的萬用測試。
    - `-LLL`：輸出簡潔 LDIF（不印 version、註解等）。
    - `-z 1`：最多回 1 筆（快速測權限即可）。

<br>

### 查詢自己的 posixAccount
```
ldapsearch -x \
  -H ldap://openldap.openldap \
  -D "uid=tj_tsai,ou=sec1,ou=dep2,ou=div3,ou=ocis,dc=www,dc=google,dc=com" \
  -w '123456' \
  -b "uid=tj_tsai,ou=sec1,ou=dep2,ou=div3,ou=ocis,dc=www,dc=google,dc=com" \
  -s base \
  -LLL
```
- `-s base` 或 `-s sub` 都可以查到資料
- 底下查不到資料是正常的：
    ```
    ldapsearch -x -H ldap://openldap.openldap \
      -D "uid=tj_tsai,ou=sec1,ou=dep2,ou=div3,ou=ocis,dc=www,dc=google,dc=com" -w '123456' \
      -b "dc=www,dc=asus,dc=com" \
      -s base \
      -LLL
    ```
    - **原因**：樹上層節點（base DN）缺少 discover/search 權限，需要調整 `olcAccess` 權限

<br>

### 怎麼知道現在有哪些編號可用？

用 root 在主機上查：
```bash
# 列出所有資料庫（看現有 {N}）
ldapsearch -Y EXTERNAL -H ldapi:/// -b cn=config '(olcDatabase=*)' olcDatabase olcSuffix

# 只列 mdb 類型
ldapsearch -Y EXTERNAL -H ldapi:/// -b cn=config '(&(objectClass=olcMdbConfig)(olcSuffix=*))' olcDatabase olcSuffix olcDbDirectory
```

<br>

### 找出下一個可用的資料庫索引 `{N}`
```bash
ldapsearch -Y EXTERNAL -H ldapi:/// -b cn=config '(olcDatabase=*)' olcDatabase
# 看現有 {0},{1},{2}...，選一個未使用的號碼，以下以 {3} 為例
```

<br>

---

<br>


## 指令 - `ldapadd` (新增資料)
- ### 範例1：新增資料
    1. **建立 `add_group.ldif`**：
        ```ldif
        dn: cn=gid10001,ou=groups,dc=www,dc=google,dc=com
        objectClass: posixGroup
        cn: gid10001
        gidNumber: 10001
        memberUid: tj_tsai
        ```

    2. **執行**：
        ```bash
        # 匯入
        ldapadd -x \
          -H ldap://openldap:389 \
          -D "cn=admin,dc=www,dc=google,dc=com" \
          -w "YourSecurePassword" \
          -f add_group.ldif
        
        # 驗證
        ldapsearch -x \
          -H ldap://openldap:389 \
          -D "cn=admin,dc=www,dc=google,dc=com" \
          -w "YourSecurePassword" \
          -b "dc=www,dc=google,dc=com" \
          "(gidNumber=10001)" -LLL
        ```

<br>

---

<br>


## 指令 - `ldapmodify` (修改資料)
- ### 範例1：刪除資料
    1. **建立 `delete_user.ldif`**：

        ```ldif
        dn: cn=tj_tsai,ou=users,dc=www,dc=google,dc=com
        changetype: delete
        ```

    2. **執行**：

        ```bash
        ldapmodify -x \
          -H ldap://openldap:389 \
          -D "cn=admin,dc=www,dc=google,dc=com" \
          -w "YourSecurePassword" \
          -f delete_user.ldif
        ```

- ### 範例2：更新密碼
    1. **建立 `update-password.ldif`**：

        ```ldif
        dn: uid=tj_tsai,ou=sec1,ou=dep2,ou=div3,ou=ocis,dc=www,dc=google,dc=com
        changetype: modify
        replace: userPassword
        userPassword: <userPassword>
        ```
        - **`<userPassword>` 產生方式**
            - 登入密碼：`123456`
            - 產生雜湊：
                ```
                $ slappasswd -h {SSHA}
                New password: 123456
                Re-enter new password: 123456
                {SSHA}1Y3Fw3kdU7f9KBwnBIN9Qp+v4VW7Vhlt
                ```
        - **最終檔案內容**
            ```ldif
            dn: uid=tj_tsai,ou=sec1,ou=dep2,ou=div3,ou=ocis,dc=www,dc=asus,dc=com
            changetype: modify
            replace: userPassword
            userPassword: {SSHA}1Y3Fw3kdU7f9KBwnBIN9Qp+v4VW7Vhlt
            ```
    2. **套用設定**：
        ```bash
        $ ldapmodify -x \
            -H ldap://openldap:389 \
            -D "cn=admin,dc=www,dc=google,dc=com" \
            -w "YourSecurePassword" \
            -f delete_user.ldif

        # 執行結果
        modifying entry "uid=tj_tsai,ou=sec1,ou=dep2,ou=div3,ou=ocis,dc=www,dc=google,dc=com"
        ```
    3. **登入驗證**：
        ```bash
        $ ldapwhoami -x -H ldap://openldap:389 \
            -D "uid=tj_tsai,ou=sec1,ou=dep2,ou=div3,ou=ocis,dc=www,dc=google,dc=com" \
            -w 123456

        # 執行結果
        dn:uid=tj_tsai,ou=sec1,ou=dep2,ou=div3,ou=ocis,dc=www,dc=google,dc=com
        ```


<br>

---

<br>

## 指令 - `ldapdelete` (刪除資料)
- ### 範例1：刪除使用者
    ```
    ldapdelete -x -H ldap://openldap:389 \
        -D "cn=admin,dc=www,dc=google,dc=com" \
        -w "YourSecurePassword" \
        "cn=tj_tsai,ou=users,dc=www,dc=google,dc=com"
    ```
    - **刪除目標**：特定 dn
- ### 範例2：遞迴刪除整個子樹（若該帳號條目有子節點）
    ```bash
    ldapdelete -x -r \
      -H ldap://openldap:389 \
      -D "cn=admin,dc=www,dc=google,dc=com" -W \
      "cn=tj_tsai,ou=users,dc=www,dc=google,dc=com"
    ```
    - `-r` 會逐一刪除該 DN 下的所有子條目（OpenLDAP 支援）。請務必小心使用。

<br>

---

<br>


## 指令 - `ldappasswd` (修改密碼)
### 重設密碼
- ### 不知道原密碼就**重設**：
    ```bash
    # 以管理者改使用者密碼（伺服器自動雜湊）
    ldappasswd -x \
      -H ldap://openldap:389 \
      -D "cn=admin,dc=www,dc=google,dc=com" \
      -w "YourSecurePassword" \
      "uid=tj_tsai,ou=sec1,ou=dep2,ou=div3,ou=ocis,dc=www,dc=google,dc=com" \
      -s 'your_new_strong_password!'
    ```
- ### 使用新密碼測試登入
    ```
    # 立即測試（注意：-w 是明碼）
    ldapwhoami -x \
      -H ldap://openldap:389 \
      -D "uid=tj_tsai,ou=sec1,ou=dep2,ou=div3,ou=ocis,dc=www,dc=google,dc=com" \
      -w 'your_new_strong_password!'
    ```
- ### 亦可透過 `ldapmodify` 指令變更密碼
    - [ldapmodify / 範例2：更新密碼](#範例2：更新密碼)
    
<br>

---

<br>

## 透過 `curl` 指令查詢

### 安裝 `curl` 指令
```
apt update
apt install curl -y
```

<br>

### 檢查 `curl` 指令是否支援 ldap / ldaps
```bash
curl -V   # 看 Protocols 列表是否含 ldap / ldaps
```
- ### 執行範例
    ```
    $ curl -V
    curl 8.5.0 (x86_64-pc-linux-gnu) libcurl/8.5.0 OpenSSL/3.0.13 zlib/1.3 brotli/1.1.0 zstd/1.5.5 libidn2/2.3.7 libpsl/0.21.2 (+libidn2/2.3.7) libssh/0.10.6/openssl/zlib nghttp2/1.59.0 librtmp/2.3 OpenLDAP/2.6.7
    Release-Date: 2023-12-06, security patched: 8.5.0-2ubuntu10.6
    Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp
    Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM PSL SPNEGO SSL threadsafe TLS-SRP UnixSockets zstd
    ```

<br>

### 基本語法

LDAP URL 由下列片段組成：

```
ldap[s]://主機:埠/基底DN?屬性清單?範圍?過濾器
```
- 基底DN,屬性清單,範圍,過濾器 -> 內容皆不分大小寫(大小寫不敏感)
- **範圍**：`base`(預設) | `one` | `sub`
- **過濾器/操作範例**：
    - `uid=tj_tsai`
    - `(UID=TJ_Tsai)`
    - `uid=tj*`
    - `(objectClass=posixGroup)`

<br>

### 常用查詢範例

> 假設你的目錄根為 `dc=www,dc=google,dc=com`，LDAP在 10.78.26.241

- ### (1) 匿名查詢（sub 範圍，取 cn,mail）

    ```bash
    curl 'ldap://10.78.26.241:389/dc=www,dc=google,dc=com?cn,mail?sub?(uid=tj_tsai)'
    ```
    - **錯誤訊息**：
        - `curl: (38) LDAP: cannot bind`
            - 不允許匿名，需要帳密

- ### (2) 帶 Bind DN/密碼（簡單繫結）

    ```bash
    curl -u 'cn=admin,dc=www,dc=google,dc=com:YourSecurePassword' \
      'ldap://10.78.26.241/dc=www,dc=google,dc=com??sub?(objectClass=posixAccount)'
    ```
    - **範例執行**：
        ```
        $ curl -u 'cn=admin,dc=asus,dc=com:YourSecurePassword' \
            ldap://10.96.42.255:389
        DN:
            objectClass: top
            objectClass: OpenLDAProotDSE
        ```
        ```
        $ curl -u 'cn=admin,dc=asus,dc=com:YourSecurePassword' \
            ldap://10.96.42.255:389/dc=asus,dc=com
        DN: dc=asus,dc=com
            objectClass: top
            objectClass: dcObject
            objectClass: organization

            o: ASUS

            dc: asus
        ```

- ### (3) LDAPS（636/TLS 直連）

    ```bash
    curl --cacert /path/ca.crt \
      -u 'cn=admin,dc=www,dc=google,dc=com:YourSecurePassword' \
      'ldaps://10.78.26.241:636/dc=www,dc=google,dc=com?uid,cn?sub?(uid=tj_tsai)'
    ```

- ### (4) STARTTLS（389，升級為 TLS）

    ```bash
    curl --starttls ldap --cacert /path/ca.crt \
      -u 'cn=admin,dc=www,dc=google,dc=com:YourSecurePassword' \
      'ldap://10.78.26.241/dc=www,dc=google,dc=com??sub?(uid=tj_tsai)'
    ```

- ### (5) 只取 base 物件

    ```bash
    curl 'ldap://10.78.26.241/dc=www,dc=google,dc=com??base?(objectClass=*)'
    ```

> 小撇步：URL 內若有空白或特殊字元請使用引號，必要時做 URL 編碼（如空白 `%20`）。

<br>

### 與 `ldapsearch` 的差異

* `curl` 的 LDAP支援**偏基本、以查詢為主**；進階操作（如分頁控制、特殊控制項、SASL/機制選擇、修改/新增/刪除）建議用 `ldapsearch/ldapmodify/ldapadd`。
* `curl` 輸出為「接近 LDIF」的文字；`ldapsearch` 在格式與選項上更完整、好用。

<br>

### 常見錯誤與排解

* `Protocol "ldap" not supported`：當前 `curl` 未啟用 LDAP；改裝有支援的套件或改用 `ldapsearch`。
* `Can't contact LDAP server`：主機/埠、網路或防火牆問題。
* `Invalid credentials`：Bind DN 或密碼錯。
* 憑證錯誤：提供 `--cacert /path/ca.crt`（不建議用 `-k` 降低驗證）。

需要我把你現有的 `ldapsearch` 查詢，等價改寫成 `curl` 範例嗎？


<br>

---

<br>

## 常見錯誤與判讀

| 錯誤/輸出 | 意義與排除 |
| -------- | -------- |
| `ldap_bind: Invalid credentials (49)` | 帳密錯（DN 不對、密碼錯、或該 DN 不允許 simple bind）。先確認 DN 拼法與管理者 DN 真的存在。 |
| `Can't contact LDAP server (-1)` 或 `(81)` | 連不通：DNS、Service 名稱、Port、NetworkPolicy / 防火牆、Service/Pod 未啟動。 |
| `Protocol error (2)`（配 `-ZZ`） | 伺服器沒啟用 StartTLS（或 TLS 協商問題）。改用 `ldaps://` 或在伺服器啟用 StartTLS。 |
| `Invalid DN syntax (34)` | `-D` 或 `-b` 的 DN 格式不正確（少逗號、屬性名打錯）。 |
| TLS/憑證錯誤（如 `unable to get local issuer certificate`） | 客戶端沒有信任的 CA。請把 CA 憑證配到容器 `/etc/openldap/certs/` 或透過 `LDAPTLS_CACERT`/`TLS_CACERT` 指定。 |

- **除錯小技巧**
    - 加上 `-d 1`（或更高，如 `-d 2`）看除錯訊息。
    - 檢查環境變數或 `/etc/openldap/ldap.conf` 中的 `TLS_CACERT` 設定。
    - 用 `$?` 看退出碼：`0` 成功；非 0 代表失敗。
        ```bash
        ldapwhoami -x -H ldap://... -D "..." -W ; echo $?
        ```

<br>

### additional info: anonymous bind disallowed
```
$ ldapsearch -x -H ldap://openldap.openldap \
  -b "" -s base namingContexts -LLL

ldap_bind: Inappropriate authentication (48)
	additional info: anonymous bind disallowed
```
- **原因**：不允許匿名查詢

<br>

---
---

<br>

## 討論
### [無法連線] `ldap_sasl_bind(SIMPLE): Can't contact LDAP server (-1)`
- ### Quesstion
    ```
    $ ldapsearch -x -b "dc=asus,dc=com" "(uid=tj_tsai)"
    ldap_sasl_bind(SIMPLE): Can't contact LDAP server (-1)
    ```
- ### gpt-5 (2025/09/19)
    下面這個錯誤訊息代表「**客戶端連不到任何 LDAP 伺服器**」，不是帳密或 Base DN 錯誤：

    ```
    ldap_sasl_bind(SIMPLE): Can't contact LDAP server (-1)
    ```

    最常見原因：沒有指定伺服器 URI（預設會連 localhost:389）、伺服器沒啟動、連錯主機/埠、或被防火牆/TLS 擋下來。照這份「**最短查核清單**」一步做，通常就能定位問題。

    ---
    
    > ### 兩種情況：
    > (A) 你在 **同一台裝有 slapd 的主機** 上（apt 安裝）
    > (B) 你在 **其他主機 / 容器 / Pod** 上查（最常見）

    ---

    ### (A) 你在 **同一台裝有 slapd 的主機** 上（apt 安裝）

    1. 先確認服務有跑

        ```bash
        sudo systemctl status slapd --no-pager
        sudo ss -ltnp | grep -E ':389|:636'
        ```

    2. 直接用 Unix socket（ldapi）查設定，驗證 **Naming Context（suffix）** 與 **管理者 DN**

        ```bash
        # 以 root 權限用本機 socket 連線，不走網路
        sudo ldapsearch -H ldapi:/// -Y EXTERNAL -b cn=config -LLL olcSuffix olcRootDN
        ```

        看輸出中的：

        * `olcSuffix`: 例如 `dc=google,dc=com`
        * `olcRootDN`: 例如 `cn=admin,dc=google,dc=com`（這就是你要用的 -D）

    3. 用 ldapi 直接找資料（先不管網路問題）

        ```bash
        sudo ldapsearch -H ldapi:/// -Y EXTERNAL -b "" -s base -LLL namingContexts
        sudo ldapsearch -H ldapi:/// -x -b 'dc=google,dc=com' '(uid=tj_tsai)' -LLL dn
        ```
        - 這兩條成功就表示 **資料已存在 + 伺服器正常運作**。

    ---

    ### (B) 你在 **其他主機 / 容器 / Pod** 上查（最常見）

    > 關鍵：**一定要加 `-H ldap://<host>:<port>`** 指向正確的服務位置與埠。

    1. 先確認你能連到 LDAP 埠

    ```bash
    # 換成你的主機名或 Service 名稱與埠（常見 389 / 1389；LDAPS 常見 636 / 1636）
    nc -vz <ldap-host> 389
    ```

    2. 用匿名 bind 讀 namingContexts（找出正確 Base DN）

    ```bash
    ldapsearch -H ldap://<ldap-host>:389 -x -b "" -s base -LLL namingContexts
    ```

    看到像 `namingContexts: dc=google,dc=com` 就代表伺服器可連，且 Base DN 是那個字串。

    3. 搜尋你的使用者
       （**建議用整體樹的 Base DN**，讓伺服器自己往下找，不要一開始就填整個使用者 DN 當 base）

    ```bash
    ldapsearch -H ldap://<ldap-host>:389 -x \
      -b 'dc=google,dc=com' '(uid=tj_tsai)' -LLL dn uidNumber gidNumber homeDirectory loginShell
    ```

    4. 需要驗證帳密時（以管理者 DN 連線）：

    ```bash
    ldapwhoami -H ldap://<ldap-host>:389 -x -D 'cn=admin,dc=google,dc=com' -W
    # 或搜尋：
    ldapsearch -H ldap://<ldap-host>:389 -x \
      -D 'cn=admin,dc=google,dc=com' -W \
      -b 'dc=google,dc=com' '(uid=tj_tsai)' -LLL dn
    ```

    > 你的 LDIF 把條目放在 `dc=www,dc=google,dc=com` 底下。這 **仍屬於** `dc=google,dc=com` 這棵樹之內（`dc=www` 只是中間節點），**只要伺服器的 suffix 是 `dc=google,dc=com`，就用它當 -b**，搜尋會自己往下遞迴找到 `uid=tj_tsai`。

    ---

    ### C)（選用）TLS／StartTLS 檢查

    * LDAPS（636）：`-H ldaps://<host>:636 -x ...`
    * StartTLS（389）：`-H ldap://<host>:389 -ZZ -x ...`
      若遇到憑證錯誤，可暫時用 `-d 1` 觀察，或把 CA 加到 `/etc/ldap/ldap.conf` 的 `TLS_CACERT`。

    ---

    ### D) 常見踩雷速解

    * **沒加 `-H`** → 預設連 localhost:389；在容器裡通常沒有 slapd，所以報 `Can't contact LDAP server (-1)`。
    * **連錯埠** → 有些映像（或 K8s Service）用 **1389/1636**。請以實際 Service/Port 為準。
    * **Base DN 用錯** → 先用 `-b "" -s base namingContexts` 查真正的 suffix。
    * **admin DN 用錯** → 以 `ldapi:///` 查 `olcRootDN` 最準。
    * **防火牆** → 伺服器要開放 389/636（或 1389/1636）；K8s 要確認 Service/NetworkPolicy。
    * **`I have no name!@...`** → 只是容器內沒有對應的 /etc/passwd 項，與 LDAP 連線無關。

    ---

    ### E) 你這份資料的「最小可驗證」指令範例

    > 假設伺服器 suffix 是 `dc=google,dc=com`，且用 389：

    1. 匯入

    ```bash
    ldapadd -H ldap://<ldap-host>:389 -x \
      -D 'cn=admin,dc=google,dc=com' -W -f tj_tsai.ldif
    ```

    2. 搜尋（匿名或管理者都可）

    ```bash
    ldapsearch -H ldap://<ldap-host>:389 -x \
      -b 'dc=google,dc=com' '(uid=tj_tsai)' -LLL dn uidNumber gidNumber homeDirectory loginShell
    ```

    如果以上第 2 步還是回 `Can't contact LDAP server (-1)`，那就回到 **A-1/B-1** 檢查服務是否在正確的主機與埠上對外監聽，以及你 `-H` 的主機與埠是否填對。

<br>

---

### [參數指令] SASL/EXTERNAL 這兩個參數差在哪裡?
- ### gpt-5 (2025/09/21)
    簡短說：**`SASL/EXTERNAL` 不是兩個獨立參數**。

    * **SASL** 是一個「認證框架」(Simple Authentication and Security Layer)。
    * **EXTERNAL** 是 **SASL 的其中一種機制**（mechanism），意思是「用外部安全層提供的身份」來認證。

    在指令上你會看到：`-Y EXTERNAL`

    * `-Y`：選擇 **SASL 機制**
    * `EXTERNAL`：指定用 **EXTERNAL 機制**

    ---

    ### 它跟一般 `-x`（簡單綁定）差在哪？

    | 比較項 | `-Y EXTERNAL`（SASL/EXTERNAL） | `-x -D ... -W`（Simple Bind） |
    | ----- | ----- | ----- |
    | 身份來源 | 來自「外部安全層」 | 明文帳號密碼（或互動輸入） |
    | 常見外部安全層 | 1) **ldapi:/// 本機 Socket**：用作業系統的 **UID/GID**（root 最常見）<br>2) **TLS/SSL**：用 **用戶端憑證** 的主題資訊 | 無 |
    | 用途 | 本機管理（改 `cn=config`、載入 schema、ACL）、或憑證式無密碼登入 | 一般遠端用戶端登入、應用程式連線 |
    | 安全性 | 很高：不須密碼；依賴 OS/憑證信任鏈 | 視密碼強度與傳輸保護（要搭配 StartTLS/LDAPS） |
    | 需要先有 | 1) ldapi：`olcAuthzRegexp` 對應 OS 身份 → LDAP DN<br>2) TLS：CA 與客戶端憑證對應 DN | 建好帳號密碼即可 |
    | 指令例子 | `ldapsearch -Y EXTERNAL -H ldapi:/// -b cn=config dn`（本機免密）<br>`ldapwhoami -Y EXTERNAL -H ldaps://ldap.example.com`（憑證） | `ldapsearch -x -H ldap://host -D "cn=admin,dc=example,dc=com" -W ...` |

    ---

    ### 兩種「EXTERNAL」最常見情境

    #### 1) 本機 `ldapi:///`（用 OS 身份）

    * 依靠 Unix Domain Socket 的 **peercred**（你的程序 UID/GID），常見是 **root**。
    * 伺服器用 `olcAuthzRegexp` 把 OS 身份**映射**成可管理的 LDAP DN（例如 `cn=admin,cn=config`）。
      **測試：**

    ```bash
    ldapwhoami -Y EXTERNAL -H ldapi:///
    # 期望輸出：dn:cn=admin,cn=config  或  dn:gidNumber=...,+uidNumber=...,cn=peercred,cn=external,cn=auth
    ```

    #### 2) `ldaps://` 或 `ldap://`+StartTLS（用客戶端憑證）

    * 你的客戶端握有 **x509 憑證**；伺服器信任該 CA，並將憑證的 Subject（或 SAN）**映射**到 LDAP DN。
      **測試（憑證已配置）：**

    ```bash
    ldapwhoami -Y EXTERNAL -H ldaps://ldap.example.com
    # 期望輸出：對應的 DN（例如：uid=tj,ou=users,dc=example,dc=com）
    ```

    ---

    ### 快速判斷何時用哪個

    * **在同一台機器上做管理**（改 `cn=config`、載 schema/ACL）→ 用 `ldapi:///` + `-Y EXTERNAL`（免密、最安全）。
    * **遠端且要免密**（CI/CD、自動化）→ 配憑證，用 `ldaps://` + `-Y EXTERNAL`。
    * **一般應用／帳號密碼** → `-x -D ... -W`，並搭配 StartTLS（`-ZZ`）或直接 `ldaps://`。

    如果你告訴我現在是 **ldapi 本機管理** 還是 **TLS 憑證登入**，我可以給你對應的 `olcAuthzRegexp` 映射範例與測試指令。


<br>

---

### [參數指令] `ldap://` vs `ldaps://` vs `ldapi:///`
- ### Question
    `ldap://` vs `ldaps://` vs `ldapi:///`
    又是什麼縮寫？用途為何？
- ### gpt-5 (2025/09/21)
    好！你問的是這三個 URI scheme：`ldap://`、`ldaps://`、`ldapi:///`——各自是什麼縮寫、用途是什麼。快速對照在這裡：

    ### 1) `ldap://`

    * **全名**：**L**ightweight **D**irectory **A**ccess **P**rotocol（LDAP）
    * **傳輸**：TCP（預設埠 **389**），連線本身**不加密**，但可再「升級為 TLS」（StartTLS）
    * **用途**：一般網路用戶端連到 LDAP 伺服器；建議**搭配 StartTLS** 變成加密連線
    * **何時用**：你要走 389 埠、需要防火牆相容性好、而且想用 **StartTLS（-ZZ）** 升級加密時
    * **例**：
      ```bash
      ldapsearch -x -H ldap://ldap.example.com -b dc=example,dc=com -ZZ
      # -ZZ = 要求在 ldap:// 上啟動 StartTLS
      ```

    ### 2) `ldaps://`
    > `http://` & `https://` <--> `ldap://` & `ldaps://`
    * **全名**：**LDAP over SSL/TLS**（俗稱「LDAPS」）
    * **傳輸**：TLS 直連（預設埠 **636**），一開始就加密
    * **用途**：歷史上常見的「固定 636、永遠加密」作法；現在仍然廣泛使用
    * **何時用**：你的環境或既有設備只接受 636/TLS 直連，或你想**強制所有連線都加密**（不走 StartTLS）
    * **例**：
      ```bash
      ldapsearch -x -H ldaps://ldap.example.com:636 -b dc=example,dc=com
      ```

    ### 3) `ldapi:///`

    * **全名**：**LDAP over IPC**（以本機 **Unix Domain Socket** 行程間通訊）
    * **傳輸**：走本機 socket（預設路徑常是 `/run/slapd/ldapi`），**不經網路、不用埠號**
    * **用途**：**伺服器本機管理**、自動化腳本、套件後設設定；可用 **SASL/EXTERNAL** 做「免密、以作業系統身份」的管理（很方便改 `cn=config`）
    * **何時用**：你在**同一台主機**上維護 slapd；要改核心設定、灌 schema、調 ACL
    * **例**：
      ```bash
      ldapsearch -Y EXTERNAL -H ldapi:/// -b cn=config dn
      # 以作業系統帳號身份（root）做本機管理，不需密碼
      ```

    ---

    ### 小結（怎麼選）

    * 想走 **389** 又要加密 → `ldap://` + **StartTLS（-ZZ）**
    * 想一律加密、或相容既有 636 → `ldaps://`
    * 在**本機**做管理、改 `cn=config`、不用密碼 → `ldapi:///` + **SASL/EXTERNAL**

    需要我用你的實際網域/Base DN，幫你寫三種連線的「可直接貼上執行」的測試指令嗎？


<br>

{%hackmd vaaMgNRPS4KGJDSFG0ZE0w %}
