程序印象

Istio源码系列2:citadel 源码分析

2019/02/06 Share

本系列链接:


安全整体架构

From: Istio 安全

源码位于 security,编译后名称为 citadel。

命令行介绍

Dockerfile istio.io/istio/security/docker/Dockerfile.citadel

1
2
3
4
5
6
7
8
9
FROM scratch

# obtained from debian ca-certs deb using fetch_cacerts.sh
ADD ca-certificates.tgz /
# All containers need a /tmp directory
WORKDIR /tmp/
ADD istio_ca /usr/local/bin/istio_ca

ENTRYPOINT [ "/usr/local/bin/istio_ca", "--self-signed-ca" ]

查看版本:

1
2
3
4
5
6
7
# kubectl exec -ti istio-citadel-55cdfdd57c-bh7dk -n istio-system -- /usr/local/bin/istio_ca version
Version: 1.0.5
GitRevision: c1707e45e71c75d74bf3a5dec8c7086f32f32fad
User: root@6f6ea1061f2b
Hub: docker.io/istio
GolangVersion: go1.10.4
BuildStatus: Clean

命令行帮助:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# kubectl exec -ti istio-citadel-55cdfdd57c-bh7dk -n istio-system -- /usr/local/bin/istio_ca --help
Istio Certificate Authority (CA)

Usage:
istio_ca [flags]
istio_ca [command]

Available Commands:
help Help about any command
probe Check the liveness or readiness of a locally-running server
version Prints out build version information

Flags:
--append-dns-names Append DNS names to the certificates for webhook services. (default true)
--cert-chain string Path to the certificate chain file
--citadel-storage-namespace string Namespace where the Citadel pod is running. Will not be used if explicit file or other storage mechanism is specified. (default "istio-system")
--custom-dns-names string The list of account.namespace:customdns names, separated by comma.
--enable-profiling Enabling profiling when monitoring Citadel.
--grpc-host-identities string The list of hostnames for istio ca server, separated by comma. (default "istio-ca,istio-citadel")
--grpc-hostname string DEPRECATED, use --grpc-host-identites. (default "istio-ca")
--grpc-port int The port number for Citadel GRPC server. If unspecified, Citadel will not serve GRPC requests. (default 8060)
-h, --help help for istio_ca
--key-size int Size of generated private key (default 2048)
--kube-config string Specifies path to kubeconfig file. This must be specified when not running inside a Kubernetes pod.
--listened-namespace string Select a namespace for the CA to listen to. If unspecified, Citadel tries to use the ${NAMESPACE} environment variable. If neither is set, Citadel listens to all namespaces.
--liveness-probe-interval duration Interval of updating file for the liveness probe.
--liveness-probe-path string Path to the file for the liveness probe.
--log_as_json Whether to format output as JSON or in plain console-friendly format
--log_caller string Comma-separated list of scopes for which to include caller information, scopes can be any of [default, model]
--log_output_level string Comma-separated minimum per-scope logging level of messages to output, in the form of <scope>:<level>,<scope>:<level>,... where scope can be one of [default, model] and level can be one of [debug, info, warn, error, none] (default "default:info")
--log_rotate string The path for the optional rotating log file
--log_rotate_max_age int The maximum age in days of a log file beyond which the file is rotated (0 indicates no limit) (default 30)
--log_rotate_max_backups int The maximum number of log file backups to keep before older files are deleted (0 indicates no limit) (default 1000)
--log_rotate_max_size int The maximum size in megabytes of a log file beyond which the file is rotated (default 104857600)
--log_stacktrace_level string Comma-separated minimum per-scope logging level at which stack traces are captured, in the form of <scope>:<level>,<scope:level>,... where scope can be one of [default, model] and level can be one of [debug, info, warn, error, none] (default "default:none")
--log_target stringArray The set of paths where to output the log. This can be any path as well as the special values stdout and stderr (default [stdout])
--max-workload-cert-ttl duration The max TTL of issued workload certificates (default 2160h0m0s)
--monitoring-port int The port number for monitoring Citadel. If unspecified, Citadel will disable monitoring. (default 9093)
--org string Organization for the cert
--probe-check-interval duration Interval of checking the liveness of the CA. (default 30s)
--requested-ca-cert-ttl duration The requested TTL for the workload (default 8760h0m0s)
--root-cert string Path to the root certificate file
--self-signed-ca Indicates whether to use auto-generated self-signed CA certificate. When set to true, the '--signing-cert' and '--signing-key' options are ignored.
--self-signed-ca-cert-ttl duration The TTL of self-signed CA root certificate (default 8760h0m0s)
--self-signed-ca-org string The issuer organization used in self-signed CA certificate (default to k8s.cluster.local) (default "k8s.cluster.local")
--sign-ca-certs Whether Citadel signs certificates for other CAs
--signing-cert string Path to the CA signing certificate file
--signing-key string Path to the CA signing key file
--upstream-ca-address string The IP:port address of the upstream CA. When set, the CA will rely on the upstream Citadel to provision its own certificate.
--workload-cert-grace-period-ratio float32 The workload certificate rotation grace period, as a ratio of the workload certificate TTL. (default 0.5)
--workload-cert-min-grace-period duration The minimum workload certificate rotation grace period. (default 10m0s)
--workload-cert-ttl duration The TTL of issued workload certificates (default 2160h0m0s)

