istio 源码 – Citadel 源码分析 (原创)

栏目: 后端 · 发布时间: 7年前

内容简介:[TOC]源码位于

[TOC]

安全整体架构

istio 源码 – Citadel 源码分析 (原创) From: Istio 安全

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

命令行介绍

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

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" ]

查看版本:

# 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

命令行帮助:

# 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.

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

- --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 节点上通过命令查看

$ /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 文件

# 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

代码流程分析

整体架构

istio 源码 – Citadel 源码分析 (原创)

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

// /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 的主函数流程如下:

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 启动的时候进行加载;

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

    // 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"

    结构体定义如下:

    // 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
    }
  2. NewServiceAccountController : 监听 ServiceAccount 对象;对于获取到 sa 信息,生成相对应的 SpiffeID 保存到 Reg 注册表的映射关系中,当前 key 和 value 都是相同 c.reg.DeleteMapping(id, id)

    结构体定义如下:

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

    // 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
    }
  3. IstioCAServiceServer

    :主要提供证书的生成和验证功能;

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

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

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

以上所述就是小编给大家介绍的《istio 源码 – Citadel 源码分析 (原创)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Build Your Own Web Site the Right Way Using HTML & CSS

Build Your Own Web Site the Right Way Using HTML & CSS

Ian Lloyd / SitePoint / 2006-05-02 / USD 29.95

Build Your Own Website The Right Way Using HTML & CSS teaches web development from scratch, without assuming any previous knowledge of HTML, CSS or web development techniques. This book introduces you......一起来看看 《Build Your Own Web Site the Right Way Using HTML & CSS》 这本书的介绍吧!

html转js在线工具
html转js在线工具

html转js在线工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具