...

Source file src/github.com/linkerd/linkerd2/pkg/identity/service.go

Documentation: github.com/linkerd/linkerd2/pkg/identity

     1  package identity
     2  
     3  import (
     4  	"context"
     5  	"crypto/sha256"
     6  	"crypto/x509"
     7  	"encoding/hex"
     8  	"errors"
     9  	"fmt"
    10  	"strings"
    11  	"sync"
    12  	"time"
    13  
    14  	v1 "k8s.io/api/core/v1"
    15  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    16  	"k8s.io/apimachinery/pkg/runtime"
    17  
    18  	pb "github.com/linkerd/linkerd2-proxy-api/go/identity"
    19  	"github.com/linkerd/linkerd2/pkg/tls"
    20  	log "github.com/sirupsen/logrus"
    21  	"google.golang.org/grpc"
    22  	"google.golang.org/grpc/codes"
    23  	"google.golang.org/grpc/status"
    24  	"google.golang.org/protobuf/types/known/timestamppb"
    25  )
    26  
    27  const (
    28  	// DefaultIssuanceLifetime is the default lifetime of certificates issued by
    29  	// the identity service.
    30  	DefaultIssuanceLifetime = 24 * time.Hour
    31  
    32  	// EnvTrustAnchors is the environment variable holding the trust anchors for
    33  	// the proxy identity.
    34  	EnvTrustAnchors         = "LINKERD2_PROXY_IDENTITY_TRUST_ANCHORS"
    35  	eventTypeSkipped        = "IssuerUpdateSkipped"
    36  	eventTypeUpdated        = "IssuerUpdated"
    37  	eventTypeFailed         = "IssuerValidationFailed"
    38  	eventTypeIssuedLeafCert = "IssuedLeafCertificate"
    39  )
    40  
    41  type (
    42  	// Service implements the gRPC service in terms of a Validator and Issuer.
    43  	Service struct {
    44  		pb.UnimplementedIdentityServer
    45  		validator    Validator
    46  		trustAnchors *x509.CertPool
    47  		issuer       *tls.Issuer
    48  		issuerMutex  *sync.RWMutex
    49  		validity     *tls.Validity
    50  		recordEvent  func(parent runtime.Object, eventType, reason, message string)
    51  
    52  		expectedName, issuerPathCrt, issuerPathKey string
    53  	}
    54  
    55  	// Validator implementors accept a bearer token, validates it, and returns a
    56  	// DNS-form identity.
    57  	Validator interface {
    58  		// Validate takes an opaque authentication token, attempts to validate its
    59  		// authenticity, and produces a DNS-like identifier.
    60  		//
    61  		// An InvalidToken error should be returned if the provided token was not in a
    62  		// correct form.
    63  		//
    64  		// A NotAuthenticated error should be returned if the authenticity of the
    65  		// token cannot be validated.
    66  		Validate(context.Context, []byte) (string, error)
    67  	}
    68  
    69  	// InvalidToken is an error type returned by Validators to indicate that the
    70  	// provided authentication token was not valid.
    71  	InvalidToken struct{ Reason string }
    72  
    73  	// NotAuthenticated is an error type returned by Validators to indicate that the
    74  	// provided authentication token could not be authenticated.
    75  	NotAuthenticated struct{}
    76  )
    77  
    78  // Initialize loads the issuer certs from disk so it can start service CSRs to proxies
    79  func (svc *Service) Initialize() error {
    80  	credentials, err := svc.loadCredentials()
    81  	if err != nil {
    82  		return err
    83  	}
    84  	svc.updateIssuer(credentials)
    85  	return nil
    86  }
    87  
    88  func (svc *Service) updateIssuer(newIssuer tls.Issuer) {
    89  	svc.issuerMutex.Lock()
    90  	svc.issuer = &newIssuer
    91  	log.Debug("Issuer has been updated")
    92  	svc.issuerMutex.Unlock()
    93  }
    94  
    95  // Run reads from the issuer and error channels and reloads the issuer certs when necessary
    96  func (svc *Service) Run(issuerEvent <-chan struct{}, issuerError <-chan error) {
    97  	for {
    98  		select {
    99  		case <-issuerEvent:
   100  			if err := svc.Initialize(); err != nil {
   101  				message := fmt.Sprintf("Skipping issuer update as certs could not be read from disk: %s", err)
   102  				log.Warn(message)
   103  				svc.recordEvent(nil, v1.EventTypeWarning, eventTypeSkipped, message)
   104  			} else {
   105  				message := "Updated identity issuer"
   106  				log.Infof(message)
   107  				svc.recordEvent(nil, v1.EventTypeNormal, eventTypeUpdated, message)
   108  			}
   109  		case err := <-issuerError:
   110  			log.Warnf("Received error from fs watcher: %s", err)
   111  		}
   112  	}
   113  }
   114  
   115  func (svc *Service) loadCredentials() (tls.Issuer, error) {
   116  	creds, err := tls.ReadPEMCreds(
   117  		svc.issuerPathKey,
   118  		svc.issuerPathCrt,
   119  	)
   120  
   121  	if err != nil {
   122  		return nil, fmt.Errorf("failed to read CA from disk: %w", err)
   123  	}
   124  
   125  	// Don't verify with dns name as this is not a leaf certificate
   126  	if err := creds.Crt.Verify(svc.trustAnchors, "", time.Time{}); err != nil {
   127  		return nil, fmt.Errorf("failed to verify issuer credentials for '%s' with trust anchors: %w", svc.expectedName, err)
   128  	}
   129  
   130  	if !creds.Certificate.IsCA {
   131  		return nil, fmt.Errorf("failed to verify issuer certificate: it must be an intermediate-CA, but it is not")
   132  	}
   133  
   134  	log.Debugf("Loaded issuer cert: %s", creds.EncodeCertificatePEM())
   135  	return tls.NewCA(*creds, *svc.validity), nil
   136  }
   137  
   138  // NewService creates a new identity service.
   139  func NewService(validator Validator, trustAnchors *x509.CertPool, validity *tls.Validity, recordEvent func(parent runtime.Object, eventType, reason, message string), expectedName, issuerPathCrt, issuerPathKey string) *Service {
   140  	return &Service{
   141  		pb.UnimplementedIdentityServer{},
   142  		validator,
   143  		trustAnchors,
   144  		nil,
   145  		&sync.RWMutex{},
   146  		validity,
   147  		recordEvent,
   148  		expectedName,
   149  		issuerPathCrt,
   150  		issuerPathKey,
   151  	}
   152  }
   153  
   154  // Register registers an identity service implementation in the provided gRPC
   155  // server.
   156  func Register(g *grpc.Server, s *Service) {
   157  	pb.RegisterIdentityServer(g, s)
   158  }
   159  
   160  // ensureIssuerStillValid should check that the CA is still good time wise
   161  // and verifies just fine with the provided trust anchors
   162  func (svc *Service) ensureIssuerStillValid() error {
   163  	issuer := *svc.issuer
   164  	switch is := issuer.(type) {
   165  	case *tls.CA:
   166  		// Don't verify with dns name as this is not a leaf certificate
   167  		return is.Cred.Verify(svc.trustAnchors, "", time.Time{})
   168  	default:
   169  		return fmt.Errorf("unsupported issuer type. Expected *tls.CA, got %v", is)
   170  	}
   171  }
   172  
   173  // Certify validates identity and signs certificates.
   174  func (svc *Service) Certify(ctx context.Context, req *pb.CertifyRequest) (*pb.CertifyResponse, error) {
   175  	svc.issuerMutex.RLock()
   176  	defer svc.issuerMutex.RUnlock()
   177  
   178  	if svc.issuer == nil {
   179  		log.Warn("Certificate issuer is not ready")
   180  		return nil, status.Error(codes.Unavailable, "cert issuer not ready yet")
   181  	}
   182  
   183  	// Extract the relevant info from the request.
   184  	reqIdentity, tok, csr, err := checkRequest(req)
   185  	if err != nil {
   186  		return nil, status.Error(codes.InvalidArgument, err.Error())
   187  	}
   188  
   189  	if err := svc.ensureIssuerStillValid(); err != nil {
   190  		log.Errorf("could not process CSR because of CA cert validation failure: %s - CSR Identity : %s", err, reqIdentity)
   191  		message := fmt.Sprintf("%s - CSR Identity : %s", err.Error(), reqIdentity)
   192  		svc.recordEvent(nil, v1.EventTypeWarning, eventTypeFailed, message)
   193  		return nil, err
   194  	}
   195  
   196  	if err = checkCSR(csr, reqIdentity); err != nil {
   197  		log.Debugf("requester sent invalid CSR: %s", err)
   198  		return nil, status.Error(codes.FailedPrecondition, err.Error())
   199  	}
   200  
   201  	// Authenticate the provided token against the Kubernetes API.
   202  	log.Debugf("Validating token for %s", reqIdentity)
   203  	tokIdentity, err := svc.validator.Validate(ctx, tok)
   204  	if err != nil {
   205  		var nae NotAuthenticated
   206  		if errors.As(err, &nae) {
   207  			log.Infof("authentication failed for %s: %s", reqIdentity, nae)
   208  			return nil, status.Error(codes.FailedPrecondition, nae.Error())
   209  		}
   210  		var ite InvalidToken
   211  		if errors.As(err, &ite) {
   212  			log.Infof("invalid token provided for %s: %s", reqIdentity, ite)
   213  			return nil, status.Error(codes.InvalidArgument, ite.Error())
   214  		}
   215  
   216  		msg := fmt.Sprintf("error validating token for %s: %s", reqIdentity, err)
   217  		log.Error(msg)
   218  		return nil, status.Error(codes.Internal, msg)
   219  	}
   220  
   221  	// Ensure the requested identity matches the token's identity.
   222  	if reqIdentity != tokIdentity {
   223  		msg := fmt.Sprintf("requested identity did not match provided token: requested=%s; found=%s",
   224  			reqIdentity, tokIdentity)
   225  		log.Info(msg)
   226  		return nil, status.Error(codes.FailedPrecondition, msg)
   227  	}
   228  
   229  	// Create a certificate
   230  	issuer := *svc.issuer
   231  	crt, err := issuer.IssueEndEntityCrt(csr)
   232  	if err != nil {
   233  		return nil, status.Error(codes.Internal, err.Error())
   234  	}
   235  	crts := crt.ExtractRaw()
   236  	if len(crts) == 0 {
   237  		//nolint:gocritic
   238  		log.Fatal("the issuer provided a certificate without key material")
   239  	}
   240  	validUntil := timestamppb.New(crt.Certificate.NotAfter)
   241  
   242  	hasher := sha256.New()
   243  	hasher.Write(crts[0])
   244  	hash := hex.EncodeToString(hasher.Sum(nil))
   245  	identitySegments := strings.Split(tokIdentity, ".")
   246  	msg := fmt.Sprintf("issued certificate for %s until %s: %s", tokIdentity, crt.Certificate.NotAfter, hash)
   247  	sa := v1.ServiceAccount{
   248  		ObjectMeta: metav1.ObjectMeta{
   249  			Name:      identitySegments[0],
   250  			Namespace: identitySegments[1],
   251  		},
   252  	}
   253  	svc.recordEvent(&sa, v1.EventTypeNormal, eventTypeIssuedLeafCert, msg)
   254  	log.Info(msg)
   255  
   256  	// Bundle issuer crt with certificate so the trust path to the root can be verified.
   257  	rsp := &pb.CertifyResponse{
   258  		LeafCertificate:          crts[0],
   259  		IntermediateCertificates: crts[1:],
   260  
   261  		ValidUntil: validUntil,
   262  	}
   263  	return rsp, nil
   264  }
   265  
   266  func checkRequest(req *pb.CertifyRequest) (string, []byte, *x509.CertificateRequest, error) {
   267  	reqIdentity := req.GetIdentity()
   268  	if reqIdentity == "" {
   269  		return "", nil, nil, errors.New("missing identity")
   270  	}
   271  
   272  	tok := req.GetToken()
   273  	if len(tok) == 0 {
   274  		return "", nil, nil, errors.New("missing token")
   275  	}
   276  
   277  	der := req.GetCertificateSigningRequest()
   278  	if len(der) == 0 {
   279  		return "", nil, nil,
   280  			errors.New("missing certificate signing request")
   281  	}
   282  	csr, err := x509.ParseCertificateRequest(der)
   283  	if err != nil {
   284  		return "", nil, nil, err
   285  	}
   286  
   287  	return reqIdentity, tok, csr, nil
   288  }
   289  
   290  func checkCSR(csr *x509.CertificateRequest, identity string) error {
   291  	if len(csr.DNSNames) != 1 {
   292  		return errors.New("CSR must have exactly one DNSName")
   293  	}
   294  	if csr.DNSNames[0] != identity {
   295  		return fmt.Errorf("CSR name does not match requested identity: csr=%s; req=%s", csr.DNSNames[0], identity)
   296  	}
   297  
   298  	switch csr.Subject.CommonName {
   299  	case "", identity:
   300  	default:
   301  		return fmt.Errorf("invalid CommonName: %s", csr.Subject.CommonName)
   302  	}
   303  
   304  	if len(csr.EmailAddresses) > 0 {
   305  		return errors.New("cannot validate email addresses")
   306  	}
   307  	if len(csr.IPAddresses) > 0 {
   308  		return errors.New("cannot validate IP addresses")
   309  	}
   310  	if len(csr.URIs) > 0 {
   311  		return errors.New("cannot validate URIs")
   312  	}
   313  
   314  	return nil
   315  }
   316  
   317  func (NotAuthenticated) Error() string {
   318  	return "authentication token could not be authenticated"
   319  }
   320  
   321  func (e InvalidToken) Error() string {
   322  	return e.Reason
   323  }
   324  

View as plain text