# caServer

证书签发 CreateCertificate

判断CA类型来进行创建CA

```
    if err := s.maybeCreateCA(caOpts); err != nil {
        return nil, err
    }
```

启动CA

```
s.startCA(caOpts)
```

根据CA类型来启动CA

```
func (s *Server) startCA(caOpts *caOptions) {
    if s.CA == nil && s.RA == nil {
        return
    }
    s.addStartFunc(func(stop <-chan struct{}) error {
        grpcServer := s.secureGrpcServer
        if s.secureGrpcServer == nil {
            grpcServer = s.grpcServer
        }
        // 判断CA类型
        if s.RA != nil {
            log.Infof("Starting RA")
            s.RunCA(grpcServer, s.RA, caOpts)
        } else if s.CA != nil {
            log.Infof("Starting IstioD CA")
            s.RunCA(grpcServer, s.CA, caOpts)
        }
        return nil
    })
}
```

启动

```go
func (s *Server) RunCA(grpc *grpc.Server, ca caserver.CertificateAuthority, opts *caOptions) {
    if !s.EnableCA() {
        return
    }
    if ca == nil {
        // When the CA to run is nil, return
        log.Warn("the CA to run is nil")
        return
    }
    iss := trustedIssuer.Get()
    aud := audience.Get()
    // 读取token
    token, err := ioutil.ReadFile(s.jwtPath)
    if err == nil {
        tok, err := detectAuthEnv(string(token))
        if err != nil {
            log.Warn("Starting with invalid K8S JWT token", err, string(token))
        } else {
            if iss == "" {
                iss = tok.Iss
            }
            if len(tok.Aud) > 0 && len(aud) == 0 {
                aud = tok.Aud[0]
            }
        }
    }

    // CA API使用带有最大工作负载证书TTL的证书。 hostlist必须为非空-在grpc server启动前无法使用。 添加客户端证书身份验证和kube（启用了SDS）
    caServer, startErr := caserver.New(ca, maxWorkloadCertTTL.Get(), opts.Authenticators)
    if startErr != nil {
        log.Fatalf("failed to create istio ca server: %v", startErr)
    }

    // 所有令牌-无需配置两次。 令牌还可以包括群集信息以自动配置网络属性。
    if iss != "" && 
        k8sInCluster.Get() == "" { // not running in cluster - in cluster use direct call to apiserver
        // 如果未在K8S中运行，则使用标准JWT验证添加自定义验证器。在K8S中运行时-我们可以使用内置验证器，该验证器还检查pod移除（无效）。
        oidcAuth, err := authenticate.NewJwtAuthenticator(iss, opts.TrustDomain, aud)
        if err == nil {
            caServer.Authenticators = append(caServer.Authenticators, oidcAuth)
            log.Info("Using out-of-cluster JWT authentication")
        } else {
            log.Info("K8S token doesn't support OIDC, using only in-cluster auth")
        }
    }

    caServer.Register(grpc)

    log.Info("Istiod CA has started")
}
```

caServer需要实现IstioCertificateServiceServer接口

```go
type IstioCertificateServiceServer interface {
    // 使用提供的csr,签发证书
    CreateCertificate(context.Context, *IstioCertificateRequest) (*IstioCertificateResponse, error)
}
```

caServer调用Register

```go
func (s *Server) Register(grpcServer *grpc.Server) {
    pb.RegisterIstioCertificateServiceServer(grpcServer, s)
}
```

具体的实现

```go
func (s *Server) CreateCertificate(ctx context.Context, request *pb.IstioCertificateRequest) (
    *pb.IstioCertificateResponse, error) {
    s.monitoring.CSR.Increment()
    // 获取客户端的身份信息
    caller := s.authenticate(ctx)
    if caller == nil {
        s.monitoring.AuthnError.Increment()
        return nil, status.Error(codes.Unauthenticated, "request authenticate failure")
    }

    // 获取证书链及根证书
    _, _, certChainBytes, rootCertBytes := s.ca.GetCAKeyCertBundle().GetAll()
    // 签发证书
    cert, signErr := s.ca.Sign(
        []byte(request.Csr), caller.Identities, time.Duration(request.ValidityDuration)*time.Second, false)
    if signErr != nil {
        serverCaLog.Errorf("CSR signing error (%v)", signErr.Error())
        s.monitoring.GetCertSignError(signErr.(*caerror.Error).ErrorType()).Increment()
        return nil, status.Errorf(signErr.(*caerror.Error).HTTPErrorCode(), "CSR signing error (%v)", signErr.(*caerror.Error))
    }
    respCertChain := []string{string(cert)}
    if len(certChainBytes) != 0 {
        respCertChain = append(respCertChain, string(certChainBytes))
    }
    respCertChain = append(respCertChain, string(rootCertBytes))
    response := &pb.IstioCertificateResponse{
        CertChain: respCertChain,
    }
    s.monitoring.Success.Increment()
    serverCaLog.Debug("CSR successfully signed.")
    // 返回响应
    return response, nil
}
```

## CertificateAuthority

```go
type CertificateAuthority interface {
    // Sign generates a certificate for a workload or CA, from the given CSR and TTL.
    // TODO(myidpt): simplify this interface and pass a struct with cert field values instead.
    Sign(csrPEM []byte, subjectIDs []string, ttl time.Duration, forCA bool) ([]byte, error)
    // SignWithCertChain is similar to Sign but returns the leaf cert and the entire cert chain.
    SignWithCertChain(csrPEM []byte, subjectIDs []string, ttl time.Duration, forCA bool) ([]byte, error)
    // GetCAKeyCertBundle returns the KeyCertBundle used by CA.
    GetCAKeyCertBundle() util.KeyCertBundle
}
```

### istioca

