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 }