LDAP 管理

ACP 可以对来自 LDAP 目录的用户进行身份验证,并且可选地同步目录组。控制台工作流支持常见的 OpenLDAP 和 Microsoft Active Directory 布局。

只有在以下所有检查都通过后,集成才算完成:

  • ACP 创建 LDAP IDP。
  • 预期的用户以及(如果启用)组已同步。
  • 一个代表性的目录用户可以登录,并获得预期的 ACP 角色。

本指南从最短的控制台工作流开始。如果目录管理员尚未提供 Base DN、Filter 或属性映射,请先使用 发现目录设置

开始之前

在配置 ACP 之前,请准备以下值:

  • 从 ACP 集群可达的 LDAP server 主机名和端口。
  • 具有对预期用户和组读取权限的 Bind DN 和 password。
  • User Base DN 和 user Filter。
  • 一个稳定、唯一、非空的用户身份属性。
  • 一个或多个 login attributes。
  • 可选的 group Base DN、group Filter、member attribute、user matching attribute 和 group name attribute。
  • 一个用于最终登录验证的代表性用户账号。

工程师笔记本上的成功查询并不能证明 ACP 可以连接。ACP Connector 创建和同步才是集群侧的检查。如果目录管理员已经提供并验证了所有必需的值,请继续查看 添加 LDAP

发现目录设置

当 Base DN、Filter 或属性映射未知时,使用此工作流。此处引用的命令行工具是外部的、集成前诊断工具,不是 ACP 功能。

1. 从正确的网络运行检查

尽可能从诊断主机或 Pod 运行目录查询,并确保其 DNS、路由、防火墙和证书信任与 ACP 集群等效。笔记本电脑上的查询可以作为有用的初步证据,但 ACP Connector 创建和同步仍是权威的集群侧检查。

2. 查找目录根

查询目录 Root DSE 并记录 naming root:

  • OpenLDAP 和兼容目录通常会发布 namingContexts
  • Active Directory 会发布 defaultNamingContext

请使用最终 On-Site Diagnostic Tools 部分中的 Root DSE 示例。返回值(例如 dc=example,dc=com)是定位用户和组分支的起点;它不会自动成为最佳的 User Base DN。

3. 选择 User 和 Group Base DN

将 Base DN 视为 ACP 搜索的边界下限。例如:

dc=example,dc=com
|- ou=People
|  |- ou=Engineering
|  `- ou=Operations
`- ou=Groups
  • 如果所有预期用户都位于 ou=People,dc=example,dc=com 下,则将其用作 User Base DN。
  • 如果预期用户分布在 ou=People 之外的多个分支中,请使用它们的最低公共父节点。
  • 如果组存储在 ou=Groups 下,请使用单独的 Group Base DN。

在运行 subtree 查询之前,请先使用外部 base-scope 查询确认 bind account 能够读取候选 Base DN。ACP 表单会在配置的 Base DN 下执行 subtree search。它不提供 Search Scope 字段。YAML 可以使用 scope: subscope: one;在当前 Connector runtime 中,如果省略 scope,默认值为 sub

4. 检查代表性用户

在选择映射之前,至少检查三个条目:

  • 一个预期可以登录的普通用户。
  • 一个位于另一个预期 OU 或目录分支中的用户。
  • 一个边界情况,例如被禁用的用户、没有组的用户,或缺少可选属性的用户。

对于 OpenLDAP,请请求 DN、objectClassuidcnmail。对于 Active Directory,请请求 DN、objectClasssAMAccountNamedisplayNamemailuserAccountControlmemberOf。在收窄 Filter 之前,确认预期对象位于候选 Base DN 之下。

5. 选择身份和登录属性

目的OpenLDAP 候选项Active Directory 候选项选择规则
ACP 用户身份uidsAMAccountName对每个预期用户都存在、唯一、稳定且非空
Login FielduidmailsAMAccountName用户熟悉,并且只返回一个条目
仅用于显示的信息cndisplayName可能变化,不应作为默认身份
Group member valuemembermemberUidmember必须与示例组条目中存储的值匹配
Group namecncn对管理员而言可读且稳定

不要仅因为 cndisplayName 或用户的 DN 可读,就将其用作默认 ACP 身份。这些值可能重复,并且在用户重命名或移动时会变化。请在创建 Connector 之前,先在目录中解决缺失或重复的身份值。

