跳到主要内容
版本:4.1

LDAP 支持

概述

RabbitMQ 可以使用 LDAP 通过外部 LDAP 服务器执行身份验证和授权。此功能由一个内置插件提供,该插件必须启用

提示

LDAP 插件通常与另一个插件 rabbitmq_auth_backend_cache 结合使用,以提高延迟并显著降低 LDAP 服务器的负载。

身份验证和授权操作被转换为 LDAP 查询,查询使用 RabbitMQ 运维人员配置的模板。

LDAP 操作流程 部分提供了插件工作原理的更详细概述。

该插件主要针对 OpenLDAP 和 Microsoft Active Directory。其他 LDAP v3 实现也应该可以很好地工作。

本指南简要概述了 LDAP 术语,但通常假定您基本熟悉 LDAP。网络上还有一些面向初学者的 LDAP 入门教程,例如,教程一教程二 以及 LDAP 词汇表

本指南涵盖了 RabbitMQ 使用的 LDAP 操作流程、LDAP 模型如何 映射到 RabbitMQ 权限模型、如何 使用 TLS 连接到 LDAP 服务器,以及哪些工具可用于 故障排除 和 LDAP 请求的 代理

先决条件

RabbitMQ LDAP 插件依赖于名为 eldap 的 LDAP 客户端。该库随 Erlang/OTP 一起提供。在某些操作系统上,Erlang 以一组软件包而不是一个整体软件包的形式提供,因此 eldap 等组件必须与主运行时分开安装

在 Debian 和 Ubuntu 上,eldaperlang-eldap 软件包提供

sudo apt-get install -y erlang-eldap

在 Erlang 安装中,如果库不可用,则无法使用 LDAP 支持。

请参阅 Erlang 兼容性指南 以了解更多信息。

启用插件

LDAP 插件随 RabbitMQ 一起发布。要启用它,请使用 rabbitmq-plugins

rabbitmq-plugins enable rabbitmq_auth_backend_ldap

启用 LDAP AuthN 和 AuthZ 后端

启用插件后,需要配置节点以使用它。

这包括

以下示例将 RabbitMQ 配置为使用 LDAP 进行身份验证和授权,并忽略内部数据库

# use LDAP exclusively for authentication and authorisation
auth_backends.1 = ldap

advanced.config 文件中,相同的设置如下所示

{rabbit, [
{auth_backends, [rabbit_auth_backend_ldap]}
]}

以下示例将指示节点首先尝试 LDAP,如果无法通过 LDAP 验证用户身份,则回退到内部数据库

# try LDAP first
auth_backends.1 = ldap
# fall back to the internal database
auth_backends.2 = internal

advanced.config 格式中的相同示例

{rabbit,[
{auth_backends, [rabbit_auth_backend_ldap, rabbit_auth_backend_internal]}
]}

在以下示例中,LDAP 将首先用于身份验证。如果在 LDAP 中找到用户,则将针对 LDAP 检查密码,并且后续授权检查将针对内部数据库执行(因此,LDAP 中的用户也必须存在于内部数据库中,可以选择使用空白密码)。如果在 LDAP 中未找到用户,则会再次尝试仅使用内部数据库。

# use LDAP for authentication first
auth_backends.1.authn = ldap
# use internal database for authorisation
auth_backends.1.authz = internal
# fall back to the internal database
auth_backends.2 = internal

advanced config 格式

{rabbit,[{auth_backends, [{rabbit_auth_backend_ldap, rabbit_auth_backend_internal},
rabbit_auth_backend_internal]}]}

配置

一旦插件启用并且其后端已连接,就必须配置许多 LDAP 特定的设置。它们包括 LDAP 服务器列表、身份验证和授权设置等等。

默认配置允许所有用户访问所有 vhost 中的所有对象,但不会使他们成为管理员。通过配置 LDAP 查询可以限制访问。

LDAP 服务器

为了使插件能够连接到 LDAP 服务器,必须至少配置一个服务器主机名或 IP 地址,使用 auth_ldap.servers 键。如果提供多个值,列表值可以是主机名或 IP 地址。此值必须配置。以下示例配置插件以使用两个 LDAP 服务器。将按顺序尝试它们,直到成功连接到其中一个

auth_ldap.servers.1 = ldap.eng.megacorp.local
auth_ldap.servers.2 = 192.168.0.100

