凭据通过,隧道开始建立,随后会话却以一条关于组而不是关于密码的消息失败。这个组合把故障放在 LDAP 插件工作的后半段:绑定与密码校验都成功了,紧随其后的成员判定却没有。两半读的是同一段配置块,所以在一处修正可能让另一半原封不动。先从点明步骤的那行日志看起,而不是从你碰巧用来测试的账号看起。

把拒绝当成授权方面的回答

插件先做服务绑定,再查找账号,校验提供的密码,然后询问该账号是否属于被允许的组。这是四个彼此独立的操作,只有最后一个与组有关。因此绑定成功之后到来的拒绝,对密码什么也没说,即使客户端显示的是笼统的认证错误。在改配置之前先提高插件的日志级别,正是把四者从输出里区分开的办法。

去找组评估本身。如果根本没有出现组查询,说明授权块没有被走到,这通常意味着某条指令落在了拥有它的块之外,或者缺少必需选项。如果查询出现了却什么都没返回,说明端点或过滤条件回答的是另一个问题,而不是你想问的那个。

确定成员关系住在过滤条件里还是住在组块里

受支持的形制有两种,把它们混着用,正是本来能用的配置开始失效的原因。第一种在用户对象上要一个属性,从而把成员关系压进用户搜索里;第二种放着用户搜索不动,让插件之后再去找组对象。每次部署选定一种,并把选了哪一种写下来。

<Authorization>
    BaseDN       "OU=People,DC=example,DC=org"
    SearchFilter "(&(sAMAccountName=%u)(memberOf=CN=VPN Access,OU=Groups,DC=example,DC=org))"
    RequireGroup false
</Authorization>

在这种形制下,过滤条件决定一切,所以 RequireGroup 必须是 false,否则插件会去找一个过滤条件从未提到的组对象。过滤条件里的属性必须存在于用户对象上并且有值,一旦组发生嵌套,这一点就没有保证。

另一种形制让搜索保持简单,在其后追加一次组查找。组对象被明确点名,插件把用户与目录为该组返回的成员作比较。用户过滤条件什么都不用改,因此在别的团队管理该组时,这种形制更容易审计。

<Authorization>
    BaseDN       "OU=People,DC=example,DC=org"
    SearchFilter "(sAMAccountName=%u)"
    RequireGroup true
    Group        "CN=VPN Access,OU=Groups,DC=example,DC=org"
</Authorization>

盲点是共有的:单纯的成员比较不会展开嵌套组,所以权限经由父组而来的账号会失败,而控制台仍旧显示成员关系。Active Directory 在组上暴露 member,其他 schema 用 uniqueMember,指错属性会返回空结果而不是报错。

挑一个承载组对象的端点

URL 决定插件去问哪个目录,而端口 389 上的域控制器只为自己那套命名上下文作答。端口 3268 上的全局编录为整片林作答,只读,且只带部分属性集。当账号在一个域、VPN 组在另一个域时,单域查询可能找到了用户,却仍然漏掉成员关系。把 URL 挪到编录端口能解决拒绝的,就是这种情况;人们过度推广的,也是这种情况。

部分属性集是边界:编录没有的属性没法用来过滤,所以同一处改动可能让一个部署跑通,让另一个返回空。在改写这一段之前,先确认真实用户与真实组在端点上实际会拿到什么。引用与 TLS 又是另一码事,因为引用必须跟随,而要求 TLS 的端点需要对应的信任文件。

把账号状态与搜索范围放在各自的字段里

禁用标志属于过滤条件,不属于组检查。在 Active Directory 里它是 userAccountControl 的第二个位;没有这个条件,一个密码有效的禁用账号会先认证通过,再在组这一步失败,看上去就像授权有 bug。搜索范围属于基 DN,因为从域根开始更慢,而且可能命中你本想覆盖的组织单元之外的对象。

绑定账号的权限也要收紧。目录权限过宽会把配置错误的过滤条件变成一次成功的查询,把缺陷一直藏到有人审计该账号为止。成员关系要在目录本身里改,绝不要放在本地例外清单里,这样停用账号就足以收回访问。如果组由别的团队管理,在写过滤条件之前先与他们约定好填充哪个属性。

用三个账号把结果证出来

让配置分别面对三种身份:被允许的成员、组外的有效账号、以及一个被停用的账号。如果三个全被拒绝,说明绑定或组查询坏了;如果三个全被接受,说明根本没有施加任何组条件。混合结果才是唯一能证明配置区分了你关心的情形的结果。

在责怪插件之前,先从目录里直接读出同样的三个答案。第一条查询显示过滤条件所依赖的组属性,第二条显示账号控制值。换成你自己的基 DN、账号与端点:

ldapsearch -H ldap://dc01.example.org:3268 -x -D "[email protected]" -W -b "DC=example,DC=org" "(sAMAccountName=alice)" memberOf
ldapsearch -H ldap://dc01.example.org:3268 -x -D "[email protected]" -W -b "DC=example,DC=org" "(sAMAccountName=bob)" userAccountControl

memberOf 返回为空的用户,是目录里的事实,不是插件的 bug;而一个被停用后仍能认证的账号,意味着过滤条件缺了账号控制这一条。先把这份证据修好,再重跑三账号测试。把点明失败步骤的那行日志留在配置改动旁边,下一个人就不必重复搜索,也能把授权与认证分开。

相关文章