6. 逐步构建 User Filter

按以下顺序构建并重新测试 Filter:

  1. 通过 objectClass 选择用户条目。
  2. 使用 (attribute=*) 要求 ACP 身份和 login attributes 存在。
  3. 如果客户的目录策略要求,排除计算机、已禁用账户、服务账户或系统账户。
  4. 仅在广泛结果与预期对象匹配后,再添加业务限制。

在每个条件之后,重新运行外部目录查询,并将返回数量和代表性条目与预期对象进行比较。使用这些 Filter 查找由于建议的身份不存在而会被跳过的条目:

# OpenLDAP entries that would be skipped because uid is missing
(&(objectClass=inetOrgPerson)(!(uid=*)))

# Active Directory user objects missing sAMAccountName
(&(objectClass=user)(!(objectClass=computer))(!(sAMAccountName=*)))

# Optional Active Directory exclusion for disabled accounts
(&(objectClass=user)(!(objectClass=computer))(sAMAccountName=*)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))

最后一个表达式是一个可复制的 Active Directory LDAP Filter,用于排除已禁用账户。仅当此排除条件与客户的目录策略一致时才应用它。

LDAP Filter 语法速查

从返回预期条目的最小表达式开始构建 Filter。以下运算符已在本指南使用的测试目录上验证过。

需求语法示例
精确值(attribute=value)(uid=alice)
属性存在(attribute=*)(mail=*)
所有条件都匹配(&(condition1)(condition2))(&(objectClass=inetOrgPerson)(uid=*))
任一条件匹配(|(condition1)(condition2))(|(uid=alice)(mail=alice@test.com))
排除某条件(!(condition))(!(objectClass=computer))
排除已禁用的 Active Directory 账户(!(userAccountControl:1.2.840.113556.1.4.803:=2))在上面显示的 Active Directory user Filter 中使用

不要在已配置的 user Filter 中放入 <username> 占位符。同步会按原样运行所配置的 Filter。在登录期间,ACP 会将该 Filter 与每个已配置 Login Field 属性的等值查找组合起来。

7. 确定 Group Membership 模型

检查一个已知组,并将其存储的 membership 值与代表性用户进行比较:

  • member 包含完整的用户 DN 时,使用 Group Attr memberUser Attr DN
  • memberUid 包含简短用户 ID 时,使用 Group Attr memberUidUser Attr uid

ACP 会将每个用户的 group membership 计算为 (<Group Attr>=<User Attr value>)。使用外部目录查询复现该查找,例如 (member=uid=alice,ou=People,dc=example,dc=com)(memberUid=alice)。在验证此映射时,不要只依赖用户的 memberOf 值。

选择经过验证的映射

以下映射是本版本为所列布局提供的起始点。Active Directory Filter 会排除计算机对象和已禁用账户。只有在客户的目录策略要求不同对象集时才修改该 Filter,并在更改后重复外部计数、ACP 同步和登录检查。

目录布局User FilterUser Name AttrLogin FieldGroup AttrUser AttrGroup Name Attr
OpenLDAP groupOfNames(&(objectClass=inetOrgPerson)(uid=*))uiduidmemberDNcn
OpenLDAP posixGroup(&(objectClass=inetOrgPerson)(uid=*))uiduidmemberUiduidcn
Active Directory(&(objectClass=user)(!(objectClass=computer))(sAMAccountName=*)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))sAMAccountNamesAMAccountNamememberDNcn

当 Active Directory group synchronization 使用 Group Attr memberUser Attr DN 时,请将 (&(objectClass=group)(member=*)) 用作 Group Filter。这样可以排除不返回 member 属性的空组,并保持同步后的 group population 与配置的 membership 映射一致。

添加 LDAP

  1. 前往 Users > IDP
  2. 点击 Add LDAP
  3. 填写 Basic InfoNameDisplay Name 以及可选的 Description
  4. 填写 LDAP Server SettingServer AddressAdmin AccountAdmin Password
  5. 填写 Search SettingFilterBase DN
  6. 如果需要组同步,请启用 Group Search Setting,并填写 FilterBase DNGroup AttrUser Attr
  7. 填写 Field MappingUser Name Attr、可选的 Group Name AttrLogin Field 以及 Username Tip In Login BoxUser Name Attr 会成为同步后的 ACP user 所使用的稳定身份值;它不仅仅是显示标签。Login Field 决定 ACP 如何搜索登录账号,并支持以逗号分隔的多个备选项。
  8. 可选地打开 Advanced Settings,启用 Auto Sync,并在 Sync Rules 中输入五字段 cron expression。ACP 会以 UTC 解释该规则。
  9. 点击 Add