使用经典配置格式的相同示例

[
{rabbitmq_auth_backend_ldap, [
{servers, ["ldap.eng.megacorp.local", "192.168.0.100"]}
]}
].

LDAP 服务器通常使用端口 389,这是 LDAP 插件默认使用的端口。可以使用 auth_ldap.port 覆盖此端口

auth_ldap.servers.1 = ldap.eng.megacorp.local
auth_ldap.servers.2 = 192.168.0.100

auth_ldap.port = 6389

使用经典配置格式的相同示例

[
{rabbitmq_auth_backend_ldap, [
{servers, ["ldap.eng.megacorp.local", "192.168.0.100"]},
{port, 6389}
]}
].

可以使用 auth_ldap.timeout 配置键为到 LDAP 服务器的 TCP 连接设置超时

auth_ldap.servers.1 = ldap.eng.megacorp.local
auth_ldap.servers.2 = 192.168.0.100

# 15 seconds in milliseconds
auth_ldap.timeout = 15000

默认值为 infinity,或无超时。

LDAP 服务器连接被池化,以避免过度的连接抖动和 LDAP 服务器负载。默认情况下,池最多有 64 个连接。可以使用 auth_ldap.connection_pool_size 设置来控制此大小

auth_ldap.servers.1 = ldap.eng.megacorp.local
auth_ldap.servers.2 = 192.168.0.100

auth_ldap.connection_pool_size = 256

在一段时间不活动后,池化的连接将被关闭,该时间段可以通过 auth_ldap.idle_timeout 配置,单位为毫秒或 infinity

auth_ldap.servers.1 = ldap.eng.megacorp.local
auth_ldap.servers.2 = 192.168.0.100

auth_ldap.connection_pool_size = 256
# 300 seconds in milliseconds
auth_ldap.idle_timeout = 300000

建议值在 120 到 300 秒之间。

为 LDAP 连接使用 TLS

重要提示

从 Erlang 26 开始,TLS 客户端对等验证默认由 TLS 实现启用。

如果未配置客户端 TLS 证书和密钥对,则启用 TLS 的 LDAP 服务器连接将失败。如果不需要对等验证,可以禁用它,否则必须为 LDAP 连接配置证书和私钥对。

可以使用 TLS 连接到 LDAP 服务器。要指示插件这样做,请将 auth_ldap.use_ssl 设置为 true。如果 LDAP 服务器使用 StartTLS,请改用 auth_ldap.use_starttls。请注意,这些设置是互斥的(不能组合)。这两个值的默认值均为 false

客户端 TLS 设置使用 ssl_options 配置,这与 RabbitMQ 中其他地方的 TLS 设置 非常相似。

这是一个最简单的示例

auth_ldap.servers.1 = ldap.eng.megacorp.local
auth_ldap.servers.2 = 192.168.0.100

# enables TLS for connections to the LDAP server
auth_ldap.use_ssl = true

# Disables peer certificate chain verification. See the section on Peer Verification
# below.
#
# Doing so loses one of the key benefits of TLS and make the setup less secure
# but also simplifies node configuration.
auth_ldap.ssl_options.verify = verify_none

该插件还可以使用 StartTLS 进行连接。这种较旧且安全性较低的选项不推荐使用,但对于较旧的 LDAP 服务器可能是必要的

auth_ldap.servers.1 = ldap.eng.megacorp.local
auth_ldap.servers.2 = 192.168.0.100

# Enables StartTLS for connections to the LDAP server.
# Prefer auth_ldap.use_ssl with reasonably modern LDAP servers!
auth_ldap.use_starttls = true

# Disables peer certificate chain verification. See the section on Peer Verification
# below.
#
# Doing so loses one of the key benefits of TLS and make the setup less secure
# but also simplifies node configuration.
auth_ldap.ssl_options.verify = verify_none

LDAP 可用的客户端 TLS 选项

有多种 TLS 客户端选项可用

CA 证书、客户端证书和私钥

# local filesystem path to a CA certificate bundle file
auth_ldap.ssl_options.cacertfile = /path/to/ca_certificate.pem

# local filesystem path to a client certificate file
auth_ldap.ssl_options.certfile = /path/to/client_certfile.pem

# local filesystem path to a client private key file
auth_ldap.ssl_options.keyfile = /path/to/client_key.pem

