【Istio】源码解读ca/controller/secret.go 观测证书发生更新时的逻辑

源代码地址https://github.com/istio/istio/blob/master/security/pkg/pki/ca/controller/secret.go
这份源代码主要是分析证书更新时的重启时间和证书更新后,执行的逻辑
https://github.com/istio/istio/blob/master/security/pkg/pki/ca/controller/secret.go#L230
这里官方文档有一句注解
// Do nothing for existing secrets. Rotating expiring certs are handled by the scrtUpdated method.
执行证书更新主要是由scrtUpdated方法执行 所以我们先分析scrtUpdated方法

Refresh the secret if 1) the certificate contained in the secret is about to expire, or 2) the root certificate in the secret is different than the one held by the ca (this may happen when the CA is restarted and a new self-signed CA cert is generated).
Istio将在Certificate将要过期或者根Certificate和CA中的不同时 生成新证书

func (sc *SecretController) scrtUpdated(oldObj, newObj interface{}) {
    scrt, ok := newObj.(*v1.Secret)
    if !ok {
        log.Warnf("Failed to convert to secret object: %v", newObj)
        return
    }

    certBytes := scrt.Data[CertChainID]
    cert, err := util.ParsePemEncodedCertificate(certBytes)
    //这里生成一个x509数字证书 具体代码可以在下面看到
    //https://github.com/istio/istio/blob/master/security/pkg/pki/util/crypto.go#L32
    if err != nil {
        // TODO: we should refresh secret in this case since the secret contains an
        // invalid cert.
        log.Errora(err)
        return
    }

    certLifeTimeLeft := time.Until(cert.NotAfter)
    //certLifeTime 就是该证书可存活时间 NotAfter-NotBefore
    certLifeTime := cert.NotAfter.Sub(cert.NotBefore)
    // TODO(myidpt): we may introduce a minimum gracePeriod, without making the config too complex.
    // Because time.Duration only takes int type, multiply gracePeriodRatio by 1000 and then divide it.
    //gracePrid时证书可更新时间 在CA中出现过相关定义
    gracePeriod := time.Duration(sc.gracePeriodRatio*1000) * certLifeTime / 1000
    if gracePeriod < sc.minGracePeriod {
        log.Warnf("gracePeriod (%v * %f) = %v is less than minGracePeriod %v. Apply minGracePeriod.",
            certLifeTime, sc.gracePeriodRatio, gracePeriod, sc.minGracePeriod)
        gracePeriod = sc.minGracePeriod
    }
    rootCertificate := sc.ca.GetCAKeyCertBundle().GetRootCertPem()

    // Refresh the secret if 1) the certificate contained in the secret is about
    // to expire, or 2) the root certificate in the secret is different than the
    // one held by the ca (this may happen when the CA is restarted and
    // a new self-signed CA cert is generated).
    if certLifeTimeLeft < gracePeriod || !bytes.Equal(rootCertificate, scrt.Data[RootCertID]) {
        namespace := scrt.GetNamespace()
        name := scrt.GetName()

        log.Infof("Refreshing secret %s/%s, either the leaf certificate is about to expire "+
            "or the root certificate is outdated", namespace, name)

        saName := scrt.Annotations[ServiceAccountNameAnnotationKey]

        chain, key, err := sc.generateKeyAndCert(saName, namespace)
        if err != nil {
            log.Errorf("Failed to generate key and certificate for service account %q in namespace %q (error %v)",
                saName, namespace, err)

            return
        }

        //scrt.Data数据被刷新
        scrt.Data[CertChainID] = chain
        scrt.Data[PrivateKeyID] = key
        scrt.Data[RootCertID] = rootCertificate

        if _, err = sc.core.Secrets(namespace).Update(scrt); err != nil {
            log.Errorf("Failed to update secret %s/%s (error: %s)", namespace, name, err)
        }
    }
}

生成证书实际逻辑

func (sc *SecretController) generateKeyAndCert(saName string, saNamespace string) ([]byte, []byte, error) {
    id := fmt.Sprintf("%s://cluster.local/ns/%s/sa/%s", util.URIScheme, saNamespace, saName)
    if sc.dnsNames != nil {
        // Control plane components in same namespace.
        if e, ok := sc.dnsNames[saName]; ok {
            if e.Namespace == saNamespace {
                // Example: istio-pilot.istio-system.svc, istio-pilot.istio-system
                id += "," + fmt.Sprintf("%s.%s.svc", e.ServiceName, e.Namespace)
                id += "," + fmt.Sprintf("%s.%s", e.ServiceName, e.Namespace)
            }
        }
        // Custom overrides using CLI
        if e, ok := sc.dnsNames[saName+"."+saName]; ok {
            for _, d := range e.CustomDomains {
                id += "," + d
            }
        }
    }
    options := util.CertOptions{
        Host:       id,
        RSAKeySize: keySize,
    }

    csrPEM, keyPEM, err := util.GenCSR(options)
    //用所有option生成 X.509 certificate sign request and private key
    //具体代码可以从以下看到
    //https://github.com/istio/istio/blob/master/security/pkg/pki/util/generate_csr.go#L30
    if err != nil {
        log.Errorf("CSR generation error (%v)", err)
        sc.monitoring.CSRError.Inc()
        return nil, nil, err
    }

    certChainPEM := sc.ca.GetCAKeyCertBundle().GetCertChainPem()
    certPEM, signErr := sc.ca.Sign(csrPEM, sc.certTTL, sc.forCA)
    //可生成WorkLoad或者CA证书 这个视sc.forCA()而定
    //具体代码可从以下看到
    //https://github.com/istio/istio/blob/master/security/pkg/pki/ca/ca.go#L173
    if signErr != nil {
        log.Errorf("CSR signing error (%v)", signErr.Error())
        sc.monitoring.GetCertSignError(signErr.(*ca.Error).ErrorType()).Inc()
        return nil, nil, fmt.Errorf("CSR signing error (%v)", signErr.(*ca.Error))
    }
    certPEM = append(certPEM, certChainPEM...)

    return certPEM, keyPEM, nil
}

你可能感兴趣的:(Istio)