217 lines
6.5 KiB
Markdown
217 lines
6.5 KiB
Markdown
|
|
---
|
|||
|
|
tags:
|
|||
|
|
- MQ
|
|||
|
|
- 安全
|
|||
|
|
- 认证
|
|||
|
|
- 授权
|
|||
|
|
- Kafka
|
|||
|
|
create time: 2026-05-24 19:52
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# MQ 认证与授权
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
MQ 作为数据流转的核心枢纽,安全防护至关重要。本文梳理 MQ 面临的安全威胁,对比 SASL/PLAIN、SASL/SCRAM、mTLS、OAuth2 四种认证机制,讲解 ACL 授权模型,并以 Kafka 为例展示完整的安全配置实践。
|
|||
|
|
|
|||
|
|
## 正文
|
|||
|
|
|
|||
|
|
### MQ 安全威胁分析
|
|||
|
|
|
|||
|
|
一个没有安全防护的 MQ 集群就像一栋没有门锁的大楼:
|
|||
|
|
|
|||
|
|
- **未授权访问**:任何人都能连接 Broker,读取或写入任意 Topic
|
|||
|
|
- **消息篡改**:中间人攻击修改消息内容,下游消费者收到错误数据
|
|||
|
|
- **消息窃听**:网络嗅探获取敏感消息(如用户订单、支付信息)
|
|||
|
|
- **DoS 攻击**:恶意客户端大量发送消息耗尽 Broker 资源
|
|||
|
|
|
|||
|
|
> [!question]
|
|||
|
|
> 如果你的 MQ 集群部署在内网,是否还需要认证授权?内网就一定安全吗?
|
|||
|
|
|
|||
|
|
### 认证机制对比
|
|||
|
|
|
|||
|
|
#### SASL/PLAIN
|
|||
|
|
|
|||
|
|
最简单的用户名密码认证。密码以明文传输(除非配合 TLS),适合开发测试环境。
|
|||
|
|
|
|||
|
|
- 优点:配置简单,无需额外基础设施
|
|||
|
|
- 缺点:密码明文传输、不支持动态添加用户(需重启 Broker)
|
|||
|
|
|
|||
|
|
#### SASL/SCRAM
|
|||
|
|
|
|||
|
|
基于挑战-响应的认证协议,密码不在网络上传输。Kafka 支持 SCRAM-SHA-256 和 SCRAM-SHA-512。
|
|||
|
|
|
|||
|
|
- 优点:密码加密存储、支持动态添加用户、无需重启 Broker
|
|||
|
|
- 缺点:性能比 mTLS 略低
|
|||
|
|
|
|||
|
|
#### mTLS(双向 TLS)
|
|||
|
|
|
|||
|
|
客户端和服务端互相验证证书。这是安全性最高的方案。
|
|||
|
|
|
|||
|
|
- 优点:双向认证、无需密码管理、证书可自动轮转
|
|||
|
|
- 缺点:证书管理复杂(签发、分发、续期、吊销)
|
|||
|
|
|
|||
|
|
#### OAuth2/OIDC
|
|||
|
|
|
|||
|
|
基于令牌的认证,适合云原生和微服务场景。客户端通过 OAuth2 Server 获取 Token,携带 Token 连接 Broker。
|
|||
|
|
|
|||
|
|
- 优点:与企业身份系统集成、支持细粒度权限、令牌可过期
|
|||
|
|
- 缺点:依赖外部 OAuth2 Server、配置复杂
|
|||
|
|
|
|||
|
|
### 认证授权流程
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
sequenceDiagram
|
|||
|
|
participant Client as "Client App"
|
|||
|
|
participant Broker as "Kafka Broker"
|
|||
|
|
participant Authn as "Authentication Module"
|
|||
|
|
participant Authz as "Authorization Module (ACL)"
|
|||
|
|
Client->>Broker: "Connect with credentials"
|
|||
|
|
Broker->>Authn: "Verify identity"
|
|||
|
|
Authn-->>Broker: "Identity: user-service-order"
|
|||
|
|
Client->>Broker: "Produce to order-topic"
|
|||
|
|
Broker->>Authz: "Check ACL: can user-service-order WRITE order-topic?"
|
|||
|
|
Authz-->>Broker: "ALLOW"
|
|||
|
|
Broker-->>Client: "Produce success"
|
|||
|
|
Client->>Broker: "Consume payment-topic"
|
|||
|
|
Broker->>Authz: "Check ACL: can user-service-order READ payment-topic?"
|
|||
|
|
Authz-->>Broker: "DENY"
|
|||
|
|
Broker-->>Client: "Authorization failed"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### ACL 授权模型
|
|||
|
|
|
|||
|
|
ACL(Access Control List)提供 Topic 级别的细粒度权限控制。Kafka 的 ACL 由五元组定义:
|
|||
|
|
|
|||
|
|
- **Principal**:认证身份(如 `User:alice`)
|
|||
|
|
- **Resource**:资源类型和名称(如 `Topic:order-events`)
|
|||
|
|
- **Operation**:操作类型(Read/Write/Create/Delete/Describe)
|
|||
|
|
- **Permission**:Allow/Deny
|
|||
|
|
- **Host**:来源 IP(可选)
|
|||
|
|
|
|||
|
|
常见权限模式:
|
|||
|
|
- 最小权限原则:每个服务只授予需要的 Topic 权限
|
|||
|
|
- 生产者只有 Write 权限,消费者只有 Read 权限
|
|||
|
|
- 管理操作(Create/Delete)只授予运维账号
|
|||
|
|
|
|||
|
|
### Kafka 完整安全配置
|
|||
|
|
|
|||
|
|
下面展示 SASL/SCRAM + ACL 的完整配置流程。
|
|||
|
|
|
|||
|
|
Broker 端配置(`server.properties`):
|
|||
|
|
|
|||
|
|
```properties
|
|||
|
|
# 启用 SASL/SCRAM 认证
|
|||
|
|
listeners=SASL_SSL://:9093
|
|||
|
|
security.inter.broker.protocol=SASL_SSL
|
|||
|
|
sasl.mechanism.inter.broker.protocol=SCRAM-SHA-256
|
|||
|
|
sasl.enabled.mechanisms=SCRAM-SHA-256
|
|||
|
|
|
|||
|
|
# 启用 ACL
|
|||
|
|
authorizer.class.name=kafka.security.authorizer.AclAuthorizer
|
|||
|
|
super.users=User:admin
|
|||
|
|
allow.everyone.if.no.acl.found=false
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 创建用户
|
|||
|
|
kafka-configs.sh --bootstrap-server localhost:9093 \
|
|||
|
|
--alter --add-config 'SCRAM-SHA-256=[iterations=8192,password=secret]' \
|
|||
|
|
--entity-type users --entity-name order-service
|
|||
|
|
|
|||
|
|
# 授权:允许 order-service 写 order-topic
|
|||
|
|
kafka-acls.sh --bootstrap-server localhost:9093 \
|
|||
|
|
--add --allow-principal User:order-service \
|
|||
|
|
--producer --topic order-topic
|
|||
|
|
|
|||
|
|
# 授权:允许 payment-service 读 order-topic
|
|||
|
|
kafka-acls.sh --bootstrap-server localhost:9093 \
|
|||
|
|
--add --allow-principal User:payment-service \
|
|||
|
|
--consumer --topic order-topic --group payment-group
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Go 代码:SASL/SCRAM 认证配置
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
package main
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"crypto/sha256"
|
|||
|
|
"crypto/sha512"
|
|||
|
|
"hash"
|
|||
|
|
|
|||
|
|
"github.com/IBM/sarama"
|
|||
|
|
"github.com/xdg-go/scram"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// SCRAM 客户端实现
|
|||
|
|
type SCRAMClient struct {
|
|||
|
|
*scram.Client
|
|||
|
|
*scram.ClientConversation
|
|||
|
|
hashGenerator func() hash.Hash
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (c *SCRAMClient) Begin(userName, password, authzID string) error {
|
|||
|
|
client, err := c.hashGenerator().NewClient(userName, password, authzID)
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
c.Client = client
|
|||
|
|
c.ClientConversation = client.NewConversation()
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (c *SCRAMClient) Step(challenge string) (string, error) {
|
|||
|
|
return c.ClientConversation.Step(challenge)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (c *SCRAMClient) Done() bool {
|
|||
|
|
return c.ClientConversation.Done()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func main() {
|
|||
|
|
config := sarama.NewConfig()
|
|||
|
|
config.Net.SASL.Enable = true
|
|||
|
|
config.Net.SASL.Mechanism = sarama.SASLTypeSCRAMSHA256
|
|||
|
|
config.Net.SASL.User = "order-service"
|
|||
|
|
config.Net.SASL.Password = "secret"
|
|||
|
|
config.Net.SASL.SCRAMClientGeneratorFunc = func() sarama.SCRAMClient {
|
|||
|
|
return &SCRAMClient{hashGenerator: sha256.New}
|
|||
|
|
}
|
|||
|
|
config.Net.TLS.Enable = true // SCRAM 通常配合 TLS 使用
|
|||
|
|
|
|||
|
|
producer, err := sarama.NewSyncProducer([]string{"localhost:9093"}, config)
|
|||
|
|
if err != nil {
|
|||
|
|
panic(err)
|
|||
|
|
}
|
|||
|
|
defer producer.Close()
|
|||
|
|
|
|||
|
|
// 发送消息,自动完成 SCRAM 认证握手
|
|||
|
|
msg := &sarama.ProducerMessage{
|
|||
|
|
Topic: "order-topic",
|
|||
|
|
Value: sarama.StringEncoder(`{"order_id": "12345"}`),
|
|||
|
|
}
|
|||
|
|
partition, offset, _ := producer.SendMessage(msg)
|
|||
|
|
_ = partition
|
|||
|
|
_ = offset
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
这段代码实现了 SASL/SCRAM-SHA-256 认证的完整流程:客户端发送用户名,Broker 返回挑战(salt + iteration),客户端计算哈希响应,Broker 验证通过后建立连接。
|
|||
|
|
|
|||
|
|
### mTLS 的简化方案
|
|||
|
|
|
|||
|
|
> [!question]
|
|||
|
|
> mTLS 安全性最高,但管理证书很麻烦。在微服务场景下如何简化证书管理?
|
|||
|
|
|
|||
|
|
几种主流方案:
|
|||
|
|
- **Service Mesh(Istio)**:Sidecar 代理自动处理证书签发和轮转,应用层完全无感
|
|||
|
|
- **cert-manager**:K8s 上的证书管理控制器,自动签发和续期
|
|||
|
|
- **SPIFFE/SPIRE**:标准化的服务身份框架,跨平台证书管理
|
|||
|
|
|
|||
|
|
## 关联笔记
|
|||
|
|
|
|||
|
|
- [[43-MQ-加密与审计]]
|
|||
|
|
- [[40-MQ-性能调优]]
|
|||
|
|
- [[39-MQ-容器化与-K8s-部署]]
|