SNI(服务器名称指示)

服务器名称指示 (SNI) 可以为到 LDAP 服务器的传出 TLS 连接配置。如果未设置,则默认值将是用于连接的主机名(请参阅上面的 auth_ldap.servers.*)。

# Sets Server Name Indication for LDAP connections.
# If an LDAP server host is available via multiple domain names, set this value
# to the preferred domain name target LDAP server
auth_ldap.ssl_options.sni = ldap.identity.eng.megacorp.local

主机名验证

主机名验证不应与 对等证书链验证 混淆。这些设置是正交的,可以组合使用。

# take wildcards into account when performing hostname verification
auth_ldap.ssl_options.hostname_verification = wildcard
# disables hostname verification
auth_ldap.ssl_options.hostname_verification = none

对等验证

从 Erlang 26 开始,LDAP TLS 客户端将默认执行 对等证书链验证

对等证书链验证 不应与主机名匹配验证混淆。这些设置是正交的,可以组合使用。

# Enables peer certificate chain verification.
# This behavior is the default starting with Erlang 26 (and thus RabbitMQ 3.13+)/
auth_ldap.ssl_options.verify = verify_peer
# Disables peer certificate chain verification.
#
# Doing so loses one of the key benefits of TLS and make the setup less secure
# but also simplifies node configuration.
auth_ldap.ssl_options.verify = verify_none
# if target LDAP server does not present a certificate, should the connection be aborted?
auth_ldap.ssl_options.fail_if_no_peer_cert = true

对等链验证深度

对于使用多个中间证书的服务器,可以增加 证书链验证深度

auth_ldap.ssl_options.depth = 5

启用的 TLS 版本

# use TLSv1.2 only
ssl_options.versions.1 = tlsv1.2

advanced.config 中的 TLS 选项

以下示例使用 advanced.config 格式

[
{rabbitmq_auth_backend_ldap, [
{servers, ["ldap1.eng.megacorp.local", "ldap2.eng.megacorp.local"]},

{use_ssl, true},
{ssl_options, [{cacertfile, "/path/to/ca_certificate.pem"},
{certfile, "/path/to/server_certificate.pem"},
{keyfile, "/path/to/server_key.pem"},
{verify, verify_peer},
{fail_if_no_peer_cert, true}]},
{server_name_indication, "ldap.identity.eng.megacorp.local"},
{ssl_hostname_verification, wildcard}
]}
].

LDAP 查询缓存以提高效率和降低负载

一个特殊的 缓存后端 可以与 组合 中的其他后端一起使用,以显著减少它们在 LDAP 服务器上产生的负载。

建议依赖 LDAP 进行身份验证和授权的生产集群将其与缓存后端结合使用。对于大多数系统,15 到 60 秒范围内的缓存间隔在安全性和效率之间取得了良好的平衡。

LDAP 要点和术语

本节介绍本文档中使用的一些基本 LDAP 术语。有关 LDAP 入门知识,请参阅 Digital Ocean 的 此概述 和 ldap.com 的 LDAP 词汇表

术语描述
绑定 (Bind)LDAP 中“身份验证请求”的说法。
专有名称 (DN)

专有名称是 LDAP 目录(树)中的唯一键,用于标识对象(如用户或组)。插件将在身份验证阶段(见下文)将客户端提供的用户名转换为专有名称。可以将 DN 视为文件系统中的绝对文件路径。

通用名称 (CN)

树中对象的简短标识符。此标识符在 LDAP 数据库中的对象类(类型)之间会有所不同。例如,一个人的通用名称将是全名。一个组的通用名称将是该组的名称。可以将 CN 视为文件系统中的文件名。

属性 (Attribute)

对象的属性(键值对)。可以将其视为面向对象编程语言中对象的字段。

对象类 (Object Class)

一组预定义的属性。可以将其视为面向对象语言中的类型(类)。

条目 (Entry)

LDAP 数据库实体,例如,一个人或一个组。它具有与其关联的对象类以及一个或多个属性,包括通用名称。由于实体位于 LDAP 数据库树中的某个位置,因此它也必须具有唯一标识它的专有名称。条目是 LDAP 插件查询使用的内容(查找、检查成员资格、比较属性等)。LDAP 数据库必须有一些条目(通常是用户、组),才能在实践中用于 RabbitMQ 身份验证和授权。

LDAP 操作流程

