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