...

Source file src/github.com/linkerd/linkerd2/pkg/tls/creds_watcher.go

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

     1  package tls
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"fmt"
     7  	"path/filepath"
     8  	"sync/atomic"
     9  
    10  	"github.com/fsnotify/fsnotify"
    11  	log "github.com/sirupsen/logrus"
    12  )
    13  
    14  const dataDirectoryLnName = "..data"
    15  
    16  // FsCredsWatcher is used to monitor tls credentials on the filesystem
    17  type FsCredsWatcher struct {
    18  	certRootPath string
    19  	certFilePath string
    20  	keyFilePath  string
    21  	EventChan    chan<- struct{}
    22  	ErrorChan    chan<- error
    23  }
    24  
    25  // NewFsCredsWatcher constructs a FsCredsWatcher instance
    26  func NewFsCredsWatcher(certRootPath string, updateEvent chan<- struct{}, errEvent chan<- error) *FsCredsWatcher {
    27  	return &FsCredsWatcher{certRootPath, "", "", updateEvent, errEvent}
    28  }
    29  
    30  // WithFilePaths completes the FsCredsWatcher instance with the cert and key files locations
    31  func (fscw *FsCredsWatcher) WithFilePaths(certFilePath, keyFilePath string) *FsCredsWatcher {
    32  	fscw.certFilePath = certFilePath
    33  	fscw.keyFilePath = keyFilePath
    34  	return fscw
    35  }
    36  
    37  // StartWatching starts watching the filesystem for cert updates
    38  func (fscw *FsCredsWatcher) StartWatching(ctx context.Context) error {
    39  	watcher, err := fsnotify.NewWatcher()
    40  	if err != nil {
    41  		return err
    42  	}
    43  	defer watcher.Close()
    44  
    45  	// no point of proceeding if we fail to watch this
    46  	if err := watcher.Add(fscw.certRootPath); err != nil {
    47  		return err
    48  	}
    49  
    50  LOOP:
    51  	for {
    52  		select {
    53  		case event := <-watcher.Events:
    54  			log.Debugf("Received event: %v", event)
    55  			// Watching the folder for create events as this indicates
    56  			// that the secret has been updated.
    57  			if event.Op&fsnotify.Create == fsnotify.Create &&
    58  				event.Name == filepath.Join(fscw.certRootPath, dataDirectoryLnName) {
    59  				fscw.EventChan <- struct{}{}
    60  			}
    61  		case err := <-watcher.Errors:
    62  			fscw.ErrorChan <- err
    63  			log.Warnf("Error while watching %s: %s", fscw.certRootPath, err)
    64  			break LOOP
    65  		case <-ctx.Done():
    66  			if err := ctx.Err(); err != nil {
    67  				fscw.ErrorChan <- err
    68  			}
    69  			break LOOP
    70  		}
    71  	}
    72  
    73  	return nil
    74  }
    75  
    76  // UpdateCert reads the cert and key files and stores the key pair in certVal
    77  func (fscw *FsCredsWatcher) UpdateCert(certVal *atomic.Value) error {
    78  	creds, err := ReadPEMCreds(fscw.keyFilePath, fscw.certFilePath)
    79  	if err != nil {
    80  		return fmt.Errorf("failed to read cert from disk: %w", err)
    81  	}
    82  
    83  	certPEM := creds.EncodePEM()
    84  	keyPEM := creds.EncodePrivateKeyPEM()
    85  	cert, err := tls.X509KeyPair([]byte(certPEM), []byte(keyPEM))
    86  	if err != nil {
    87  		return err
    88  	}
    89  	certVal.Store(&cert)
    90  	return nil
    91  }
    92  
    93  // ProcessEvents reads from the update and error channels and reloads the certs when necessary
    94  func (fscw *FsCredsWatcher) ProcessEvents(
    95  	log *log.Entry,
    96  	certVal *atomic.Value,
    97  	updateEvent <-chan struct{},
    98  	errEvent <-chan error,
    99  ) {
   100  	for {
   101  		select {
   102  		case <-updateEvent:
   103  			if err := fscw.UpdateCert(certVal); err != nil {
   104  				log.Warnf("Skipping update as cert could not be read from disk: %s", err)
   105  			} else {
   106  				log.Infof("Updated certificate")
   107  			}
   108  		case err := <-errEvent:
   109  			log.Warnf("Received error from fs watcher: %s", err)
   110  		}
   111  	}
   112  }
   113  

View as plain text