github.com/cilium/cilium@v1.16.2/pkg/auth/spire/certificate_provider.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package spire 5 6 import ( 7 "crypto/tls" 8 "crypto/x509" 9 "errors" 10 "fmt" 11 "strings" 12 13 "github.com/cilium/cilium/api/v1/models" 14 "github.com/cilium/cilium/pkg/auth/certs" 15 "github.com/cilium/cilium/pkg/identity" 16 ) 17 18 // This file implements the CertificateProvider interface 19 20 func (s *SpireDelegateClient) GetTrustBundle() (*x509.CertPool, error) { 21 if s.trustBundle == nil { 22 return nil, errors.New("trust bundle not yet available") 23 } 24 return s.trustBundle, nil 25 } 26 27 func (s *SpireDelegateClient) GetCertificateForIdentity(id identity.NumericIdentity) (*tls.Certificate, error) { 28 spiffeID := s.sniToSPIFFEID(id) 29 s.svidStoreMutex.RLock() 30 svid, ok := s.svidStore[spiffeID] 31 s.svidStoreMutex.RUnlock() 32 if !ok { 33 return nil, fmt.Errorf("no SPIFFE ID for %s", spiffeID) 34 } 35 36 if len(svid.X509Svid.CertChain) == 0 { 37 return nil, fmt.Errorf("no certificate chain inside %s", spiffeID) 38 } 39 40 var leafCert *x509.Certificate 41 for _, cert := range svid.X509Svid.CertChain { 42 cert, err := x509.ParseCertificate(cert) 43 if err != nil { 44 return nil, fmt.Errorf("failed to parse certificate: %w", err) 45 } 46 47 if !cert.IsCA { 48 leafCert = cert 49 break 50 } 51 } 52 if leafCert == nil { 53 return nil, fmt.Errorf("no leaf certificate inside %s", spiffeID) 54 } 55 56 privKey, err := x509.ParsePKCS8PrivateKey(svid.X509SvidKey) 57 if err != nil { 58 return nil, fmt.Errorf("failed to parse private keyof %s: %w", spiffeID, err) 59 } 60 61 return &tls.Certificate{ 62 Certificate: svid.X509Svid.CertChain, 63 PrivateKey: privKey, 64 Leaf: leafCert, 65 }, nil 66 } 67 68 func (s *SpireDelegateClient) sniToSPIFFEID(id identity.NumericIdentity) string { 69 return "spiffe://" + s.cfg.SpiffeTrustDomain + "/identity/" + id.String() 70 } 71 72 func (s *SpireDelegateClient) spiffeIDToNumericIdentity(spiffeID string) (identity.NumericIdentity, error) { 73 prefix := "spiffe://" + s.cfg.SpiffeTrustDomain + "/identity/" 74 if !strings.HasPrefix(spiffeID, prefix) { 75 return 0, fmt.Errorf("SPIFFE ID %s does not belong to our trust domain or is not in the valid format", spiffeID) 76 } 77 78 idStr := strings.TrimPrefix(spiffeID, prefix) 79 return identity.ParseNumericIdentity(idStr) 80 } 81 82 func (s *SpireDelegateClient) ValidateIdentity(id identity.NumericIdentity, cert *x509.Certificate) (bool, error) { 83 spiffeID := s.sniToSPIFFEID(id) 84 85 // Spec: SVIDs containing more than one URI SAN MUST be rejected 86 if len(cert.URIs) != 1 { 87 return false, errors.New("SPIFFE IDs must have exactly one URI SAN") 88 } 89 90 return cert.URIs[0].String() == spiffeID, nil 91 } 92 93 func (s *SpireDelegateClient) NumericIdentityToSNI(id identity.NumericIdentity) string { 94 return id.String() + "." + s.cfg.SpiffeTrustDomain 95 } 96 97 func (s *SpireDelegateClient) SNIToNumericIdentity(sni string) (identity.NumericIdentity, error) { 98 suffix := "." + s.cfg.SpiffeTrustDomain 99 if !strings.HasSuffix(sni, suffix) { 100 return 0, fmt.Errorf("SNI %s does not belong to our trust domain", sni) 101 } 102 103 idStr := strings.TrimSuffix(sni, suffix) 104 return identity.ParseNumericIdentity(idStr) 105 } 106 107 func (s *SpireDelegateClient) SubscribeToRotatedIdentities() <-chan certs.CertificateRotationEvent { 108 return s.rotatedIdentitiesChan 109 } 110 111 func (s *SpireDelegateClient) Status() *models.Status { 112 s.connectedMutex.RLock() 113 defer s.connectedMutex.RUnlock() 114 if !s.connected { 115 msg := "Not connected to SPIRE server" 116 if s.lastConnectError != nil { 117 msg = fmt.Sprintf("Cannot connect to SPIRE server: %q", s.lastConnectError) 118 } 119 120 return &models.Status{ 121 State: models.StatusStateFailure, 122 Msg: msg, 123 } 124 } 125 return &models.Status{ 126 State: models.StatusStateOk, 127 } 128 }