You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RabbitMQ SSL认证问题:如何基于客户端公钥实现身份验证

RabbitMQ 基于客户端公钥的无密码身份认证实现

要实现类似ZeroMQ从文件夹加载公钥做身份校验的功能,RabbitMQ可以通过自定义认证后端或结合外部脚本来实现,以下是两种可行方案:


方案一:自定义Erlang认证后端(原生贴合需求)

直接编写Erlang模块实现公钥比对逻辑,无需依赖外部服务:

  1. 编写认证模块代码
    创建rabbitmq_pubkey_auth.erl文件,实现RabbitMQ认证回调:
-module(rabbitmq_pubkey_auth).
-behaviour(rabbit_auth_backend).

-export([user_login_authentication/2, user_login_authorization/1, check_vhost_access/3, check_resource_access/4, check_topic_access/4]).

user_login_authentication(#{ssl_cert := Cert}, _AuthProps) ->
    % 提取客户端证书中的公钥(DER格式)
    ClientCert = public_key:pkix_decode_cert(Cert, otp),
    ClientPubKeyInfo = ClientCert#'OTPCertificate'.tbsCertificate#'OTPTBSCertificate'.subjectPublicKeyInfo,
    ClientPubKeyDer = public_key:der_encode('SubjectPublicKeyInfo', ClientPubKeyInfo),

    % 加载授权公钥文件夹中的所有公钥
    PubKeyDir = "/etc/rabbitmq/authorized_pubkeys",
    {ok, Files} = file:list_dir(PubKeyDir),

    % 遍历比对公钥
    case lists:any(fun(File) ->
        FilePath = filename:join(PubKeyDir, File),
        {ok, PubKeyData} = file:read_file(FilePath),
        [{PubKeyDer, _}] = public_key:pem_decode(PubKeyData),
        PubKeyDer =:= ClientPubKeyDer
    end, Files) of
        true -> {ok, #{user => <<"authorized_client">>, tags => [administrator]}}; % 可根据公钥配置不同权限标签
        false -> {refused, "Unauthorized public key", []}
    end;
user_login_authentication(_Creds, _AuthProps) ->
    {refused, "Only SSL certificate authentication is allowed", []}.

% 默认实现其他授权回调(可按需修改)
user_login_authorization(_User) -> ok.
check_vhost_access(_User, _VHost, _AuthzContext) -> ok.
check_resource_access(_User, _VHost, _Resource, _Permission) -> ok.
check_topic_access(_User, _VHost, _Resource, _Permission) -> ok.
  1. 编译并部署模块
  • 用Erlang编译器编译成.beam文件:erlc rabbitmq_pubkey_auth.erl
  • 将编译后的文件复制到RabbitMQ Docker容器的Erlang代码目录(例如/usr/lib/rabbitmq/lib/rabbitmq_server-<version>/ebin)
  1. 修改RabbitMQ配置
    在rabbitmq.conf中启用自定义认证后端并配置SSL基础参数:
# SSL监听配置
listeners.ssl.default = 5671
ssl_options.cacertfile = /etc/rabbitmq/ca.pem
ssl_options.certfile = /etc/rabbitmq/server_cert.pem
ssl_options.keyfile = /etc/rabbitmq/server_key.pem
ssl_options.verify = verify_peer
ssl_options.fail_if_no_peer_cert = true

# 启用自定义认证后端
auth_backends = [rabbitmq_pubkey_auth, rabbit_auth_backend_internal]
  1. 挂载授权公钥文件夹
    将存放授权客户端公钥的文件夹(PEM格式)挂载到容器的/etc/rabbitmq/authorized_pubkeys路径,确保RabbitMQ用户(uid 999)有读取权限。

方案二:结合外部脚本(无需Erlang开发)

通过RabbitMQ的rabbitmq-auth-backend-http插件,调用外部HTTP服务完成公钥比对:

  1. 启用HTTP认证插件
    在容器内执行命令启用插件:rabbitmq-plugins enable rabbitmq_auth_backend_http

  2. 配置RabbitMQ HTTP认证
    修改rabbitmq.conf:

# SSL基础配置同方案一
listeners.ssl.default = 5671
ssl_options.cacertfile = /etc/rabbitmq/ca.pem
ssl_options.certfile = /etc/rabbitmq/server_cert.pem
ssl_options.keyfile = /etc/rabbitmq/server_key.pem
ssl_options.verify = verify_peer
ssl_options.fail_if_no_peer_cert = true

# HTTP认证配置
auth_backends = [rabbit_auth_backend_http, rabbit_auth_backend_internal]
auth_http.url = http://localhost:8080/auth
auth_http.method = post
auth_http.user_path = /login
  1. 编写HTTP认证服务(示例用Python Flask)
    创建pubkey_auth_server.py:
from flask import Flask, request
import os
from cryptography import x509
from cryptography.hazmat.primitives import serialization

app = Flask(__name__)
AUTH_PUBKEY_DIR = "/etc/rabbitmq/authorized_pubkeys"

def load_authorized_pubkeys():
    """加载授权文件夹中的所有公钥(DER格式)"""
    pubkey_der_list = []
    for filename in os.listdir(AUTH_PUBKEY_DIR):
        if not filename.endswith(".pem"):
            continue
        with open(os.path.join(AUTH_PUBKEY_DIR, filename), "rb") as f:
            pubkey = serialization.load_pem_public_key(f.read())
            pubkey_der = pubkey.public_bytes(
                encoding=serialization.Encoding.DER,
                format=serialization.PublicFormat.SubjectPublicKeyInfo
            )
            pubkey_der_list.append(pubkey_der)
    return pubkey_der_list

@app.route('/login', methods=['POST'])
def authenticate():
    # 从请求头获取客户端证书
    client_cert_pem = request.headers.get('X-SSL-Client-Cert')
    if not client_cert_pem:
        return {"refused": "No client certificate provided"}, 401

    # 解析客户端证书并提取公钥
    cert = x509.load_pem_x509_certificate(client_cert_pem.encode('utf-8'))
    client_pubkey_der = cert.public_key().public_bytes(
        encoding=serialization.Encoding.DER,
        format=serialization.PublicFormat.SubjectPublicKeyInfo
    )

    # 比对公钥
    if client_pubkey_der in load_authorized_pubkeys():
        return {"user": "authorized_client", "tags": ["administrator"]}
    else:
        return {"refused": "Unauthorized public key"}, 401

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080)
  1. 部署HTTP服务
    将脚本和授权公钥文件夹挂载到Docker容器中,确保服务和RabbitMQ通信正常。

通用注意事项

  • 授权公钥需为PEM格式,可从客户端证书中提取:openssl x509 -in client_cert.pem -pubkey -noout > client_pubkey.pem
  • Docker容器内的SSL文件和公钥文件夹需设置正确权限:chown -R 999:999 /path/to/mounted/files
  • 测试连接可使用命令:openssl s_client -connect <rabbitmq-host>:5671 -cert client_cert.pem -key client_key.pem

内容的提问来源于stack exchange,提问作者nogabemist

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 09:25:43