NOTE

在创建过程中,ACP 会验证 server connectivity、bind、Base DN、Filter 以及映射属性。当前表单在创建时不会认证普通目录用户,因为它不会收集测试用户凭证。提交的 bind password 会在创建后移动到托管 Secret 中。

控制台字段参考

使用此表将通过 ldapsearch 发现的值映射到当前 ACP 表单,以及(必要时)映射到后续描述的 YAML 字段。

ACP console fieldYAML field如何选择该值
Nameid and metadata.name两个字段都使用相同、符合 DNS 的值。
Display Namename显示在 ACP IDP 选择器上的名称。
Descriptionmetadata.annotations["cpaas.io/description"]可选的、面向 operator 的描述。
Server Addressspec.config.host从 ACP 可访问的目录主机名或 IP 和端口。
Admin Accountspec.config.bindDN对目标条目具有读取权限的 bind account DN。
Admin Passwordspec.config.bindPW初始创建输入;ACP 会将其存储在托管 Secret 中。
User Filterspec.config.userSearch.filter仅返回预期用户对象集的 Filter。
User Base DNspec.config.userSearch.baseDN包含所有预期用户的最低目录分支。
Group Filterspec.config.groupSearch.filter用于预期组的 Filter;当存在空组时,需要成员属性。
Group Base DNspec.config.groupSearch.baseDN包含预期组的最低目录分支。
Group Attrspec.config.groupSearch.groupAttr存储成员值的组属性,例如 membermemberUid
User Attrspec.config.groupSearch.userAttr可与 Group Attr 比较的用户值,例如 DNuid
User Name Attrspec.config.userSearch.nameAttr稳定、唯一、非空的身份值,用于 v2 ACP username 和主标识符。
Group Name Attrspec.config.groupSearch.nameAttr可读的组名称,通常是 cn
Login Fieldspec.config.userSearch.username一个或多个以逗号分隔的登录属性。仅配置你已验证且存在于过滤后条目上的属性。
Username Tip In Login Boxspec.config.usernamePromptLDAP 登录页面上显示的提示文本。
Auto Syncmetadata.labels["cpaas.io/ldap.autoSync"]仅在手动同步正确后启用。
Sync Rulesmetadata.annotations["cpaas.io/ldap.autoSyncRule"]以 UTC 解释的五字段 cron expression。

表单不暴露 scope、TLS 标志、CA data 或可选的 profile mappings。仅当需要时,才通过 YAML 配置这些字段。

验证集成

  1. 打开新的 LDAP IDP,并选择 Actions > Sync user
  2. 确认同步结果,并将同步后的 user 和 group 数量与预期的目录对象数量进行比较。
  3. 打开一个已同步的用户,并验证其 source、身份值和状态是否正确。
  4. 使用 Manage User RolesManage User Group Roles 分配一个最小角色。
  5. 使用一个不处于 ACP 登录状态的独立浏览器会话,并使用代表性的目录账号登录。
  6. 确认身份验证成功,并且已分配的 ACP permission 可用。
WARNING

整体同步可能会报告成功,但缺少 User Name Attr 的条目会被跳过。用户没有匹配的目录组,并不意味着用户同步失败。请比较数量并检查代表性条目,而不要只依赖整体结果。

同步与生命周期

首次登录同步

目录用户可以在没有完整手动同步的情况下先登录。身份验证成功后,ACP 会同步该单个 LDAP 用户。请使用上面的完整验收路径来验证预期清单、组映射和授权,而不要将首次登录视为完整的集成验收。

手动同步

打开 LDAP IDP 并选择 Actions > Sync user,即可按需同步并协调目录对象。查看结果、计数、被跳过条目的消息,以及代表性的用户和组。

新同步的 LDAP 用户是 active、valid 的,并且默认 validity period 为 Permanent,除非管理员稍后更改其 validity。

在同步开始之前,ACP 会提示:如果一个 IDP user 与现有本地用户同名,可能会覆盖或重新关联该用户,同时保留现有的角色分配。首次完整同步之前,请检查建议的 User Name Attr 是否与当前 ACP users 冲突。不要将内置本地管理员账号用作集成测试身份。

