身份验证、授权和访问控制
概述
本文档描述了 RabbitMQ 中的身份验证和授权功能。它们共同允许操作员控制对系统的访问。
不同的用户可以被授予仅访问特定虚拟主机的权限。他们每个虚拟主机中的权限也可以被限制。
RabbitMQ 支持两种主要的身份验证机制以及多种身份验证和授权后端。
本指南涵盖了各种身份验证、授权和用户管理主题,例如
- 访问控制要点
- 默认虚拟主机和用户
- 对默认用户施加的连接限制
- 授权和资源权限
- 如何使用 CLI 工具管理用户和权限
- 如何更改使用的身份验证或授权后端,或使用后端的组合
- 如何使用其 TLS 证书信息对客户端进行身份验证
- 如何限制对主题交换机上的主题的访问
- 用户标签及其用法
- 如何轮换凭据并撤销用户的访问权限
- 在生成的密码中对字符进行 Shell 转义
- 如何预创建用户及其权限
- 针对身份验证和授权失败进行故障排除
基于密码的身份验证有一个配套指南。两个密切相关的主题OAuth 2 支持和TLS 支持(包括基于 x.509 证书的身份验证)在专门的指南中进行了介绍。
术语和定义
身份验证和授权经常被混淆或互换使用。这是错误的,在 RabbitMQ 中,两者是分开的。为了简单起见,我们将身份验证定义为“识别用户是谁”,将授权定义为“确定用户被允许和不被允许做什么”。
基础知识
当客户端连接到 RabbitMQ 时,它们会指定一组凭据:用户名-密码对、JWT 令牌或x.509 证书。每个连接都有一个关联的用户,该用户经过身份验证。它还针对一个虚拟主机,用户必须为此虚拟主机具有一定的权限集。
用户凭据、目标虚拟主机和(可选)客户端证书在连接启动时指定。
有一对默认的凭据称为默认用户。默认情况下,此用户只能用于**主机本地连接**。使用默认用户的远程连接将被拒绝。
生产环境不应使用默认用户。而是创建具有生成凭据的新用户帐户。
默认虚拟主机和用户
当服务器第一次开始运行并检测到其数据库未初始化或已重置或删除(节点为“空白节点”)时,它会使用以下资源初始化一个新的数据库
- 一个名为
/
(斜杠)的虚拟主机 - 一个名为
guest
的用户,其默认密码为guest
,被授予对/
虚拟主机的完全访问权限
如果空白节点在启动时导入定义,则不会创建此默认用户。
**强烈建议**预先配置一个使用生成的用户名和密码的新用户或删除guest
用户,或者至少更改其密码为合理安全的生成值,以防止公众知晓。
身份验证:您自称是谁?
在应用程序连接到 RabbitMQ 并执行操作之前,它必须进行身份验证,即出示并证明其身份。使用该身份,RabbitMQ 节点可以查找其权限并授权访问资源,例如虚拟主机、队列、交换机等。
客户端身份验证的两种主要方法是用户名/密码对和X.509 证书。用户名/密码对可与多种身份验证后端一起使用,这些后端验证凭据。
无法进行身份验证的连接将关闭,并在服务器日志中显示错误消息。
使用客户端 TLS (x.509) 证书数据进行身份验证
要使用 X.509 证书对客户端连接进行身份验证,必须启用内置插件rabbitmq-auth-mechanism-ssl,并且必须配置客户端以使用 EXTERNAL 机制。使用此机制,将忽略任何客户端提供的密码。
"guest" 用户只能从本地主机连接
默认情况下,guest
用户被禁止从远程主机连接;它只能通过环回接口(localhost
)连接。这适用于无论协议如何的连接。任何其他用户默认情况下都不会受到此限制。
使用默认用户的远程连接将被拒绝,并显示类似于以下内容的日志消息
[error] <0.918.0> PLAIN login refused: user 'guest' can only connect via localhost
在生产系统中解决此问题的推荐方法是创建一个具有生成凭据的新用户或一组用户,并具有访问必要虚拟主机的权限。这可以使用CLI 工具、HTTP API 或定义导入来完成。
这通过配置文件中的loopback_users
项进行配置。
可以通过将loopback_users
配置设置为none
来允许guest
用户从远程主机连接。
一个允许guest
进行远程连接的最小化RabbitMQ 配置文件如下所示
# DANGER ZONE!
#
# allowing remote connections for default user is highly discouraged
# as it dramatically decreases the security of the system. Delete the default user
# instead and create a new one with generated secure credentials, or use JWT tokens,
# or x.509 certificates for clients to authenticate themselves
loopback_users = none
管理用户和权限
可以使用CLI 工具和定义导入(如下所述)来管理用户和权限。
开始之前:Shell 转义和生成的密码
生成复杂密码(通常包含非字母数字字符)是一种常见的安全实践。此做法完全适用于 RabbitMQ 用户。
Shell(bash
、zsh
等)将某些字符(!
、?
、&
、^
、"
、'
、*
、~
等)解释为控制字符。
当在命令行上为rabbitmqctl add_user
、rabbitmqctl change_password
和其他接受密码的命令指定密码时,必须针对所使用的 Shell 对此类控制字符进行适当的转义。如果转义不当,则命令将失败,或者 RabbitMQ CLI 工具将从 Shell 接收不同的值。
在生成将在命令行上传递的密码时,使用符号集非常有限(例如:
、=
)的长(例如 40 到 100 个字符)字母数字值是最安全的选择。
当通过HTTP API创建用户而不使用 Shell(例如curl
)时,控制字符限制不适用。但是,根据所使用的编程语言,可能需要不同的转义规则。
添加用户
要添加用户,请使用rabbitmqctl add_user
。它有多种方法可以指定密码
- bash
- PowerShell
- cmd
# will prompt for password, only use this option interactively
rabbitmqctl add_user "username"
# Password is provided via standard input.
# Note that certain characters such as !, &, $, #, and so on must be escaped to avoid
# special interpretation by the shell.
echo '2a55f70a841f18b97c3a7db939b7adc9e34a0f1b' | rabbitmqctl add_user 'username'
# Password is provided as a command line argument.
# Note that certain characters such as !, &, $, #, and so on must be escaped to avoid
# special interpretation by the shell.
rabbitmqctl add_user 'username' '2a55f70a841f18b97c3a7db939b7adc9e34a0f1b'
# password is provided as a command line argument
rabbitmqctl.bat add_user 'username' '9a55f70a841f18b97c3a7db939b7adc9e34a0f1d'
# passwords with special characters must be quoted correctly
rabbitmqctl.bat add_user 'username' '"w63pnZ&LnYMO(t"'
rem password is provided as a command line argument
rabbitmqctl.bat add_user "username" "9a55f70a841f18b97c3a7db939b7adc9e34a0f1d"
rem passwords with special characters must be quoted correctly
rabbitmqctl.bat add_user "username" "w63pnZ&LnYMO(t"
新添加的用户必须被授予一个或多个虚拟主机的权限,否则其连接将被拒绝。
列出用户
要列出集群中的用户,请使用rabbitmqctl list_users
- bash
- PowerShell
rabbitmqctl list_users
rabbitmqctl.bat list_users
输出可以更改为 JSON 格式
- bash
- PowerShell
rabbitmqctl list_users --formatter=json
rabbitmqctl.bat list_users --formatter=json
删除用户
要删除用户,请使用rabbitmqctl delete_user
- bash
- PowerShell
rabbitmqctl delete_user 'username'
rabbitmqctl.bat delete_user 'username'
授予用户权限
要授予权限给虚拟主机中的用户,请使用rabbitmqctl set_permissions
- bash
- PowerShell
# First ".*" for configure permission on every entity
# Second ".*" for write permission on every entity
# Third ".*" for read permission on every entity
rabbitmqctl set_permissions -p "custom-vhost" "username" ".*" ".*" ".*"
# First ".*" for configure permission on every entity
# Second ".*" for write permission on every entity
# Third ".*" for read permission on every entity
rabbitmqctl.bat set_permissions -p 'custom-vhost' 'username' '.*' '.*' '.*'
清除虚拟主机中用户的权限
要撤销权限从虚拟主机中的用户,请使用rabbitmqctl clear_permissions
- bash
- PowerShell
# Revokes permissions in a virtual host
rabbitmqctl clear_permissions -p "custom-vhost" "username"
# Revokes permissions in a virtual host
rabbitmqctl.bat clear_permissions -p 'custom-vhost' 'username'
对多个虚拟主机进行操作
每个rabbitmqctl
权限管理操作都限定在一个虚拟主机范围内。批量操作必须通过脚本编写,虚拟主机列表来自rabbitmqctl list_vhosts --silent
- bash
- PowerShell
# Assumes a Linux shell.
# Grants a user permissions to all virtual hosts.
for v in $(rabbitmqctl list_vhosts --silent); do rabbitmqctl set_permissions -p $v "a-user" ".*" ".*" ".*"; done
rabbitmqctl.bat list_vhosts --silent | %{ rabbitmqctl.bat set_permissions -p $_ 'a-user' '.*' '.*' '.*' }
用户和权限的预创建(播种)
生产环境通常需要预配置(播种)一些虚拟主机、用户和用户权限。
这可以通过几种方式完成
- 使用CLI 工具
- 节点启动时的定义导出和导入(推荐)
- 覆盖配置文件中的默认凭据
CLI 工具
请参阅有关用户管理的部分。
节点启动时的定义导入
此过程涉及以下步骤
- 设置一个临时节点,并使用 CLI 工具创建必要的虚拟主机、用户、权限等
- 将定义导出到定义文件
- 删除与当前场景无关的文件部分
- 配置节点以在节点启动时或之后导入文件
请参阅定义指南中的节点启动时导入定义以了解更多信息。
节点启动后导入定义
请参阅定义指南中的节点启动后导入定义。
覆盖默认用户凭据
可以使用两个配置选项来覆盖默认用户凭据。此用户仅在**第一次节点启动时**创建,因此它们必须在第一次启动之前存在于配置文件中。
设置如下:
# default is "guest", and its access is limited to localhost only.
# See ./access-control#default-state
default_user = a-user
# default is "guest"
default_pass = 768a852ed69ce916fa7faa278c962de3e4275e5f
与rabbitmq.conf
中的所有值一样,#
字符用于开始注释,因此在生成的凭据中必须避免此字符。
默认用户凭据也可以加密。这需要使用高级配置文件,即advanced.config
。本主题在配置值加密中进行了更详细的介绍。
授权:权限的工作原理
当 RabbitMQ 客户端建立与服务器的连接并进行身份验证时,它会指定一个它打算在其内操作的虚拟主机。此时会执行第一级访问控制,服务器会检查用户是否有权访问虚拟主机,否则会拒绝连接尝试。
资源(即交换机和队列)是在特定虚拟主机内的命名实体;相同的名称在每个虚拟主机中表示不同的资源。当对资源执行某些操作时,会执行第二级访问控制。
RabbitMQ 区分资源上的configure
、write
和read
操作。configure
操作创建或销毁资源,或更改其行为。write
操作将消息注入资源。read
操作从资源中检索消息。
为了对资源执行操作,用户必须被授予相应的权限。下表显示了对所有执行权限检查的 AMQP 0-9-1 命令所需的资源类型上的权限。
AMQP 0-9-1 操作 | configure | write | read | |
---|---|---|---|---|
exchange.declare | (passive=false) | exchange | ||
exchange.declare | (passive=true) | |||
exchange.declare | (使用AE) | exchange | exchange (AE) | exchange |
exchange.delete | exchange | |||
queue.declare | (passive=false) | queue | ||
queue.declare | (passive=true) | |||
queue.declare | (使用DLX) | queue | exchange (DLX) | queue |
queue.delete | queue | |||
exchange.bind | exchange (目标) | exchange (源) | ||
exchange.unbind | exchange (目标) | exchange (源) | ||
queue.bind | queue | exchange | ||
queue.unbind | queue | exchange | ||
basic.publish | exchange | |||
basic.get | queue | |||
basic.consume | queue | |||
queue.purge | queue |
权限表示为每个虚拟主机上的一组三个正则表达式——分别用于配置、写入和读取。用户被授予对名称与正则表达式匹配的所有资源执行操作的相应权限。
例如,上表表明queue.bind
协议操作需要目标queue
上的write
权限,并且目标exchange
上需要read
权限。
换句话说,为了允许用户将名为queueA
的队列绑定到名为exchangeB
的交换机,用户将需要write
权限正则表达式(对于正确的虚拟主机)匹配queueA
,并且read
权限正则表达式匹配exchangeB
。
为了方便起见,RabbitMQ 在执行权限检查时将 AMQP 0-9-1 的默认交换机的空名称映射到 'amq.default'。
正则表达式'^$'
(即仅匹配空字符串)涵盖所有资源,并有效地阻止用户执行任何操作。内置的 AMQP 0-9-1 资源名称以amq.
为前缀,服务器生成的名称以amq.gen
为前缀。
例如,'^(amq\.gen.*|amq\.default)$'
允许用户访问服务器生成的名称和默认交换机。空字符串''
是'^$'
的同义词,并以完全相同的方式限制权限。
RabbitMQ 可能会在每个连接或每个通道的基础上缓存访问控制检查的结果。因此,用户权限的更改可能仅在用户重新连接时才生效。
有关如何设置访问控制的详细信息,请参阅用户管理部分以及rabbitmqctl 手册页。
用户标签和管理 UI 访问
除了上面介绍的权限外,用户还可以关联标签。目前,只有管理 UI 访问受用户标签控制。
标签使用rabbitmqctl管理。默认情况下,新创建的用户没有任何标签。
请参阅管理插件指南,以了解支持哪些标签以及它们如何限制管理 UI 访问。
主题授权
RabbitMQ 支持主题交换机的主题授权。发布到主题交换机的消息的路由键在执行发布授权时会被考虑在内(例如,在 RabbitMQ 默认授权后端中,路由键与正则表达式匹配以决定是否可以将消息路由到下游)。主题授权针对 STOMP 和 MQTT 等协议,这些协议的结构围绕主题,并在后台使用主题交换机。
主题授权是在发布者现有检查之上添加的另一层。发布到主题类型交换机的消息将同时经过basic.publish
和路由键检查。如果前者拒绝访问,则永远不会调用后者。
主题授权也可以对主题使用者强制执行。请注意,它对不同协议的工作方式不同。主题授权的概念仅对面向主题的协议(如 MQTT 和 STOMP)真正有意义。例如,在 AMQP 0-9-1 中,使用者从队列中消费,因此标准资源权限适用。此外,对于 AMQP 0-9-1,如果已配置任何主题权限,则会根据配置的主题权限检查 AMQP 0-9-1 主题交换机和队列/交换机之间的绑定路由键。有关 RabbitMQ 如何处理主题授权的更多信息,请参阅STOMP和MQTT文档指南。
当使用默认授权后端时,如果未定义任何主题权限(在新的 RabbitMQ 安装中就是这样),则始终授权发布到主题交换机或从主题消费。使用此授权后端,主题授权是可选的:您无需批准任何交换机。因此,要使用主题授权,您需要选择加入并为一个或多个交换机定义主题权限。有关详细信息,请参阅rabbitmqctl 手册页。
内部(默认)授权后端支持权限模式中的变量扩展。支持三个变量:username
、vhost
和client_id
。请注意,client_id
仅适用于 MQTT。例如,如果连接的用户为tonyg
,则权限^{username}-.*
将扩展为^tonyg-.*
如果使用其他授权后端(例如LDAP、HTTP、OAuth 2),请参阅这些后端的文档。
如果使用自定义授权后端,则通过实现rabbit_authz_backend
行为的check_topic_access
回调来强制执行主题授权。
撤销用户访问权限和凭据轮换
撤销用户访问权限
要撤销用户访问权限,建议的方法是删除用户。属于已删除用户的所有打开的连接都将关闭。
也可以清除用户权限,但这不会影响任何当前打开的连接。连接使用授权操作缓存,因此客户端操作最终将被拒绝。时间段取决于使用的授权后端。
凭据轮换
存储在内部数据存储中的用户的凭据轮换通常涉及以下步骤
- 使用 CLI 工具更改用户密码或使用
PUT /api/users/{user}
HTTP API端点更新它 - 使用
rabbitmqctl close_all_user_connections
或rabbitmqctl close_all_connections
关闭所有现有连接 - 确保应用程序可以发现新的凭据并重新连接,例如,通过重新启动它们
如果怀疑凭据泄露,可以使用更彻底的凭据轮换过程
- 评估用户的权限并获取完整的集群定义文件
- 删除用户,从而撤销其访问权限并关闭使用这些凭据的所有现有连接
- 使用新生成的密码重新添加用户
- 重新授予用户访问其之前有权访问的所有虚拟主机的权限
- 确保应用程序可以发现新的凭据并重新连接,例如,通过重新启动它们
对于像LDAP这样的外部身份验证后端,用户帐户是在 RabbitMQ 外部管理的,因此凭据轮换例程也将位于 RabbitMQ 外部。
身份验证和授权后端
身份验证和授权是可插拔的。插件可以提供以下内容的实现:
- 身份验证 (“authn”) 后端:它们确定客户端身份并决定是否允许客户端连接
- 授权 (“authz”) 后端:它们确定已识别的(已认证的)客户端是否有权执行特定操作
一个插件可以同时提供这两种后端,这很常见也很实用。RabbitMQ 附带以下内置插件,它们同时提供身份验证和授权后端
以下内置插件提供授权后端实现
某些插件(例如Source IP 范围插件)也只提供授权后端。
身份验证应该由内部数据库、LDAP 等处理。
一个特殊的缓存后端可以与其他后端结合使用,以显著减少它们对外部服务(如 LDAP 或 HTTP 服务器)产生的负载。
组合后端
可以使用 `auth_backends` 配置键为 `authn` 或 `authz` 使用多个后端。当使用多个身份验证后端时,链中后端返回的第一个正结果被视为最终结果。这不要与混合后端混淆(例如,使用 LDAP 进行身份验证和使用内部后端进行授权)。
以下示例配置 RabbitMQ 仅使用内部后端(也是默认设置)
# rabbitmq.conf
#
# 1 here is a backend name. It can be anything.
# Since we only really care about backend
# ordering, we use numbers throughout this guide.
#
# "internal" is an alias for rabbit_auth_backend_internal
auth_backends.1 = internal
上面的示例使用别名 `internal` 代替 `rabbit_auth_backend_internal`。可以使用以下别名:
- `internal` 代表 `rabbit_auth_backend_internal`
- `ldap` 代表 `rabbit_auth_backend_ldap`(来自LDAP 插件)
- `http` 代表 `rabbit_auth_backend_http`(来自HTTP 身份验证后端插件)
- `dummy` 代表 `rabbit_auth_backend_dummy`
某些插件目前没有快捷方式。在这种情况下,必须使用完整的模块(而不是插件的名称)
# note that the module name begins with a "rabbit_", not "rabbitmq_", like in the name
# of the plugin
auth_backends.1 = rabbit_auth_backend_oauth2
使用第三方插件时,必须提供完整的模块名称。
以下示例配置 RabbitMQ 使用LDAP 后端进行身份验证和授权。不会查询内部数据库
auth_backends.1 = ldap
这将首先检查 LDAP,如果无法通过 LDAP 认证用户,则回退到内部数据库。
auth_backends.1 = ldap
auth_backends.2 = internal
与上面相同,但将回退到HTTP 后端。
# rabbitmq.conf
#
auth_backends.1 = ldap
# uses module name instead of a short alias, "http"
auth_backends.2 = rabbit_auth_backend_http
# See HTTP backend docs for details
auth_http.user_path = https://my-authenticator-app/auth/user
auth_http.vhost_path = https://my-authenticator-app/auth/vhost
auth_http.resource_path = https://my-authenticator-app/auth/resource
auth_http.topic_path = https://my-authenticator-app/auth/topic
以下示例配置 RabbitMQ 使用内部数据库进行身份验证,并使用源 IP 范围后端进行授权。
# rabbitmq.conf
#
auth_backends.1.authn = internal
# uses module name because this backend is from a 3rd party
auth_backends.1.authz = rabbit_auth_backend_ip_range
以下示例配置 RabbitMQ 使用LDAP 后端进行身份验证,并使用内部后端进行授权。
# rabbitmq.conf
#
auth_backends.1.authn = ldap
auth_backends.1.authz = internal
以下示例比较高级。它将首先检查 LDAP。如果在 LDAP 中找到用户,则会针对 LDAP 检查密码,并且后续的授权检查将针对内部数据库执行(因此 LDAP 中的用户也必须存在于内部数据库中,但不需要在其中设置密码)。如果在 LDAP 中找不到用户,则使用仅内部数据库进行第二次尝试。
# rabbitmq.conf
#
auth_backends.1.authn = ldap
auth_backends.1.authz = internal
auth_backends.2 = internal
身份验证机制
RabbitMQ 支持多种 SASL 身份验证机制。服务器中内置了四种机制:`PLAIN`、`AMQPLAIN`、`ANONYMOUS` 和 `RABBIT-CR-DEMO`,以及一种 — `EXTERNAL` — 可作为插件使用。
插件可以提供更多身份验证机制。有关一般插件开发的更多信息,请参阅插件开发指南。
内置身份验证机制
内置机制包括:
机制 | 描述 |
PLAIN | SASL PLAIN 身份验证。默认情况下,在 RabbitMQ 服务器和客户端中启用此功能,并且是大多数其他客户端的默认设置。 |
AMQPLAIN | 为向后兼容性保留的 PLAIN 的非标准版本。默认情况下,在 RabbitMQ 服务器中启用此功能。 |
ANONYMOUS | 默认情况下启用此机制,允许匿名客户端在不提供任何凭据的情况下连接。RabbitMQ 将使用 `anonymous_login_user` 和 `anonymous_login_pass` 中配置的凭据在内部对客户端进行身份验证和授权(默认情况下,两者都设置为 `guest`)。换句话说,任何未经身份验证的客户端都将能够连接并充当配置的 `anonymous_login_user`。**对于生产环境,请删除此机制。** 请参阅部署指南。 |
EXTERNAL | 使用带外机制(例如x509 证书对等验证、客户端 IP 地址范围或类似机制)进行身份验证。此类机制通常由 RabbitMQ 插件提供。 |
RABBIT-CR-DEMO | 演示挑战-响应身份验证的非标准机制。此机制的安全性等同于 `PLAIN`,并且**默认情况下**未在 RabbitMQ 服务器中启用。 |
服务器中的机制配置
`auth_mechanisms` 配置键确定向连接的客户端提供哪些已安装的机制。
此变量应为对应于机制名称的接受值列表,例如,以下列表
auth_mechanisms.1 = PLAIN
auth_mechanisms.2 = AMQPLAIN
auth_mechanisms.3 = ANONYMOUS
默认情况下使用。
服务器机制按优先级降序排列。请参阅配置文件文档。
客户端中的机制配置
应用程序必须选择加入才能使用其他身份验证机制,例如 `EXTERNAL`。
Java 中的机制配置
Java 客户端默认情况下不使用 `javax.security.sasl` 包,因为这在非 Oracle JDK 上可能不可预测,并且在 Android 上完全缺失。有一个 RabbitMQ 特定的 SASL 实现,由 `SaslConfig` 接口配置。提供了 `DefaultSaslConfig` 类以在常见情况下更方便地配置 SASL。提供了 `JDKSaslConfig` 类充当 `javax.security.sasl` 的桥梁。
`ConnectionFactory.getSaslConfig()` 和 `ConnectionFactory.setSaslConfig(SaslConfig)` 是与身份验证机制交互的主要方法。
.NET 中的机制配置
.NET 客户端基于 `AuthMechanism` 和 `AuthMechanismFactory` 接口提供自己的 SASL 机制实现。`ConnectionFactory.AuthMechanisms` 属性是按优先级排序的身份验证机制工厂列表。
Erlang 中的机制配置
Erlang 客户端在 `amqp_auth_mechanisms` 模块中提供了自己的 SASL 机制实现。`#amqp_params` 记录可以提供按优先级排序的身份验证函数列表,用于网络连接。
身份验证故障排除
本节介绍了与身份验证相关的几个非常常见的问题。有关授权(权限)错误,请参阅下面的另一节。
在调查与身份验证相关的问题时,检查服务器日志至关重要。
权限错误
身份验证尝试失败,即客户端指定的凭据不正确时,将导致服务器日志消息如下所示
2019-03-25 12:28:19.047 [info] <0.1613.0> accepting AMQP connection <0.1613.0> (127.0.0.1:63839 -> 127.0.0.1:5672)
2019-03-25 12:28:19.056 [error] <0.1613.0> Error on AMQP connection <0.1613.0> (127.0.0.1:63839 -> 127.0.0.1:5672, state: starting):
PLAIN login refused: user 'user2' - invalid credentials
2019-03-25 12:28:22.057 [info] <0.1613.0> closing AMQP connection <0.1613.0> (127.0.0.1:63839 -> 127.0.0.1:5672)
使用X.509 证书进行身份验证的连接上的身份验证失败将以不同的方式记录。有关详细信息,请参阅TLS 故障排除指南。
rabbitmqctl authenticate_user 可用于测试用户名和密码对的身份验证
- bash
- PowerShell
rabbitmqctl authenticate_user "a-username" "a/password"
# note that double quotes are required due to the & character
rabbitmqctl.bat authenticate_user 'a-username' '"a/p&assword"'
如果身份验证成功,它将以代码零退出。如果失败,将使用非零退出代码并打印失败错误消息。
`rabbitmqctl authenticate_user` 将使用 CLI 到节点的通信连接尝试针对内部 API 端点对用户名/密码对进行身份验证。假定连接是受信任的。如果不是这种情况,则其流量可以使用TLS 进行加密。
拒绝默认用户从远程主机连接
默认用户无法从远程主机连接是有原因的。启用此类连接会大大降低集群的安全性。请考虑为所有生产集群创建具有生成凭据的唯一用户。
如果报告凭据正确,本地主机连接成功但远程连接失败,则问题是默认用户连接限制。
在这种情况下,错误将如下所示
2024-08-24 17:28:32.153698-04:00 [error] <0.1567.0> PLAIN login refused: user 'guest' can only connect via localhost
必须为从远程主机连接的客户端(包括Shovel和联合链接)创建一个具有生成凭据的单独用户。
AMQP 0-9-1 中的身份验证失败通知
根据 AMQP 0-9-1 规范,身份验证失败应该导致服务器立即关闭 TCP 连接。但是,使用 RabbitMQ 客户端可以选择接收更具体的通知,方法是使用 AMQP 0-9-1 的身份验证失败通知扩展。现代客户端库透明地支持该扩展,用户无需任何配置:身份验证失败将导致可见的返回错误、异常或其他用于在特定编程语言或环境中传达问题的方式。
授权故障排除
在调查与授权相关的问题时,检查服务器日志至关重要。
权限缺失
授权失败最常见的情况是,用户已创建但未授予客户端尝试连接到的虚拟主机的权限。
在这种情况下,连接将被拒绝,并显示如下消息
2019-03-25 12:26:16.301 [info] <0.1594.0> accepting AMQP connection <0.1594.0> (127.0.0.1:63793 -> 127.0.0.1:5672)
2019-03-25 12:26:16.309 [error] <0.1594.0> Error on AMQP connection <0.1594.0> (127.0.0.1:63793 -> 127.0.0.1:5672, user: 'user2', state: opening):
access to vhost '/' refused for user 'user2'
2019-03-25 12:26:16.310 [info] <0.1594.0> closing AMQP connection <0.1594.0> (127.0.0.1:63793 -> 127.0.0.1:5672, vhost: 'none', user: 'user2')
权限不足
另一种非常常见的情况是,权限已定义,但它们不足以执行客户端尝试执行的操作。
在这种情况下,连接将被接受
rabbitmqctl list_permissions 可用于检查用户在给定虚拟主机中的权限
- bash
- PowerShell
rabbitmqctl list_permissions --vhost /
# => Listing permissions for vhost "/" ...
# => user configure write read
# => user2 .* .* .*
# => guest .* .* .*
# => temp-user .* .* .*
rabbitmqctl list_permissions --vhost gw1
# => Listing permissions for vhost "gw1" ...
# => user configure write read
# => guest .* .* .*
# => user2 ^user2 ^user2 ^user2
rabbitmqctl.bat list_permissions --vhost /
rabbitmqctl.bat list_permissions --vhost gw1
授权失败(权限违规)会记录以下消息
2019-03-25 12:30:05.209 [error] <0.1627.0> Channel error on connection <0.1618.0> (127.0.0.1:63881 -> 127.0.0.1:5672, vhost: 'gw1', user: 'user2'), channel 1:
operation queue.declare caused a channel exception access_refused: access to queue 'user3.q1' in vhost 'gw1' refused for user 'user2'