---
# System prepended metadata

title: 在 Kubernetes 上部署 OpenLDAP 的詳細教學
tags: [OpenLDAP, LDAP, Kubernetes, K8s]

---

在 Kubernetes 上部署 OpenLDAP 的詳細教學
===
###### tags: `LDAP`
###### tags: `LDAP`, `OpenLDAP`, `Kubernetes`, `K8s`

<br>

[TOC]

<br>

:::success
### :bulb: GitHub:
https://github.com/jp-gouin/helm-openldap
- [values.yaml](https://github.com/jp-gouin/helm-openldap/blob/master/values.yaml)
:::

<br>

## 前言
本教學將引導您在 Kubernetes 叢集上部署高可用性的 OpenLDAP 服務。我們將使用 Helm Chart `openldap-stack-ha` 來實現這一目標，該 Chart 包含 OpenLDAP 服務器、phpLDAPadmin 管理界面和 ltb-passwd 自助密碼服務。

<br>

## 步驟一：準備環境

### 前提條件
- 執行中的 Kubernetes 叢集 (v1.8+)
- 已安裝 Helm (v3+)
- 具有建立 PV 的儲存支援
- `kubectl` 指令列工具已設定

### 安裝 Helm (如果尚未安裝)

```bash
# 下載並安裝 Helm
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

# 驗證安裝
helm version
```

<br>

## 步驟二：新增 Helm 儲存庫
> [[github] OpenLDAP Helm Chart](https://github.com/jp-gouin/helm-openldap?tab=readme-ov-file)

首先，新增包含 OpenLDAP Chart 的 Helm 儲存庫：

```bash
# 新增 Helm 儲存庫
helm repo add helm-openldap https://jp-gouin.github.io/helm-openldap/

# 更新儲存庫資訊
helm repo update
```

<br>

## 步驟三：自訂設定

在安裝 Chart 前，先建立自訂的 values 檔，覆蓋預設設定。

建立 `openldap-values.yaml`：

```yaml=
# openldap-values.yaml

# 全域設定
global:
  ldapDomain: "esc4000.ocis.asus.com"  # 改為您的網域名稱
  # 等效於：dc=esc4000,dc=ocis,dc=asus,dc=com (<- 最終轉換結果)
  adminPassword: "YourSecurePassword"  # 管理員密碼
  configPassword: "YourSecurePassword"  # 設定管理員密碼（cn=config）

# 副本數量 （用於高可用）
replicaCount: 3

# 日誌等級
logLevel: info

# 持久化設定
persistence:
  enabled: true
  accessMode: ReadWriteOnce
  size: 8Gi

# TLS/LDAPS 設定（選用）
initTLSSecret:
  tls_enabled: false  # 設為 true 以啟用 TLS
  # secret: "ldap-tls-secret"  # 含 TLS 憑證與金鑰的 Secret 名稱

# 複寫設定
replication:
  enabled: true
  retry: 60
  timeout: 1
  interval: "00:00:00:10"

# PHPLDAPAdmin 設定
phpldapadmin:
  enabled: true
  ingress:
    enabled: false  # 設為 true 以啟用 Ingress
    # hosts:
    #   - ldapadmin.example.org

# 自助密碼服務
ltb-passwd:
  enabled: true
  ingress:
    enabled: false  # 設為 true 以啟用 Ingress
    # hosts:
    #   - passwd.example.org
```
- ### 「第一次啟動」時會自動建立預設樹
    ![](https://hackmd.io/_uploads/HyCmj2n6ll.png)
    
    Helm chart（jp-gouin/helm-openldap）底層用的是 Bitnami 的 OpenLDAP 映像，它在「第一次啟動」時會自動建立預設樹，包括 `ou=users`、`ou=groups`，以及範例帳號 `user01`、`user02` 與群組 `readers`。除非你明確關閉它，否則都會被種下來。這個行為可用環境變數 `LDAP_SKIP_DEFAULT_TREE` 關掉。([ComputingForGeeks][1])

    ### 停用預設樹
    > https://github.com/jp-gouin/helm-openldap?tab=readme-ov-file#to-400
    > A default tree (Root organisation, users and group) is created during startup, this can be skipped using `LDAP_SKIP_DEFAULT_TREE` , however you need to use customLdifFiles or customLdifCm to create a root organisation.
    
    在 `openldap-values.yaml` 加上：

    ```yaml=
    env:
      LDAP_SKIP_DEFAULT_TREE: "yes"   # 不要建立預設 users/groups/範例帳號
    
    # （可選）若要自訂最小化樹，只放 base DN：
    customLdifFiles:
      00-base.ldif: |-
        dn: dc=asus,dc=com
        objectClass: top
        objectClass: dcObject
        objectClass: organization
        o: ASUS
        dc: asus
    ```

    - ### 說明
        - 看到 `user01/user02/readers` 正是 Bitnami 預設示範的組合；若想完全避免，務必啟用 `LDAP_SKIP_DEFAULT_TREE`。
        - Bitnami 映像提供 `env` 傳入環境變數 `LDAP_SKIP_DEFAULT_TREE`；`customLdifFiles` 可在啟動時載入你要的 LDIF。
        - 如果你把 `LDAP_SKIP_DEFAULT_TREE` 設為 `yes`，而且**不**提供任何 `customLdifFiles`，系統就不會替你建立 `ou=users`/`ou=groups` 或範例帳號。這正是你想要的狀態。([ComputingForGeeks](https://computingforgeeks.com/how-to-run-openldap-in-bitnami-docker-container/?utm_source=chatgpt.com))
        - 之後要擴充樹，再用 LDIF 匯入或在 phpLDAPadmin 裡自行新增即可。

<br>

## 步驟四：安裝 Chart

現在，讓我們使用自定義配置安裝 OpenLDAP Chart：

```bash
# 安裝 Chart
helm install openldap helm-openldap/openldap-stack-ha \
  -f openldap-values.yaml \
  --namespace openldap \
  --create-namespace
```
- **確認部署狀態**
    ```
    $ helm list -n openldap
    NAME       NAMESPACE   REVISION   UPDATED                                   STATUS     CHART                     APP VERSION
    openldap   openldap    1          2025-09-10 15:55:21.223470788 +0800 CST   deployed   openldap-stack-ha-4.3.3   2.6.9
    ```
- **執行過程**
    ```
    NAME: openldap
    LAST DEPLOYED: Mon Sep 22 08:45:48 2025
    NAMESPACE: openldap
    STATUS: deployed
    REVISION: 1
    TEST SUITE: None
    NOTES:
    ** Please be patient while the chart is being deployed **

    OpenLDAP-Stack-HA has been installed. You can access the server from within the k8s cluster using:

      openldap.openldap.svc.cluster.local:

      Or

      openldap.openldap.svc.cluster.local:


    You can access the LDAP adminPassword and configPassword using:

      kubectl get secret --namespace openldap openldap -o jsonpath="{.data.LDAP_ADMIN_PASSWORD}" | base64 --decode; echo
      kubectl get secret --namespace openldap openldap -o jsonpath="{.data.LDAP_CONFIG_ADMIN_PASSWORD}" | base64 --decode; echo


    You can access the LDAP service, from within the cluster (or with kubectl port-forward) with a command like (replace password and domain):
      ldapsearch -x -H ldap://openldap.openldap.svc.cluster.local: -b dc=example,dc=org -D "cn=admin,dc=example,dc=org" -w $LDAP_ADMIN_PASSWORD

    You can access PHPLdapAdmin, using

    You can access Self Service Password, using

    Test server health using Helm test:
      helm test openldap

    Enjoy :)
    ```

<br>

## 步驟五：驗證部署

安裝完成後，確認所有 Pod 是否正常運作：

```bash
# 查看 Pod 狀態
kubectl -n openldap get pods -l app.kubernetes.io/component=openldap

# 查看服務
kubectl -n openldap get svc -l app.kubernetes.io/component=openldap
```
- 等待所有 Pod 進入 Running 狀態。
- **備註**
    - `-l app.kubernetes.io/name=openldap-stack-ha` 找不到
    - 服務的標籤與名稱如下：
        | Service | name | labels |
        |---------|------|--------|
        | LDAP 服務 | `svc/openldap` | `app.kubernetes.io/component=openldap`<br>`release=openldap`<br>`chart=openldap-4.3.3` |
        | Headless |`svc/openldap-headless` | `app.kubernetes.io/component=openldap`<br>`release=openldap`<br>`chart=openldap-4.3.3` |
        | phpLDAPadmin |`svc/openldap-phpldapadmin` | `app=phpldapadmin`<br>`release=openldap`<br>`chart=phpldapadmin-0.1.2` |
        | ltb-passwd |`svc/openldap-ltb-passwd` | `app.kubernetes.io/name=openldap-ltb`<br>`app.kubernetes.io/instance=openldap`<br>`helm.sh/chart=ltb-passwd-0.1.0` |

<br>

或，**查看所有元件**：
```bash
$ kubectl -n openldap get all,cm,secret,sa

NAME                                         READY   STATUS    RESTARTS      AGE
pod/openldap-0                               1/1     Running   0             16m
pod/openldap-1                               1/1     Running   1 (15m ago)   15m
pod/openldap-2                               1/1     Running   1 (14m ago)   14m
pod/openldap-ltb-passwd-8684b5684b-96smr     1/1     Running   0             16m
pod/openldap-phpldapadmin-84b7b947fd-x6svs   1/1     Running   0             16m

NAME                            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)           AGE
service/openldap                ClusterIP   10.102.140.215   <none>        389/TCP,636/TCP   16m
service/openldap-headless       ClusterIP   None             <none>        389/TCP,636/TCP   16m
service/openldap-ltb-passwd     ClusterIP   10.109.51.252    <none>        80/TCP            16m
service/openldap-phpldapadmin   ClusterIP   10.107.170.66    <none>        80/TCP            16m

NAME                                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/openldap-ltb-passwd     1/1     1            1           16m
deployment.apps/openldap-phpldapadmin   1/1     1            1           16m

NAME                                               DESIRED   CURRENT   READY   AGE
replicaset.apps/openldap-ltb-passwd-8684b5684b     1         1         1       16m
replicaset.apps/openldap-phpldapadmin-84b7b947fd   1         1         1       16m

NAME                        READY   AGE
statefulset.apps/openldap   3/3     16m

NAME                                    DATA   AGE
configmap/kube-root-ca.crt              1      16m
configmap/openldap-env                  13     16m
configmap/openldap-ltb-passwd-ldap-cm   1      16m
configmap/openldap-phpldapadmin         4      16m
configmap/openldap-replication-acls     7      16m

NAME                                    TYPE                 DATA   AGE
secret/openldap                         Opaque               3      16m
secret/openldap-ltb-passwd              Opaque               1      16m
secret/sh.helm.release.v1.openldap.v1   helm.sh/release.v1   1      16m

NAME                                            SECRETS   AGE
serviceaccount/default                          0         16m
serviceaccount/openldap-openldap-stack-ha-foo   0         16m
```

<br>

## 步驟六：存取 LDAP 服務

### 從叢集內部存取

```bash
# 取得 LDAP 服務名稱與連接埠
LDAP_SERVICE=$(kubectl -n openldap get svc -l app.kubernetes.io/component=openldap -o jsonpath='{.items[0].metadata.name}')
LDAP_PORT=$(kubectl -n openldap get svc -l app.kubernetes.io/component=openldap -o jsonpath='{.items[0].spec.ports[?(@.name=="ldap-port")].port}')

echo "LDAP 服務可透過 $LDAP_SERVICE:$LDAP_PORT 存取"
```
- 執行範例
    ```
    LDAP 服務可以通過 openldap:389 訪問
    ```

<br>

---

### 使用暫時性的 Pod 進行測試

```bash
# 建立一個暫時性 Pod ，來測試 LDAP 連線
kubectl run ldap-test --rm -it --image=bitnami/openldap:latest -- bash

$ ldapsearch -x \
  -H ldap://openldap[.<namespace>]:389 \
  -D "cn=admin,dc=esc4000,dc=ocis,dc=asus,dc=com" \
  -w YourSecurePassword \
  -b "dc=esc4000,dc=ocis,dc=asus,dc=com" \
  "(objectClass=*)" -LLL
```
- ### `ldapsearch -x ...` 指令完整解說
    
    ### 這條指令在做什麼（大意）

    用**簡易認證**（`-x`）以**管理者 DN**登入位於 `openldap:389` 的 LDAP 伺服器，從**基底 DN** `dc=esc4000,dc=ocis,dc=asus,dc=com` 往下做**整棵樹（subtree）搜尋**，把**所有物件（`(objectClass=*)`）**與其屬性輸出為**精簡 LDIF**（`-LLL`）格式。

    ---

    ### 逐項參數解釋

    - `ldapsearch`
        OpenLDAP 的查詢工具。
    - `-x`
        用「**簡易綁定**（simple bind）」方式登入，而不是 SASL。這在用 `cn=admin,...` + 密碼的情境是正確的。
    - `-H ldap://openldap:389`
        指定目標伺服器的 **URI**。
        - `openldap` 是 **Service 名稱**（在 `openldap` 命名空間內部署時，DNS 會解析成 `openldap.openldap.svc.cluster.local`）。
        - `389` 是 LDAP（明文或 StartTLS）的服務埠。Chart 內部容器用 1389，但 Service 對外是 389——你用 Service 即可。
    - `-D "cn=admin,dc=esc4000,dc=ocis,dc=asus,dc=com"`
        **Bind DN**（要用來登入的身分）。這裡是你 Chart 產生的 **管理者 DN**。

        > 你的 `global.ldapDomain: esc4000.ocis.asus.com` 會對應 Base DN `dc=esc4000,dc=ocis,dc=asus,dc=com`，預設管理者 DN 為 `cn=admin,<BaseDN>`。

    - `-w YourSecurePassword`
        管理者密碼（對應你 values 中 `adminPassword`）。
        🔒 建議平常用 `-W`（大寫 W）讓工具**互動式輸入**密碼，避免密碼出現在 shell history。

    - `-b "dc=esc4000,dc=ocis,dc=asus,dc=com"`
      **Base DN**（搜尋的起點）。
      預設搜尋範圍是 **sub**（整棵子樹），等同 `-s sub`。

    - `"(objectClass=*)"`
      **過濾條件**（LDAP filter）。這個條件匹配「**所有物件**」，也就是把整棵樹都列出。
      你也可以改成：

      * `"(uid=user1)"`：找某個使用者
      * `"(&(objectClass=inetOrgPerson)(sn=Lin))"`：符合多條件

    - `-LLL`
      **輸出格式**調整：LDIF 但**去掉版本列與註解**，最適合丟管線或做自動化。

      * `-L`：LDIF（含版本/註解）
      * `-LL`：LDIF（去註解）
      * `-LLL`：LDIF（去版本 + 去註解）

    > 若你在 **default** 命名空間執行工具 Pod，`openldap` 這個短名不會解析，請改用 FQDN：
    > `-H ldap://openldap.openldap.svc.cluster.local:389`

    ---

    ## 預期結果（節選示意）

    輸出是 **LDIF**，每個條目都有 `dn:`，接著列屬性，例如：

    ```
    dn: dc=esc4000,dc=ocis,dc=asus,dc=com
    objectClass: top
    objectClass: dcObject
    objectClass: organization
    o: esc4000.ocis.asus.com
    dc: esc4000

    dn: ou=People,dc=esc4000,dc=ocis,dc=asus,dc=com
    objectClass: top
    objectClass: organizationalUnit
    ou: People
    ...
    ```

    ---

    ## 常見變體與小技巧

    * **只取特定屬性**
      在 filter 後面加屬性清單（空白分隔）：

      ```bash
      ldapsearch -x -H ldap://openldap:389 \
        -D "cn=admin,dc=..." -W \
        -b "dc=..." "(uid=user1)" uid cn mail -LLL
      ```

    * **改搜尋範圍（scope）**

      * `-s base`：只看 Base DN 本身
      * `-s one`：只看 Base 的**下一層**
      * `-s sub`：**整棵**子樹（預設）

    * **LDAPS 或 StartTLS**

      * LDAPS（636）：`-H ldaps://openldap:636`（記得證書 SAN 要能匹配你連的主機/IP）
      * StartTLS：在 389 上加 `-ZZ`

        ```bash
        ldapsearch -x -ZZ -H ldap://openldap:389 ...
        ```

    * **從叢集外連（只有內網 IP）**
      先把 `svc/openldap` 設為 **NodePort**，再改 URI：
      `-H ldap://10.78.26.241:30389`

    * **排查密碼/身分錯**
      `Invalid credentials (49)` 通常是 **Bind DN 或密碼不對**；請對照 `adminPassword` 與 DN 是否完全吻合。
      `anonymous bind disallowed (48)` 表示你沒帶 `-D/-w` 企圖匿名查詢，但伺服器禁了匿名綁定——加上 `-D/-W` 即可。

    ---

    ## 小結

    * 這條指令用 **管理者 DN + 密碼**，在 `openldap:389` 做 **subtree 搜尋**，把整個目錄以**精簡 LDIF**印出。
    * 依需求常改的只有三處：**URI（-H）**、**Bind DN/密碼（-D/-W）**、**Base DN（-b）/filter**。
    * 自動化腳本建議用 `-W` 互動式輸入或讀環境變數，避免把密碼寫在命令列。

- **執行結果**
    ```
    dn: dc=esc4000,dc=ocis,dc=asus,dc=com
    objectClass: dcObject
    objectClass: organization
    dc: esc4000
    o: example

    dn: ou=users,dc=esc4000,dc=ocis,dc=asus,dc=com
    objectClass: organizationalUnit
    ou: users

    dn: ou=groups,dc=esc4000,dc=ocis,dc=asus,dc=com
    objectClass: organizationalUnit
    ou: groups

    dn: cn=user01,ou=users,dc=esc4000,dc=ocis,dc=asus,dc=com
    cn: User1
    cn: user01
    sn: Bar1
    objectClass: inetOrgPerson
    objectClass: posixAccount
    objectClass: shadowAccount
    userPassword:: Yml0bmFtaTE=
    uid: user01
    uidNumber: 1000
    gidNumber: 1000
    homeDirectory: /home/user01

    dn: cn=user02,ou=users,dc=esc4000,dc=ocis,dc=asus,dc=com
    cn: User2
    cn: user02
    sn: Bar2
    objectClass: inetOrgPerson
    objectClass: posixAccount
    objectClass: shadowAccount
    userPassword:: Yml0bmFtaTI=
    uid: user02
    uidNumber: 1001
    gidNumber: 1001
    homeDirectory: /home/user02

    dn: cn=readers,ou=users,dc=esc4000,dc=ocis,dc=asus,dc=com
    cn: readers
    objectClass: groupOfNames
    member: cn=user01,ou=users,dc=esc4000,dc=ocis,dc=asus,dc=com
    member: cn=user02,ou=users,dc=esc4000,dc=ocis,dc=asus,dc=com
    ```

<br>

---

### 存取 LDAP 服務的流程解說

1. 叢集**內部**用 `ldap://openldap.<namespace>:389`；
2. 叢集**外部**（NodePort）用 `ldap://10.78.26.241:30389`。

---

#### (1) 叢集內部：`ldapsearch` → Service → Pod（簡易綁定 + 子樹查詢）
- #### FlowChart
    ```mermaid
    flowchart LR
        T["工具 Pod：ldapsearch"]
        SVC["Service：openldap（ClusterIP 389）"]
        P1["Pod：openldap-0（slapd 1389）"]
        P2["Pod：openldap-1（slapd 1389）"]
        P3["Pod：openldap-2（slapd 1389）"]
        D["DIT：dc=esc4000,dc=ocis,dc=asus,dc=com"]

        T -- "ldap://openldap:389" --> SVC
        SVC -- "Endpoints" --> P1
        SVC -- "Endpoints" --> P2
        SVC -- "Endpoints" --> P3
        P1 --- D
        P2 --- D
        P3 --- D
    ```
- #### SequenceDiagram
    ```mermaid
    sequenceDiagram
    autonumber
    participant U as ldapsearch（工具 Pod）
    participant DNS as CoreDNS
    participant SVC as Service openldap<br/>(ClusterIP:389 → targetPort:1389)
    participant P as openldap-* Pod（slapd:1389）
    participant D as 目錄資料樹（DIT）<br/>dc=esc4000,dc=ocis,dc=asus,dc=com

    U->>DNS: 解析 openldap（短名或 FQDN）
    DNS-->>U: 回傳 ClusterIP
    U->>SVC: TCP 389 建立連線
    U->>SVC: Bind 請求（-x 簡易綁定，-D 管理者 DN，-w 密碼）
    SVC->>P: 轉送到後端 Pod（slapd:1389）
    P-->>U: BindResponse（成功/失敗）
    U->>P: SearchRequest（-b BaseDN，scope=sub，filter=objectClass=*）
    P->>D: 評估查詢、擷取條目
    D-->>P: 條目與屬性
    P-->>U: SearchResultEntry… + SearchResultDone
    ```
- #### 對應參數
    - `-H ldap://openldap[.<namespace>]:389` → 透過 K8s **Service** 存取（ClusterIP 389 → Pod 1389）
    - `-D "cn=admin,dc=esc4000,dc=ocis,dc=asus,dc=com"` → **Bind DN**（管理者 DN）
    - `-w YourSecurePassword` → 管理者密碼（建議平時用 `-W` 互動輸入）
    - `-b "dc=esc4000,dc=ocis,dc=asus,dc=com"` → **Base DN**（查詢起點，預設 scope=sub）
    - `"(objectClass=*)"` → 取整棵子樹所有物件
    - `-LLL` → 精簡 LDIF 輸出（去版本/註解）

---

### (2) 叢集外部（選用）：NodePort 對外提供
- #### FlowChart
    ```mermaid
    flowchart LR
        C["用戶端/跳板機<br/>ldapsearch"]
        N["叢集節點 IP<br/>NodePort 30389"]
        SVC["Service：openldap<br/>（ClusterIP 389）"]
        P1["Pod：openldap-0（slapd 1389）"]
        P2["Pod：openldap-1（slapd 1389）"]
        P3["Pod：openldap-2（slapd 1389）"]
        D["DIT<br/>dc=esc4000,dc=ocis,dc=asus,dc=com"]

        C -- "ldap://10.78.26.241:30389" --> N
        N -- "Kube-Proxy / iptables" --> SVC
        SVC -- "Endpoints" --> P1
        SVC -- "Endpoints" --> P2
        SVC -- "Endpoints" --> P3
        P1 --- D
        P2 --- D
        P3 --- D
    ```
- #### 對應作法
    - Service 改成 `NodePort`（例：`389→30389`、`636→30636`）
    - 外部測試：
      `ldapsearch -x -H ldap://10.78.26.241:30389 -D "cn=admin,dc=..." -W -b "dc=..." "(objectClass=*)" -LLL`
    - 若用 **LDAPS**：`ldaps://10.78.26.241:30636`，憑證 SAN 需包含 **IP** 才不會驗證失敗

---

#### 小提醒

- 在 **不同命名空間或 default** 內測試，建議用 FQDN：
  `-H ldap://openldap.openldap.svc.cluster.local:389`
- `Invalid credentials (49)` 幾乎都是 **Bind DN 或密碼不符**。
- `anonymous bind disallowed (48)` 表示需帶 `-D/-W`。
- Chart 新版容器內部預設埠是 **1389/1636**，**Service** 對外仍是 **389/636**（或你的 NodePort）。

<br>

---

<br>

## 步驟七：配置 phpLDAPadmin 訪問

如果您啟用了 phpLDAPadmin，您可以通過端口轉發來訪問管理界面：

```bash
# 尋找 phpLDAPadmin Pod 的名稱
PHPLDAP_POD=$(kubectl get pods -l app.kubernetes.io/name=phpldapadmin -o jsonpath='{.items[0].metadata.name}')

# 設置端口轉發
kubectl port-forward pod/$PHPLDAP_POD 8080:80
```

現在，您可以在瀏覽器中訪問 http://localhost:8080 來使用 phpLDAPadmin。

登錄憑證：
- 登錄 DN: `cn=admin,dc=example,dc=org`
- 密碼: `YourSecurePassword` (您在 values 文件中設置的 adminPassword)

<br>

## 步驟八：初始化 LDAP 數據

您可以通過 phpLDAPadmin 界面或使用 LDIF 文件來初始化 LDAP 數據。

### 使用 LDIF 文件添加用戶和組

創建一個 LDIF 文件 `initial-data.ldif`：

```ldif
# 添加組織單位
dn: ou=People,dc=example,dc=org
objectClass: organizationalUnit
ou: People

dn: ou=Groups,dc=example,dc=org
objectClass: organizationalUnit
ou: Groups

# 添加用戶
dn: uid=user1,ou=People,dc=example,dc=org
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: user1
sn: User1
givenName: Test
cn: Test User1
displayName: Test User1
uidNumber: 10000
gidNumber: 10000
userPassword: password1
gecos: Test User1
loginShell: /bin/bash
homeDirectory: /home/user1

# 添加組
dn: cn=users,ou=Groups,dc=example,dc=org
objectClass: posixGroup
cn: users
gidNumber: 10000
memberUid: user1
```

應用 LDIF 文件：

```bash
# 使用臨時 Pod 應用 LDIF 文件
kubectl run ldap-add --rm -it --image=bitnami/openldap:latest -- \
  ldapadd -x -H ldap://my-openldap-openldap-stack-ha:389 \
  -D "cn=admin,dc=example,dc=org" \
  -w YourSecurePassword -f /tmp/initial-data.ldif
```

注意：您需要先將 LDIF 文件複製到 Pod 中，或者使用 ConfigMap 來掛載它。

<br>

## 步驟九：配置 TLS/LDAPS (可選)

若要啟用 LDAPS，您需要創建一個包含 TLS 證書和密鑰的 Secret：

```bash
# 生成自簽名證書 (生產環境中應使用正式 CA 簽發的證書)
openssl req -new -x509 -days 365 -nodes \
  -out tls.crt -keyout tls.key \
  -subj "/CN=my-openldap-openldap-stack-ha.default.svc.cluster.local"

# 創建 Secret
kubectl create secret generic ldap-tls-secret \
  --from-file=tls.crt \
  --from-file=tls.key \
  --from-file=ca.crt=tls.crt
```

更新 values 文件以啟用 TLS：

```yaml
initTLSSecret:
  tls_enabled: true
  secret: "ldap-tls-secret"
```

然後升級 Helm 釋出：

```bash
helm upgrade my-openldap helm-openldap/openldap-stack-ha -f my-openldap-values.yaml
```

<br>

## 步驟十：故障排除指南

### 常見問題與解決方案

#### 1. 連接問題

如果無法連接到 LDAP 服務：

```bash
# 檢查服務是否正常運行
kubectl get pods -l app.kubernetes.io/name=openldap-stack-ha

# 檢查服務端點
kubectl get endpoints my-openldap-openldap-stack-ha

# 測試從叢集內部連接
kubectl run ldap-test --rm -it --image=bitnami/openldap:latest -- \
  ldapsearch -x -H ldap://my-openldap-openldap-stack-ha:389 \
  -b dc=example,dc=org \
  -D "cn=admin,dc=example,dc=org" \
  -w YourSecurePassword
```

#### 2. 認證失敗

如果認證失敗：

```bash
# 重設管理員密碼
kubectl exec -it my-openldap-openldap-stack-ha-0 -- \
  ldappasswd -x -H ldap://localhost:389 \
  -D "cn=admin,dc=example,dc=org" \
  -w OldPassword \
  -s NewPassword \
  "cn=admin,dc=example,dc=org"
```

#### 3. 複製問題

如果複製不同步：

```bash
# 檢查複製狀態
kubectl exec -it my-openldap-openldap-stack-ha-0 -- \
  ldapsearch -x -H ldap://localhost:389 \
  -b cn=config \
  -D "cn=admin,cn=config" \
  -w YourSecurePassword \
  "olcSyncrepl=*"
```

<br>

## 總結

通過這個步驟指南，您已經學會了如何：

1. 在 Kubernetes 上部署高可用的 OpenLDAP 服務
2. 配置基本的 LDAP 設定和安全選項
3. 初始化和管理 LDAP 目錄資料
4. 啟用進階功能如 TLS 加密和多主複製
5. 與其他 Kubernetes 應用程式整合
6. 進行常見的故障排除和維護操作

這個部署使用了 helm-openldap Chart，提供了易於管理的高可用 LDAP 解決方案，適合企業級環境使用。

如需更詳細的配置選項，請參考原始文檔和 Chart 說明：`helm show values helm-openldap/openldap-stack-ha`

祝您順利部署！


<br>

{%hackmd vaaMgNRPS4KGJDSFG0ZE0w %}