自动同步

当需要定期协调时,请在 Advanced Settings 下启用 Auto Sync,并提供 Sync Rules。自动同步使用已配置的 Base DN、Filters 和 mappings;它不会对目录用户密码进行身份验证。ACP 会以 UTC 解释五字段 cron expression。对于验收测试,*/1 * * * * 会每分钟运行一次;在观察到计划运行后,请替换该临时规则。

更新 Connector

当 server、search boundary、Filter、mapping 或自动同步设置发生变化时,请更新 LDAP IDP。更新后运行一次手动同步,以确认新配置并协调现有源用户和组。

协调上游已删除的用户

在手动或自动协调之后,从上游目录中删除的用户会在 ACP 中变为无效,并且不再能通过该 source 进行身份验证。

删除 Connector

删除 Connector 且不执行清理时,会保留来自该 source 的用户和组,并将这些用户标记为无效。如果在删除过程中选择了 Clean up IDP users and User Groups,ACP 会删除由该 Connector 生成的用户和组。

使用 YAML 配置 LDAP

以下 v2 Connector 模板仅将 bindPW 作为初始创建输入。请通过 ACP 控制台的 YAML 选项卡提交它们。ACP 会把提交的 password 移动到托管 Secret 中,之后保存的 YAML 会包含托管的 clientSecretRef,而不是 bindPW。不要使用带有内联 bindPWkubectl apply:Kubernetes last-applied annotation 可能会将提交的 password 保留在 Connector metadata 中。使用前请替换每个示例值。

模板中刻意显示的字段

创建 Connector 时,ACP 会添加 metadata.labels["cpaas.io/idp.version"]: v2。对于不带 rootCAData 的普通 LDAP 连接,ACP 还会持久化 insecureNoSSL: trueinsecureSkipVerify: truestartTLS: false。模板显式展示这些值,以便提交的 YAML 描述实际的连接模式。

ACP 可以从 Filter 中的第一个 objectClass 推断部分用户和组属性。请不要依赖这种推断来交付客户环境:它无法确定目录的实际身份和 membership 模型。在检查代表性条目后,请显式设置 idAttrnameAttrusernamegroupAttruserAttr 以及 group 的 nameAttr。模板还显式写出了 scope: sub,尽管 sub 是 runtime 默认值。clientSecretRef 不是这些模板中的输入字段,因为 ACP 只会在将 bindPW 移动到托管 Secret 之后才写入它。

严格验证的 OpenLDAP 模板

636 端口上的证书验证限制

在当前 ACP 版本中,host:636 结尾的 Connector 会以 insecureSkipVerify: true 存储,即使提交的值为 falserootCAData 包含正确的 CA 也是如此。标准端口上的 LDAPS 流量是加密的,但 ACP 不会验证 LDAP server certificate。

下面的模板适用于在非 636 端口上提供 LDAPS 的目录。在该端口上,请保持 insecureSkipVerify: false,并通过 rootCAData 提供受信任的 CA。如果需要证书验证,请与目录和网络管理员协调端口,然后完成 Connector 创建、同步和真实登录,再进行推广。

apiVersion: dex.coreos.com/v1
kind: Connector
id: corporate-ldap
name: Corporate LDAP
type: ldap
metadata:
  name: corporate-ldap
  namespace: cpaas-system
  labels:
    cpaas.io/idp.version: v2
spec:
  config:
    host: "<LDAPS_HOST>:<NON_636_LDAPS_PORT>"
    bindDN: cn=reader,dc=example,dc=com
    bindPW: <BIND_PASSWORD>
    insecureNoSSL: false
    insecureSkipVerify: false
    startTLS: false
    rootCAData: <BASE64_ENCODED_CA_CERTIFICATE_PEM>
    usernamePrompt: Directory username
    userSearch:
      baseDN: ou=People,dc=example,dc=com
      filter: "(&(objectClass=inetOrgPerson)(uid=*))"
      scope: sub
      idAttr: uid
      nameAttr: uid
      username: uid
    groupSearch:
      baseDN: ou=Groups,dc=example,dc=com
      filter: "(objectClass=groupOfNames)"
      scope: sub
      groupAttr: member
      userAttr: DN
      nameAttr: cn

