Istio私钥管理利器SDS浅析
Why SDS
传统方式下Envoy证书是通过secret卷挂载的方式以文件挂载到sidecar容器中,当证书发生轮转时需要重启服务让Envoy重新加载证书;同时证书私钥在secret中存储并在服务节点外跨节点传输的方式也存在明显的安全漏洞。为此Istio1.1版本后增加了SDS(Secret Discovery Service)API,在Citadel服务的基础上,增加了nodeagent组件,以ds的形式部署在每个节点上,而SDS服务由nodeagent管理,在每一个节点上会启动实现了SecretDiscoveryService 这个gRPC服务的SDS服务端,同时在一个节点上的sds server和client之间通过Unix domian socker通讯。nodeagent除了支持对接Citadel发送证书签发请求外,还可以对接Vault,GoogleCA等证书签发组件。
通过如上设计给整个mesh系统带来了如下好处:
• envoy可以动态获取轮转后的证书而无需重启
• 安全性提升:私钥不出节点传输;证书在memory中传递而无需落盘
sds服务请求签发证书的流程如下:
- Pilot将SDS相关config发送至istio sidecar,比如envoy
- 使用指定的serviceaccount向NodeAgent发送请求
- nodeagent发送CSR到citadel请求证书签发,支持自定义CA,这里同样使用sa作为访问凭证
- Citadel向apiserver认证sa
- 如果认证通过,Citadel向NodeAgent返回签发后的证书
- SDS返回证书内容给sidecar
SDS安装
Enable Service Account Token Volumes
1.12版本前应用pod中使用的serviceaccount中对应的JWT token是永不过期的,也就是说直到sa被删除前该token都可以被用来请求apiserver,也就是说如果sa发生泄漏,应用管理员需要删除所有关联的secret并重启服务。除此之外,传统方式下每一个serviceaccount都需要存储在一个对应的secret,而具有secret读取权限的组件或人员可以获取到所有其可见范围内的sa token,比如ingress controller需要由路由TLS相关secret的读取权限,但是同时它也能查看所有应用中使用到的sa token。这样的情况导致sa的泄露变得难以防范,因此k8s 1.12后ServiceAccountTokenVolumeProjection成为了beta特性,开启了该特性的集群允许kubelet将sa以projected volume的形式挂载到pod中,当pod删除时,相应的sa token也会同时删除。同时kubelet的token manager会自动刷新临近过期的token,以及增加对token audiences的校验。该特性大大增强了pod在使用sa作为apiserver访问凭证的安全性。
有关ServiceAccountTokenVolumeProjection特性的开启和使用可参见 https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#service-account-token-volume-projection
BoundServiceAccountTokenVolume
在1.13后成为alpha的feature-gate,注意在apiserver和controller-manager中均需要开启该feature-gate,相关配置如下所示:
apiserver:
controller-manager:
helm安装
这里参考官方文档进行sds的定制化helm安装,这里通过将sds中的enable置为true开启sds的安装,同时需要指定udsPath
和token中用于认证的audience
参数
helm repo add istio.io https://storage.googleapis.com/istio-release/releases/1.4.2/charts/ kubectl apply -f install/kubernetes/helm/helm-service-account.yaml helm init --service-account tiller helm install install/kubernetes/helm/istio-init --name istio-init --namespace istio-system kubectl -n istio-system wait --for=condition=complete job --all helm install install/kubernetes/helm/istio --name istio --namespace istio-system --values install/kubernetes/helm/istio/values-istio-sds-auth.yaml
SDS源码解析
下图描述了启用sds后证书签发和轮转时组件之间的交互流程:
本小节我们结合代码来分析一下sds具体的工作流程:首先sds的启动代码在security/cmd/node_agent_k8s/main.go
中,这里首先会根据sds配置类型创建secretCache实例并在NewServer
方法中启动服务端。
workloadSecretCache, gatewaySecretCache := newSecretCache(serverOptions) if workloadSecretCache != nil { defer workloadSecretCache.Close() } if gatewaySecretCache != nil { defer gatewaySecretCache.Close() } server, err := sds.NewServer(serverOptions, workloadSecretCache, gatewaySecretCache) if err != nil { log.Errorf("failed to create sds service: %v", err) return fmt.Errorf("failed to create sds service") }
在初始化SecretCache的过程中,首先会根据sds服务的两种模式(默认Workload,选装IngressGateway)实例化不同的SecretFetcher,这里的SecretFetcher相当于sds的客户端,可以从不同的目标secret中获取到对应的证书内容;之后基于SecretFetcher封装一个secretCache实例,用于在memory中以sync.map
的形式缓存不同应用或ingressgateway的证书内容,同时还存储了根证书,证书变更需要触发的回调函数和一些证书相关变更的计数统计等信息。SecretCache的初始化函数如下所示:
// newSecretCache creates the cache for workload secrets and/or gateway secrets. // Although currently not used, Citadel Agent can serve both workload and gateway secrets at the same time. func newSecretCache(serverOptions sds.Options) (workloadSecretCache, gatewaySecretCache *cache.SecretCache) { if serverOptions.EnableWorkloadSDS { wSecretFetcher, err := secretfetcher.NewSecretFetcher(false, serverOptions.CAEndpoint, serverOptions.CAProviderName, true, []byte(serverOptions.VaultTLSRootCert), serverOptions.VaultAddress, serverOptions.VaultRole, serverOptions.VaultAuthPath, serverOptions.VaultSignCsrPath) ... workloadSecretCache = cache.NewSecretCache(wSecretFetcher, sds.NotifyProxy, workloadSdsCacheOptions) } else { workloadSecretCache = nil } if serverOptions.EnableIngressGatewaySDS { gSecretFetcher, err := secretfetcher.NewSecretFetcher(true, "", "", false, nil, "", "", "", "") if err != nil { log.Errorf("failed to create secretFetcher for gateway proxy: %v", err) os.Exit(1) } gatewaySecretChan = make(chan struct{}) gSecretFetcher.Run(gatewaySecretChan) gatewaySecretCache = cache.NewSecretCache(gSecretFetcher, sds.NotifyProxy, gatewaySdsCacheOptions) } else { gatewaySecretCache = nil } return workloadSecretCache, gatewaySecretCache }
首先我们来看下SecretFetcher的实例化流程,如果是ingressGateway模式的agent,这里会在InitWithKubeClient
方法创建一个指定ns下的secret的informer,同时启动对应的controller,在eventHandler中注册相应的处理函数用于证书在fetcher实例中证书缓存对应的动态更新。如果是在默认的workload模式下,会调用nodeagent/caclient/client.go
中的NewCAClient
方法去初始化证书获取的客户端,这里会根据nodeagent对接的不同的后端CA签发机构去处理,支持包括Vault,googleCA和默认的citadel。
// NewSecretFetcher returns a pointer to a newly constructed SecretFetcher instance. func NewSecretFetcher(ingressGatewayAgent bool, endpoint, caProviderName string, tlsFlag bool, tlsRootCert []byte, vaultAddr, vaultRole, vaultAuthPath, vaultSignCsrPath string) (*SecretFetcher, error) { ret := &SecretFetcher{} if ingressGatewayAgent { ret.UseCaClient = false cs, err := kube.CreateClientset("", "") ... ret.FallbackSecretName = ingressFallbackSecret ret.InitWithKubeClient(cs.CoreV1()) } else { caClient, err := ca.NewCAClient(endpoint, caProviderName, tlsFlag, tlsRootCert, vaultAddr, vaultRole, vaultAuthPath, vaultSignCsrPath) ... ret.UseCaClient = true ret.CaClient = caClient } return ret, nil }
我们以默认的citadel为例,这里会从指定的configmap istio-security中读取根证书,之后基于指定的grpc协议建立与Citadel连接的客户端,代码如下:
case citadelName: cs, err := kube.CreateClientset("", "") if err != nil { return nil, fmt.Errorf("could not create k8s clientset: %v", err) } controller := configmap.NewController(namespace, cs.CoreV1()) rootCert, err := getCATLSRootCertFromConfigMap(controller, retryInterval, maxRetries) if err != nil { return nil, err } return citadel.NewCitadelClient(endpoint, tlsFlag, rootCert)
在完成了secretFetcher的实例化后,我们再来看下如何构建sds服务端对应的secretCache,在secretCache中封装了一组重要的接口SecretManager
,包含了sds服务端进行证书管理的主要逻辑,接口如下:
// SecretManager defines secrets management interface which is used by SDS. type SecretManager interface { // GenerateSecret generates new secret and cache the secret. GenerateSecret(ctx context.Context, connectionID, resourceName, token string) (*model.SecretItem, error) // ShouldWaitForIngressGatewaySecret indicates whether a valid ingress gateway secret is expected. ShouldWaitForIngressGatewaySecret(connectionID, resourceName, token string) bool // SecretExist checks if secret already existed. // This API is used for sds server to check if coming request is ack request. SecretExist(connectionID, resourceName, token, version string) bool // DeleteSecret deletes a secret by its key from cache. DeleteSecret(connectionID, resourceName string) }
这里我们主要了解一下证书签发的逻辑,首先接口实现GenerateSecret中会调动generateSecret
函数,该函数包含了证书签发的主要逻辑,包括CSR的创建,然后通过sendRetriableRequest
方法去尝试通过之前实例化后的CaClient中的CSRSign方法完成证书的签发,我们可以在nodeagent/caclient/providers/
下找到对接不同后端的CaClient对应实现。
精简后的SecretCache的初始化方法如下,对于默认workload模式下的服务端,这里同样需要为fetcher中的informer controller实现不同的事件处理函数。
// NewSecretCache creates a new secret cache. func NewSecretCache(fetcher *secretfetcher.SecretFetcher, notifyCb func(ConnKey, *model.SecretItem) error, options Options) *SecretCache { ret := &SecretCache{ fetcher: fetcher, closing: make(chan bool), notifyCallback: notifyCb, rootCertMutex: &sync.Mutex{}, configOptions: options, randMutex: &sync.Mutex{}, } ... fetcher.AddCache = ret.UpdateK8sSecret fetcher.DeleteCache = ret.DeleteK8sSecret fetcher.UpdateCache = ret.UpdateK8sSecret ... go ret.keyCertRotationJob() return ret }
在函数的最后启动了一个证书轮转的任务,任务中的rotate方法会以固定的间隔时间不断地遍历secretCache中缓存map的所有证书内容,如果有即将过期的证书(默认1小时前),同样会触发上面提到的generateSecret
函数去重新签发证书并更新到缓存中。
在完成了secretCache的实例化后就可以启动sds服务端,这里secretCache实例会被传入到Server端对应的sdsService中,在service中会基于sbs_pb.go中的grpc协议实现对应的证书Secret请求方法。最后根据不同的启动模式在相应的initService方法中启动grpc服务端并开始服务。
func NewServer(options Options, workloadSecretCache, gatewaySecretCache cache.SecretManager) (*Server, error) { s := &Server{ workloadSds: newSDSService(workloadSecretCache, false, options.UseLocalJWT, options.RecycleInterval), gatewaySds: newSDSService(gatewaySecretCache, true, options.UseLocalJWT, options.RecycleInterval), } if options.EnableWorkloadSDS { if err := s.initWorkloadSdsService(&options); err != nil { sdsServiceLog.Errorf("Failed to initialize secret discovery service for workload proxies: %v", err) return nil, err } sdsServiceLog.Infof("SDS gRPC server for workload UDS starts, listening on %q \n", options.WorkloadUDSPath) } if options.EnableIngressGatewaySDS { if err := s.initGatewaySdsService(&options); err != nil { sdsServiceLog.Errorf("Failed to initialize secret discovery service for ingress gateway: %v", err) return nil, err } sdsServiceLog.Infof("SDS gRPC server for ingress gateway controller starts, listening on %q \n", options.IngressGatewayUDSPath) } version.Info.RecordComponentBuildTag("citadel_agent") if options.DebugPort > 0 { s.initDebugServer(options.DebugPort) } return s, nil }
使用小结
Istio官方文档中介绍了sds 在workload和IngressGateway两种工作模式下的使用介绍,我们可以在开启了SDS的istio集群中手工验证。这里我们总结下使用SDS模式后ingress gateway agent支持了如下特性:
• 不需要重启ingress gateway即可增加,删除或更新其对应使用的证书
• 无需使用挂载volume的方式引用证书,seceret内容不落盘
• gateway agent支持配置多host证书对
对于开启了SDS后的应用负载sidecar也带来了如下优点:
• 应用私钥只存在于Citadal agent和Envoy内存中,绝不会出节点传输
• 无需依赖Kubernetes Secret使用挂载volume的方式引用证书
• Sidecar Envoy会通过SDS API动态轮转证书而无需重启
在后续容器服务Istio集群也会支持开启SDS相关能力,敬请期待。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
谷歌将逐步弃用 Chrome 中的 User-Agent 字符串
谷歌宣布,将从 Chrome 81 开始,逐步淘汰 Chrome 浏览器中 User-Agent 字符串的使用。 User-Agent 字符串(以下简称 “UA 字符串”)是浏览器启动连接时发送到网站的一段文本。它包含了有关浏览器类型、渲染引擎和操作系统等在内的详细信息。例如,Windows 10 上用于 Firefox 的 UA 字符串如下所示: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:71.0) Gecko/20100101 Firefox/72.0 UA 字符串在 90 年代作为 Netscape 浏览器的一部分被开发出来,并且一直沿用至今。根据谷歌的评估,多达 90% 的网站以各种方式阅读和使用浏览器的用户代理。它的最初目的是让服务器确保用户接收到的是针对特定浏览器需求进行过优化的页面。可现如今,User-Agent 嗅探已成为不少隐私问题的根源,大量广告商已将该字符串用作跟踪和识别网站访问者的方式。 不仅是隐私问题,UA 字符串还带来了其他困扰。前不久,基于 Chromium 的浏览器 Vivaldi 伪造其用户代理字符串以显...
- 下一篇
带你解读TOF与结构光的区别
去年9月份苹果推出了iPhone 11、iPhone 11 Pro和iPhone 11 Pro Max三款新iPhone,新机型的性能在拍照和续航上得到大幅度的提升,同时连续三年依旧延续保留FACE ID功能。在人脸识别竞争激烈市场中,结构光与TOF两种主流解决方案为各大厂商所受用,为何苹果一直钟情于3D结构光,其背后的秘密是什么呢? 从平面到立体的秘密 结构光3D视觉技术中的其中一种,用于获取物体平面与深度数据。这项技术的原理是通过将激光散斑图像投射到物体表面,再由红外相机接收物体表面反射的散斑信息,交给ASIC处理芯片,最后根据物体造成光信号的变化计算物体位置和深度信息。 简单来说就是通过近红外激光器,将具有一定结构特征的光线投射到被拍摄物体上,再由专门的红外摄像头进行采集。这种具备一定结构的光线,会因被摄物体的不同深度区域,而采集不同的图像相位信息,然后通过运算单元将这种结构的变化换算成深度信息,以此来获得三维结构。 另外一种TOF时间飞行法的原理是通过专用传感器,捕捉近红外光从发射到接收的飞行时间,判断并计算出物体的距离信息。这种方案具有实时性好,算法简单,不受光照变化和物体纹...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- CentOS8安装Docker,最新的服务器搭配容器使用
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- 设置Eclipse缩进为4个空格,增强代码规范
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS8编译安装MySQL8.0.19
- SpringBoot2整合Redis,开启缓存,提高访问速度
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Hadoop3单机部署,实现最简伪集群