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

     1  // Package enforcerproxy :: This is the implementation of the RPC client
     2  // It implements the interface of Trireme Enforcer and forwards these
     3  // requests to the actual remote enforcer instead of implementing locally
     4  package enforcerproxy
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"strconv"
    10  	"sync"
    11  	"time"
    12  
    13  	"go.aporeto.io/enforcerd/trireme-lib/collector"
    14  	"go.aporeto.io/enforcerd/trireme-lib/common"
    15  	"go.aporeto.io/enforcerd/trireme-lib/controller/constants"
    16  	"go.aporeto.io/enforcerd/trireme-lib/controller/internal/enforcer"
    17  	"go.aporeto.io/enforcerd/trireme-lib/controller/internal/enforcer/utils/rpcwrapper"
    18  	"go.aporeto.io/enforcerd/trireme-lib/controller/internal/processmon"
    19  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/ebpf"
    20  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/env"
    21  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/fqconfig"
    22  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/packettracing"
    23  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/remoteenforcer"
    24  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/secrets"
    25  	"go.aporeto.io/enforcerd/trireme-lib/controller/runtime"
    26  	"go.aporeto.io/enforcerd/trireme-lib/policy"
    27  	"go.aporeto.io/enforcerd/trireme-lib/utils/crypto"
    28  	"go.uber.org/zap"
    29  )
    30  
    31  // ProxyInfo is the struct used to hold state about active enforcers in the system
    32  type ProxyInfo struct {
    33  	mutualAuth             bool
    34  	packetLogs             bool
    35  	Secrets                secrets.Secrets
    36  	serverID               string
    37  	validity               time.Duration
    38  	prochdl                processmon.ProcessManager
    39  	rpchdl                 rpcwrapper.RPCClient
    40  	filterQueue            fqconfig.FilterQueue
    41  	commandArg             string
    42  	statsServerSecret      string
    43  	procMountPoint         string
    44  	ExternalIPCacheTimeout time.Duration
    45  	collector              collector.EventCollector
    46  	cfg                    *runtime.Configuration
    47  	tokenIssuer            common.ServiceTokenIssuer
    48  	binaryTokens           bool
    49  	isBPFEnabled           bool
    50  	ipv6Enabled            bool
    51  	serviceMeshType        policy.ServiceMesh
    52  	rpcServer              rpcwrapper.RPCServer
    53  	iptablesLockfile       string
    54  	sync.RWMutex
    55  }
    56  
    57  // Enforce method makes a RPC call for the remote enforcer enforce method
    58  func (s *ProxyInfo) Enforce(ctx context.Context, contextID string, puInfo *policy.PUInfo) error {
    59  
    60  	initEnforcer, err := s.prochdl.LaunchRemoteEnforcer(
    61  		contextID,
    62  		puInfo.Runtime.Pid(),
    63  		puInfo.Runtime.NSPath(),
    64  		s.commandArg,
    65  		s.statsServerSecret,
    66  		s.procMountPoint,
    67  		puInfo.Policy.EnforcerType(),
    68  	)
    69  
    70  	if err != nil {
    71  		return err
    72  	}
    73  
    74  	zap.L().Debug("Called enforce and launched remote process", zap.String("contextID", contextID),
    75  		zap.String("enforcer type", puInfo.Policy.EnforcerType().String()),
    76  		zap.String("serviceMeshType", puInfo.Runtime.ServiceMeshType.String()),
    77  		zap.String("name", puInfo.Runtime.Name()))
    78  
    79  	s.serviceMeshType = puInfo.Runtime.ServiceMeshType
    80  	if initEnforcer {
    81  		if err := s.initRemoteEnforcer(contextID); err != nil {
    82  			s.prochdl.KillRemoteEnforcer(contextID, true) // nolint errcheck
    83  			return err
    84  		}
    85  	}
    86  
    87  	enforcerPayload := &rpcwrapper.EnforcePayload{
    88  		ContextID: contextID,
    89  		Policy:    puInfo.Policy.ToPublicPolicy(),
    90  	}
    91  
    92  	//Only the secrets need to be under lock. They can change async to the enforce call from Updatesecrets
    93  	s.RLock()
    94  	enforcerPayload.Secrets = s.Secrets.RPCSecrets()
    95  	s.RUnlock()
    96  	request := &rpcwrapper.Request{
    97  		Payload: enforcerPayload,
    98  	}
    99  
   100  	if err := s.rpchdl.RemoteCall(contextID, remoteenforcer.Enforce, request, &rpcwrapper.Response{}); err != nil {
   101  		s.prochdl.KillRemoteEnforcer(contextID, true) // nolint errcheck
   102  		return fmt.Errorf("failed to send message to remote enforcer: %s", err)
   103  	}
   104  
   105  	return nil
   106  }
   107  
   108  // Unenforce stops enforcing policy for the given contextID.
   109  func (s *ProxyInfo) Unenforce(ctx context.Context, contextID string) error {
   110  
   111  	request := &rpcwrapper.Request{
   112  		Payload: &rpcwrapper.UnEnforcePayload{
   113  			ContextID: contextID,
   114  		},
   115  	}
   116  
   117  	if err := s.rpchdl.RemoteCall(contextID, remoteenforcer.Unenforce, request, &rpcwrapper.Response{}); err != nil {
   118  		zap.L().Error("failed to send message to remote enforcer", zap.Error(err))
   119  	}
   120  
   121  	return s.prochdl.KillRemoteEnforcer(contextID, true)
   122  }
   123  
   124  // UpdateSecrets updates the secrets used for signing communication between trireme instances
   125  func (s *ProxyInfo) UpdateSecrets(token secrets.Secrets) error {
   126  	s.Lock()
   127  	s.Secrets = token
   128  	s.Unlock()
   129  
   130  	var allErrors string
   131  
   132  	resp := &rpcwrapper.Response{}
   133  	request := &rpcwrapper.Request{
   134  		Payload: &rpcwrapper.UpdateSecretsPayload{
   135  			Secrets: s.Secrets.RPCSecrets(),
   136  		},
   137  	}
   138  
   139  	for _, contextID := range s.rpchdl.ContextList() {
   140  		if err := s.rpchdl.RemoteCall(contextID, remoteenforcer.UpdateSecrets, request, resp); err != nil {
   141  			allErrors = allErrors + " contextID " + contextID + ":" + err.Error()
   142  		}
   143  	}
   144  
   145  	if len(allErrors) > 0 {
   146  		return fmt.Errorf("unable to update secrets for some remotes: %s", allErrors)
   147  	}
   148  
   149  	return nil
   150  }
   151  
   152  // SetLogLevel sets log level.
   153  func (s *ProxyInfo) SetLogLevel(level constants.LogLevel) error {
   154  
   155  	resp := &rpcwrapper.Response{}
   156  	request := &rpcwrapper.Request{
   157  		Payload: &rpcwrapper.SetLogLevelPayload{
   158  			Level: level,
   159  		},
   160  	}
   161  
   162  	var allErrors string
   163  
   164  	for _, contextID := range s.rpchdl.ContextList() {
   165  		if err := s.rpchdl.RemoteCall(contextID, remoteenforcer.SetLogLevel, request, resp); err != nil {
   166  			allErrors = allErrors + " contextID " + contextID + ":" + err.Error()
   167  		}
   168  	}
   169  
   170  	if len(allErrors) > 0 {
   171  		return fmt.Errorf("unable to set log level: %s", allErrors)
   172  	}
   173  
   174  	return nil
   175  }
   176  
   177  // CleanUp sends a cleanup command to all the remotes forcing them to exit and clean their state.
   178  func (s *ProxyInfo) CleanUp() error {
   179  	var synch sync.Mutex
   180  	var wg sync.WaitGroup
   181  
   182  	contextList := s.rpchdl.ContextList()
   183  	lenCids := len(contextList)
   184  
   185  	if lenCids == 0 {
   186  		return nil
   187  	}
   188  
   189  	zap.L().Info(strconv.Itoa(lenCids) + " remote enforcers waiting to be exited")
   190  
   191  	var chs []chan string
   192  
   193  	wg.Add(lenCids)
   194  	for i := 0; i < 4; i++ {
   195  		ch := make(chan string)
   196  		chs = append(chs, ch)
   197  
   198  		go func(ch chan string) {
   199  			var cid string
   200  			for {
   201  				cid = <-ch
   202  				if err := s.prochdl.KillRemoteEnforcer(cid, false); err != nil {
   203  					zap.L().Error("enforcer with contextID "+cid+"failed to exit", zap.Error(err))
   204  				}
   205  				synch.Lock()
   206  				lenCids = lenCids - 1
   207  				m := 0
   208  				switch {
   209  				case lenCids >= 500:
   210  					m = 250
   211  				case lenCids >= 100:
   212  					m = 100
   213  				case lenCids >= 10:
   214  					m = 10
   215  				default:
   216  					m = 1
   217  				}
   218  
   219  				if lenCids%m == 0 {
   220  					zap.L().Info(strconv.Itoa(lenCids) + " remote enforcers waiting to be exited")
   221  				}
   222  				synch.Unlock()
   223  				wg.Done()
   224  			}
   225  		}(ch)
   226  	}
   227  
   228  	for i, contextID := range contextList {
   229  		chs[i%4] <- contextID
   230  	}
   231  
   232  	wg.Wait()
   233  	zap.L().Info("All remote enforcers have exited...")
   234  
   235  	return nil
   236  }
   237  
   238  // EnableDatapathPacketTracing enable nfq packet tracing in remote container
   239  func (s *ProxyInfo) EnableDatapathPacketTracing(ctx context.Context, contextID string, direction packettracing.TracingDirection, interval time.Duration) error {
   240  
   241  	resp := &rpcwrapper.Response{}
   242  
   243  	request := &rpcwrapper.Request{
   244  		Payload: &rpcwrapper.EnableDatapathPacketTracingPayLoad{
   245  			Direction: direction,
   246  			Interval:  interval,
   247  			ContextID: contextID,
   248  		},
   249  	}
   250  
   251  	if err := s.rpchdl.RemoteCall(contextID, remoteenforcer.EnableDatapathPacketTracing, request, resp); err != nil {
   252  		return fmt.Errorf("unable to enable datapath packet tracing %s -- %s", err, resp.Status)
   253  	}
   254  
   255  	return nil
   256  }
   257  
   258  // EnableIPTablesPacketTracing enable iptables tracing
   259  func (s *ProxyInfo) EnableIPTablesPacketTracing(ctx context.Context, contextID string, interval time.Duration) error {
   260  
   261  	request := &rpcwrapper.Request{
   262  		Payload: &rpcwrapper.EnableIPTablesPacketTracingPayLoad{
   263  			IPTablesPacketTracing: true,
   264  			Interval:              interval,
   265  			ContextID:             contextID,
   266  		},
   267  	}
   268  
   269  	if err := s.rpchdl.RemoteCall(contextID, remoteenforcer.EnableIPTablesPacketTracing, request, &rpcwrapper.Response{}); err != nil {
   270  		return fmt.Errorf("Unable to enable iptables tracing for contextID %s: %s", contextID, err)
   271  	}
   272  
   273  	return nil
   274  }
   275  
   276  // SetTargetNetworks does the RPC call for SetTargetNetworks to the corresponding
   277  // remote enforcers
   278  func (s *ProxyInfo) SetTargetNetworks(cfg *runtime.Configuration) error {
   279  	resp := &rpcwrapper.Response{}
   280  	request := &rpcwrapper.Request{
   281  		Payload: &rpcwrapper.SetTargetNetworksPayload{
   282  			Configuration: cfg,
   283  		},
   284  	}
   285  
   286  	var allErrors string
   287  
   288  	for _, contextID := range s.rpchdl.ContextList() {
   289  		if err := s.rpchdl.RemoteCall(contextID, remoteenforcer.SetTargetNetworks, request, resp); err != nil {
   290  			allErrors = allErrors + " contextID " + contextID + ":" + err.Error()
   291  		}
   292  	}
   293  
   294  	s.Lock()
   295  	s.cfg = cfg
   296  	s.Unlock()
   297  
   298  	if len(allErrors) > 0 {
   299  		return fmt.Errorf("Remote enforcers failed: %s", allErrors)
   300  	}
   301  
   302  	return nil
   303  }
   304  
   305  // GetBPFObject returns the bpf object
   306  func (s *ProxyInfo) GetBPFObject() ebpf.BPFModule {
   307  	return nil
   308  }
   309  
   310  // GetServiceMeshType is unimplemented in the envoy authorizer
   311  func (s *ProxyInfo) GetServiceMeshType() policy.ServiceMesh {
   312  	return policy.None
   313  }
   314  
   315  // GetFilterQueue returns the current FilterQueueConfig.
   316  func (s *ProxyInfo) GetFilterQueue() fqconfig.FilterQueue {
   317  	return s.filterQueue
   318  }
   319  
   320  // Run starts the the remote enforcer proxy.
   321  func (s *ProxyInfo) Run(ctx context.Context) error {
   322  
   323  	handler := &ProxyRPCServer{
   324  		rpchdl:      s.rpcServer,
   325  		collector:   s.collector,
   326  		secret:      s.statsServerSecret,
   327  		tokenIssuer: s.tokenIssuer,
   328  		ctx:         ctx,
   329  	}
   330  
   331  	// Start the server for statistics collection.
   332  	go s.rpcServer.StartServer(ctx, "unix", constants.StatsChannel, handler) // nolint
   333  
   334  	return nil
   335  }
   336  
   337  // Ping runs ping from the given config.
   338  func (s *ProxyInfo) Ping(ctx context.Context, contextID string, pingConfig *policy.PingConfig) error {
   339  
   340  	resp := &rpcwrapper.Response{}
   341  
   342  	request := &rpcwrapper.Request{
   343  		Payload: &rpcwrapper.PingPayload{
   344  			ContextID:  contextID,
   345  			PingConfig: pingConfig,
   346  		},
   347  	}
   348  
   349  	if err := s.rpchdl.RemoteCall(contextID, remoteenforcer.Ping, request, resp); err != nil {
   350  		return fmt.Errorf("unable to run ping %s -- %s", err, resp.Status)
   351  	}
   352  
   353  	return nil
   354  }
   355  
   356  // DebugCollect tells remote enforcer to start collecting debug info (pcap or misc commands).
   357  // It does not wait for pcap collection to complete: the pid of tcpdump is returned.
   358  // If another command is meant to be executed in remote enforcer, it should be quick, and its output is returned.
   359  func (s *ProxyInfo) DebugCollect(ctx context.Context, contextID string, debugConfig *policy.DebugConfig) error {
   360  	resp := &rpcwrapper.Response{}
   361  
   362  	request := &rpcwrapper.Request{
   363  		Payload: &rpcwrapper.DebugCollectPayload{
   364  			ContextID:    contextID,
   365  			PcapFilePath: debugConfig.FilePath,
   366  			PcapFilter:   debugConfig.PcapFilter,
   367  			CommandExec:  debugConfig.CommandExec,
   368  		},
   369  	}
   370  
   371  	if err := s.rpchdl.RemoteCall(contextID, remoteenforcer.DebugCollect, request, resp); err != nil {
   372  		return fmt.Errorf("unable to run debug collect %s -- %s", err, resp.Status)
   373  	}
   374  
   375  	responsePayload := resp.Payload.(rpcwrapper.DebugCollectResponsePayload)
   376  	debugConfig.PID = responsePayload.PID
   377  	debugConfig.CommandOutput = responsePayload.CommandOutput
   378  
   379  	return nil
   380  }
   381  
   382  // initRemoteEnforcer method makes a RPC call to the remote enforcer
   383  func (s *ProxyInfo) initRemoteEnforcer(contextID string) error {
   384  
   385  	resp := &rpcwrapper.Response{}
   386  
   387  	request := &rpcwrapper.Request{
   388  		Payload: &rpcwrapper.InitRequestPayload{
   389  			MutualAuth:             s.mutualAuth,
   390  			Validity:               s.validity,
   391  			ServerID:               s.serverID,
   392  			ExternalIPCacheTimeout: s.ExternalIPCacheTimeout,
   393  			PacketLogs:             s.packetLogs,
   394  			Secrets:                s.Secrets.RPCSecrets(),
   395  			Configuration:          s.cfg,
   396  			BinaryTokens:           s.binaryTokens,
   397  			IsBPFEnabled:           s.isBPFEnabled,
   398  			ServiceMeshType:        s.serviceMeshType,
   399  			IPv6Enabled:            s.ipv6Enabled,
   400  			IPTablesLockfile:       s.iptablesLockfile,
   401  		},
   402  	}
   403  
   404  	return s.rpchdl.RemoteCall(contextID, remoteenforcer.InitEnforcer, request, resp)
   405  }
   406  
   407  // NewProxyEnforcer creates a new proxy to remote enforcers.
   408  func NewProxyEnforcer(
   409  	ctx context.Context,
   410  	mutualAuth bool,
   411  	filterQueue fqconfig.FilterQueue,
   412  	collector collector.EventCollector,
   413  	secrets secrets.Secrets,
   414  	serverID string,
   415  	validity time.Duration,
   416  	cmdArg string,
   417  	procMountPoint string,
   418  	ExternalIPCacheTimeout time.Duration,
   419  	packetLogs bool,
   420  	cfg *runtime.Configuration,
   421  	runtimeError chan *policy.RuntimeError,
   422  	remoteParameters *env.RemoteParameters,
   423  	tokenIssuer common.ServiceTokenIssuer,
   424  	isBPFEnabled bool,
   425  	ipv6Enabled bool,
   426  	iptablesLockfile string,
   427  	rpcServer rpcwrapper.RPCServer,
   428  ) enforcer.Enforcer {
   429  
   430  	statsServersecret, err := crypto.GenerateRandomString(32)
   431  	if err != nil {
   432  		// There is a very small chance of this happening we will log an error here.
   433  		zap.L().Error("Failed to generate random secret for stats reporting", zap.Error(err))
   434  		// We will use current time as the secret
   435  		statsServersecret = time.Now().String()
   436  	}
   437  
   438  	rpcClient := rpcwrapper.NewRPCWrapper()
   439  
   440  	return &ProxyInfo{
   441  		mutualAuth:             mutualAuth,
   442  		Secrets:                secrets,
   443  		serverID:               serverID,
   444  		validity:               validity,
   445  		prochdl:                processmon.New(ctx, remoteParameters, runtimeError, rpcClient, filterQueue.GetNumQueues()),
   446  		rpchdl:                 rpcClient,
   447  		filterQueue:            filterQueue,
   448  		commandArg:             cmdArg,
   449  		statsServerSecret:      statsServersecret,
   450  		procMountPoint:         procMountPoint,
   451  		ExternalIPCacheTimeout: ExternalIPCacheTimeout,
   452  		packetLogs:             packetLogs,
   453  		collector:              collector,
   454  		cfg:                    cfg,
   455  		tokenIssuer:            tokenIssuer,
   456  		isBPFEnabled:           isBPFEnabled,
   457  		ipv6Enabled:            ipv6Enabled,
   458  		iptablesLockfile:       iptablesLockfile,
   459  		rpcServer:              rpcServer,
   460  	}
   461  }