Active Directory 模板

apiVersion: dex.coreos.com/v1
kind: Connector
id: corporate-ad
name: Corporate Active Directory
type: ldap
metadata:
  name: corporate-ad
  namespace: cpaas-system
  labels:
    cpaas.io/idp.version: v2
spec:
  config:
    host: ad.example.com:389
    bindDN: CN=ACP Reader,OU=Service Accounts,DC=example,DC=com
    bindPW: <BIND_PASSWORD>
    insecureNoSSL: true
    insecureSkipVerify: true
    startTLS: false
    usernamePrompt: AD username
    userSearch:
      baseDN: OU=Employees,DC=example,DC=com
      filter: "(&(objectClass=user)(!(objectClass=computer))(sAMAccountName=*)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))"
      scope: sub
      idAttr: sAMAccountName
      nameAttr: sAMAccountName
      username: sAMAccountName
    groupSearch:
      baseDN: OU=Groups,DC=example,DC=com
      filter: "(&(objectClass=group)(member=*))"
      scope: sub
      groupAttr: member
      userAttr: DN
      nameAttr: cn

完整 LDAP YAML 字段参考

以下骨架包含 v2 LDAP Connector 有用的 ACP 集成字段。可选的 profile mappings 已注释掉,这样即使目录不提供这些属性,示例仍然有效。请删除未使用的可选字段,而不要命名那些在过滤后用户中不存在的属性。

apiVersion: dex.coreos.com/v1
kind: Connector
id: corporate-ldap
name: Corporate LDAP
type: ldap
metadata:
  name: corporate-ldap
  namespace: cpaas-system
  annotations:
    cpaas.io/description: Corporate directory
    cpaas.io/ldap.autoSyncRule: "0 2 * * *"
  labels:
    cpaas.io/idp.version: v2
    cpaas.io/ldap.autoSync: "true"
spec:
  config:
    host: ldap.example.com:1636
    bindDN: cn=reader,dc=example,dc=com
    bindPW: <BIND_PASSWORD>
    insecureNoSSL: false
    insecureSkipVerify: false
    startTLS: false
    rootCAData: <BASE64_ENCODED_CA_CERTIFICATE_PEM>
    usernamePrompt: Directory username
    userSearch:
      baseDN: ou=People,dc=example,dc=com
      filter: "(&(objectClass=inetOrgPerson)(uid=*))"
      scope: sub
      idAttr: uid
      nameAttr: uid
      username: uid
      # emailAttr: mail
      # preferredUsernameAttr: cn
      # phoneAttr: telephoneNumber
      # emailSuffix: test.com
    groupSearch:
      baseDN: ou=Groups,dc=example,dc=com
      filter: "(&(objectClass=groupOfNames)(member=*))"
      scope: sub
      groupAttr: member
      userAttr: DN
      nameAttr: cn

顶层和 metadata 字段:

字段是否必需产品行为
apiVersion使用 dex.coreos.com/v1
kind使用 Connector
idConnector 身份。请与 metadata.name 保持一致。
name显示在 ACP 登录选择器上的名称。
type使用 ldap
metadata.nameKubernetes 资源名称和 ACP Connector 查找键。
metadata.namespace使用 cpaas-system
metadata.annotations["cpaas.io/description"]ACP 显示的描述。
metadata.labels["cpaas.io/idp.version"]对本文档中的映射使用 v2
metadata.labels["cpaas.io/ldap.autoSync"]字符串值 "true""false";启用计划同步。
metadata.annotations["cpaas.io/ldap.autoSyncRule"]启用自动同步时需要以 UTC 解释的五字段 cron expression。

连接和传输字段:

字段是否必需产品行为
host从 ACP 集群可达的目录主机和端口。
bindDN用于读取用户和组的 bind account DN。
bindPW初始创建时需要ACP YAML 页面接受的明文输入。ACP 会移除它并将 password 存储在托管 Secret 中。
clientSecretRef存储后的输出ACP 写入的托管 Secret 引用。不要将其与 bindPW 一起提交,也不要手动编辑它。
insecureNoSSLtrue 选择普通 LDAP;false 根据 startTLS 选择 TLS。
startTLSfalseinsecureNoSSL: false 时选择 LDAPS。true 时选择通过 LDAP 的 StartTLS。
insecureSkipVerifyTLS 连接时适用false 请求服务器证书验证,但受下面 636 端口限制的影响。
rootCAData私有 CA 严格 TLSConnector 信任池使用的 Base64 编码 PEM CA certificate。
usernamePrompt登录页 LDAP 用户名字段上方显示的文本。