为了执行 LDAP 查询,插件将打开与列表中第一个可访问的 LDAP 服务器的连接。然后,根据凭据配置,它将执行匿名绑定或“简单绑定”(使用用户名/密码对向 LDAP 服务器验证用户身份)。用于执行绑定的凭据可以从客户端提供的用户名派生,如下节所述。

如果配置了 vhost 访问查询,则接下来将执行该查询,否则将无条件授予 vhost 访问权限。

此时,可以认为连接已成功协商和建立。例如,应该可以在其上打开通道。在连接上执行的所有进一步操作都将执行授权查询之一。例如,声明队列将执行资源访问查询(如下所述)。将消息发布到主题交换还将额外执行主题访问查询。请参阅访问控制指南以了解更多信息。

用户名和专有名称

在简单绑定阶段,user_dn_pattern 模式用于将提供的用户名转换为用于绑定的值。默认情况下,该模式按原样传递提供的值(即,该模式为 ${username})。如果指定了 user_bind_pattern,则它优先于 user_dn_pattern。如果在专有名称查找阶段需要使用不同的 user_dn_pattern,这将非常方便。请注意,以上内容不适用于匿名绑定,也不适用于 dn_lookup_bind 未设置为 as_user 的情况。

客户端连接提供用户名,这些用户名在 LDAP 中转换为专有名称 (DN)。有两种方法可以做到这一点。最简单的方法是通过字符串替换和 user_dn_pattern。要使用此选项,请将 user_dn_pattern 设置为包含恰好一个 ${username} 实例的字符串,该变量将被替换为客户端提供的用户名值。

例如,将 user_dn_pattern 设置为 "cn=${username},ou=People,dc=example,dc=com" 将导致用户名 simon 被转换为 DN cn=simon,ou=People,dc=example,dc=com。默认值为 "${username}",换句话说,用户名按字面意思使用。

将用户名转换为专有名称的另一种方法是通过 LDAP 查找。为此,请将 auth_ldap.dn_lookup_attribute 设置为表示用户名的属性名称,并将 auth_ldap.dn_lookup_base 设置为查询的基本 DN。查找可以在两个时间点之一完成,要么在尝试以相关用户身份绑定之前,要么之后。

要在绑定后执行查找,请将 auth_ldap.dn_lookup_bind 设置为其默认值 as_user。LDAP 插件将然后使用用户的纯文本(未修改)用户名绑定以进行登录,然后查找其 DN。为了使此操作有效,需要将 LDAP 服务器配置为允许使用纯文本用户名进行绑定(Microsoft Active Directory 通常会这样做)。

要在绑定前执行查找,请按如下方式设置 dn_lookup_binddn_lookup_basedn_lookup_attribute。LDAP 插件将然后首先使用这些凭据绑定以进行查找,然后使用用户的 DN 和密码绑定以进行登录。

auth_ldap.dn_lookup_bind.user_dn = CN=myuser,OU=users,DC=gopivotal,DC=com
auth_ldap.dn_lookup_bind.password = test1234
auth_ldap.dn_lookup_attribute = userPrincipalName
auth_ldap.dn_lookup_base = DC=gopivotal,DC=com

考虑以下示例

auth_ldap.dn_lookup_attribute = userPrincipalName
auth_ldap.dn_lookup_base = DC=gopivotal,DC=com

通过此配置,可以使用电子邮件地址(userPrincipalName 值通常是电子邮件地址)进行身份验证,并让本地 Active Directory 服务器返回实际的 DN 以进行登录。

如果同时设置了 auth_ldap.dn_lookup_attributeauth_ldap.user_dn_pattern,则会将这两种方法结合使用:插件填写模板,然后搜索 DN。

auth_ldap.dn_lookup_bind 的默认值为 as_user。对于 auth_ldap.dn_lookup_baseauth_ldap.dn_lookup_attribute,默认值为 none

LDAP 活动日志记录

插件可以使用 auth_ldap.log (经典配置格式中的 rabbitmq_auth_backend_ldap.log) 设置来控制 LDAP 活动日志记录的详细程度。这对于故障排除至关重要。

将值设置为 true 将启用 LDAP 插件用于做出决策的逻辑的详细日志记录。绑定请求结果中的凭据将在此模式下被清除。不建议在生产系统中使用此模式,但偶尔可能很有用。

