croessner/docker-openldap

GitHub: croessner/docker-openldap

一个基于 Alpine Linux、从源码编译固定版本 OpenLDAP 的 Docker 镜像,提供以环境变量驱动的配置与持久化 cn=config 支持。

Stars: 0 | Forks: 0

# Alpine 上的 OpenLDAP [![Publish Docker Image](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/bc5f43aa49025700.svg)](https://github.com/croessner/docker-openldap/actions/workflows/docker-publish.yml) [![License: MIT](https://img.shields.io/github/license/croessner/docker-openldap)](./LICENSE) [![Last Commit](https://img.shields.io/github/last-commit/croessner/docker-openldap)](https://github.com/croessner/docker-openldap/commits/main) [![Stars](https://img.shields.io/github/stars/croessner/docker-openldap?style=social)](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加密, 企业级目录, 健康检查, 动态后端, 发布镜像, 安全防御评估, 容器化, 容器编排, 密码模块, 应用安全, 持久化存储, 源码固定版本, 源码编译, 环境变量驱动, 目录服务, 认证服务, 请求拦截, 轻量级镜像, 配置片段