Use "istio_ca [command] --help" for more information about a command.

容器内部启动添加的命令行如下:

1
2
3
4
5
6
- --append-dns-names=true
- --grpc-port=8060
- --grpc-hostname=citadel
- --citadel-storage-namespace=istio-system
- --custom-dns-names=istio-pilot-service-account.istio-system:istio-pilot.istio-system,istio-ingressgateway-service-account.istio-system:istio-ingressgateway.istio-system
- --self-signed-ca=true

可以在其运行的 node 节点上通过命令查看

1
$ /usr/local/bin/istio_ca --self-signed-ca --append-dns-names=true --grpc-port=8060 --grpc-hostname=citadel --citadel-storage-namespace=istio-system --custom-dns-names=istio-pilot-service-account.istio-system:istio-pilot.istio-system,istio-ingressgateway-service-account.istio-system:istio-ingressgateway.istio-system --self-signed-ca=true

istio-citadel 启动的 yaml 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# kubectl get pod istio-citadel-55cdfdd57c-bh7dk -n istio-system -o yaml
apiVersion: v1
kind: Pod
metadata:
annotations:
scheduler.alpha.kubernetes.io/critical-pod: ""
sidecar.istio.io/inject: "false"
creationTimestamp: 2019-01-15T08:24:24Z
generateName: istio-citadel-55cdfdd57c-
labels:
istio: citadel
pod-template-hash: 55cdfdd57c
name: istio-citadel-55cdfdd57c-bh7dk
namespace: istio-system
ownerReferences:
- apiVersion: apps/v1
blockOwnerDeletion: true
controller: true
kind: ReplicaSet
name: istio-citadel-55cdfdd57c
uid: f6db1f80-189e-11e9-ab53-00163e0c1552
resourceVersion: "16685125"
selfLink: /api/v1/namespaces/istio-system/pods/istio-citadel-55cdfdd57c-bh7dk
uid: f710ae31-189e-11e9-ab53-00163e0c1552
spec:
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- preference:
matchExpressions:
- key: beta.kubernetes.io/arch
operator: In
values:
- amd64
weight: 2
- preference:
matchExpressions:
- key: beta.kubernetes.io/arch
operator: In
values:
- ppc64le
weight: 2
- preference:
matchExpressions:
- key: beta.kubernetes.io/arch
operator: In
values:
- s390x
weight: 2
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: beta.kubernetes.io/arch
operator: In
values:
- amd64
- ppc64le
- s390x
containers:
- args:
- --append-dns-names=true
- --grpc-port=8060
- --grpc-hostname=citadel
- --citadel-storage-namespace=istio-system
- --custom-dns-names=istio-pilot-service-account.istio-system:istio-pilot.istio-system,istio-ingressgateway-service-account.istio-system:istio-ingressgateway.istio-system
- --self-signed-ca=true
image: docker.io/istio/citadel:1.0.5
imagePullPolicy: IfNotPresent
name: citadel
resources:
requests:
cpu: 10m
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: istio-citadel-service-account-token-gdxfk
readOnly: true
dnsPolicy: ClusterFirst
nodeName: node02
priority: 0
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: istio-citadel-service-account
serviceAccountName: istio-citadel-service-account
terminationGracePeriodSeconds: 30
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 10
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 10
volumes:
- name: istio-citadel-service-account-token-gdxfk
secret:
defaultMode: 420
secretName: istio-citadel-service-account-token-gdxfk

