LDAP 支持
概述
RabbitMQ 可以通过委托给外部 LDAP 服务器来使用 LDAP 执行 身份验证和授权。此功能由一个内置插件提供,该插件必须启用。
LDAP 插件通常与另一个插件 rabbitmq_auth_backend_cache 结合使用,以提高延迟并显著降低 LDAP 服务器的负载。
身份验证和授权操作将使用 RabbitMQ 操作员配置的模板转换为LDAP 查询。
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 上,eldap 由 erlang-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 后端
启用插件后,有必要配置节点以使用它。
这包括:
- 将 LDAP 列为身份验证 (authN) 和/或授权 (authZ) 后端。
- 配置LDAP 服务器端点。
- 指定各种 authZ 权限检查将使用哪些LDAP 查询。
下面的示例将配置 RabbitMQ仅用于身份验证和授权,并忽略内部数据库。
# 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
在 高级配置格式中:
{rabbit,[{auth_backends, [{rabbit_auth_backend_ldap, rabbit_auth_backend_internal},
rabbit_auth_backend_internal]}]}
配置
插件启用并连接其后端后,必须配置许多特定于 LDAP 的设置。它们包括 LDAP 服务器列表、身份验证和授权设置等。
默认配置允许所有用户访问所有 vhost 中的所有对象,但不会使他们成为管理员。通过配置 LDAP 查询可以限制访问。
LDAP 服务器
为了使插件能够连接到 LDAP 服务器,必须使用 auth_ldap.servers 键配置至少一个服务器主机名或 IP 地址。如果提供了多个值,列表值可以是主机名或 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 秒之间。
使用 TLS 进行 LDAP 连接
从 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 significantly simplifies node configuration.
auth_ldap.ssl_options.verify = verify_none
使用 advanced.conf 格式的相同示例:
[
{rabbitmq_auth_backend_ldap, [
{servers, ["ldap1.eng.megacorp.local", "ldap2.eng.megacorp.local"]},
{use_ssl, true},
{ssl_options, [
%% Enables peer certificate chain verification.
%% This behavior is the default starting with Erlang 26 (and thus RabbitMQ 3.13+)
{verify, verify_peer}
]}
]}
].
[
{rabbitmq_auth_backend_ldap, [
{servers, ["ldap1.eng.megacorp.local", "ldap2.eng.megacorp.local"]},
{use_ssl, true},
{ssl_options, [
%% Disables peer certificate chain verification.
%%
%% Doing so loses one of the key benefits of TLS and make the setup less secure
%% but also significantly simplifies node configuration.
{verify, verify_none}
]}
]}
].
对等链验证深度
对于使用多个中间证书的服务器,可以增加证书链验证深度。
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"},
%% Disables peer certificate chain verification.
%%
%% Doing so loses one of the key benefits of TLS and make the setup less secure
%% but also significantly simplifies node configuration.
{verify, verify_none},
{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 术语表。
| 术语 | 描述 |
|---|---|
| 绑定 | LDAP 语境下的“身份验证请求”。 |
| 专有名称 (DN) | 专有名称是 LDAP 目录(树)中的唯一键,用于标识对象(如用户或组)。插件将在身份验证阶段(见下文)将客户端提供的用户名转换为专有名称。一种思考 DN 的方式是将其视为文件系统中的绝对文件路径。 |
| 通用名称 (CN) | 树中对象的简短标识符。此标识符在 LDAP 数据库中的对象类别(类型)之间会有所不同。例如,人的通用名称是全名。组的通用名称是该组的名称。一种思考 CN 的方式是将其视为文件系统中的文件名。 |
| 属性 | 对象的属性(键值对)。将其视为面向对象编程语言中对象的字段。 |
| 对象类别 | 一组预定义的属性。将其视为面向对象语言中的类型(类)。 |
| 条目 | 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_bind、dn_lookup_base 和 dn_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_attribute 和 auth_ldap.user_dn_pattern,则会结合使用这两种方法:插件将填充模板,然后搜索 DN。
auth_ldap.dn_lookup_bind 的默认值为 as_user。对于 auth_ldap.dn_lookup_base 和 auth_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_dn 和 other_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_dn 和 auth_ldap.other_bind.password 或 anon 选项。
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_queryrabbitmq_auth_backend_ldap.resource_access_queryrabbitmq_auth_backend_ldap.topic_access_queryrabbitmq_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 或 dn_lookup_base 变量(如果前者为 none)中搜索。如果两个查找基变量都设置为 none,则查询将始终返回 false。搜索范围可以设置为 subtree 或 single_level。
subtree搜索查找基下的所有对象。single_level直接搜索查找基内的组。
范围的默认值为 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}}
]
这会在用户是位于 ou=groups,dc=example,dc=com 目录中的 member 属性值定义的组层次结构中的成员时,授予对虚拟主机的访问权限。
对于查询
{for, [{Name, Value, SubQuery}, ...]}
这允许您拆分查询并用不同的子查询处理不同的情况。
选项应为三元组列表,每个三元组包含一个名称、一个值和一个子查询。名称是变量的名称(即,可以放入 ${} 替换中的内容)。值是该变量的可能值。
请注意,值是不同的 Erlang 类型;resource 和 permission 具有原子值(例如,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 组的成员声明和删除交换机,并允许所有用户执行所有其他操作。
布尔查询
{'not', SubQuery}
{'and', [SubQuery1, SubQuery2, SubQuery3, ...]}
{'or', [SubQuery1, SubQuery2, SubQuery3, ...]}
这些可用于使用布尔逻辑组合子查询。“and”和“or”查询各自接受任意数量的子查询,当所有或任何子查询评估为 true 时分别返回 true。
请注意,“and”、“or”和“not”是 Erlang 中的保留字,因此关键字在配置文件中需要用单引号括起来,如上所示。示例:
{resource_access_query,
{'or',
[{'and',
[{equals, "${name}", "test1"},
{equals, "${username}", "user1"}]},
{'and',
[{equals, "${name}", "test2"},
{'not', {equals, "${username}", "user1"}}]}
]}}
此示例授予对名为“test1”到“user1”的对象的完全访问权限,并授予对“test2”的访问权限给除“user1”以外的所有人。
相等查询
{equals, StringSubQuery1, StringSubQuery2}
接受两个字符串,并检查其中一个是否与另一个匹配。请注意,这两个字符串都是子查询(分别为 string 和 attribute 类型)。
这对于将字符串替换变量的值与常量或属性值等进行比较非常有用。示例:
{resource_access_query,
{for, [{permission, configure, {equals, {attribute, "${user_dn}", "description"},
{string, "can-declare-${resource}s"}
}
},
{permission, write, {constant, true}},
{permission, read, {constant, true}}
]
}
这根据用户描述字段中是否存在字符串“can-declare-exchanges”和“can-declare-queues”来授予声明和删除交换机和队列的权限,并授予所有人写入和读取交换机的权限。
匹配查询
{match, StringSubQuery, RESubQuery}
接受一个字符串和一个正则表达式,并检查其中一个是否与另一个匹配。请注意,字符串和正则表达式都是子查询(分别为 string 和 attribute 类型)。示例:
{resource_access_query, {match, {string, "${name}"},
{string, "^${username}-"}}
}
这允许用户配置、读取和写入任何其名称以其用户名后跟一个连字符开头的对象。
字符串子查询
{string, Pattern}
仅将参数替换到字符串中。由于这返回的是字符串而不是布尔值,因此应将其用于 match 或 equals 查询。请参阅上面的示例。作为快捷方式,您可以使用纯字符串而不是 {string, Pattern}。
属性子查询
{attribute, DNPattern, AttributeName}
返回从 LDAP 检索的对象属性的值。由于这返回的是字符串而不是布尔值,因此应将其用于 match 或 equals 查询。请参阅上面的示例。
示例配置
将所有内容汇总在一起,这里有一个示例配置。它同时使用了标准配置和高级配置文件。这使得所有用户都能访问管理插件,但其中没有一个是管理员。虚拟主机的访问权限由每个虚拟主机的组成员资格控制。只有 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}}]}
]
}
].
故障排除
使用 LDAP 进行身份验证和/或授权会在系统中引入另一个活动组件。由于 LDAP 服务器是通过网络访问的,因此 网络故障排除 和 TLS 故障排除 指南中涵盖的某些主题适用于 LDAP。
为了对身份验证和授权阶段执行的 LDAP 操作进行故障排除,强烈建议启用 LDAP 流量日志记录。
ldapsearch 是 LDAP 附带的命令行工具,可以针对 OpenLDAP 安装执行任意 LDAP 查询。这在对复杂的授权查询进行故障排除时很有用。ldp.exe 是 Active Directory 的对应工具。
LDAP 代理
可以使用 LDAP 代理来修改此插件执行的 LDAP 请求。ldaptor 是一个可用于将自定义逻辑集成到代理中的库。
操作员应认识到使用代理会使 LDAP 请求的故障排除更加困难。