Istio安全主要包括认证授权,有关授权的RBAC使用参考之前的文章【Istio安全】服务间访问控制-RBAC ,不过RBAC已经准备废弃Authorization替代, 认证则分为服务间身份验证来源身份认证,服务间身份验证Istio提供了双向TLS方案,其中涉及的秘钥管理服务,主要由istio/security模块的 CitadelNode Agent提供,本文主要分析这部分的源码实现。

  • Istio 1.14.0

PKI

首先参考官方文档[en]看下几种不同场景的方案,其中Kubernetes 方案最简单, Kubernetes 中的代理节点更适合生产,具体优势参考[en]。

  • Kubernetes 方案
    • Citadel 监视 Kubernetes apiserver,为每个现有和新的服务帐户创建 SPIFFE 证书和密钥对。Citadel 将证书和密钥对存储为 Kubernetes secret。
    • 创建 pod 时,Kubernetes 会根据其服务帐户通过 Kubernetes secret volume 将证书和密钥对挂载到 pod 上。
    • Citadel 监视每个证书的生命周期,并通过重写 Kubernetes secret 自动轮换证书。
    • Pilot 生成安全命名信息,该信息定义了哪些 Service Account 可以运行哪些服务。Pilot 然后将安全命名信息传递给 envoy sidecar。
  • 本地机器方案
    • Citadel 创建 gRPC 服务来接受证书签名请求(CSR)。
    • 节点代理生成私钥和 CSR,并将 CSR 及其凭据发送给 Citadel 进行签名。
    • Citadel 验证 CSR 承载的凭证,并签署 CSR 以生成证书。
    • 节点代理将从 Citadel 接收的证书和私钥发送给 Envoy。
    • 上述 CSR 过程会定期重复进行证书和密钥轮换。
  • Kubernetes 中的代理节点
    • Citadel 创建一个 gRPC 服务来接受 CSR 请求。
    • Envoy 通过 Envoy secret 发现服务(SDS)API 发送证书和密钥请求。
    • 收到 SDS 请求后,节点代理会创建私钥和 CSR,并将 CSR 及其凭据发送给 Citadel 进行签名。
    • Citadel 验证 CSR 中携带的凭证,并签署 CSR 以生成证书。
    • 节点代理通过 Envoy SDS API 将从 Citadel 接收的证书和私钥发送给 Envoy。
    • 上述 CSR 过程会定期重复进行证书和密钥轮换。

CA管理-Citadel

Citadel(源码入口istio/security/cmd/istio_ca)是Istio自带的秘钥管理服务,使用代理节点也支持外部CA系统,如:VaultCA和GoogleCA。

包括以下能力:

TODO 配图

IstioCA

IstioCa支持两种方式:

  • 自签名证书,自动轮换SelfSignedCA
  • 外部文件证书

SelfSignedCA

自签名证书,自动轮换管理由SelfSignedCARootCertRotator实现,默认1小时做一次证书的检查。

SecretController

在Kubernetes方案中工作负载的证书管理,Citadel通过修改secret对工作负载的证书进行管理,SecretController监控ServiceAccount的创建、删除, Secret的删除、更新,以及Namespace的更新,在产生变更时修改secret完成证书的轮换,同时Pilot Agent会监控secret的变更,并重启Envoy使配置生效,完成证书的轮换。

Server

证书服务,与nodeagentcaclient交互,包括服务的认证及鉴权等

gRPC服务如下:

  • CertificateService
    • CreateCertificate(context.Context, *IstioCertificateRequest) (*IstioCertificateResponse, error)
  • CAService废弃
    • HandleCSR(context.Context, *CsrRequest) (*CsrResponse, error)

Authenticator

gRPC服务认证,默认一个ClientCertAuthenticator,当启动SDS时添加了KubeJWTAuthenticator

Authorizer

gRPC服务CAServiceCertificateService的鉴权

服务的Authorizer都是TODO状态

registry提供了一个身份授权的注册表

  • kube/ServiceController
    • 监控Service创建、删除与修改
  • kube/ServiceAccountController
    • 监控ServiceAccount创建、删除与修改

Node Agent K8S

NodeAgent(源码入口istio/security/cmd/node_agent_k8s)是Citadel在工作负载node或者网关pod中的代理,为Envoy提供SDS服务,并与Citadel交互发起CSR请求。 使用Kubernetes中的代理节点模式时Citadel可以运行为server-only模式,即不做工作负载的证书管理。

TODO 配图

Node Agent源码均在nodeagent目录

CA Client

提供CSR签名接口,服务实现支持citadelgooglevault

Client接口

// Client interface defines the clients need to implement to talk to CA for CSR.
type Client interface {
	CSRSign(ctx context.Context, csrPEM []byte, subjectID string,
		certValidTTLInSec int64) ([]string /*PEM-encoded certificate chain*/, error)
}

Secret Fetcher

作为Workload代理时根据不同的Provider实例化CA Client,或者作为Ingress Gateway代理时监控secret变更,并通知到secretcache

  • Workload代理以DaemonSetnode上运行,使用CA Client与CA Server交互
  • Ingress Gateway代理与Workload部署方式不同,代理在Pod内运行,而不是DaemonSet
    • 监控secret的创建、删除和更新,用于捕获Ingress Gateway的TLS配置
    • secret有变更时通过AddCacheDeleteCacheUpdateCache方法通知secretcacheDeleteK8sSecretUpdateK8sSecret,再由sds.NotifyProxy通知SDS服务
    • 使用SDS的优势[en]
    • Ingress Gateway的TLS的默认配置方式是文件挂载[en]

Secret Cache

秘钥缓存,负责秘钥的轮换

  • 默认10分钟做一次检查,对即将过期的做轮换,默认1小时后过期的证书就做更新
    • 如果轮询时token过期会返回secret=nil,这时SDS服务在收到notify后需要将连接断开重连
  • 产生轮换时通过sds.NotifyProxy通知到SDS服务,SDS服务通过connKey找到对应的client,并通过SDS的StreamSecrets将新的证书推送到Pod
  • generateSecret时,如果检测到rootCertnil或者与certChainPEM的不一致,将触发rootCert的更新
    • SDS服务将新的rootCert加入到可信CA

SecretManager接口

// 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)
}

SDS Service

SDS服务实现

  • NotifyProxy接收secret变更的通知
    • 查找clientconnection,通过pushChannelsecret变更事件分发给SDS服务接口StreamSecrets,然后推送到Envoy
    • 事件类型:
      • Secret_TlsCertificateTLS证书
      • Secret_ValidationContext可信CA
  • 接收Envoy对SDS服务StreamSecretsFetchSecrets请求,SecretManager.GenerateSecret()生成秘钥

Ref