代码流程分析

整体架构

image-20190205170654001

istio.io/istio/security/cmd/istio_ca/main.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// /usr/local/bin/istio_ca 
// --self-signed-ca
// --append-dns-names=true
// --grpc-port=8060
// --grpc-hostname=citadel
// --citadel-storage-namespace=istio-system
// --custom-dns-names=istio-pilot-service-account.istio-system:istio-pilot.istio-system,
// istio-ingressgateway-service-account.istio-system:istio-ingressgateway.istio-system // --self-signed-ca=true

rootCmd = &cobra.Command{
Use: "istio_ca",
Short: "Istio Certificate Authority (CA).",
Args: cobra.ExactArgs(0),
Run: func(cmd *cobra.Command, args []string) {
runCA()
},
}

runCA 的主函数流程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
func runCA() {
//...
// --listened-namespace 设置 CA 监控的 namespace,如果没有指定会从 ${NAMESPACE} 环境变量中获取,如果都没有设置,Citadel 则会监听全部的 namespace.

if value, exists := os.LookupEnv(cmd.ListenedNamespaceKey); exists {
// When -namespace is not set, try to read the namespace from environment variable.
if opts.listenedNamespace == "" {
opts.listenedNamespace = value
}
// Use environment variable for istioCaStorageNamespace if it exists
opts.istioCaStorageNamespace = value
}

// 验证命令行
verifyCommandLineOptions()

var webhooks map[string]controller.DNSNameEntry

// 如果设置了添加 DNS 名字的后缀
if opts.appendDNSNames {
webhooks = make(map[string]controller.DNSNameEntry)
/*
// ServiceAccount/DNS pair for generating DNS names in certificates.
// TODO: move it to a configmap later when we have more services to support.
webhookServiceAccounts = []string{
"istio-sidecar-injector-service-account",
"istio-galley-service-account",
}

webhookServiceNames = []string{
"istio-sidecar-injector",
"istio-galley",
}
*/
for i, svcAccount := range webhookServiceAccounts {
// istio-sidecar-injector-service-account
// istio-galley-service-account
webhooks[svcAccount] = controller.DNSNameEntry{
ServiceName: webhookServiceNames[i],
Namespace: opts.istioCaStorageNamespace,
// opts.istioCaStorageNamespace 运行的 namespace,默认为 istio-system
}
}

// ...

// 创建连接到集群中的 client
cs := createClientset()

// 返回 ca.IstioCA,用于管理证书链和签新的证书
ca := createCA(cs.CoreV1())

// For workloads in K8s, we apply the configured workload cert TTL.
// 1. 创建 NewSecretController 来完成对于 API Server 中的 ServiceAccount 和 Secret 的创建
sc, err := controller.NewSecretController(ca,
opts.workloadCertTTL,
opts.workloadCertGracePeriodRatio, opts.workloadCertMinGracePeriod, opts.dualUse,
cs.CoreV1(), opts.signCACerts, opts.listenedNamespace, webhooks)
if err != nil {
fatalf("Failed to create secret controller: %v", err)
}

stopCh := make(chan struct{})
// !!! 运行 NewSecretController
sc.Run(stopCh)

// 2. 如果设置了 grpcPort,则启动相关 server
if opts.grpcPort > 0 {
// ...
ch := make(chan struct{})
// monitor service objects with "alpha.istio.io/kubernetes-serviceaccounts" and
// "alpha.istio.io/canonical-serviceaccounts" annotations
// 2.1 NewServiceController
serviceController := kube.NewServiceController(cs.CoreV1(), opts.listenedNamespace, reg)

// ServiceController
serviceController.Run(ch)

// 2.2 NewServiceAccountController
// monitor service account objects for istio mesh expansion
serviceAccountController := kube.NewServiceAccountController(cs.CoreV1(), opts.listenedNamespace, reg)
serviceAccountController.Run(ch)

// The CA API uses cert with the max workload cert TTL.
hostnames := append(strings.Split(opts.grpcHosts, ","), fqdn())
caServer, startErr := caserver.New(ca, opts.maxWorkloadCertTTL, opts.signCACerts, hostnames, opts.grpcPort, spiffe.GetTrustDomain())
if startErr != nil {
fatalf("Failed to create istio ca server: %v", startErr)
}
if serverErr := caServer.Run(); serverErr != nil {
// stop the registry-related controllers
ch <- struct{}{}

log.Warnf("Failed to start GRPC server with error: %v", serverErr)
}
}

monitorErrCh := make(chan error)

// 3. Start the monitoring server.
if opts.monitoringPort > 0 {
monitor, mErr := monitoring.NewMonitor(opts.monitoringPort, opts.enableProfiling)
if mErr != nil {
fatalf("Unable to setup monitoring: %v", mErr)
}
go monitor.Start(monitorErrCh)
log.Info("Citadel monitor has started.")
defer monitor.Close()
}

log.Info("Citadel has started")

rotatorErrCh := make(chan error)
// Start CA client if the upstream CA address is specified.
if len(opts.cAClientConfig.CAAddress) != 0 {
config := &opts.cAClientConfig
config.Env = "onprem"
config.Platform = "vm"
config.ForCA = true
config.CertFile = opts.signingCertFile
config.KeyFile = opts.signingKeyFile
config.CertChainFile = opts.certChainFile
config.RootCertFile = opts.rootCertFile
config.CSRGracePeriodPercentage = cmd.DefaultCSRGracePeriodPercentage
config.CSRMaxRetries = cmd.DefaultCSRMaxRetries
config.CSRInitialRetrialInterval = cmd.DefaultCSRInitialRetrialInterval
rotator, creationErr := caclient.NewKeyCertBundleRotator(config, ca.GetCAKeyCertBundle())
if creationErr != nil {
fatalf("Failed to create key cert bundle rotator: %v", creationErr)
}

// 4. rotator 启动
go rotator.Start(rotatorErrCh)
log.Info("Key cert bundle rotator has started.")
defer rotator.Stop()
}

// Blocking until receives error.
for {
select {
case <-monitorErrCh:
fatalf("Monitoring server error: %v", err)
case <-rotatorErrCh:
fatalf("Key cert bundle rotator error: %v", err)
}
}
}

NewSecretController

SecretController 内部会创建两个 Controller:

  1. ServiceAccount 的监听,如果设置了 listened-namespace,则监听该 namespace 下,否则是全部;
  2. Secret 的监听,namespace 同上,但是 Controller 只会监听自己创建的类型,即:type:”istio.io/key-and-cert”

实现的主要功能是为 ServiceAccount 创建对应的 Secret,Secret 中设置了相关的证书,在对应的 Pod 启动的时候进行加载;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// NewSecretController returns a pointer to a newly constructed SecretController instance.
func NewSecretController(ca ca.CertificateAuthority, certTTL time.Duration,
gracePeriodRatio float32, minGracePeriod time.Duration, dualUse bool,
core corev1.CoreV1Interface, forCA bool, namespace string, dnsNames map[string]DNSNameEntry) (*SecretController, error) {

//...

c := &SecretController{
ca: ca,
certTTL: certTTL,
gracePeriodRatio: gracePeriodRatio,
minGracePeriod: minGracePeriod,
dualUse: dualUse,
core: core,
forCA: forCA,
dnsNames: dnsNames,
monitoring: newMonitoringMetrics(),
}

// 监听特定 namespace 下的 ServiceAccount
c.saStore, c.saController = cache.NewInformer(saLW, &v1.ServiceAccount{}, time.Minute, rehf)

istioSecretSelector := fields.SelectorFromSet(map[string]string{"type": IstioSecretType}).String()

// 监听 type:”istio.io/key-and-cert” 的 secret
c.scrtStore, c.scrtController =
cache.NewInformer(scrtLW, &v1.Secret{}, secretResyncPeriod, cache.ResourceEventHandlerFuncs{
DeleteFunc: c.scrtDeleted,
UpdateFunc: c.scrtUpdated,
})

// ...
}

// Run starts the SecretController until a value is sent to stopCh.
func (sc *SecretController) Run(stopCh chan struct{}) {
go sc.scrtController.Run(stopCh)

// saAdded calls upsertSecret to update and insert secret
// it throws error if the secret cache is not synchronized, but the secret exists in the system
cache.WaitForCacheSync(stopCh, sc.scrtController.HasSynced)

go sc.saController.Run(stopCh)
}

gRPC Server 启动

如果设置了 gRPC 相关的参数,则会启动相关的服务,同上也会启动两个 Controller 和 一个 gRPC Server,Controller 监听的 namespace 由 listened-namespace 设置,同上:

  1. NewServiceController:用于监听添加了注解 alpha.istio.io/kubernetes-serviceaccountsalpha.istio.io/canonical-serviceaccounts 的 Service 对象;从注解中解出来对应的用户名对应的 Reg 注册表的映射关系中,当前 key 和 value 都是相同 c.reg.AddMapping(svcAcct, svcAcct)

    istio.io/istio/security/pkg/registry/kube/service.go

    1
    2
    3
    4
    5
    6
    7
    // KubeServiceAccountsOnVMAnnotation is to specify the K8s service accounts that are allowed to run
    // this service on the VMs
    KubeServiceAccountsOnVMAnnotation = "alpha.istio.io/kubernetes-serviceaccounts"

    // CanonicalServiceAccountsAnnotation is to specify the non-Kubernetes service accounts that
    // are allowed to run this service.
    CanonicalServiceAccountsAnnotation = "alpha.istio.io/canonical-serviceaccounts"

    结构体定义如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // ServiceController monitors the service definition changes in a namespace. If a
    // new service is added with "alpha.istio.io/kubernetes-serviceaccounts" or
    // "alpha.istio.io/canonical-serviceaccounts" annotations enabled,
    // the corresponding service account will be added to the identity registry
    // for whitelisting.
    type ServiceController struct {
    core corev1.CoreV1Interface

    // identity registry object
    reg registry.Registry

    // controller for service objects
    controller cache.Controller
    }
  1. NewServiceAccountController: 监听 ServiceAccount 对象;对于获取到 sa 信息,生成相对应的 SpiffeID 保存到 Reg 注册表的映射关系中,当前 key 和 value 都是相同 c.reg.DeleteMapping(id, id)

    结构体定义如下:

    istio.io/istio/security/pkg/registry/kube/serviceaccount.go

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // ServiceAccountController monitors service account definition changes in a namespace.
    // For each service account object, its SpiffeID is added to identity registry for
    // whitelisting purpose.
    type ServiceAccountController struct {
    core corev1.CoreV1Interface

    // identity registry object
    reg registry.Registry

    // controller for service objects
    controller cache.Controller
    }
  2. IstioCAServiceServer:主要提供证书的生成和验证功能;

    istio.io/istio/security/pkg/server/ca/server.go

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    // CreateCertificate handles an incoming certificate signing request (CSR). It does
    // authentication and authorization. Upon validated, signs a certificate that:
    // the SAN is the identity of the caller in authentication result.
    // the subject public key is the public key in the CSR.
    // the validity duration is the ValidityDuration in request, or default value if the given duration is invalid.
    // it is signed by the CA signing key.
    func (s *Server) CreateCertificate(ctx context.Context, request *pb.IstioCertificateRequest) (
    *pb.IstioCertificateResponse, error) {

    // 根据请求生成对应的证书
    _, _, certChainBytes, rootCertBytes := s.ca.GetCAKeyCertBundle().GetAll()
    cert, signErr := s.ca.Sign(
    []byte(request.Csr), caller.Identities, time.Duration(request.ValidityDuration)*time.Second, false)

    respCertChain := []string{string(cert)}
    respCertChain = append(respCertChain, string(rootCertBytes))
    response := &pb.IstioCertificateResponse{
    CertChain: respCertChain,
    }
    log.Debug("CSR successfully signed.")

    return response, nil
    }

    // HandleCSR handles an incoming certificate signing request (CSR). It does
    // proper validation (e.g. authentication) and upon validated, signs the CSR
    // and returns the resulting certificate. If not approved, reason for refusal
    // to sign is returned as part of the response object.
    // [TODO](myidpt): Deprecate this function.
    func (s *Server) HandleCSR(ctx context.Context, request *pb.CsrRequest) (*pb.CsrResponse, error) {

    csr, err := util.ParsePemEncodedCSR(request.CsrPem)

    _, err = util.ExtractIDs(csr.Extensions)

    // TODO: Call authorizer.

    _, _, certChainBytes, _ := s.ca.GetCAKeyCertBundle().GetAll()
    cert, signErr := s.ca.Sign(request.CsrPem, []string{}, time.Duration(request.RequestedTtlMinutes)*time.Minute, s.forCA)

    response := &pb.CsrResponse{
    IsApproved: true,
    SignedCert: cert,
    CertChain: certChainBytes,
    }
    log.Debug("CSR successfully signed.")

    return response, nil
    }

Monitor

Monitor 服务主要用于对外输出检查,为 HttpServer, 主要提供 /metrics/version ,如果启用了 enableProfiling 还会启用 /debug/pprof/ 相关的路径;当 Monitor 启动以后, Citadel 则任务已经启动成功,打印以下信息:log.Info("Citadel has started")

CA client

如果指定了 upstream CA Server,还会启动一个 CA Client, 创建一个 rotator go routine,定期用于证书的轮转替换;

istio.io/istio/security/pkg/caclient/keycertbundlerotator.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// Start periodically rotates the KeyCertBundle by interacting with the upstream CA.
// It is a blocking function that should run as a go routine. Thread safe.
func (c *KeyCertBundleRotator) Start(errCh chan<- error) {
c.stoppedMutex.Lock()
if !c.stopped {
errCh <- fmt.Errorf("rotator already started")
c.stoppedMutex.Unlock()
return
}
c.stopped = false
c.stoppedMutex.Unlock()

// Make sure we mark rotator stopped after this method finishes.
defer func() {
c.stoppedMutex.Lock()
c.stopped = true
c.stoppedMutex.Unlock()
}()

for {
certBytes, _, _, _ := c.keycert.GetAllPem()
if len(certBytes) != 0 {
waitTime, ttlErr := c.certUtil.GetWaitTime(certBytes, time.Now())
if ttlErr != nil {
log.Errorf("Error getting TTL from cert: %v. Rotate immediately.", ttlErr)
} else {
timer := time.NewTimer(waitTime)
log.Infof("Will rotate key and cert in %v.", waitTime)
select {
case <-c.stopCh:
return
case <-timer.C:
// Continue in the loop.
}
}
}
co, coErr := c.keycert.CertOptions()
if coErr != nil {
err := fmt.Errorf("failed to extact CertOptions from bundle: %v, abort auto rotation", coErr)
log.Errora(err)
errCh <- err
return
}
certBytes, certChainBytes, privKeyBytes, rErr := c.retriever.Retrieve(co)
if rErr != nil {
err := fmt.Errorf("error retrieving the key and cert: %v, abort auto rotation", rErr)
log.Errora(err)
errCh <- err
return
}
_, _, _, rootCertBytes := c.keycert.GetAllPem()
if vErr := c.keycert.VerifyAndSetAll(certBytes, privKeyBytes, certChainBytes, rootCertBytes); vErr != nil {
err := fmt.Errorf("cannot verify the retrieved key and cert: %v, abort auto rotation", vErr)
log.Errora(err)
errCh <- err
return
}
log.Infof("Successfully retrieved new key and certs.")
}
}

TODO: istio-security-post-install 和 istio-cleanup-secrets Job 作用


除特别声明本站文章均属原创(翻译内容除外),如需要转载请事先联系,转载需要注明作者原文链接地址。


CATALOG
  1. 1. 安全整体架构
  2. 2. 命令行介绍
  3. 3. 代码流程分析
    1. 3.1. 整体架构
    2. 3.2. NewSecretController
    3. 3.3. gRPC Server 启动
    4. 3.4. Monitor
    5. 3.5. CA client