github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/controller/internal/enforcer/envoyauthorizer/envoyauthorizerenforcer.go (about)

     1  package envoyauthorizer
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"crypto/x509"
     7  	"fmt"
     8  	"sync"
     9  	"time"
    10  
    11  	"go.aporeto.io/enforcerd/trireme-lib/collector"
    12  	"go.aporeto.io/enforcerd/trireme-lib/common"
    13  	"go.aporeto.io/enforcerd/trireme-lib/controller/constants"
    14  	"go.aporeto.io/enforcerd/trireme-lib/controller/internal/enforcer/applicationproxy/serviceregistry"
    15  	enforcerconstants "go.aporeto.io/enforcerd/trireme-lib/controller/internal/enforcer/constants"
    16  	"go.aporeto.io/enforcerd/trireme-lib/controller/internal/enforcer/envoyauthorizer/envoyproxy"
    17  	"go.aporeto.io/enforcerd/trireme-lib/controller/internal/enforcer/metadata"
    18  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/ebpf"
    19  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/fqconfig"
    20  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/packettracing"
    21  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/pucontext"
    22  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/secrets"
    23  	"go.aporeto.io/enforcerd/trireme-lib/controller/runtime"
    24  	"go.aporeto.io/enforcerd/trireme-lib/policy"
    25  	"go.aporeto.io/enforcerd/trireme-lib/utils/cache"
    26  	"go.uber.org/zap"
    27  )
    28  
    29  // Enforcer implements the Enforcer interface as an envoy authorizer
    30  // and starts envoy external authz filter gRPC servers for enforcement.
    31  type Enforcer struct {
    32  	mode                   constants.ModeType
    33  	collector              collector.EventCollector
    34  	externalIPCacheTimeout time.Duration
    35  	secrets                secrets.Secrets
    36  	tokenIssuer            common.ServiceTokenIssuer
    37  
    38  	puContexts   cache.DataStore
    39  	clients      cache.DataStore
    40  	systemCAPool *x509.CertPool
    41  
    42  	metadata *metadata.Client
    43  	sync.RWMutex
    44  }
    45  
    46  // envoyAuthzServers, envoy servers used my enforcer
    47  type envoyServers struct {
    48  	ingress *envoyproxy.AuthServer
    49  	egress  *envoyproxy.AuthServer
    50  	sds     *envoyproxy.SdsServer
    51  }
    52  
    53  // NewEnvoyAuthorizerEnforcer creates a new envoy authorizer
    54  func NewEnvoyAuthorizerEnforcer(mode constants.ModeType, eventCollector collector.EventCollector, externalIPCacheTimeout time.Duration, secrets secrets.Secrets, tokenIssuer common.ServiceTokenIssuer) (*Enforcer, error) {
    55  	// abort if this is not the right mode
    56  	if mode != constants.RemoteContainerEnvoyAuthorizer && mode != constants.LocalEnvoyAuthorizer {
    57  		return nil, fmt.Errorf("enforcer mode type must be either RemoteContainerEnvoyAuthorizer or LocalEnvoyAuthorizer, got: %d", mode)
    58  	}
    59  	zap.L().Info("Creating Envoy Authorizer Enforcer")
    60  	// same logic as in the nfqdatapath
    61  	if externalIPCacheTimeout <= 0 {
    62  		var err error
    63  		externalIPCacheTimeout, err = time.ParseDuration(enforcerconstants.DefaultExternalIPTimeout)
    64  		if err != nil {
    65  			externalIPCacheTimeout = time.Second
    66  		}
    67  	}
    68  
    69  	// same logic as in app proxy
    70  	systemPool, err := x509.SystemCertPool()
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	if ok := systemPool.AppendCertsFromPEM(secrets.CertAuthority()); !ok {
    76  		return nil, fmt.Errorf("error while adding provided CA")
    77  	}
    78  	// TODO: systemPool needs the same treatment as the AppProxy and a `processCertificateUpdates` and `expandCAPool` implementation as well
    79  	return &Enforcer{
    80  		mode:                   mode,
    81  		collector:              eventCollector,
    82  		externalIPCacheTimeout: externalIPCacheTimeout,
    83  		secrets:                secrets,
    84  		tokenIssuer:            tokenIssuer,
    85  		puContexts:             cache.NewCache("puContexts"),
    86  		clients:                cache.NewCache("clients"),
    87  		// auth:                   apiauth.New(puContexts, registry, secrets),
    88  		// metadata:               metadata.NewClient(puContext, registry, tokenIssuer),
    89  	}, nil
    90  }
    91  
    92  // Secrets implements the LockedSecrets
    93  func (e *Enforcer) Secrets() (secrets.Secrets, func()) {
    94  	e.RLock()
    95  	return e.secrets, e.RUnlock
    96  }
    97  
    98  // Enforce starts enforcing policies for the given policy.PUInfo.
    99  // here we do the following:
   100  // 1. create a new PU always and instantiate a new apiAuth, as we want to be as stateless as possible.
   101  // 2. create a PUcontext as this will be used in auth code.
   102  // 3. If envoy servers are not present then create all 3 envoy servers.
   103  // 4. If the servers are already present under policy update then update the service certs.
   104  func (e *Enforcer) Enforce(ctx context.Context, contextID string, puInfo *policy.PUInfo) error {
   105  	e.Lock()
   106  	defer e.Unlock()
   107  
   108  	zap.L().Debug("Enforce for the envoy for pu", zap.String("puID", contextID))
   109  	// here we 1st need to create a PuContext, as the PU context will derive the
   110  	// serviceCtxt which will be used by the authorizer to determine the policyInfo.
   111  
   112  	pu, err := pucontext.NewPU(contextID, puInfo, nil, e.externalIPCacheTimeout)
   113  	if err != nil {
   114  		return fmt.Errorf("error creating new pu: %s", err)
   115  	}
   116  	// Add the puContext to the cache as we need to later while serving the requests.
   117  	e.puContexts.AddOrUpdate(contextID, pu)
   118  
   119  	sctx, err := serviceregistry.Instance().Register(contextID, puInfo, pu, e.secrets)
   120  	if err != nil {
   121  		return fmt.Errorf("policy conflicts detected: %s", err)
   122  	}
   123  
   124  	caPool := e.expandCAPool(sctx.RootCA)
   125  
   126  	// now instantiate the apiAuth and metadata
   127  	// create a new server if it doesn't exist yet
   128  	if _, err := e.clients.Get(contextID); err != nil {
   129  		zap.L().Debug("creating new auth and sds servers", zap.String("puID", contextID))
   130  		ingressServer, err := envoyproxy.NewExtAuthzServer(contextID, e.puContexts, e.collector, envoyproxy.IngressDirection, e.secrets, e.tokenIssuer)
   131  		if err != nil {
   132  			zap.L().Error("Cannot create and run IngressServer", zap.Error(err))
   133  			return err
   134  		}
   135  
   136  		egressServer, err := envoyproxy.NewExtAuthzServer(contextID, e.puContexts, e.collector, envoyproxy.EgressDirection, e.secrets, e.tokenIssuer)
   137  		if err != nil {
   138  			zap.L().Error("Cannot create and run EgressServer", zap.Error(err))
   139  			ingressServer.Stop()
   140  			return err
   141  		}
   142  		sdsServer, err := envoyproxy.NewSdsServer(contextID, puInfo, caPool, e.secrets)
   143  		if err != nil {
   144  			zap.L().Error("Cannot create and run SdsServer", zap.Error(err))
   145  			return err
   146  		}
   147  		// Add the EnvoyServers to our cache
   148  		if err := e.clients.Add(contextID, &envoyServers{ingress: ingressServer, egress: egressServer, sds: sdsServer}); err != nil {
   149  			ingressServer.Stop()
   150  			egressServer.Stop()
   151  			sdsServer.Stop()
   152  			return err
   153  		}
   154  
   155  	} else {
   156  		// we have this client already, this is only a policy update
   157  		zap.L().Debug("handling policy update for envoy servers", zap.String("puID", contextID))
   158  		// For updates we need to update the certificates if we have new ones. Otherwise
   159  		// we return. There is nothing else to do in case of policy update.
   160  		// this required for the Envoy servers.
   161  		if c, cerr := e.clients.Get(contextID); cerr == nil {
   162  			_, perr := e.processCertificateUpdates(puInfo, c.(*envoyServers), caPool)
   163  			if perr != nil {
   164  				zap.L().Error("unable to update certificates for services", zap.Error(perr))
   165  				return perr
   166  			}
   167  			return nil
   168  		}
   169  	}
   170  
   171  	return nil
   172  }
   173  
   174  // processCertificateUpdates processes the certificate information and updates
   175  // the servers.
   176  func (e *Enforcer) processCertificateUpdates(puInfo *policy.PUInfo, server *envoyServers, caPool *x509.CertPool) (bool, error) {
   177  
   178  	// If there are certificates provided, we will need to update them for the
   179  	// services. If the certificates are nil, we ignore them.
   180  	certPEM, keyPEM, caPEM := puInfo.Policy.ServiceCertificates()
   181  	if certPEM == "" || keyPEM == "" {
   182  		return false, nil
   183  	}
   184  
   185  	// Process any updates on the cert pool
   186  	if caPEM != "" {
   187  		if !caPool.AppendCertsFromPEM([]byte(caPEM)) {
   188  			zap.L().Warn("Failed to add Services CA")
   189  		}
   190  	}
   191  
   192  	// Create the TLS certificate
   193  	tlsCert, err := tls.X509KeyPair([]byte(certPEM), []byte(keyPEM))
   194  	if err != nil {
   195  		return false, fmt.Errorf("Invalid certificates: %s", err)
   196  	}
   197  	// Here update the enforcer secrets because we are using the LockedSecrets.
   198  	// Also, send a update event to the SDS server so it can send a new cert to the envoy Sidecar.
   199  	// // update all the server certs, the Write lock has already been acquired by the Enforce function, so no need to lock again.
   200  	server.ingress.UpdateSecrets(&tlsCert, caPool, e.secrets, certPEM, keyPEM)
   201  	server.egress.UpdateSecrets(&tlsCert, caPool, e.secrets, certPEM, keyPEM)
   202  	server.sds.UpdateSecrets(&tlsCert, caPool, e.secrets, certPEM, keyPEM)
   203  
   204  	if e.metadata != nil {
   205  		e.metadata.UpdateSecrets([]byte(certPEM), []byte(keyPEM))
   206  	}
   207  	return true, nil
   208  }
   209  
   210  func (e *Enforcer) expandCAPool(externalCAs [][]byte) *x509.CertPool {
   211  	systemPool, err := x509.SystemCertPool()
   212  	if err != nil {
   213  		zap.L().Error("cannot process system pool", zap.Error(err))
   214  		return e.systemCAPool
   215  	}
   216  	if ok := systemPool.AppendCertsFromPEM(e.secrets.CertAuthority()); !ok {
   217  		zap.L().Error("cannot appen system CA", zap.Error(err))
   218  		return e.systemCAPool
   219  	}
   220  	for _, ca := range externalCAs {
   221  		if ok := systemPool.AppendCertsFromPEM(ca); !ok {
   222  			zap.L().Error("cannot append external service ca", zap.String("CA", string(ca)))
   223  		}
   224  	}
   225  	return systemPool
   226  }
   227  
   228  // Unenforce stops enforcing policy for the given IP.
   229  func (e *Enforcer) Unenforce(ctx context.Context, contextID string) error {
   230  	e.Lock()
   231  	defer e.Unlock()
   232  
   233  	// stop the authz servers
   234  	rawAuthzServers, err := e.clients.Get(contextID)
   235  	if err != nil {
   236  		return err
   237  	}
   238  
   239  	server := rawAuthzServers.(*envoyServers)
   240  	shutdownCtx, shutdownCtxCancel := context.WithTimeout(ctx, time.Second*10)
   241  	defer shutdownCtxCancel()
   242  
   243  	var wg sync.WaitGroup
   244  	shutdownCh := make(chan struct{})
   245  	wg.Add(3)
   246  	go func() {
   247  		server.ingress.GracefulStop()
   248  		wg.Done()
   249  	}()
   250  	go func() {
   251  		server.egress.GracefulStop()
   252  		wg.Done()
   253  	}()
   254  	go func() {
   255  		server.sds.GracefulStop()
   256  		wg.Done()
   257  	}()
   258  	go func() {
   259  		wg.Wait()
   260  		shutdownCh <- struct{}{}
   261  	}()
   262  
   263  	select {
   264  	case <-shutdownCtx.Done():
   265  		zap.L().Warn("Graceful shutdown of envoy server did not finish in time. Shutting down hard now...", zap.String("puID", contextID), zap.Error(shutdownCtx.Err()))
   266  		var wg sync.WaitGroup
   267  		wg.Add(3)
   268  		go func() {
   269  			server.ingress.Stop()
   270  			wg.Done()
   271  		}()
   272  		go func() {
   273  			server.egress.Stop()
   274  			wg.Done()
   275  		}()
   276  		go func() {
   277  			server.sds.Stop()
   278  			wg.Done()
   279  		}()
   280  		wg.Wait()
   281  	case <-shutdownCh:
   282  	}
   283  
   284  	if err := e.puContexts.RemoveWithDelay(contextID, 10*time.Second); err != nil {
   285  		zap.L().Debug("Unable to remove PU context from cache", zap.String("puID", contextID), zap.Error(err))
   286  	}
   287  
   288  	return nil
   289  }
   290  
   291  // UpdateSecrets -- updates the secrets of running enforcers managed by trireme. Remote enforcers will get the secret updates with the next policy push
   292  func (e *Enforcer) UpdateSecrets(secrets secrets.Secrets) error {
   293  	e.Lock()
   294  	defer e.Unlock()
   295  	e.secrets = secrets
   296  	return nil
   297  }
   298  
   299  // SetTargetNetworks is unimplemented in the envoy authorizer
   300  func (e *Enforcer) SetTargetNetworks(cfg *runtime.Configuration) error {
   301  	return nil
   302  }
   303  
   304  // SetLogLevel is unimplemented in the envoy authorizer
   305  func (e *Enforcer) SetLogLevel(level constants.LogLevel) error {
   306  	return nil
   307  }
   308  
   309  // CleanUp is unimplemented in the envoy authorizer
   310  func (e *Enforcer) CleanUp() error {
   311  	return nil
   312  }
   313  
   314  // Run is unimplemented in the envoy authorizer
   315  func (e *Enforcer) Run(ctx context.Context) error {
   316  	return nil
   317  }
   318  
   319  // GetBPFObject is unimplemented in the envoy authorizer
   320  func (e *Enforcer) GetBPFObject() ebpf.BPFModule {
   321  	return nil
   322  }
   323  
   324  // GetServiceMeshType is unimplemented in the envoy authorizer
   325  func (e *Enforcer) GetServiceMeshType() policy.ServiceMesh {
   326  	return policy.None
   327  }
   328  
   329  // GetFilterQueue is unimplemented in the envoy authorizer
   330  func (e *Enforcer) GetFilterQueue() fqconfig.FilterQueue {
   331  	return nil
   332  }
   333  
   334  // EnableDatapathPacketTracing is unimplemented in the envoy authorizer
   335  func (e *Enforcer) EnableDatapathPacketTracing(ctx context.Context, contextID string, direction packettracing.TracingDirection, interval time.Duration) error {
   336  	return nil
   337  }
   338  
   339  // EnableIPTablesPacketTracing is unimplemented in the envoy authorizer
   340  func (e *Enforcer) EnableIPTablesPacketTracing(ctx context.Context, contextID string, interval time.Duration) error {
   341  	return nil
   342  }
   343  
   344  // Ping is unimplemented in the envoy authorizer
   345  func (e *Enforcer) Ping(ctx context.Context, contextID string, pingConfig *policy.PingConfig) error {
   346  	return nil
   347  }
   348  
   349  // DebugCollect is unimplemented in the envoy authorizer
   350  func (e *Enforcer) DebugCollect(ctx context.Context, contextID string, debugConfig *policy.DebugConfig) error {
   351  	return nil
   352  }