源代码地址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
}