network 的工作方式与上述类似,但另外会导致 LDAP 网络流量在较低级别(LDAP 客户端)进行日志记录,并清除绑定请求凭据。此设置可能会导致过多的日志记录,应谨慎使用。

network_unsafe 会导致 LDAP 网络流量在较低级别(LDAP 客户端)进行日志记录,并将绑定请求凭据(如密码)写入日志;此模式绝不能在生产中使用,并且会违反几乎所有广泛采用的安全策略。但是,它在开发和 QA 环境中进行故障排除时可能非常有用。

最后,值 false(默认值)会停用 LDAP 流量日志记录。

以下示例将 LDAP 日志记录级别设置为 network

auth_ldap.log = network

经典配置格式中的相同示例

[
{rabbitmq_auth_backend_ldap, [
%% ...
{log, network}
]}
]

用于授权查询的绑定

对于身份验证,此插件以尝试进行身份验证的用户的身份绑定到 LDAP 服务器。other_bind 设置控制如何为授权查询绑定,以及检索在不提供密码的情况下登录的用户详细信息(例如,使用 EXTERNAL 身份验证机制)。

接受的值为 as_user(以已验证用户的身份绑定)或 anon(以匿名方式绑定),或由两个选项 other_bind.user_dnother_bind.password 呈现,以使用指定的用户名和密码进行绑定。例如

auth_ldap.other_bind.user_dn = a-username
auth_ldap.other_bind.password = a-password

使用经典配置格式

[
{rabbitmq_auth_backend_ldap, [
{other_bind, {"a-username", "a-password"}}
]}
].

请注意,当用户在不提供密码的情况下连接时,无法使用默认的 as_user 配置。在这种情况下,请使用 auth_ldap.other_bind.user_dnauth_ldap.other_bind.passwordanon 选项。

auth_ldap.other_bind 的默认值为 as_user

组成员关系查找

该插件支持多个组成员关系查找查询。group_lookup_base 设置控制将使用哪个基本 DN 来搜索嵌套组。它仅由 {in_group_nested, ...} 查询使用。有关更多信息,请参阅关于查询的部分

在以下示例中,ou=groups,dc=example,dc=com 是包含所有组的目录。请注意,它使用了 经典配置格式

[
{rabbitmq_auth_backend_ldap, [
%% ...
{group_lookup_base, "ou=groups,dc=example,dc=com"}
]}
]

默认值为 'none'

配置授权

RabbitMQ 权限模型如何映射到 LDAP

RabbitMQ 权限模型与 LDAP 的权限模型不同。此外,LDAP 模式的使用方式在不同的公司之间也会有所不同。因此,需要一种机制来定义 RabbitMQ 授权功能使用的 LDAP 请求。授权由四个可配置的查询控制

  • rabbitmq_auth_backend_ldap.vhost_access_query
  • rabbitmq_auth_backend_ldap.resource_access_query
  • rabbitmq_auth_backend_ldap.topic_access_query
  • rabbitmq_auth_backend_ldap.tag_queries

每个查询定义一个查询,该查询将确定用户是否具有 vhost 的访问权限,他们是否具有资源的访问权限(例如,交换机、队列、绑定)以及他们具有哪些标签

请注意较长的 rabbitmq_auth_backend_ldap 前缀。查询使用 Erlang 术语(数据结构)表示的特定于域的语言表示,因此它们只能使用 高级配置格式 定义。

查询及其类型

上面提到的每个查询都在不同的授权阶段使用,并且必须评估为 true 或 false。特定查询类型(表达式,例如,值比较或组成员关系检查)将在本指南的后面部分介绍。

查询可以是以下几种类型之一。每种类型代表一个布尔表达式或函数:比较、字符串匹配、对象存在性检查、组成员关系检查等等。查询可以使用布尔运算符嵌套和组合。

默认值(表达式)可以在下表中找到

查询目的默认表达式
rabbitmq_auth_backend_ldap.vhost_access_query验证是否允许用户访问虚拟主机{constant, true}
rabbitmq_auth_backend_ldap.resource_access_query验证是否允许用户访问资源(队列、交换机等){constant, true}
rabbitmq_auth_backend_ldap.topic_access_query验证是否允许用户发布到主题{constant, true}
rabbitmq_auth_backend_ldap.tag_queries检查著名的标签是否适用于用户[{administrator, {constant, false}}]