userSearch 字段:

字段是否必需产品行为
baseDN用户条目的搜索边界。
filter用于选择同步用户对象集的 LDAP Filter。
scopesub 搜索整个 subtree,是 runtime 默认值;one 仅搜索 baseDN 下一级。
nameAttrv2 必需在同步期间用作 ACP username 和主标识符的稳定唯一值。
idAttr验证和兼容性身份字段。对于 v2,请将其设置为与 nameAttr 相同的稳定属性。
username登录时用于查找用户的一个或多个以逗号分隔的属性。
emailAttr将目录值复制到 ACP user 的 mail 字段。仅当过滤后的用户提供该属性时才配置。
preferredUsernameAttr在 LDAP 登录期间提供 preferred-username Claim;它不会替代来自 nameAttr 的 v2 已同步身份。
phoneAttr将目录值复制到 ACP user 的 mobile 字段。仅当过滤后的用户提供该属性时才配置。
emailSuffix在 LDAP 登录期间,将身份 email 构造为 <nameAttr>@<suffix>;输入 suffix 时不要包含 @
Warning

emailSuffix 与 v2 同步

在已验证的 v2 行为中,emailSuffix: test.com 会在首次登录身份中生成 admin@test.com,而手动同步仍会将不带后缀的 nameAttr 作为主标识符进行存储。除非已针对客户的同步和冲突要求测试过这种差异,否则请不要设置 emailSuffix

groupSearch 字段:

字段启用组同步时是否必需产品行为
baseDN组条目的搜索边界。
filter用于选择组的 LDAP Filter。要求 membership 属性可以避免对空组进行验证。
scopesub 是 runtime 默认值;one 搜索 baseDN 下一级。
groupAttr包含成员值的组条目属性。
userAttrgroupAttr 比较的用户条目中的属性或 DN 值。
nameAttr用作 ACP group 显示名称的属性,通常是 cn

如果不需要 ACP group 同步,则完全省略 groupSearch

将传输标志作为一组来选择:

传输方式常见端口insecureNoSSLstartTLS证书验证
普通 LDAP389truefalse不适用;仅在网络策略允许且信任网络时使用
标准端口上的 LDAPS636falsefalseConnector 实现会存储 insecureSkipVerify: true;流量是加密的,但不会验证 LDAP server certificate
严格证书验证的 LDAPS配置的非 636 LDAPS 端口falsefalse保持 insecureSkipVerify: false,通过 rootCAData 提供受信任的 CA,并在推广前完成验收检查

上面的 Active Directory 模板使用普通 LDAP。普通 LDAP 不会在网络上传输 bind password 的过程中保护它。仅当客户的网络和安全策略允许时才使用。需要 TLS 时,请先确定策略是否也要求证书验证:本版本的标准 636 端口提供加密,但不提供 server certificate 验证,而严格验证需要非 636 的 LDAPS 端口和 rootCAData。推广前请完成一次 ACP LDAPS 创建、同步和登录测试。

对于非 636 LDAPS 端口上的严格证书验证,请将 PEM CA certificate 编码为不含换行符的 Base64,并将输出用作 rootCAData

openssl base64 -A -in ldap-ca.pem

如果要在 YAML 中配置计划同步,请将受支持的 metadata 添加到 Connector:

metadata:
  labels:
    cpaas.io/ldap.autoSync: "true"
  annotations:
    cpaas.io/ldap.autoSyncRule: "*/1 * * * *"

示例规则仅适用于观察验收测试运行。在验证之后,请将其替换为所需的 UTC 计划。顶层 id 必须等于 metadata.name;当前计划同步会通过该身份查找 Connector。

按症状排查

