github.com/cilium/cilium@v1.16.2/pkg/auth/manager.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package auth
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  
    10  	"github.com/sirupsen/logrus"
    11  
    12  	"github.com/cilium/cilium/api/v1/models"
    13  	"github.com/cilium/cilium/pkg/auth/certs"
    14  	"github.com/cilium/cilium/pkg/datapath/types"
    15  	"github.com/cilium/cilium/pkg/identity"
    16  	"github.com/cilium/cilium/pkg/lock"
    17  	"github.com/cilium/cilium/pkg/maps/authmap"
    18  	"github.com/cilium/cilium/pkg/policy"
    19  	"github.com/cilium/cilium/pkg/time"
    20  )
    21  
    22  // signalAuthKey used in the signalmap. Must reflect struct auth_key in the datapath
    23  type signalAuthKey authmap.AuthKey
    24  
    25  // low-cardinality stringer for metrics
    26  func (key signalAuthKey) String() string {
    27  	return policy.AuthType(key.AuthType).String()
    28  }
    29  
    30  type AuthManager struct {
    31  	logger                logrus.FieldLogger
    32  	nodeIDHandler         types.NodeIDHandler
    33  	authHandlers          map[policy.AuthType]authHandler
    34  	authmap               authMapCacher
    35  	authSignalBackoffTime time.Duration
    36  
    37  	mutex                    lock.Mutex
    38  	pending                  map[authKey]struct{}
    39  	handleAuthenticationFunc func(a *AuthManager, k authKey, reAuth bool)
    40  }
    41  
    42  // authHandler is responsible to handle authentication for a specific auth type
    43  type authHandler interface {
    44  	authenticate(*authRequest) (*authResponse, error)
    45  	authType() policy.AuthType
    46  	subscribeToRotatedIdentities() <-chan certs.CertificateRotationEvent
    47  	certProviderStatus() *models.Status
    48  }
    49  
    50  type authRequest struct {
    51  	localIdentity  identity.NumericIdentity
    52  	remoteIdentity identity.NumericIdentity
    53  	remoteNodeIP   string
    54  }
    55  
    56  type authResponse struct {
    57  	expirationTime time.Time
    58  }
    59  
    60  func newAuthManager(logger logrus.FieldLogger, authHandlers []authHandler, authmap authMapCacher, nodeIDHandler types.NodeIDHandler, authSignalBackoffTime time.Duration) (*AuthManager, error) {
    61  	ahs := map[policy.AuthType]authHandler{}
    62  	for _, ah := range authHandlers {
    63  		if ah == nil {
    64  			continue
    65  		}
    66  		if _, ok := ahs[ah.authType()]; ok {
    67  			return nil, fmt.Errorf("multiple handlers for auth type: %s", ah.authType())
    68  		}
    69  		ahs[ah.authType()] = ah
    70  	}
    71  
    72  	return &AuthManager{
    73  		logger:                   logger,
    74  		authHandlers:             ahs,
    75  		authmap:                  authmap,
    76  		nodeIDHandler:            nodeIDHandler,
    77  		pending:                  make(map[authKey]struct{}),
    78  		handleAuthenticationFunc: handleAuthentication,
    79  		authSignalBackoffTime:    authSignalBackoffTime,
    80  	}, nil
    81  }
    82  
    83  // handleAuthRequest receives auth required signals and spawns a new go routine for each authentication request.
    84  func (a *AuthManager) handleAuthRequest(_ context.Context, key signalAuthKey) error {
    85  	k := authKey{
    86  		localIdentity:  identity.NumericIdentity(key.LocalIdentity),
    87  		remoteIdentity: identity.NumericIdentity(key.RemoteIdentity),
    88  		remoteNodeID:   key.RemoteNodeID,
    89  		authType:       policy.AuthType(key.AuthType),
    90  	}
    91  
    92  	if k.localIdentity.IsReservedIdentity() || k.remoteIdentity.IsReservedIdentity() {
    93  		a.logger.
    94  			WithField("key", k).
    95  			Info("Reserved identity, skipping authentication as reserved identities are not compatible with authentication")
    96  		return nil
    97  	}
    98  
    99  	a.logger.
   100  		WithField("key", k).
   101  		Debug("Handle authentication request")
   102  
   103  	a.handleAuthenticationFunc(a, k, false)
   104  
   105  	return nil
   106  }
   107  
   108  func (a *AuthManager) handleCertificateRotationEvent(_ context.Context, event certs.CertificateRotationEvent) error {
   109  	a.logger.
   110  		WithField("identity", event.Identity).
   111  		Debug("Handle certificate rotation event")
   112  
   113  	all, err := a.authmap.All()
   114  	if err != nil {
   115  		return fmt.Errorf("failed to get all auth map entries: %w", err)
   116  	}
   117  
   118  	for k := range all {
   119  		if k.localIdentity == event.Identity || k.remoteIdentity == event.Identity {
   120  			if event.Deleted {
   121  				a.logger.
   122  					WithField("key", k).
   123  					Debug("Certificate delete event: deleting auth map entry")
   124  				if err := a.authmap.Delete(k); err != nil {
   125  					return fmt.Errorf("failed to delete auth map entry: %w", err)
   126  				}
   127  			} else {
   128  				a.handleAuthenticationFunc(a, k, true)
   129  			}
   130  		}
   131  	}
   132  
   133  	return nil
   134  }
   135  
   136  func handleAuthentication(a *AuthManager, k authKey, reAuth bool) {
   137  	if !a.markPendingAuth(k) {
   138  		a.logger.
   139  			WithField("key", k).
   140  			Debug("Pending authentication, skipping authentication")
   141  		return
   142  	}
   143  
   144  	go func(key authKey) {
   145  		defer a.clearPendingAuth(key)
   146  
   147  		if !reAuth {
   148  			// Check if the auth is actually required, as we might have
   149  			// updated the authmap since the datapath issued the auth
   150  			// required signal.
   151  			// If the entry was cached more than authSignalBackoffTime
   152  			// it will authenticate again, this is to make sure that
   153  			// we re-authenticate if the authmap was updated by an
   154  			// external source.
   155  			if i, err := a.authmap.GetCacheInfo(key); err == nil && i.expiration.After(time.Now()) && time.Now().Before(i.storedAt.Add(a.authSignalBackoffTime)) {
   156  				a.logger.
   157  					WithField("key", key).
   158  					WithField("storedAt", i.storedAt).
   159  					Debugf("Already authenticated in the past %s, skipping authentication", a.authSignalBackoffTime.String())
   160  				return
   161  			}
   162  		}
   163  
   164  		if err := a.authenticate(key); err != nil {
   165  			a.logger.
   166  				WithError(err).
   167  				WithField("key", key).
   168  				Warning("Failed to authenticate request")
   169  		}
   170  	}(k)
   171  }
   172  
   173  // markPendingAuth checks if there is a pending authentication for the given key.
   174  // If an auth is already pending returns false, otherwise marks the key as pending
   175  // and returns true.
   176  func (a *AuthManager) markPendingAuth(key authKey) bool {
   177  	a.mutex.Lock()
   178  	defer a.mutex.Unlock()
   179  
   180  	if _, exists := a.pending[key]; exists {
   181  		// Auth for this key is already pending
   182  		return false
   183  	}
   184  	a.pending[key] = struct{}{}
   185  	return true
   186  }
   187  
   188  // clearPendingAuth marks the pending authentication as finished.
   189  func (a *AuthManager) clearPendingAuth(key authKey) {
   190  	a.logger.
   191  		WithField("key", key).
   192  		Debug("Clearing pending authentication")
   193  
   194  	a.mutex.Lock()
   195  	defer a.mutex.Unlock()
   196  	delete(a.pending, key)
   197  }
   198  
   199  func (a *AuthManager) authenticate(key authKey) error {
   200  	a.logger.
   201  		WithField("key", key).
   202  		Debug("Policy is requiring authentication")
   203  
   204  	// Authenticate according to the requested auth type
   205  	h, ok := a.authHandlers[key.authType]
   206  	if !ok {
   207  		return fmt.Errorf("unknown requested auth type: %s", key.authType)
   208  	}
   209  
   210  	nodeIP := a.nodeIDHandler.GetNodeIP(key.remoteNodeID)
   211  	if nodeIP == "" {
   212  		return fmt.Errorf("remote node IP not available for node ID %d", key.remoteNodeID)
   213  	}
   214  
   215  	authReq := &authRequest{
   216  		localIdentity:  key.localIdentity,
   217  		remoteIdentity: key.remoteIdentity,
   218  		remoteNodeIP:   nodeIP,
   219  	}
   220  
   221  	authResp, err := h.authenticate(authReq)
   222  	if err != nil {
   223  		return fmt.Errorf("failed to authenticate with auth type %s: %w", key.authType, err)
   224  	}
   225  
   226  	if err = a.updateAuthMap(key, authResp.expirationTime); err != nil {
   227  		return fmt.Errorf("failed to update BPF map in datapath: %w", err)
   228  	}
   229  
   230  	a.logger.
   231  		WithField("key", key).
   232  		WithField("remote_node_ip", nodeIP).
   233  		Debug("Successfully authenticated")
   234  
   235  	return nil
   236  }
   237  
   238  func (a *AuthManager) updateAuthMap(key authKey, expirationTime time.Time) error {
   239  	val := authInfo{
   240  		expiration: expirationTime,
   241  	}
   242  
   243  	if err := a.authmap.Update(key, val); err != nil {
   244  		return fmt.Errorf("failed to write auth information to BPF map: %w", err)
   245  	}
   246  
   247  	return nil
   248  }
   249  
   250  func (a *AuthManager) CertProviderStatus() *models.Status {
   251  	for _, h := range a.authHandlers {
   252  		status := h.certProviderStatus()
   253  		if status != nil {
   254  			// for now we only can have one cert provider
   255  			// once this changes we need to merge the statuses
   256  			return status
   257  		}
   258  	}
   259  
   260  	// if none was found auth is disabled
   261  	return &models.Status{
   262  		State: models.StatusStateDisabled,
   263  	}
   264  }