这意味着所有用户都被授予访问所有 vhost 中的所有对象的权限,但他们不是系统管理员。

所有采用字符串作为参数的查询类型都支持字符串替换,其中可以替换与正在进行的查询相关的变量。每个查询都支持不同的变量。

vhost_access_query 支持

  • ${username}:身份验证时提供的用户名
  • ${user_dn}:用户的专有名称
  • ${vhost}:我们正在查询访问权限的虚拟主机

resource_access_query 支持

  • ${username}:身份验证时提供的用户名
  • ${user_dn}:用户的专有名称
  • ${vhost}:资源所在的虚拟主机
  • ${resource}:资源的类型,为 “exchange” 或 “queue” 之一
  • ${name}:资源的名称
  • ${permission}:请求对资源的访问类型,为 “configure”、“write” 或 “read” 之一

tag_queries 支持

  • ${username}:身份验证时提供的用户名
  • ${user_dn}:用户的专有名称
  • ${vhost}虚拟主机信息并非在所有场景下都可用。它可以用于其他上下文,例如,对应用程序或用户进行分组

topic_access_query 支持

  • ${username}:身份验证时提供的用户名
  • ${user_dn}:用户的专有名称
  • ${vhost}:资源所在的虚拟主机
  • ${resource}:在本例中始终为 “topic”
  • ${name}:资源的名称
  • ${permission}:“write”(发布)或 “read”(消费、队列和主题交换的交换机到交换机绑定)之一
  • ${routing_key}:已发布消息的路由键(“write” 权限)或主题交换到队列/交换机绑定的路由键(“read” 权限)

最后,如果身份验证时提供的用户名采用 Domain\User 格式(在某些 Active Directory 环境中是这种情况),则将为上述每个查询提供两个附加变量

  • ${ad_domain} - Domain\User 的域部分
  • ${ad_user} - Domain\User 的用户部分

资源访问的术语 configure、write 和 read 与内置 RabbitMQ 权限系统中的含义相同,请参阅 ./access-control。另请参阅 主题授权 以了解 topic_access_query

当首次熟悉查询 DSL 时,打开上面记录的 log 配置参数可能会有所帮助。这将导致 LDAP 插件将相当详细的查询描述及其因此做出的决策写入 RabbitMQ 日志。

虚拟主机访问

rabbitmq_auth_backend_ldap.vhost_access_query 是用于控制虚拟主机访问的查询。如果查询评估为 true,则授予访问权限。

请注意,在用户可以访问虚拟主机之前,必须已在 RabbitMQ 中创建虚拟主机;与用户和权限不同,虚拟主机不能完全存在于 LDAP 中。

用户标签

tag_queries 由键值映射组成,该映射将标签名称映射到要执行的查询,以确定用户是否具有该标签。必须列出用户应具有的所有标签的查询列表。

授权查询参考

常量查询

{constant, Bool}

这将始终返回 true 或 false,无条件地授予或拒绝访问权限。示例

{tag_queries, [{administrator, {constant, false}},
{management, {constant, true}}]}

这授予所有用户使用管理插件的能力,但不会使他们成为管理员。

存在查询

{exists, Pattern}

这将将变量替换为模式,如果存在具有结果 DN 的对象,则返回 true。示例

{vhost_access_query, {exists, "ou=${vhost},ou=vhosts,dc=example,dc=com"}}

这会将对 ou=vhosts,dc=example,dc=com 中作为组织单位存在的所有虚拟主机的访问权限授予所有用户。

组成员查询

{in_group, Pattern}
{in_group, Pattern, AttributeName}

与存在查询类似,将参数替换为模式以查找对象。但是,如果登录用户是成员,则此查询返回 true;检查 member 属性或任何命名的属性。示例

{vhost_access_query, {in_group, "cn=${vhost}-users,ou=vhosts,dc=example,dc=com"}}

当用户被列为 ou=vhosts,dc=example,dc=com 中适当命名的对象(例如 groupOfNames)的 member 属性时,这将授予对虚拟主机的访问权限。

嵌套组成员查询

{in_group_nested, Pattern}
{in_group_nested, Pattern, AttributeName}
{in_group_nested, Pattern, AttributeName, Scope}

