istio.io/istio@v0.0.0-20240520182934-d79c90f27776/security/pkg/nodeagent/test/mock/caserver.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package mock 16 17 import ( 18 "context" 19 "encoding/pem" 20 "fmt" 21 "net" 22 "sync" 23 "time" 24 25 "google.golang.org/grpc" 26 "google.golang.org/grpc/codes" 27 ghc "google.golang.org/grpc/health/grpc_health_v1" 28 "google.golang.org/grpc/status" 29 30 pb "istio.io/api/security/v1alpha1" 31 "istio.io/istio/pkg/log" 32 "istio.io/istio/pkg/security" 33 "istio.io/istio/pkg/spiffe" 34 caerror "istio.io/istio/security/pkg/pki/error" 35 "istio.io/istio/security/pkg/pki/util" 36 ) 37 38 var caServerLog = log.RegisterScope("ca", "CA service debugging") 39 40 // CAServer is a mock CA server. 41 type CAServer struct { 42 pb.UnimplementedIstioCertificateServiceServer 43 URL string 44 GRPCServer *grpc.Server 45 Authenticators []security.Authenticator 46 47 certPem []byte 48 keyPem []byte 49 KeyCertBundle *util.KeyCertBundle 50 certLifetime time.Duration 51 52 rejectCSR bool 53 emptyCert bool 54 faultInjectLock *sync.Mutex 55 } 56 57 func NewCAServerWithKeyCert(port int, key, cert []byte, opts ...grpc.ServerOption) (*CAServer, error) { 58 keyCertBundle, err := util.NewVerifiedKeyCertBundleFromPem(cert, key, nil, cert) 59 if err != nil { 60 caServerLog.Errorf("failed to create CA KeyCertBundle: %+v", err) 61 return nil, err 62 } 63 64 server := &CAServer{ 65 certPem: cert, 66 keyPem: key, 67 certLifetime: 24 * time.Hour, 68 KeyCertBundle: keyCertBundle, 69 GRPCServer: grpc.NewServer(opts...), 70 faultInjectLock: &sync.Mutex{}, 71 } 72 // Register CA service at gRPC server. 73 pb.RegisterIstioCertificateServiceServer(server.GRPCServer, server) 74 ghc.RegisterHealthServer(server.GRPCServer, server) 75 return server, server.start(port) 76 } 77 78 // NewCAServer creates a new CA server that listens on port. 79 func NewCAServer(port int, opts ...grpc.ServerOption) (*CAServer, error) { 80 // Create root cert and private key. 81 options := util.CertOptions{ 82 TTL: 3650 * 24 * time.Hour, 83 Org: spiffe.GetTrustDomain(), 84 IsCA: true, 85 IsSelfSigned: true, 86 RSAKeySize: 2048, 87 IsDualUse: true, 88 } 89 cert, key, err := util.GenCertKeyFromOptions(options) 90 if err != nil { 91 caServerLog.Errorf("cannot create CA cert and private key: %+v", err) 92 return nil, err 93 } 94 return NewCAServerWithKeyCert(port, key, cert, opts...) 95 } 96 97 func (s *CAServer) start(port int) error { 98 listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port)) 99 if err != nil { 100 caServerLog.Errorf("cannot listen on port %d (error: %v)", port, err) 101 return err 102 } 103 104 // If passed in port is 0, get the actual chosen port. 105 port = listener.Addr().(*net.TCPAddr).Port 106 s.URL = fmt.Sprintf("localhost:%d", port) 107 go func() { 108 caServerLog.Infof("start CA server on %s", s.URL) 109 if err := s.GRPCServer.Serve(listener); err != nil && (err != grpc.ErrServerStopped) { 110 caServerLog.Errorf("CA Server failed to serve in %q: %v", s.URL, err) 111 } 112 }() 113 return nil 114 } 115 116 // RejectCSR specifies whether to send error response to CSR. 117 func (s *CAServer) RejectCSR(reject bool) { 118 s.faultInjectLock.Lock() 119 s.rejectCSR = reject 120 s.faultInjectLock.Unlock() 121 if reject { 122 caServerLog.Info("force CA server to return error to CSR") 123 } 124 } 125 126 func (s *CAServer) shouldReject() bool { 127 var reject bool 128 s.faultInjectLock.Lock() 129 reject = s.rejectCSR 130 s.faultInjectLock.Unlock() 131 return reject 132 } 133 134 // SendEmptyCert force CA server send empty cert chain. 135 func (s *CAServer) SendEmptyCert() { 136 s.faultInjectLock.Lock() 137 s.emptyCert = true 138 s.faultInjectLock.Unlock() 139 caServerLog.Info("force CA server to send empty cert chain") 140 } 141 142 func (s *CAServer) sendEmpty() bool { 143 var empty bool 144 s.faultInjectLock.Lock() 145 empty = s.emptyCert 146 s.faultInjectLock.Unlock() 147 return empty 148 } 149 150 // CreateCertificate handles CSR. 151 func (s *CAServer) CreateCertificate(ctx context.Context, request *pb.IstioCertificateRequest) ( 152 *pb.IstioCertificateResponse, error, 153 ) { 154 caServerLog.Infof("received CSR request") 155 if s.shouldReject() { 156 caServerLog.Info("force rejecting CSR request") 157 return nil, status.Error(codes.Unavailable, "CA server is not available") 158 } 159 if s.sendEmpty() { 160 caServerLog.Info("force sending empty cert chain in CSR response") 161 response := &pb.IstioCertificateResponse{ 162 CertChain: []string{}, 163 } 164 return response, nil 165 } 166 id := []string{"client-identity"} 167 if len(s.Authenticators) > 0 { 168 caller, err := security.Authenticate(ctx, s.Authenticators) 169 if caller == nil || err != nil { 170 return nil, status.Error(codes.Unauthenticated, "request authenticate failure") 171 } 172 id = caller.Identities 173 } 174 cert, err := s.sign([]byte(request.Csr), id, time.Duration(request.ValidityDuration)*time.Second, false) 175 if err != nil { 176 caServerLog.Errorf("failed to sign CSR: %+v", err) 177 return nil, status.Errorf(err.(*caerror.Error).HTTPErrorCode(), "CSR signing error: %+v", err.(*caerror.Error)) 178 } 179 respCertChain := []string{string(cert)} 180 respCertChain = append(respCertChain, string(s.certPem)) 181 response := &pb.IstioCertificateResponse{ 182 CertChain: respCertChain, 183 } 184 caServerLog.Info("send back CSR success response") 185 return response, nil 186 } 187 188 func (s *CAServer) sign(csrPEM []byte, subjectIDs []string, _ time.Duration, forCA bool) ([]byte, error) { 189 csr, err := util.ParsePemEncodedCSR(csrPEM) 190 if err != nil { 191 caServerLog.Errorf("failed to parse CSR: %+v", err) 192 return nil, caerror.NewError(caerror.CSRError, err) 193 } 194 signingCert, signingKey, _, _ := s.KeyCertBundle.GetAll() 195 certBytes, err := util.GenCertFromCSR(csr, signingCert, csr.PublicKey, *signingKey, subjectIDs, s.certLifetime, forCA) 196 if err != nil { 197 caServerLog.Errorf("failed to generate cert from CSR: %+v", err) 198 return nil, caerror.NewError(caerror.CertGenError, err) 199 } 200 block := &pem.Block{ 201 Type: "CERTIFICATE", 202 Bytes: certBytes, 203 } 204 cert := pem.EncodeToMemory(block) 205 206 return cert, nil 207 } 208 209 // Check handles health check requests. 210 func (s *CAServer) Check(ctx context.Context, in *ghc.HealthCheckRequest) (*ghc.HealthCheckResponse, error) { 211 return &ghc.HealthCheckResponse{ 212 Status: ghc.HealthCheckResponse_SERVING, 213 }, nil 214 } 215 216 // Watch handles health check streams. 217 func (s *CAServer) Watch(_ *ghc.HealthCheckRequest, _ ghc.Health_WatchServer) error { 218 return nil 219 }