github.com/nginxinc/kubernetes-ingress@v1.12.5/internal/k8s/spiffe.go (about)

     1  package k8s
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/golang/glog"
    10  	"github.com/spiffe/go-spiffe/workload"
    11  )
    12  
    13  type spiffeController struct {
    14  	watcher *spiffeWatcher
    15  	client  *workload.X509SVIDClient
    16  }
    17  
    18  // NewSpiffeController creates the spiffeWatcher and the Spiffe Workload API Client,
    19  // returns an error if the client cannot connect to the Spire Agent.
    20  func NewSpiffeController(sync func(*workload.X509SVIDs), spireAgentAddr string) (*spiffeController, error) {
    21  	watcher := &spiffeWatcher{sync: sync}
    22  	client, err := workload.NewX509SVIDClient(watcher, workload.WithAddr("unix://"+spireAgentAddr))
    23  	if err != nil {
    24  		return nil, fmt.Errorf("failed to create Spiffe Workload API Client: %w", err)
    25  	}
    26  	sc := &spiffeController{
    27  		watcher: watcher,
    28  		client:  client,
    29  	}
    30  	return sc, nil
    31  }
    32  
    33  // Start starts the Spiffe Workload API Client and waits for the Spiffe certs to be written to disk.
    34  // If the certs are not available after 30 seconds an error is returned.
    35  // On success, calls onStart function and kicks off the Spiffe Controller's run loop.
    36  func (sc *spiffeController) Start(stopCh <-chan struct{}, onStart func()) error {
    37  	glog.V(3).Info("Starting SPIFFE Workload API Client")
    38  	err := sc.client.Start()
    39  	if err != nil {
    40  		return fmt.Errorf("failed to start Spiffe Workload API Client: %w", err)
    41  	}
    42  	timeout := time.After(30 * time.Second)
    43  	duration := 100 * time.Millisecond
    44  	for {
    45  		if sc.watcher.synced {
    46  			glog.V(3).Info("initial SPIFFE trust bundle written to disk")
    47  			break
    48  		}
    49  		select {
    50  		case <-timeout:
    51  			return errors.New("timed out waiting for SPIFFE trust bundle")
    52  		case <-stopCh:
    53  			return sc.client.Stop()
    54  		default:
    55  			break
    56  		}
    57  		time.Sleep(duration)
    58  	}
    59  	onStart()
    60  	go sc.Run(stopCh)
    61  	return nil
    62  }
    63  
    64  // Run waits until a message is sent on the stop channel and stops the Spiffe Workload API Client.
    65  func (sc *spiffeController) Run(stopCh <-chan struct{}) {
    66  	<-stopCh
    67  	err := sc.client.Stop()
    68  	if err != nil {
    69  		glog.Errorf("failed to stop Spiffe Workload API Client: %v", err)
    70  	}
    71  }
    72  
    73  // spiffeWatcher is a sample implementation of the workload.X509SVIDWatcher interface
    74  type spiffeWatcher struct {
    75  	sync   func(*workload.X509SVIDs)
    76  	synced bool
    77  }
    78  
    79  // UpdateX509SVIDs is run every time an SVID is updated
    80  func (w *spiffeWatcher) UpdateX509SVIDs(svids *workload.X509SVIDs) {
    81  	for _, svid := range svids.SVIDs {
    82  		glog.V(3).Infof("SVID updated for spiffeID: %q", svid.SPIFFEID)
    83  	}
    84  	w.sync(svids)
    85  	w.synced = true
    86  }
    87  
    88  // OnError is run when the client runs into an error
    89  func (w *spiffeWatcher) OnError(err error) {
    90  	if strings.Contains(err.Error(), "PermissionDenied") {
    91  		glog.V(3).Infof("X509SVIDClient still waiting for certificates: %v", err)
    92  		return
    93  	}
    94  	glog.Fatal(err)
    95  }