```go
func (ca *IstioCA) sign(csrPEM []byte, subjectIDs []string, requestedLifetime time.Duration, checkLifetime, forCA bool) ([]byte, error) {
    signingCert, signingKey, _, _ := ca.keyCertBundle.GetAll()
    if signingCert == nil {
        return nil, caerror.NewError(caerror.CANotReady, fmt.Errorf("Istio CA is not ready")) // nolint
    }

    csr, err := util.ParsePemEncodedCSR(csrPEM)
    if err != nil {
        return nil, caerror.NewError(caerror.CSRError, err)
    }

    lifetime := requestedLifetime
    // If the requested requestedLifetime is non-positive, apply the default TTL.
    if requestedLifetime.Seconds() <= 0 {
        lifetime = ca.defaultCertTTL
    }
    // If checkLifetime is set and the requested TTL is greater than maxCertTTL, return an error
    if checkLifetime && requestedLifetime.Seconds() > ca.maxCertTTL.Seconds() {
        return nil, caerror.NewError(caerror.TTLError, fmt.Errorf(
            "requested TTL %s is greater than the max allowed TTL %s", requestedLifetime, ca.maxCertTTL))
    }

    certBytes, err := util.GenCertFromCSR(csr, signingCert, csr.PublicKey, *signingKey, subjectIDs, lifetime, forCA)
    if err != nil {
        return nil, caerror.NewError(caerror.CertGenError, err)
    }

    block := &pem.Block{
        Type:  "CERTIFICATE",
        Bytes: certBytes,
    }
    cert := pem.EncodeToMemory(block)

    return cert, nil
}
```

### istio ra(k8s ra)

传入参数构造k8sra

```go
func (s *Server) createIstioRA(client kubelib.Client,
    opts *caOptions) (ra.RegistrationAuthority, error) {

    caCertFile := path.Join(ra.DefaultExtCACertDir, constants.CACertNamespaceConfigMapDataName)
    if _, err := os.Stat(caCertFile); err != nil {
        caCertFile = defaultCACertPath
    }
    raOpts := &ra.IstioRAOptions{
        ExternalCAType: opts.ExternalCAType,
        DefaultCertTTL: workloadCertTTL.Get(),
        MaxCertTTL:     maxWorkloadCertTTL.Get(),
        CaSigner:       opts.ExternalCASigner,
        CaCertFile:     caCertFile,
        VerifyAppendCA: true,
        K8sClient:      client.CertificatesV1beta1(),
    }
    return ra.NewIstioRA(raOpts)

}
```

校验C类型

```go
func NewIstioRA(opts *IstioRAOptions) (RegistrationAuthority, error) {
    if opts.ExternalCAType == ExtCAK8s {
        istioRA, err := NewKubernetesRA(opts)
        if err != nil {
            return nil, fmt.Errorf("failed to create an K8s CA: %v", err)
        }
        return istioRA, err
    }
    return nil, fmt.Errorf("invalid CA Name %s", opts.ExternalCAType)
}
```

生成istio ra

```go
func NewKubernetesRA(raOpts *IstioRAOptions) (*KubernetesRA, error) {
    keyCertBundle, err := util.NewKeyCertBundleWithRootCertFromFile(raOpts.CaCertFile)
    if err != nil {
        return nil, raerror.NewError(raerror.CAInitFail, fmt.Errorf("error processing Certificate Bundle for Kubernetes RA"))
    }
    istioRA := &KubernetesRA{csrInterface: raOpts.K8sClient,
        raOpts:        raOpts,
        keyCertBundle: keyCertBundle}
    return istioRA, nil
}
```

```go
func (r *KubernetesRA) Sign(csrPEM []byte, subjectIDs []string, requestedLifetime time.Duration, forCA bool) ([]byte, error) {

    if forCA {
        return nil, raerror.NewError(raerror.CSRError, fmt.Errorf(
            "unable to generate CA certifificates"))
    }

    if !ValidateCSR(csrPEM, subjectIDs) {
        return nil, raerror.NewError(raerror.CSRError, fmt.Errorf(
            "unable to validate SAN Identities in CSR"))
    }

    // TODO: Need to pass the lifetime into the CSR.
    /*    If the requested requestedLifetime is non-positive, apply the default TTL.
            lifetime := requestedLifetime
            if requestedLifetime.Seconds() <= 0 {
                lifetime = ra.defaultCertTTL
        }
    */

    // If the requested TTL is greater than maxCertTTL, return an error
    if requestedLifetime.Seconds() > r.raOpts.MaxCertTTL.Seconds() {
        return nil, raerror.NewError(raerror.TTLError, fmt.Errorf(
            "requested TTL %s is greater than the max allowed TTL %s", requestedLifetime, r.raOpts.MaxCertTTL))
    }
    csrName := chiron.GenCsrName()
    return r.kubernetesSign(csrPEM, csrName, r.raOpts.CaCertFile)
}
```

调用csr进行证书签发

```go
func (r *KubernetesRA) kubernetesSign(csrPEM []byte, csrName string, caCertFile string) ([]byte, error) {
    csrSpec := &cert.CertificateSigningRequestSpec{
        SignerName: &r.raOpts.CaSigner,
        Request:    csrPEM,
        Groups:     []string{"system:authenticated"},
        Usages: []cert.KeyUsage{
            cert.UsageDigitalSignature,
            cert.UsageKeyEncipherment,
            cert.UsageServerAuth,
            cert.UsageClientAuth,
        },
    }
    certChain, _, err := chiron.SignCSRK8s(r.csrInterface.CertificateSigningRequests(), csrName, csrSpec, "", caCertFile, false)
    if err != nil {
        return nil, raerror.NewError(raerror.CertGenError, err)
    }
    return certChain, err
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://rocdu.gitbook.io/deep-understanding-of-istio/10/8.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