症状产品阶段可能原因具体检查
ACP 无法创建 Connector,因为它无法连接。Connector 创建集群 DNS、路由、防火墙、端口、TLS 模式或证书信任不正确。从一个与 ACP 可达性等效的主机或 Pod 运行下面的外部 TCP 或 TLS 检查,然后检查 auth-controller2 日志。
Bind 成功,但 Base DN 或 Filter 验证失败。Connector 创建和搜索验证bind account 的 Base DN 不存在,Filter 无效,或者 Filter 返回了不可用条目。先针对 Base DN 运行外部 base-scope 查询,然后运行精确的 subtree Filter,并检查 auth-controller2 日志。
同步报告成功,但返回的用户数少于预期。手动或计划同步Filter 排除了用户,用户不在 Base DN 范围内,或者缺少必需的 User Name Attr 值。将外部 subtree 结果与预期数量进行比较,运行上面的缺失属性 Filter,并检查 apollo 日志中的跳过条目信息。
Connector 创建成功,但目录用户无法登录。登录Login Field 未能找到唯一条目,账户被排除或已禁用,或者代表性凭证无效。使用每个已配置的 login attribute 查询代表性的登录值,确认只返回一个结果,在独立浏览器会话中重试,并检查 apollo 日志。
用户已同步,但组没有同步。组同步Group AttrUser Attr 与组中存储的值不对应,或者 Group Base DN 或 Filter 有误。检查一个已知组,并使用外部 subtree 查询复现 (<Group Attr>=<User Attr value>),然后检查 apollo 中的组查找消息。
身份发生冲突或意外变化。用户同步和协调User Name Attr 重复、为空、可变,或基于在重命名或移动后发生变化的 DN。在整个 User Base DN 范围内查询建议的身份属性,检查缺失和重复值,并选择一个稳定且唯一的属性。
自动同步未运行。计划同步Auto Sync metadata 缺失或无效,或者顶层 idmetadata.name 不同。检查存储的 Connector metadata 和身份,然后检查 apollo 日志中的计划同步消息。

现场诊断工具

本节中的工具是外部集成前诊断工具或高级 ACP 证据收集工具,不是 ACP 内置功能。

外部:Linux 和 macOS LDAP 工具

LDAP 命令需要已安装的 OpenLDAP client,例如 ldapsearchldapwhoami。它们会使用 -W 提示输入 bind password。该选项只能防止 password 出现在命令行中;它不会加密网络传输。

下面的私有 CA 环境变量已在 Linux OpenLDAP client 上验证。macOS 内置的 /usr/bin/ldapsearch/usr/bin/ldapwhoami 使用的是 macOS 系统信任库;请使用 Linux 诊断主机或 Pod,或者先在 macOS Keychain 中配置 CA,然后再使用这些内置 client。

对于每个 ldapwhoamildapsearch 命令,请使用与正在调查的 Connector 相同的传输组合:

Connector 传输Connector 设置OpenLDAP client 传输bind 凭证保护
普通 LDAPinsecureNoSSL: true, startTLS: false-H ldap://ldap.example.com:389,不带 -ZZ无 TLS 保护;仅在网络策略允许时使用
LDAPSinsecureNoSSL: false, startTLS: false-H ldaps://ldap.example.com:636,不带 -ZZ连接建立时开始 TLS

下面的查询示例使用普通 LDAP,因此只匹配 insecureNoSSL: true 的 Connector。对于 LDAPS,请在运行每个示例之前,将 -H 值替换为对应行。若 LDAP certificate 由私有 CA 签名,诊断 client 也必须信任该 CA。对于 Linux OpenLDAP client,请按如下方式为每个 ldapwhoamildapsearch 命令添加前缀:

LDAPTLS_REQCERT=demand \
LDAPTLS_CACERT=/path/to/ldap-ca.pem \
ldapwhoami -x -H ldaps://ldap.example.com:636 \
  -D 'cn=reader,dc=example,dc=com' -W

如果没有 LDAPTLS_CACERT 设置,经过验证的 Linux client 会失败并报错 unable to get local issuer certificate;使用 CA 文件后,同样的 bind 成功了。

外部命令:仅测试 TCP 端口可达性

nc -vz ldap.example.com 389

此命令只检查 TCP 端口是否可达。它不会验证 TLS、bind 凭证或 LDAP search 行为。在检查 LDAPS 端口可达性时,请改用端口 636

外部命令:验证 server、transport 和 bind account

ldapwhoami -x -H ldap://ldap.example.com:389 \
  -D 'cn=reader,dc=example,dc=com' -W

外部命令:读取目录 Root DSE

ldapsearch -LLL -x -H ldap://ldap.example.com:389 \
  -D 'cn=reader,dc=example,dc=com' -W \
  -b '' -s base '(objectClass=*)' namingContexts defaultNamingContext

