Confd watch etcdv3数据Token超时失效问题的分析
背景:
Confd的etcdv3 backend支持watch ETCD Server的数据变更,并试试应用模板刷新配置.
在配置https+username/password的时候,当etcd进程失败或者主备倒换后watch一直失败. server端持续提示"Invalid Token"
根因:
ETCD采用token方式做api鉴权.
Confd支持JWT和Simple两种跟ETCD鉴权,JWT的安全新就不说了.
Simple依赖用户名和密码,这个鉴权只在首次连接的时候会触发.
backends/etcdv3/client.go:NewEtcdClient-> clientv3.New(cfg)
这个首次连接会自动认证并获取Token
vendor/github.com/coreos/etcd/clientv3/client.go: getToken
后面即使断链也不会再刷新,又由于Token存在有效期,在server端空闲时或者异常到倒换时delete token后将无法再次获取到新的数据.
规避方法:
在watch异常时调用getToken刷新一下.
步骤:
1\修改confd vender里etcd client代码对外提供RefreshToken接口(原来是内部接口,外面无法调用)
vendor/github.com/coreos/etcd/clientv3/client.go
func (c *Client) RefreshToken() error {
return c.getToken(c.ctx)
}2\watch异常时自动Refresh
2.1\不更新ETCD版本场景不区分异常码,直接更新(confd依赖的etcd版本过低,配套的api无法识别相关异常)
backends/etcdv3/client.go:createWatch
log.Warning("Watch to '%s' stopped at revision %d", prefix, w.revision)
// Disconnected or cancelled
// Wait for a moment to avoid reconnecting
// too quickly
time.Sleep(time.Duration(1) * time.Second)
//异常后直接刷新
refreshRst := client.RefreshToken()
log.Info("Token Refreshed. %v", refreshRst)
// Start from next revision so we are not missing anything
if w.revision > 0 {
rch = client.Watch(context.Background(), prefix, clientv3.WithPrefix(),
clientv3.WithRev(w.revision + 1))
} else {
// Start from the latest revision
rch = client.Watch(context.Background(), prefix, clientv3.WithPrefix(),
clientv3.WithCreatedNotify())
}2.2\ETCD更新到3.3.15以上(同步更新grpc配套版本),在出现PermissionDenied异常时刷新Token
if err := wresp.Err(); err != nil {
log.Error("Watch error: %s", err.Error())
if strings.Index(err.Error(),"code = PermissionDenied")>=0 {
refreshRst := client.RefreshToken()
log.Info("Token Refreshed. %v", refreshRst)
}
}后语:
期待ETCD内部能优化失效,链接重连时自动刷新.
- 点赞
- 收藏
- 关注作者
评论(0)