身份验证、授权、访问控制
概述
本文档介绍了 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” 用户只能从 localhost 连接
默认情况下,禁止 guest
用户从远程主机连接; 它只能通过环回接口 (localhost
) 连接。 这适用于连接,无论协议如何。 任何其他用户(默认情况下)都不会受到此方式的限制。
使用默认用户的远程连接将被拒绝,并显示类似于以下的日志消息
[error] <0.918.0> PLAIN login refused: user 'guest' can only connect via localhost
在生产系统中解决此问题的推荐方法是创建一个具有生成的凭据的新用户或一组用户,并具有访问必要虚拟主机的权限。 这可以使用CLI 工具、HTTP API 或定义导入来完成。
Fedora Linux 的 RabbitMQ 包被修补,以允许使用众所周知的凭据进行远程连接。
强烈建议不要这样做。 Fedora Linux 用户应考虑从 Team RabbitMQ 的软件包存储库安装 RabbitMQ,并避免使用发行版打包的版本,除非这个严重特定于发行版的安全漏洞得到解决。
这是通过配置文件中的 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 工具创建必要的虚拟主机、用户、权限等
- 将定义导出到定义文件
- 删除文件中不相关的部分
- 配置节点以在节点启动时或之后导入文件
有关更多信息,请参阅定义指南中关于在节点启动时导入定义的内容。
在节点启动后导入定义
请参阅定义指南中关于在节点启动后导入定义的内容。
覆盖默认用户凭据
可以使用两个配置选项来覆盖默认用户凭据。 此用户仅在首次节点启动时创建,因此它们必须在首次启动之前存在于配置文件中。
设置如下:
# 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) | 交换机 (AE) | 交换机 (exchange) |
exchange.delete | 交换机 (exchange) | |||
queue.declare | (passive=false) | 队列 (queue) | ||
queue.declare | (passive=true) | |||
queue.declare | (使用 DLX) | 队列 (queue) | 交换机 (DLX) | 队列 (queue) |
queue.delete | 队列 (queue) | |||
exchange.bind | 交换机(目标)(exchange (destination)) | 交换机(源)(exchange (source)) | ||
exchange.unbind | 交换机(目标)(exchange (destination)) | 交换机(源)(exchange (source)) | ||
queue.bind | 队列 (queue) | 交换机 (exchange) | ||
queue.unbind | 队列 (queue) | 交换机 (exchange) | ||
basic.publish | 交换机 (exchange) | |||
basic.get | 队列 (queue) | |||
basic.consume | 队列 (queue) | |||
queue.purge | 队列 (queue) |
权限表示为一组三元正则表达式 - 每个虚拟主机一个 configure、write 和 read。 用户被授予对名称与正则表达式匹配的所有资源执行操作的相应权限。
例如,上表演示了 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
关闭所有现有连接 - 确保应用程序可以发现新凭据并重新连接,例如,通过重新启动它们
如果怀疑凭据泄露,可以使用更彻底的凭据轮换程序
- 评估用户的权限并获取完整的集群定义文件
- 删除用户,从而撤销其访问权限并关闭所有使用这些凭据的现有连接
- 使用新的生成密码重新添加用户
- 重新授予用户对其先前有权访问的所有虚拟主机的访问权限
- 确保应用程序可以发现新凭据并重新连接,例如,通过重新启动它们
对于外部 authN 后端(例如 LDAP),用户帐户在 RabbitMQ 外部进行管理,因此凭据轮换例程也将在 RabbitMQ 外部进行。
身份验证和授权后端
身份验证和授权是可插拔的。 插件可以提供以下实现的:
- 身份验证 (“authn”) 后端:它们确定客户端身份并决定是否应允许客户端连接
- 授权 (“authz”) 后端:它们确定已识别(已验证身份)的客户端是否有权执行特定操作
插件可以同时提供这两个后端是可能且常见的。 RabbitMQ 附带以下内置插件,这些插件同时提供身份验证和授权后端
以下内置插件提供授权后端实现
某些插件(例如 Source IP range one)也仅提供授权后端。
身份验证应该由内部数据库、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 插件)oauth
或oauth2
表示rabbit_auth_backend_oauth2
(来自 OAuth 2.0 插件)http
表示rabbit_auth_backend_http
(来自 HTTP 身份验证后端插件)dummy
表示rabbit_auth_backend_dummy
对于没有快捷方式的插件,必须使用完整的模块(而不是插件的名称!)
# note that the module name begins with a "rabbit_", not "rabbitmq_", like plugin
# names usually do
auth_backends.1 = rabbit_auth_backend_ip_range
当使用第三方插件时,必须提供完整的模块名称。
以下示例配置 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 = http://my-authenticator-app/auth/user
auth_http.vhost_path = http://my-authenticator-app/auth/vhost
auth_http.resource_path = http://my-authenticator-app/auth/resource
auth_http.topic_path = http://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 将使用在 |
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 加密其流量。
拒绝来自远程主机的默认用户连接
默认用户无法从远程主机连接是有原因的。启用此类连接会大大降低集群的安全性。考虑为所有生产集群创建一个具有生成的凭据的唯一用户。
如果凭据报告为正确,来自 localhost 的连接成功,但远程连接失败,则问题在于默认用户连接限制。
在这种情况下,错误将如下所示
2024-08-24 17:28:32.153698-04:00 [error] <0.1567.0> PLAIN login refused: user 'guest' can only connect via localhost
必须为从远程主机连接的客户端(包括shovels 和 federation links)创建单独的、具有生成凭据的用户。
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'