in_group 查询类似,但也遍历组层次结构,例如,如果登录用户是某个组的成员,而该组又是另一个组的成员。针对 member 属性或任何命名的属性检查成员资格。在由 group_lookup_base 配置键定义的 DN 中搜索组,如果前者为 none,则在 dn_lookup_base 变量中搜索组。如果两个查找基本变量都设置为 none,则查询将始终返回 false。搜索范围可以设置为 subtreesingle_level

  • subtree 搜索查找基本目录下的所有对象
  • single_level 搜索直接包含在查找基本目录内的组

scope 的默认值为 subtree。此查询使用深度搜索,从用户向上搜索到目标组。搜索过程将检测并跳过循环路径。如果用户是许多组的成员,而这些组也是许多组的成员,则此查询可能非常耗时且消耗大量内存。当组用于成员关系层次结构时,请使用此查询。如果可能,仍然建议使用简单的 {in_group, ...} 查询:嵌套组可能难以推理。示例

[
{group_lookup_base, "ou=groups,dc=example,dc=com"},
{vhost_access_query, {in_group_nested, "cn=${vhost}-groups,ou=groups,dc=example,dc=com", "member", single_level}}
]

当用户是 member 属性值定义的组层次结构中的成员,并且位于 ou=groups,dc=example,dc=com 目录中时,这将授予对虚拟主机的访问权限。

For Query

{for, [{Name, Value, SubQuery}, ...]}

这允许您拆分查询,并使用不同的子查询处理不同的情况。

选项应为包含三个元素的元组列表,每个元组包含名称、值和子查询。名称是变量的名称(即,将放入 ${} 替换中的内容)。值是该变量的可能值。

请注意,这些值是不同的 Erlang 类型;resourcepermission 具有原子值(例如,resource 可以是 exchange),而其他键具有二进制值(例如,name 可能是 <<"amq.fanout">>)。

示例

{resource_access_query,
{for, [{resource, exchange, {for, [{permission, configure,
{in_group, "cn=wheel,dc=example,dc=com"}
},
{permission, write, {constant, true}},
{permission, read, {constant, true}}
]}},
{resource, queue, {constant, true}}]}}

这允许 wheel 组的成员声明和删除交换机,并允许所有用户执行其他所有操作。

Boolean Queries

{'not', SubQuery}
{'and', [SubQuery1, SubQuery2, SubQuery3, ...]}
{'or', [SubQuery1, SubQuery2, SubQuery3, ...]}

这些可用于使用布尔逻辑组合子查询。“and”和“or”查询各自接受任意长的子查询列表,如果所有或任何子查询的计算结果为 true,则返回 true。

请注意,“and”、“or”和“not”是 Erlang 中的保留字,因此关键字需要在配置文件中用单引号引起来,如上例所示。示例

 {'or',
[{'and',
[{equals, "${name}", "test1"},
{equals, "${username}", "user1"}]},
{'and',
[{equals, "${name}", "test2"},
{'not', {equals, "${username}", "user1"}}]}
]}}

此示例为 “user1” 提供对名为 “test1” 的对象的完全访问权限,并为除 “user1” 以外的所有人提供对 “test2” 的访问权限。

Equals Query

{equals, StringSubQuery1, StringSubQuery2}

接受两个字符串,并检查其中一个字符串是否与另一个字符串匹配。请注意,这两个字符串依次都是子查询(属于下面的 stringattribute 类型)。

这对于将字符串替换变量的值与常量或属性值等进行比较非常有用。示例

