croessner/docker-openldap
GitHub: croessner/docker-openldap
一个基于 Alpine Linux、从源码编译固定版本 OpenLDAP 的 Docker 镜像,提供以环境变量驱动的配置与持久化 cn=config 支持。
Stars: 0 | Forks: 0
# Alpine 上的 OpenLDAP
[](https://github.com/croessner/docker-openldap/actions/workflows/docker-publish.yml)
[](./LICENSE)
[](https://github.com/croessner/docker-openldap/commits/main)
[](https://github.com/croessner/docker-openldap/stargazers)
该项目基于 **Alpine Linux** 构建完整的 OpenLDAP 镜像,**直接从源码编译固定版本的 OpenLDAP**,并内置 **动态后端(backends)、覆盖层(overlays)和密码模块**。默认情况下,容器会从生成的 **`slapd.conf`** 启动并初始化数据,但也可以切换到持久的 **`slapd.d` / `cn=config`** 运行模式。配置风格遵循知名容器镜像的惯例:尽可能通过 **环境变量** 配置,其余部分通过 **LDIF 引导、模式目录和配置片段** 完成。
## 目录
- [目标](#goals)
- [项目结构](#project-structure)
- [包含组件](#included-components)
- [快速开始](#quick-start)
- [发布](#publishing)
- [许可证](#license)
- [运行模型](#operating-model)
- [重要卷/挂载点](#important-volumes--mountpoints)
- [环境变量](#environment-variables)
- [启用 TLS](#enabling-tls)
- [初始化脚本与 LDIF](#init-scripts-and-ldifs)
- [自定义模式](#custom-schemas)
- [自定义配置片段](#custom-config-snippets)
- [完全自定义配置](#fully-custom-configuration)
- [一次性 `slapd.d` 模式](#one-shot-slapdd-mode)
- [健康检查](#health-check)
- [注意事项与限制](#notes-and-limitations)
- [开发与便捷用法](#development--convenience)
- [参考资料](#references)
## 目标
- 基于 Alpine 的运行时
- 通过 Docker 构建参数固定 OpenLDAP 源码版本
- 以环境变量驱动的默认配置,支持可选的持久化 `slapd.d` / `cn=config`
- 完整镜像,包含编译好的后端、覆盖层和密码模块
- 为 `mdb` 提供健全的默认设置
- 通过 `docker-entrypoint-initdb.d` 进行首次初始化
- 支持通过环境变量启用 TLS、访问日志、syncprov、memberOf/refint
- 可通过自定义 `.schema` 文件和 `.conf` 片段扩展
- 通过 `stdout/stderr` 输出干净的容器日志
- 通过本地 `ldapi` 进行健康检查
## 项目结构
```
.
├── Dockerfile
├── docker-entrypoint.sh
├── docker-healthcheck.sh
├── README.md
├── .env.example
├── .gitignore
├── Makefile
└── examples
├── docker-compose.yml
├── bootstrap
│ └── 20-demo-user.ldif
└── custom-config
├── post
│ └── 70-extra.conf
└── pre
└── 50-global.conf
```
## 包含组件
镜像构建分为两个阶段:
- **构建阶段**:从官方源码 tarball 编译 OpenLDAP
- **运行时阶段**:仅使用 Alpine 作为基础镜像,并添加必要的运行时库和 `su-exec`
OpenLDAP 构建启用了动态模块,因此镜像不仅可以加载 `mdb`,还可以从上游源码树加载额外的后端和覆盖层。生成的默认配置**有意专注于干净的 `mdb` 设置**。对于特殊场景,可以加载额外模块、提供自己的 `slapd.conf`,或持久化 `slapd.d` 作为运行时的数据源。
## 快速开始
### 1. 构建
```
docker build -t openldap .
```
固定特定上游版本:
```
docker build \
--build-arg OPENLDAP_VERSION=2.6.13 \
--build-arg OPENLDAP_SHA256=d693b49517a42efb85a1a364a310aed16a53d428d1b46c0d31ef3fba78fcb656 \
-t openldap:2.6.13 .
```
使用 `buildx` 构建多架构镜像:
```
docker buildx build \
--platform linux/amd64,linux/arm64 \
--build-arg OPENLDAP_VERSION=2.6.13 \
--build-arg OPENLDAP_SHA256=d693b49517a42efb85a1a364a310aed16a53d428d1b46c0d31ef3fba78fcb656 \
-t openldap:2.6.13 \
.
```
发布到您自己的 Docker Hub 命名空间:
```
docker login
make push IMAGE_NAME=/openldap TAG=latest
```
创建本地 SBOM 导出:
```
make sbom-local
```
### 2. 启动容器
```
docker run -d \
--name openldap \
-p 389:389 \
-p 636:636 \
-e LDAP_DOMAIN=example.org \
-e LDAP_BASE_DN=dc=example,dc=org \
-e LDAP_ORGANISATION="Example Inc." \
-e LDAP_ADMIN_PASSWORD=supersecret \
-v $(pwd)/data/openldap:/var/lib/openldap/openldap-data \
-v $(pwd)/data/accesslog:/var/lib/openldap/accesslog \
-v $(pwd)/examples/bootstrap:/docker-entrypoint-initdb.d:ro \
openldap
```
### 3. 测试
```
ldapsearch -x -H ldap://127.0.0.1:389 -b dc=example,dc=org -D "cn=admin,dc=example,dc=org" -w supersecret
```
## 发布
该仓库包含一个 GitHub Actions 工作流 `.github/workflows/docker-publish.yml`,用于将维护者镜像发布到 Docker Hub,镜像名为 `chrroessner/openldap`。
还包含 `.github/workflows/openldap-upstream-check.yml`,该工作流每天运行,检查官方 OpenLDAP 发布目录是否有更新的上游 tarball,刷新固定的 SHA256,并在该仓库的固定版本落后上游时自动打开或更新 Pull Request。
如果您 fork 本仓库,请调整工作流中的镜像名称并使用您自己的 Docker Hub 命名空间。出于此原因,本 README 中的示例推送命令有意使用 `/openldap`。
工作流在以下情况下运行:
- 推送到 `main`
- 推送到 `master`
- 匹配 `v*` 的 Git 标签
- 每周一通过 `schedule`
- 通过 `workflow_dispatch` 手动触发
计划运行使用 `docker/build-push-action` 并启用 `pull: true`。这意味着重建时会自动拉取 Dockerfile 中固定 Alpine 小版本标签的最新 digest,例如 `alpine:3.23`。
SBOM 在三个位置集成:
- 发布的镜像会包含 OCI 证明(provenance)和 SBOM 附件
- 工作流导出适用于 `linux/amd64` 和 `linux/arm64` 的可下载 SPDX JSON 文件
- Dockerfile 启用了 BuildKit SBOM 扫描,适用于构建上下文和构建阶段,因此 SBOM 不仅限于最终运行时层
所需的 GitHub 仓库密钥:
- `DOCKERHUB_USERNAME`
- `DOCKERHUB_TOKEN`
推荐的 Docker Hub 设置:
- 在您自己的命名空间中创建公共仓库,例如 `/openldap`
- 创建专用于 CI 的 Docker Hub 访问令牌
- 将 `latest` 保留给默认分支
- 以格式 `v-r` 发布标签,例如 `v2.6.13-r1`
重要限制:
- 这不会自动从 Alpine `3.23` 跳转到 `3.24`
- 对于新的 Alpine 小版本发布,请在 `Dockerfile` 中更新 `ALPINE_VERSION`
本地 SBOM 用法:
```
make sbom-local
ls dist/sbom-local | grep sbom
```
注册表 SBOM 检查(推送后):
```
make sbom-registry IMAGE_NAME=/openldap TAG=latest
```
建议首次发布:
```
git tag v2.6.13-r1
git push origin main --tags
```
## 许可证
仓库内容根据 MIT 许可证授权。参见 [LICENSE](./LICENSE)。
发布的容器镜像还包含 OpenLDAP,其根据 `OLDAP-2.8` 分发。因此,OCI 镜像元数据声明为 `MIT AND OLDAP-2.8`。
## 运行模型
启动时按以下步骤执行:
1. 入口脚本读取并规范化环境变量。
2. 如果 `LDAP_CONFIG_BACKEND=slapd.conf`,则验证并从 `slapd.conf` 启动。
3. 如果 `LDAP_CONFIG_BACKEND=slapd.d`,容器行为如下:
- 如果 `LDAP_CONFIG_DIR` 为空,则使用 `slaptest -f ... -F ...` 一次性从 `slapd.conf` 种子 `slapd.d`
- 如果 `LDAP_CONFIG_DIR` 已包含数据,则将持久化的 `slapd.d` 树视为权威
4. 如果数据目录为空且活动配置从当前环境驱动的 `slapd.conf` 种子,则初始化一个新目录:
- 基础条目(`LDAP_BASE_DN`)
- 可选的管理员条目
- 可选的 `people` 和 `groups` OU
- 随后处理 `docker-entrypoint-initdb.d`
5. 如果重用持久化的 `slapd.d` 树,则有意跳过基于环境的引导,因为活动的 `cn=config` 树可能已与当前环境值不同。
6. 之后,`slapd` 在前台启动并输出日志到容器输出。
## 重要卷/挂载点
| 容器路径 | 用途 |
|----------|------|
| `/var/lib/openldap/openldap-data` | 主 `mdb` 数据库 |
| `/var/lib/openldap/accesslog` | 独立的访问日志数据库 |
| `/docker-entrypoint-initdb.d` | 首次初始化(`.ldif`、`.sh`) |
| `/etc/openldap/custom-schema` | 额外的 `.schema` 文件 |
| `/etc/openldap/custom/pre` | 额外全局配置(在数据库块之前) |
| `/etc/openldap/custom-config/post` | 额外配置(在生成的 DB 块之后) |
| `/etc/openldap/certs` | TLS 证书/密钥 |
## 环境变量
### 核心参数
| 变量 | 默认值 | 含义 |
|------|--------|------|
| `LDAP_DOMAIN` | 空 | DNS 域,作为便捷输入 |
| `LDAP_BASE_DN` | `dc=example,dc=org` | 基 DN / 后缀 |
| `LDAP_ORGANISATION` | 从基 DN 派生 | 基础条目的值 |
| `LDAP_ADMIN_USERNAME` | `admin` | 默认管理员的 CN |
| `LDAP_ADMIN_PASSWORD` | — | 明文密码 |
| `LDAP_ADMIN_PASSWORD_FILE` | — | 从文件/密钥读取密码 |
| `LDAP_ADMIN_PASSWORD_HASH` | — | 用于运行时认证的预哈希替代方案;首次启动引导仍需要明文密码 |
| `LDAP_PASSWORD_HASH_SCHEME` | `{ARGON2}` | 用于从 `LDAP_ADMIN_PASSWORD` 派生 `LDAP_ADMIN_PASSWORD_HASH` 的方案 |
| `LDAP_ADMIN_DN` | `cn=,` | 管理员的完整 DN |
### 监听器 / 运行时
| 变量 | 默认值 | 含义 |
|------|--------|------|
| `LDAP_ENABLE_LDAP` | `true` | 启用 389 端口的 LDAP |
| `LDAP_ENABLE_LDAPS` | 同 `LDAP_ENABLE_TLS` | 启用 636 端口的 LDAPS |
| `LDAP_PORT_NUMBER` | `389` | LDAP 端口 |
| `LDAP_LDAPS_PORT_NUMBER` | `636` | LDAPS 端口 |
| `LDAP_LDAPI_URI` | `ldapi://%2Fvar%2Frun%2Fopenldap%2Fldapi` | 用于健康检查和引导的本地 IPC 套接字 |
| `LDAP_LOG_LEVEL` | `256` | `slapd.conf` 的 `loglevel` |
| `LDAP_DEBUG_LEVEL` | 同 `LDAP_LOG_LEVEL` | 数值 `slapd -d` |
| `LDAP_THREADS` | 空 | 可选的线程调整 |
| `LDAP_TIMELIMIT` | 空 | 可选的全局搜索限制 |
| `LDAP_SIZELIMIT` | 空 | 可选的全局大小限制 |
### 数据库 / 引导
| 变量 | 默认值 | 含义 |
|------|--------|------|
| `LDAP_DB_DIR` | `/var/lib/openldap/openldap-data` | 主数据库路径 |
| `LDAP_MDB_MAXSIZE` | `1073741824` | `mdb maxsize` |
| `LDAP_MDB_CHECKPOINT` | `1024 5` | `mdb checkpoint` |
| `LDAP_MDB_DBNOSYNC` | `false` | 可选的 `dbnosync` |
| `LDAP_SKIP_DEFAULT_TREE` | `false` | 不自动创建基础树 |
| `LDAP_CREATE_PEOPLE_OU` | `true` | 创建 `ou=people` |
| `LDAP_CREATE_GROUPS_OU` | `true` | 创建 `ou=groups` |
| `LDAP_PEOPLE_OU` | `people` | 用户 OU 名称 |
| `LDAP_GROUPS_OU` | `groups` | 组 OU 名称 |
| `LDAP_INITDB_DIR` | `/docker-entrypoint-initdb.d` | 初始化目录 |
### 模式 / 模块 / 扩展
| 变量 | 默认值 | 含义 |
|------|--------|------|
| `LDAP_CONFIG_BACKEND` | `slapd.conf` | 运行时配置后端:`slapd.conf` 或 `slapd.d` |
| `LDAP_CONFIG_DIR` | `/etc/openldap/slapd.d` | 持久化 `slapd.d` 目录 |
| `LDAP_EXTRA_SCHEMAS` | `cosine inetorgperson nis` | 额外的标准模式 |
| `LDAP_LOAD_MODULES` | 空 | 额外模块,逗号或空格分隔 |
| `LDAP_CUSTOM_SCHEMA_DIR` | `/etc/openldap/custom-schema` | 自定义 `.schema` 文件目录 |
| `LDAP_CUSTOM_PRECONFIG_DIR` | `/etc/openldap/custom-config/pre` | 额外全局配置(位于数据库块之前) |
| `LDAP_CUSTOM_POSTCONFIG_DIR` | `/etc/openldap/custom-config/post` | 额外配置(位于生成的 DB 块之后) |
| `LDAP_SKIP_DEFAULT_CONFIG` | `false` | 使用您自己的完整 `slapd.conf` |
### TLS
| 变量 | 默认值 | 含义 |
|------|--------|------|
| `LDAP_ENABLE_TLS` | `false` | 在 `slapd.conf` 中启用 TLS 指令 |
| `LDAP_REQUIRE_TLS` | `false` | 禁止未加密的简单绑定 |
| `LDAP_TLS_CERT_FILE` | 空 | 服务器证书 |
| `LDAP_TLS_KEY_FILE` | 空 | 私钥 |
| `LDAP_TLS_CA_FILE` | 空 | CA 证书 |
| `LDAP_TLS_DH_PARAM_FILE` | 空 | 可选的 DH 参数 |
| `LDAP_TLS_CIPHER_SUITE` | 空 | 可选的密码套件 |
| `LDAP_TLS_VERIFY_CLIENT` | `never` | `TLSVerifyClient` |
| `LDAP_SIMPLE_BIND_MIN_SSF` | `128` | 当 `LDAP_REQUIRE_TLS=true` 时的最小 SSF |
### 覆盖层 / 额外功能
| 变量 | 默认值 | 含义 |
|------|--------|------|
| `LDAP_ENABLE_MONITOR_DB` | `true` | 添加 `database monitor` |
| `LDAP_ENABLE_SYNCPROV` | `false` | 启用 `overlay syncprov` |
| `LDAP_SYNCPROV_CHECKPOINT` | `100 10` | `syncprov-checkpoint` |
| `LDAP_SYNCPROV_SESSIONLOG` | 空 | 可选的 `syncprov-sessionlog` |
| `LDAP_ENABLE_MEMBEROF` | `false` | 启用 `memberOf` 覆盖层 |
| `LDAP_ENABLE_REFINT` | 同 `LDAP_ENABLE_MEMBEROF` | 启用 `refint` 覆盖层 |
| `LDAP_ENABLE_ACCESSLOG` | `false` | 启用访问日志数据库 + 覆盖层 |
| `LDAP_ACCESSLOG_SUFFIX` | `cn=accesslog` | 访问日志数据库后缀 |
| `LDAP_ACCESSLOG_ROOTDN` | `cn=accesslog` | 访问日志数据库的 RootDN |
| `LDAP_ACCESSLOG_DB_DIR` | `/var/lib/openldap/accesslog` | 访问日志数据库路径 |
| `LDAP_ACCESSLOG_MAXSIZE` | `268435456` | `mdb maxsize`(访问日志) |
| `LDAP_ACCESSLOG_LOGOPS` | `writes` | `logops` |
| `LDAP_ACCESSLOG_LOGPURGE` | `07+00:00 01+00:00` | `logpurge` |
上游贡献的模块、`.schema` 和 `.ldif` 文件会在构建时复制到镜像的模式目录中(如果存在)。`accesslog` 覆盖层是一个特例:OpenLDAP 2.6 会从模块本身注册其审计模式,因此没有单独的 upstream `audit.schema` 文件可供安装。
## 启用 TLS
示例:
```
docker run -d \
--name openldap \
-p 389:389 \
-p 636:636 \
-e LDAP_DOMAIN=example.org \
-e LDAP_BASE_DN=dc=example,dc=org \
-e LDAP_ADMIN_PASSWORD=supersecret \
-e LDAP_ENABLE_TLS=true \
-e LDAP_ENABLE_LDAPS=true \
-e LDAP_REQUIRE_TLS=true \
-e LDAP_TLS_CERT_FILE=/etc/openldap/certs/tls.crt \
-e LDAP_TLS_KEY_FILE=/etc/openldap/certs/tls.key \
-e LDAP_TLS_CA_FILE=/etc/openldap/certs/ca.crt \
-v $(pwd)/certs:/etc/openldap/certs:ro \
openldap
```
## 初始化脚本与 LDIF
`/docker-entrypoint-initdb.d` 目录**仅在首次初始化空数据库时**处理。
支持的文件类型:
- `*.ldif`
- 包含 `changetype:` → `ldapmodify`
- 不包含 `changetype:` → `ldapadd`
- `*.sh`
- 可执行文件或通过 `/bin/sh` 运行
所有 LDIF 均通过本地 `ldapi:///` 在引导时应用。当运行时策略需要传输安全时,请选择仍允许本地引导路径但拒绝不安全远程简单绑定的 `LDAP_SIMPLE_BIND_MIN_SSF` 值。
示例文件:`examples/bootstrap/20-demo-user.ldif`
## 自定义模式
将 `.schema` 文件放置在:
```
/etc/openldap/custom-schema
```
这些文件会通过 `include` 自动包含。
## 自定义配置片段
**在数据库块之前**:
**在生成的 DB 块之后**:
```
/etc/openldap/custom-config/post
```
这允许您添加,例如:
- 全局 `security`、`limits`、`threads`
- 额外数据库
- 更多覆盖层
- 细粒度的 ACL 调整
- 实验性或罕见模块
## 完全自定义配置
如果您希望完全绕过默认生成:
1. 挂载您自己的 `slapd.conf`
2. 设置 `LDAP_SKIP_DEFAULT_CONFIG=true`
示例:
```
docker run -d \
--name openldap \
-e LDAP_SKIP_DEFAULT_CONFIG=true \
-v $(pwd)/my-slapd.conf:/etc/openldap/slapd.conf:ro \
-v $(pwd)/data:/var/lib/openldap/openldap-data \
openldap
```
在此模式下,您完全负责配置。
## 一次性 `slapd.d` 模式
如果偏好运行时使用 `cn=config` / `slapd.d`:
```
docker run -d \
--name openldap \
-e LDAP_CONFIG_BACKEND=slapd.d \
-v $(pwd)/slapd.d:/etc/openldap/slapd.d \
-v $(pwd)/data:/var/lib/openldap/openldap-data \
openldap
```
此模式下的行为:
- 首次启动时,若 `LDAP_CONFIG_DIR` 为空,则从当前 `slapd.conf` 种子 `slapd.d`
- 后续启动时,若 `LDAP_CONFIG_DIR` 已包含数据,则直接使用持久化的 `slapd.d` 树
- `slapd.d` 存在后,基于环境的配置更改不再自动重新应用
- 这是有意的:持久化的 `cn=config` 树成为数据源
## 健康检查
健康检查针对以下内容运行:
```
ldapi://%2Fvar%2Frun%2Fopenldap%2Fldapi
```
它使用:
```
ldapsearch -Q -Y EXTERNAL -H ldapi://%2Fvar%2Frun%2Fopenldap%2Fldapi -LLL -s base -b "" namingContexts
```
这意味着容器健康状态不依赖外部可访问端口或管理员凭据。
## 注意事项与限制
- 默认环境驱动的引导路径从 `slapd.conf` 种子,但支持 `slapd.conf` 和持久化 `slapd.d` 两种运行时模式。
- OpenLDAP **在构建阶段从源码编译**;Alpine 仅提供运行时基础镜像和共享运行时库。
- **默认生成针对 `mdb` 优化**。其他后端存在于镜像中,但应通过您自己的片段或自定义 `slapd.conf` 进行配置。
- 使用 `LDAP_CONFIG_BACKEND=slapd.d` 时,填充的 `LDAP_CONFIG_DIR` 将成为权威数据源。基于环境的配置更改此时仅作为种子使用,不再自动重新应用。
- 基础条目和初始化 LDIF 的自动引导**仅在活动运行时配置从当前环境驱动的 `slapd.conf` 种子时执行**。
- 自动 LDAP 引导需要 `LDAP_ADMIN_PASSWORD` 或 `LDAP_ADMIN_PASSWORD_FILE`。仅 `LDAP_ADMIN_PASSWORD_HASH` 足以用于运行时认证,但不足以用于首次启动初始化。
- `LDAP_DEBUG_LEVEL` 应设置为**数值**,因为它会直接传递给 `slapd -d`。
- 对于生产 TLS 操作,证书和密钥必须**可被 `ldap` 用户读取**。
- 初始化 LDIF 仅在**空数据目录**上运行一次。
## 开发与便捷用法
构建:
```
make build
```
通过 Compose 启动:
```
make compose-up
```
停止:
```
make compose-down
```
## 参考资料
- OpenLDAP 管理指南:
- OpenLDAP 下载:
- Alpine Wiki OpenLDAP:
标签:Alpine Linux, cn=config, Docker, LDIF引导, NIDS, OpenLDAP, overlay, Schema目录, slapd.conf, slapd.d, TLS加密, 企业级目录, 健康检查, 动态后端, 发布镜像, 安全防御评估, 容器化, 容器编排, 密码模块, 应用安全, 持久化存储, 源码固定版本, 源码编译, 环境变量驱动, 目录服务, 认证服务, 请求拦截, 轻量级镜像, 配置片段