未鉴权gRPC接口暴露公网风险,gRPC网关安全配置
未鉴权的 gRPC 接口一旦暴露到公网,等于把你的后端服务直接交给任何人调用。
本文先教你用命令判断接口是否裸奔,再通过 JWT 鉴权和 Nginx 反向代理完成 gRPC 网关安全配置,全程给出可执行命令和验证方法,零基础也能跟着操作。
先确认 gRPC 接口是否真的暴露在公网
登录服务器后,先看端口监听情况:
ss -tlnp | grep 9090
如果监听地址是 0.0.0.0:9090 或 :::9090,说明服务绑定了所有网卡,公网 IP 也能直连。
再用 grpcurl 验证能否直接调用:
grpcurl -plaintext 你的公网IP:9090 list
只要返回服务列表或方法名,就说明未鉴权 gRPC 接口已经裸奔,任何知道地址的人都能操作你的后端。
即使没有 grpcurl,也可以用 nc -vz 你的公网IP 9090 快速判断端口是否可连通。
在 gRPC 网关层启用 JWT 鉴权
使用 grpc-gateway 时,可以写一个拦截器校验 JWT。
下面是一段常见中间件示例(Go):
func JWTInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
token, err := auth.FromIncomingContext(ctx)
if err != nil || !auth.ValidateToken(token) {
return nil, status.Error(codes.Unauthenticated, "invalid token")
}
return handler(ctx, req)
}
初始化 gRPC Server 时挂上拦截器:
grpc.NewServer(grpc.UnaryInterceptor(JWTInterceptor))
如果你用 Envoy 作为 gRPC 网关,也能直接配置 JWT 认证过滤器,效果类似。
这里顺手提醒一下,JWT 签名密钥不要硬编码在代码里,建议放在环境变量或密钥管理服务中,并定期轮换。
用 Nginx 做 TLS 终结并隐藏源端口
Nginx 从 1.13 开始支持 gRPC 反向代理。
在站点配置中写:
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
location / {
grpc_pass grpc://127.0.0.1:9090;
}
}
关键操作是让 gRPC 服务只监听 127.0.0.1:9090,防火墙只放行 Nginx 的 443 端口。
这样可以避免客户端不经过 TLS 和鉴权直接访问到原始服务。只要服务端口还能从公网访问,反向代理配置得再漂亮也没有意义。
避坑:这些细节会导致配置失效
配置完成后,至少验证三件事:
- 未带 token 是否正确拒绝:执行
grpcurl -plaintext 127.0.0.1:9090 list,预期返回Unauthenticated或PermissionDenied,而不是服务列表。 - 通过 Nginx 是否能正常访问:执行
grpcurl -insecure -servername api.example.com -H "authorization: Bearer 你的token" api.example.com:443 list,预期返回服务列表。 - 源端口是否已从公网消失:用
nmap 你的公网IP -p 9090检查,预期显示filtered或closed,只有 443 是开放状态。
常见坑有两个:一是只改 Nginx 配置,没有关闭 gRPC 服务的公网监听,导致 9090 仍然可访问;
二是健康检查接口也需要鉴权,导致 Kubernetes 探活失败,应单独放行 /grpc.health.v1.Health/Check。
最后自查清单
完成上述操作后,建议把下面几条写进发布流程:服务监听只绑定内网 IP;
外部调用必须经 Nginx 走 TLS;
所有业务接口必须携带 JWT;
密钥通过环境变量注入;
定期检查访问日志中是否有大量未授权请求。
gRPC 网关安全配置不是一次性工作,每次改动端口、路由或新增接口时,都要重新确认公网暴露面。
只有把“未鉴权即拒绝”变成默认行为,接口才能真正安全。