外部命令:确认候选 Base DN 可读

ldapsearch -LLL -x -H ldap://ldap.example.com:389 \
  -D 'cn=reader,dc=example,dc=com' -W \
  -b 'ou=People,dc=example,dc=com' -s base \
  '(objectClass=*)' dn

外部命令:复现 OpenLDAP 用户 subtree 搜索

ldapsearch -LLL -x -H ldap://ldap.example.com:389 \
  -D 'cn=reader,dc=example,dc=com' -W \
  -b 'ou=People,dc=example,dc=com' -s sub \
  '(&(objectClass=inetOrgPerson)(uid=*))' \
  dn uid cn mail objectClass

外部命令:查找缺少建议身份的 OpenLDAP 条目

ldapsearch -LLL -x -H ldap://ldap.example.com:389 \
  -D 'cn=reader,dc=example,dc=com' -W \
  -b 'ou=People,dc=example,dc=com' -s sub \
  '(&(objectClass=inetOrgPerson)(!(uid=*)))' dn cn mail

外部命令:打印建议的 OpenLDAP 身份的重复值

ldapsearch -LLL -x -H ldap://ldap.example.com:389 \
  -D 'cn=reader,dc=example,dc=com' -W \
  -b 'ou=People,dc=example,dc=com' -s sub \
  '(&(objectClass=inetOrgPerson)(uid=*))' uid | \
  awk -F ': ' '$1 == "uid" { print $2 }' | \
  LC_ALL=C sort -f | uniq -di

在评估其他候选身份属性时,请替换 uid、Base DN 和 Filter。如果没有输出,表示返回的条目中未发现重复值。与其他 ldapsearch 示例一样,请使用与 Connector 匹配的传输组合。

外部命令:复现 groupOfNames membership 查找

ldapsearch -LLL -x -H ldap://ldap.example.com:389 \
  -D 'cn=reader,dc=example,dc=com' -W \
  -b 'ou=Groups,dc=example,dc=com' -s sub \
  '(&(objectClass=groupOfNames)(member=uid=alice,ou=People,dc=example,dc=com))' \
  dn cn member

外部命令:使用目标 CA 验证 LDAPS certificate chain

openssl s_client -connect ldap.example.com:636 \
  -servername ldap.example.com \
  -CAfile ldap-ca.pem -verify_return_error

只有当输出报告验证成功时,证书检查才算通过,例如 Verify return code: 0 (ok)Verification: OK

外部:使用 ldapsearch 查询 Active Directory

同一个 OpenLDAP 命令行 client 无需 Windows 或 RSAT 即可检查 Active Directory。请使用具有读取权限的目录账号。

外部命令:检查预期的 Active Directory 用户

ldapsearch -LLL -x -H ldap://ad.example.com:389 \
  -D 'CN=ACP Reader,OU=Service Accounts,DC=example,DC=com' -W \
  -b 'OU=Employees,DC=example,DC=com' -s sub \
  '(&(objectClass=user)(!(objectClass=computer))(sAMAccountName=*)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))' \
  dn sAMAccountName displayName mail userAccountControl memberOf

外部命令:复现 Active Directory group membership 查找

ldapsearch -LLL -x -H ldap://ad.example.com:389 \
  -D 'CN=ACP Reader,OU=Service Accounts,DC=example,DC=com' -W \
  -b 'OU=Groups,DC=example,DC=com' -s sub \
  '(&(objectClass=group)(member=CN=Alice,OU=Employees,DC=example,DC=com))' \
  dn cn member

高级:ACP 组件日志

以下 deployment 名称是当前 ACP 组件名称。运行这些命令需要读取集群日志的权限。

ACP 高级命令:检查 Connector 创建验证

kubectl -n cpaas-system logs deployment/auth-controller2 --since=10m

auth-controller2 负责 Connector 创建验证,包括网络连接、TLS、bind、Base DN、Filter 和映射属性。

ACP 高级命令:检查同步和登录行为

kubectl -n cpaas-system logs deployment/apollo --since=10m

apollo 负责手动和计划同步、跳过的条目、组查找以及登录行为。

NOTE

使用外部工具成功并不能证明 ACP 能够访问或使用该目录。请通过 ACP Connector 创建、预期的同步结果,以及使用预期 ACP permission 进行的真实目录登录,完成完整的证据链。