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  }