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
    })
}

启动

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接口

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

caServer调用Register

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

具体的实现

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

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

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

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类型

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

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
}
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进行证书签发

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
}

Last updated