微服务间通信安全
微服务通信安全概述
微服务架构下的安全挑战
在单体应用架构中,所有功能模块运行在同一个进程内,模块间的调用通过函数调用完成,不存在网络层面的安全风险。然而,当应用拆分为微服务架构后,原本的内存级调用转变为网络级调用,服务之间通过 HTTP/REST、gRPC、消息队列等方式进行通信。这种架构转变带来了新的安全挑战:
- 网络开放:每个服务都需要监听端口,攻击面显著增加。
- 身份模糊:服务间调用不再通过用户会话标识调用方身份,需要建立服务级别的身份认证机制。
- 数据泄露:通信内容若未加密,可能在网络传输中被窃听或篡改。
- 内部威胁:一旦攻击者攻破某个微服务,可能以此为跳板横向移动至其他服务。
服务间信任模型
微服务间的信任模型主要分为以下几种:
| 信任模型 | 描述 | 适用场景 |
|---|---|---|
| 零信任模型 | 默认不信任任何请求,每次调用都需要认证和授权 | 高安全要求、多租户环境 |
| 网络边界信任 | 信任内网流量,仅对外部请求进行严格校验 | 传统微服务、内网隔离良好的场景 |
| 证书信任 | 通过 TLS 证书建立双向信任关系 | 跨网络边界、跨集群通信 |
| JWT 信任 | 通过签发的 JSON Web Token 传递调用方身份 | 网关代理、服务代理场景 |
当前业界主流趋势是采用零信任模型,即"永远不信任,始终验证"(Never Trust, Always Verify)。在这种模型下,即使请求来自集群内部或内网环境,仍然需要进行身份认证和授权检查。
南北向流量 vs 东西向流量
在微服务安全架构中,流量分为两类:
南北向流量(North-South Traffic):指外部客户端与微服务集群之间的流量。这类流量通常经过 API 网关、负载均衡器等边缘组件,安全控制集中在网关层,包括 TLS 终止、身份认证、限流、WAF 防护等。
东西向流量(East-West Traffic):指微服务集群内部各服务之间的流量。这类流量规模远大于南北向流量,且通常不经过 API 网关,直接在服务间通过内部网络通信。东西向流量的安全控制是微服务安全的核心难点,需要借助 mTLS、JWT 传播、Service Mesh 等技术手段来实现。
mTLS 双向认证
mTLS 原理
TLS(Transport Layer Security)是保障通信安全的基础协议,提供加密、数据完整性和端点认证三大能力。标准的 TLS 单向认证仅验证服务器端的身份,而 mTLS(Mutual TLS)在标准 TLS 基础上增加了客户端证书验证,即客户端和服务端都需要出示证书来证明各自的身份。
mTLS 的工作流程如下:
- 客户端发起 TLS 握手请求。
- 服务端返回自己的证书(Server Certificate),客户端验证服务端证书的合法性。
- 服务端请求客户端证书(Client Certificate Request)。
- 客户端返回自己的证书,服务端验证客户端证书的合法性。
- 双方完成证书验证后,协商对称加密密钥,后续通信使用对称加密。
通过 mTLS,每个微服务都可以确认为其通信的另一方是经过认证的合法服务,而非恶意冒充者。
证书颁发与分发
在微服务环境中,证书的生命周期管理是关键环节。典型的证书管理流程包括:
证书签发:由内部的证书颁发机构(CA)为每个微服务签发 X.509 证书。证书中包含服务身份信息,通常使用 SPIFFE(Secure Production Identity Framework for Everyone)标准格式,例如
spiffe://cluster.local/ns/namespace/sa/serviceaccount。证书分发:证书和私钥需要安全地分发到每个 Pod 或容器中。分发方式包括:
- Kubernetes Secret 挂载:将证书和私钥以 Secret 资源的形式挂载到 Pod 文件系统。
- Sidecar 代理注入:通过 Sidecar(如 Istio Envoy)自动获取和轮换证书。
- 临时证书 API:使用 K8s CertificateSigningRequest(CSR)API 动态签发短期证书。
证书轮换:证书应有较短的有效期(通常为 24 小时至 7 天),并实现自动轮换,以降低私钥泄露的风险。
K8s cert-manager 管理证书
cert-manager 是 Kubernetes 生态中最流行的证书管理工具,它可以自动颁发、续期和轮换 TLS 证书。在微服务 mTLS 场景中,cert-manager 可以充当内部 CA 的角色。
以下是使用 cert-manager 配置内部 CA 的示例:
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: internal-ca-issuer
namespace: security
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: internal-ca-cert
namespace: security
spec:
isCA: true
commonName: internal-ca
secretName: internal-ca-secret
duration: 87600h # 10 years
issuerRef:
name: internal-ca-issuer
kind: Issuer
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: mtls-issuer
namespace: security
spec:
ca:
secretName: internal-ca-secret基于上述 CA 配置后,可以为每个微服务签发独立的证书:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: user-service-cert
namespace: default
spec:
commonName: user-service
dnsNames:
- user-service.default.svc.cluster.local
- user-service.default.svc
duration: 168h # 7 days
renewBefore: 24h
secretName: user-service-tls
usages:
- server auth
- client auth
issuerRef:
name: mtls-issuer
kind: Issuer通过上述配置,cert-manager 会自动生成证书和私钥并以 Secret 资源的形式存储在 Kubernetes 中,Pod 可以将其挂载到容器的文件系统路径下。
gRPC mTLS 配置示例
gRPC 原生支持 mTLS,以下是在 Go 语言中配置 gRPC mTLS 服务端和客户端的示例。
服务端配置:
import (
"crypto/tls"
"crypto/x509"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)
func loadGRPCServerTLSCredentials() (credentials.TransportCredentials, error) {
// 加载服务端证书和私钥
serverCert, err := tls.LoadX509KeyPair(
"/etc/certs/server.crt",
"/etc/certs/server.key",
)
if err != nil {
return nil, err
}
// 加载 CA 证书用于验证客户端
caCert, err := os.ReadFile("/etc/certs/ca.crt")
if err != nil {
return nil, err
}
caPool := x509.NewCertPool()
caPool.AppendCertsFromPEM(caCert)
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{serverCert},
ClientCAs: caPool,
ClientAuth: tls.RequireAndVerifyClientCert,
MinVersion: tls.VersionTLS12,
}
return credentials.NewTLS(tlsConfig), nil
}
func main() {
creds, _ := loadGRPCServerTLSCredentials()
server := grpc.NewServer(grpc.Creds(creds))
// 注册服务并启动...
}客户端配置:
func loadGRPCClientTLSCredentials() (credentials.TransportCredentials, error) {
// 加载客户端证书和私钥
clientCert, err := tls.LoadX509KeyPair(
"/etc/certs/client.crt",
"/etc/certs/client.key",
)
if err != nil {
return nil, err
}
// 加载 CA 证书用于验证服务端
caCert, err := os.ReadFile("/etc/certs/ca.crt")
if err != nil {
return nil, err
}
caPool := x509.NewCertPool()
caPool.AppendCertsFromPEM(caCert)
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{clientCert},
RootCAs: caPool,
MinVersion: tls.VersionTLS12,
}
return credentials.NewTLS(tlsConfig), nil
}
func main() {
creds, _ := loadGRPCClientTLSCredentials()
conn, _ := grpc.Dial("user-service.default.svc.cluster.local:443",
grpc.WithTransportCredentials(creds))
// 使用连接调用远程服务...
}REST mTLS 配置示例
对于 REST API 的 mTLS 配置,以 Nginx 反向代理为例:
server {
listen 443 ssl;
server_name api.example.com;
ssl_certificate /etc/certs/server.crt;
ssl_certificate_key /etc/certs/server.key;
ssl_client_certificate /etc/certs/ca.crt;
ssl_verify_client on;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
# 将客户端证书信息传递给后端服务
proxy_set_header X-SSL-Client-Cert $ssl_client_escaped_cert;
proxy_set_header X-SSL-Client-DN $ssl_client_s_dn;
proxy_pass http://backend-service:8080;
}
}JWT Propagation
Token 传递规范
在微服务架构中,用户身份信息需要在多个服务间传递,JWT(JSON Web Token)是最常见的身份凭证格式。当 API 网关验证用户身份后,后续的内部服务调用需要将用户身份信息一并传递,这一过程称为 JWT 传播(JWT Propagation)。
JWT 传播主要有两种方案:
1. 透传原始 Token(Header Propagation)
请求链路中的每个服务将原始 JWT 通过 HTTP 头传递给下游服务。这种方式简单直接,但所有服务都需要了解 JWT 格式并验证签名。
GET /api/v1/orders HTTP/1.1
Authorization: Bearer <original-jwt-token>
X-User-ID: user-12345
X-User-Roles: admin,operator2. Token 中继(Token Relay)
由网关或代理负责将原始 JWT 转换为新的服务间 Token。原始用户 Token 可能包含用户隐私信息,不适合在内网全部透传。Token Relay 通过替换或包装 Token 实现对下游服务的身份传递。
# Spring Cloud Gateway TokenRelay 配置示例
spring:
cloud:
gateway:
routes:
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
filters:
- TokenRelay=在 OAuth2 体系中,Token Relay 使用 OAuth2 客户端凭证授权(Client Credentials Grant)来获取一个新的 Token 用于服务间调用,该 Token 不包含用户身份,仅标识调用方服务身份。
JWT 验证链
在服务链路中,JWT 的验证应当形成完整的验证链(Validation Chain),每个服务都需要验证 JWT 的合法性,而不仅仅是入口网关。
典型的 JWT 验证流程包括:
- 签名验证:使用 JWT 签发方的公钥验证 Token 签名,确保 Token 未被篡改。
- 有效期验证:检查
exp(过期时间)、nbf(生效时间)和iat(签发时间)字段。 - 颁发者验证:检查
iss(颁发者)字段是否为受信任的颁发方。 - 受众验证:检查
aud(受众)字段是否包含当前服务。 - 权限验证:基于
scp(scope)或自定义权限字段进行授权判断。
以下是在 Java Spring Boot 中验证 JWT 的示例:
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Value("${jwt.issuer}")
private String jwtIssuer;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
String token = extractToken(request);
if (token != null) {
try {
Claims claims = Jwts.parserBuilder()
.setSigningKeyResolver(signingKeyResolver)
.requireIssuer(jwtIssuer)
.build()
.parseClaimsJws(token)
.getBody();
// 将解析出的用户信息放入请求上下文
SecurityContextHolder.getContext().setAuthentication(
new JwtAuthenticationToken(claims));
} catch (JwtException e) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
return;
}
}
chain.doFilter(request, response);
}
}内网 JWT 签发与校验
内网服务间通信所使用的 JWT 通常由独立的内部 Token 服务(Token Service)签发,而不是直接透传用户的原始 JWT。内部 JWT 的设计要点:
- 短期有效:内网 Token 的过期时间应设为较短(如 15 分钟),减少 Token 泄露风险。
- 服务身份标识:Token 中应包含调用方服务的身份信息,而非用户身份。
- 独立签名密钥:使用与服务网关不同的签名密钥,防止外部 Token 冒充内部调用。
内部 JWT 签发示例:
# Python 内部 Token 签发示例
import jwt
import time
def issue_internal_token(service_name: str, target_service: str) -> str:
payload = {
"iss": "internal-token-service",
"sub": service_name,
"aud": target_service,
"iat": int(time.time()),
"exp": int(time.time()) + 900, # 15 分钟有效期
"scope": ["internal"],
}
# 使用独立的内部签名密钥
with open("/etc/secrets/internal.key", "r") as f:
private_key = f.read()
token = jwt.encode(payload, private_key, algorithm="RS256")
return tokenToken 到期与刷新
JWT 一旦签发无法撤销,因此合理的过期策略至关重要。常见策略包括:
| 策略 | 说明 | 适用场景 |
|---|---|---|
| 短生命周期 | Token 有效期 5~15 分钟,无刷新机制 | 高安全环境 |
| Refresh Token | Access Token 短期有效,Refresh Token 长期有效用于续期 | 用户面服务 |
| Token 轮换 | 每次刷新同时更换 Refresh Token | 敏感操作服务 |
| 透明续期 | 网关或 Sidecar 自动续期 Token,服务无感知 | 后端服务间调用 |
在服务间通信中建议采用透明续期策略:由服务网格的 Sidecar 代理或 API 网关自动刷新 Token,业务服务无需关心 Token 的生命周期管理。
Service Mesh 安全
Istio mTLS 自动注入
Service Mesh(服务网格)通过 Sidecar 代理(通常是 Envoy)拦截所有进出 Pod 的流量,并自动注入 mTLS 能力,业务代码无需任何修改。Istio 是最流行的服务网格实现之一。
在 Istio 中,启用 mTLS 自动注入的配置非常简洁:
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
meshConfig:
defaultConfig:
terminationDrainDuration: 30s
components:
ingressGateways:
- name: istio-ingressgateway
enabled: true
values:
global:
meshID: cluster1
multiCluster:
clusterName: cluster1
network: network1
proxy:
autoInject: enabled启用自动注入后,每个 Pod 启动时 Istio 会自动注入 Envoy Sidecar 容器。所有流入流出 Pod 的流量都会被 Sidecar 拦截。当目标服务也启用了 Sidecar 时,两个 Sidecar 之间会自动建立 mTLS 连接。
PeerAuthentication 配置
PeerAuthentication(对等认证)是 Istio 提供的用于配置服务间 mTLS 模式的 CRD 资源。它支持三种模式:
| 模式 | 说明 |
|---|---|
STRICT | 服务间通信必须使用 mTLS |
PERMISSIVE | 同时接受 mTLS 和明文流量(迁移过渡期使用) |
DISABLE | 禁用 mTLS |
以下是为命名空间级别启用 STRICT mTLS 的配置:
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: default
spec:
mtls:
mode: STRICT针对特定服务更细粒度的配置:
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: payment-service
namespace: default
spec:
selector:
matchLabels:
app: payment-service
mtls:
mode: STRICT
portLevelMtls:
8080:
mode: STRICTAuthorizationPolicy 配置
AuthorizationPolicy(授权策略)定义了哪些服务可以访问哪些资源。它通常与 PeerAuthentication 配合使用:PeerAuthentication 负责加密和身份认证,AuthorizationPolicy 负责授权控制。
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: order-service-policy
namespace: default
spec:
selector:
matchLabels:
app: order-service
rules:
- from:
- source:
principals:
- "cluster.local/ns/default/sa/user-service-sa"
- "cluster.local/ns/default/sa/api-gateway-sa"
to:
- operation:
methods: ["GET", "POST"]
paths: ["/api/v1/orders/*"]
- from:
- source:
principals:
- "cluster.local/ns/default/sa/admin-sa"
to:
- operation:
methods: ["DELETE"]
paths: ["/api/v1/orders/*"]上述策略的含义:
- 只允许
user-service-sa和api-gateway-sa两个服务账号对订单服务执行 GET 和 POST 请求。 - 只有
admin-sa服务账号可以执行 DELETE 操作。
Envoy Sidecar 安全策略
除了 Istio 层级的配置外,Envoy Sidecar 本身也提供了丰富的安全能力:
1. 访问日志审计
启用 Envoy 访问日志可以记录所有服务间通信的详细信息,用于安全审计和异常检测:
apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
name: mesh-default
namespace: istio-system
spec:
accessLogging:
- providers:
- name: envoy2. 请求超时与重试限制
防止因上游服务异常导致的级联故障:
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: payment-service
spec:
hosts:
- payment-service
http:
- route:
- destination:
host: payment-service
timeout: 5s
retries:
attempts: 2
perTryTimeout: 2s3. 断路器
当检测到上游服务异常时,自动断开连接保护调用方:
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: payment-service-circuit-breaker
spec:
host: payment-service
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100
http:
http1MaxPendingRequests: 10
maxRequestsPerConnection: 10
outlierDetection:
consecutiveErrors: 5
interval: 30s
baseEjectionTime: 30s服务网格 vs 传统方式对比
| 维度 | 传统方式 | 服务网格方式 |
|---|---|---|
| 身份认证 | 各服务自行实现 mTLS 或 JWT 验证,代码侵入性强 | Sidecar 自动注入 mTLS,业务代码无侵入 |
| 证书管理 | 需自行管理证书发放和轮换,可使用 cert-manager | Istio 内置 CA 自动管理证书生命周期 |
| 授权策略 | 在各服务代码中硬编码权限逻辑 | 通过 CRD 声明式配置 AuthorizationPolicy |
| 流量加密 | 需在每个服务的 HTTP 客户端库中配置 TLS | Sidecar 自动加密所有流量 |
| 可观测性 | 需在各服务中嵌入 metrics/tracing 代码 | 自动收集全链路遥测数据 |
| 部署复杂度 | 较低,无需额外组件 | 较高,需部署和管理控制平面 |
| 性能开销 | 无额外代理层,延迟最低 | Envoy Sidecar 引入少量延迟(通常 < 5ms) |
| 灵活性 | 技术栈无关,但需各团队分别实现安全逻辑 | 统一控制面,但受限于 Mesh 技术栈 |
选型建议:
- 对于规模较小(少于 10 个微服务)、团队技术栈单一的团队,传统方式 + cert-manager 足够满足需求。
- 对于大规模微服务集群(50+ 服务)、多语言技术栈、需要统一安全策略管理的团队,强烈建议采用 Service Mesh。
- 对于正在逐步迁移的团队,可采用渐进式方案:先使用 cert-manager 管理证书 + 应用层 mTLS,再逐步引入 Service Mesh。
API 网关作为东西向网关
传统网关的局限
传统的 API 网关(如 Kong、APISIX、Spring Cloud Gateway)主要用于管理南北向流量:接收外部请求、进行身份认证、限流、路由到后端服务。然而,这类网关通常不参与东西向流量的管理,服务间的直接调用不受网关控制。
但随着微服务规模的扩大,东西向流量同样需要集中化的访问控制、流量管理和安全策略。这就催生了东西向网关的概念。
服务网格网关
在 Service Mesh 架构中,Istio Gateway 不仅可以作为入口网关处理南北向流量,也可以配置为东西向网关来处理跨集群、跨网络的服务间通信。
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: east-west-gateway
namespace: istio-system
spec:
selector:
istio: eastwestgateway
servers:
- port:
number: 443
name: tls
protocol: TLS
tls:
mode: AUTO_PASSTHROUGH
maxProtocolVersion: TLSV1_3
minProtocolVersion: TLSV1_2
hosts:
- "*.local"东西向网关的主要作用:
- 跨集群服务发现:通过 DNS 或 ServiceEntry 将跨集群的服务暴露给本集群。
- 跨网络 mTLS:在不同网络域的服务之间建立 mTLS 连接。
- 统一流量策略:跨集群的流量策略集中管理,包括超时、重试、熔断等。
- 安全隔离:不同环境(生产/测试)或不同租户之间的流量隔离。
服务间访问控制
RBAC + 服务账号
Kubernetes 的 RBAC(Role-Based Access Control)与服务账号(ServiceAccount)机制是服务间访问控制的基础。每个 Pod 都与一个 ServiceAccount 关联,服务间的 API 访问鉴权可以基于此进行。
创建服务账号并绑定权限:
apiVersion: v1
kind: ServiceAccount
metadata:
name: order-service-sa
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: payment-reader
rules:
- apiGroups: [""]
resources: ["services", "endpoints"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: order-service-payment-reader
namespace: default
subjects:
- kind: ServiceAccount
name: order-service-sa
namespace: default
roleRef:
kind: Role
name: payment-reader
apiGroup: rbac.authorization.k8s.io在 Istio 环境中,ServiceAccount 还承担着更重要的角色:它直接映射为 SPIFFE 身份标识。例如,order-service-sa 在 Istio 中的 SPIFFE ID 为 spiffe://cluster.local/ns/default/sa/order-service-sa,AuthorizationPolicy 通过该标识进行授权判断。
网络策略(K8s NetworkPolicy)
NetworkPolicy 是 Kubernetes 提供的一种网络隔离机制,通过标签选择器定义哪些 Pod 可以相互通信。它实现了服务间的网络层访问控制,作为应用层安全策略的补充。
以下是一个典型的 NetworkPolicy 配置,限制仅允许 API 网关访问订单服务:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: order-service-network-policy
namespace: default
spec:
podSelector:
matchLabels:
app: order-service
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: api-gateway
- namespaceSelector:
matchLabels:
name: monitoring
podSelector:
matchLabels:
app: prometheus
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
app: payment-service
- podSelector:
matchLabels:
app: user-service
ports:
- protocol: TCP
port: 443该策略的含义:
- 只允许标签为
app: api-gateway的 Pod 和监控命名空间下的 Prometheus Pod 访问订单服务的 8080 端口。 - 订单服务只能访问标签为
payment-service和user-service的 Pod 的 443 端口。
多层防御体系
在实际生产环境中,服务间访问控制应该采用多层防御体系:
| 层级 | 技术手段 | 控制粒度 |
|---|---|---|
| 网络层 | K8s NetworkPolicy | 基于 IP 和端口的网络隔离 |
| 传输层 | mTLS / PeerAuthentication | 基于证书的服务身份认证 |
| 应用层 | AuthorizationPolicy / JWT | 基于服务账号和请求方法的授权 |
| 数据层 | 字段级加密 / 数据脱敏 | 敏感数据保护 |
这种分层防御的策略也称为纵深防御(Defense in Depth),即使某一层被绕过,后续层级仍然能够提供保护。
总结
微服务间通信安全是云原生安全体系的核心组成部分。本文从信任模型出发,系统介绍了保障微服务通信安全的各项关键技术:
- mTLS 通过双向证书认证确保通信双方身份的合法性,配合 cert-manager 实现证书的自动化生命周期管理。
- JWT Propagation 实现了用户身份在服务链路中的安全传递,结合内部 Token 服务和短期过期策略降低安全风险。
- Service Mesh(以 Istio 为例)通过 Sidecar 代理自动注入 mTLS 和细粒度的授权策略,实现了安全能力的平台化和无侵入化。
- 网络策略和服务账号 RBAC 提供了网络层和应用层的多层访问控制。
建议团队根据自身的微服务规模、技术栈和安全要求,选择合适的方案组合。对于新建的微服务体系,推荐优先考虑 Service Mesh 方案;对于已有系统,可以采用渐进式迁移策略,先落地 mTLS 和 JWT 传播,再逐步引入服务网格。