Consul启用ACL后必须显式传Token,否则ServiceRegister静默失败并返回403;Token需在api.Config初始化时设置,不可依赖环境变量或后期注入,且须配service:write权限;健康检查Check字段不可省略,否则服务被标记为critical,无法被发现。

Consul ACL启用后不传Token就403
Consul开启ACL后,所有HTTP API请求默认拒绝,client.Agent().ServiceRegister()这类调用会静默返回403,而不是明确报错。Gin本身不处理服务注册,真正出问题的是你封装的Consul客户端——如果没把Token塞进请求头或配置里,服务看似注册成功,实则被标记为critical,Health().Service()查不到,负载均衡直接跳过。
关键判断:只要Consul集群的acl.enabled = true,就必须传Token;反之,若ACL未启用却硬塞Token字段,部分SDK(如Go的hashicorp/consul/api)会触发403,且无明显日志提示。
- 检查Consul服务端是否启用ACL:读取任意Server节点的
config/acl.hcl,确认enabled = true - 确认当前使用的Token权限:至少需
service:write(注册)、service:read(发现),不能只靠masterToken硬扛生产环境 - Go client初始化时必须显式设置
token字段,不是靠环境变量自动注入
Go client初始化时Token怎么传才生效
Gin是HTTP框架,不参与Consul通信;真正干活的是hashicorp/consul/api客户端。Token必须在创建*api.Client实例时通过api.DefaultConfig()或手动构造api.Config传入,不能后期补。
常见错误是只配了环境变量CONSUL_HTTP_TOKEN,但Go client默认不读它——除非你显式调用api.LoadConfigFromEnvironment()(极少用),否则无效。
- 正确方式:构造
api.Config时填Token字段,例如cfg.Token = os.Getenv("CONSUL_TOKEN") - 不要在
ServiceRegister()参数里单独传Token:该函数不接受Token参数,Token必须来自client实例 - Token值建议从环境变量或Secret文件读取,避免硬编码;K8s场景优先用
Secret挂载到容器内文件,再读取解析
Gin启动时注册服务必须带Check,否则变僵尸
只调client.Agent().ServiceRegister()却不附带健康检查(Check),Consul会把服务标记为critical状态。这意味着:Health().Service()查不到该服务,catalog.Service()能查到但Passing为false,所有基于健康状态的负载均衡(如Fabio、Traefik)都会忽略它。
这不是Gin的问题,但Gin服务启动流程里常在这里翻车——开发者以为“注册完就完了”,其实漏掉了心跳保活逻辑。
- 必须在
api.AgentServiceRegistration结构体中设置Checks字段,至少包含一个HTTP或TCP类型检查 - HTTP检查地址应指向Gin服务自身,例如
http://127.0.0.1:8080/health,并确保该路由返回200 - 别用
Script类型检查:Consul agent需执行本地脚本,容器环境通常不支持,且增加运维复杂度
K8s部署时Token怎么安全注入到Gin服务
K8s里明文写deployment.yaml的env.value等于把Token贴在公告栏上。真正安全的做法是用Secret对象存Token,再以文件形式挂载进Pod,Gin服务启动时读取该文件。
环境变量方式虽快,但会被ps aux或/proc/<pid>/environ泄露;ConfigMap更不安全,纯文本存储。
- 创建Secret:
kubectl create secret generic consul-token --from-literal=token=xxxx-xx-xx-xxxx - 挂载为文件:
volumeMounts指定mountPath: /etc/consul/token,对应volumes引用该Secret - Gin服务启动前读取
/etc/consul/token内容,赋值给Consul client的Token字段 - 别忘了给Pod加RBAC:ServiceAccount需有读取该Secret的权限,否则挂载失败且无明确错误


