{resource_access_query,
{for, [{permission, configure, {equals, {attribute, "${user_dn}", "description"},
{string, "can-declare-${resource}s"}
}
},
{permission, write, {constant, true}},
{permission, read, {constant, true}}
]
}

这根据用户的 description 字段中是否存在字符串 “can-declare-exchanges” 和 “can-declare-queues” 来授予声明和删除交换机和队列的权限,并授予所有人写入和读取交换机的权限。

Match Query

{match, StringSubQuery, RESubQuery}

接受一个字符串和一个正则表达式,并检查其中一个字符串是否与另一个字符串匹配。请注意,字符串和正则表达式都是子查询(属于下面的 stringattribute 类型)。示例

{resource_access_query, {match, {string, "${name}"},
{string, "^${username}-"}}
}

这允许用户配置、读取和写入名称以他们自己的用户名后跟连字符开头的任何对象。

String Sub-query

{string, Pattern}

仅将参数替换为字符串。由于这返回的是字符串而不是布尔值,因此应在 matchequals 查询中使用。有关示例,请参见上文。作为简写,您可以使用纯字符串代替 {string, Pattern}

Attribute Sub-query

{attribute, DNPattern, AttributeName}

返回从 LDAP 检索的对象的属性值。由于这返回的是字符串而不是布尔值,因此应在 matchequals 查询中使用。有关示例,请参见上文。

Example Configuration

将所有内容整合在一起,这是一个示例配置。它同时使用了标准配置高级配置文件。这使所有用户都能够访问管理插件,但使他们都不是管理员。虚拟主机的访问权限由每个虚拟主机的组成员身份控制。只有 admin 组的成员可以声明、删除或绑定交换机和队列,但所有用户都可以发布到交换机并从队列声明。发布到主题类型的交换机仅限于路由键以 “a” 开头的消息,并且从主题消费不受限制(主题授权)。

标准配置 (rabbitmq.conf) 用于配置身份验证后端和几个 LDAP 插件参数

auth_backends.1 = ldap

auth_ldap.servers.1 = my-ldap-server
auth_ldap.user_dn_pattern = cn=${username},ou=People,dc=example,dc=com
auth_ldap.use_ssl = false
auth_ldap.port = 389
auth_ldap.log = false

高级配置 用于定义 LDAP 查询

[{rabbitmq_auth_backend_ldap,[
{vhost_access_query, {in_group,
"ou=${vhost}-users,ou=vhosts,dc=example,dc=com"}},
{resource_access_query,
{for, [{permission, configure, {in_group, "cn=admin,dc=example,dc=com"}},
{permission, write,
{for, [{resource, queue, {in_group, "cn=admin,dc=example,dc=com"}},
{resource, exchange, {constant, true}}]}},
{permission, read,
{for, [{resource, exchange, {in_group, "cn=admin,dc=example,dc=com"}},
{resource, queue, {constant, true}}]}}
]
}},
{topic_access_query,
{for, [{permission, write, {match, {string, "${routing_key}"}, {string, "^a"}}},
{permission, read, {constant, true}}
]
}},
{tag_queries, [{administrator, {constant, false}},
{management, {constant, true}}]}
]}].

或者,您可以使用经典配置格式在一个文件中配置所有内容

[
{rabbit, [{auth_backends, [rabbit_auth_backend_ldap]}]},
{rabbitmq_auth_backend_ldap,
[ {servers, ["my-ldap-server"]},
{user_dn_pattern, "cn=${username},ou=People,dc=example,dc=com"},
{use_ssl, false},
{port, 389},
{log, false},
{vhost_access_query, {in_group,
"ou=${vhost}-users,ou=vhosts,dc=example,dc=com"}},
{resource_access_query,
{for, [{permission, configure, {in_group, "cn=admin,dc=example,dc=com"}},
{permission, write,
{for, [{resource, queue, {in_group, "cn=admin,dc=example,dc=com"}},
{resource, exchange, {constant, true}}]}},
{permission, read,
{for, [{resource, exchange, {in_group, "cn=admin,dc=example,dc=com"}},
{resource, queue, {constant, true}}]}}
]
}},
{topic_access_query,
{for, [{permission, write, {match, {string, "${routing_key}"}, {string, "^a"}}},
{permission, read, {constant, true}}
]
}},
{tag_queries, [{administrator, {constant, false}},
{management, {constant, true}}]}
]
}
].

Troubleshooting

使用 LDAP 进行身份验证和/或授权会在系统中引入另一个活动部件。由于 LDAP 服务器是通过网络访问的,因此 网络故障排除TLS 故障排除 指南中涵盖的一些主题适用于 LDAP。

为了排除身份验证和授权阶段执行的 LDAP 操作故障,强烈建议启用 LDAP 流量日志记录

ldapsearch 是 LDAP 附带的命令行工具,可以针对 OpenLDAP 安装执行任意 LDAP 查询。这在排除复杂的授权查询故障时非常有用。ldp.exe 是 Active Directory 的对应工具。

LDAP Proxies

LDAP 代理可用于修改此插件执行的 LDAP 请求。ldaptor 是一个库,可用于将自定义逻辑构建到代理中。

操作员应认识到,使用代理将使 LDAP 请求的故障排除更加困难。

© . All rights reserved.