github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/controller/pkg/remoteenforcer/remoteenforcer_linux.go (about) 1 // +build linux 2 3 package remoteenforcer 4 5 /* 6 #cgo CFLAGS: -Wall 7 #include <stdlib.h> 8 */ 9 import "C" 10 11 import ( 12 "context" 13 "fmt" 14 "os" 15 "os/exec" 16 "os/signal" 17 "strings" 18 "sync" 19 "syscall" 20 21 "github.com/blang/semver" 22 "go.aporeto.io/enforcerd/internal/diagnostics" 23 "go.aporeto.io/enforcerd/internal/logging/remotelog" 24 "go.aporeto.io/enforcerd/trireme-lib/controller/constants" 25 "go.aporeto.io/enforcerd/trireme-lib/controller/internal/enforcer" 26 _ "go.aporeto.io/enforcerd/trireme-lib/controller/internal/enforcer/utils/nsenter" // nolint 27 "go.aporeto.io/enforcerd/trireme-lib/controller/internal/enforcer/utils/rpcwrapper" 28 "go.aporeto.io/enforcerd/trireme-lib/controller/internal/supervisor" 29 "go.aporeto.io/enforcerd/trireme-lib/controller/pkg/fqconfig" 30 "go.aporeto.io/enforcerd/trireme-lib/controller/pkg/remoteenforcer/internal/client" 31 reports "go.aporeto.io/enforcerd/trireme-lib/controller/pkg/remoteenforcer/internal/client/reportsclient" 32 "go.aporeto.io/enforcerd/trireme-lib/controller/pkg/remoteenforcer/internal/client/statsclient" 33 "go.aporeto.io/enforcerd/trireme-lib/controller/pkg/remoteenforcer/internal/statscollector" 34 "go.aporeto.io/enforcerd/trireme-lib/controller/pkg/remoteenforcer/internal/tokenissuer" 35 "go.aporeto.io/enforcerd/trireme-lib/controller/pkg/secrets/rpc" 36 "go.aporeto.io/enforcerd/trireme-lib/policy" 37 "go.uber.org/zap" 38 "golang.org/x/sys/unix" 39 ) 40 41 // Initialization functions as variables in order to enable testing. 42 var ( 43 createEnforcer = enforcer.New 44 45 createSupervisor = supervisor.NewSupervisor 46 ) 47 48 var cmdLock sync.Mutex 49 50 // newRemoteEnforcer starts a new server 51 func newRemoteEnforcer( 52 ctx context.Context, 53 rpcHandle rpcwrapper.RPCServer, 54 secret string, 55 statsClient client.Reporter, 56 collector statscollector.Collector, 57 reportsClient client.Reporter, 58 tokenIssuer tokenissuer.TokenClient, 59 logLevel string, 60 logFormat string, 61 logID string, 62 numQueues int, 63 enforcerType policy.EnforcerType, 64 agentVersion semver.Version, 65 ) (*RemoteEnforcer, error) { 66 67 var err error 68 69 if collector == nil { 70 collector = statscollector.NewCollector() 71 } 72 73 if statsClient == nil { 74 statsClient, err = statsclient.NewStatsClient(collector) 75 if err != nil { 76 return nil, err 77 } 78 } 79 80 if reportsClient == nil { 81 reportsClient, err = reports.NewClient(collector) 82 if err != nil { 83 return nil, err 84 } 85 } 86 87 if tokenIssuer == nil { 88 tokenIssuer, err = tokenissuer.NewClient() 89 if err != nil { 90 return nil, err 91 } 92 93 } 94 95 procMountPoint := os.Getenv(constants.EnvMountPoint) 96 if procMountPoint == "" { 97 procMountPoint = constants.DefaultProcMountPoint 98 } 99 100 fqConfig := fqconfig.NewFilterQueue( 101 numQueues, 102 []string{"0.0.0.0/0"}, 103 ) 104 105 return &RemoteEnforcer{ 106 collector: collector, 107 rpcSecret: secret, 108 rpcHandle: rpcHandle, 109 procMountPoint: procMountPoint, 110 statsClient: statsClient, 111 reportsClient: reportsClient, 112 ctx: ctx, 113 exit: make(chan bool), 114 tokenIssuer: tokenIssuer, 115 enforcerType: enforcerType, 116 agentVersion: agentVersion, 117 config: logConfig{logLevel: logLevel, logFormat: logFormat, logID: logID}, 118 fqConfig: fqConfig, 119 }, nil 120 } 121 122 // InitEnforcer is a function called from the controller using RPC. It intializes 123 // data structure required by the remote enforcer 124 func (s *RemoteEnforcer) InitEnforcer(req rpcwrapper.Request, resp *rpcwrapper.Response) error { 125 126 zap.L().Debug("Configuring remote enforcer") 127 128 if !s.rpcHandle.CheckValidity(&req, s.rpcSecret) { 129 resp.Status = fmt.Sprintf("init message authentication failed") // nolint:gosimple 130 return fmt.Errorf(resp.Status) 131 } 132 cmdLock.Lock() 133 defer cmdLock.Unlock() 134 135 payload, ok := req.Payload.(rpcwrapper.InitRequestPayload) 136 if !ok { 137 resp.Status = fmt.Sprintf("invalid request payload") // nolint:gosimple 138 return fmt.Errorf(resp.Status) 139 } 140 141 if s.supervisor != nil || s.enforcer != nil { 142 resp.Status = fmt.Sprintf("remote enforcer is already initialized") // nolint:gosimple 143 return fmt.Errorf(resp.Status) 144 } 145 146 var err error 147 148 defer func() { 149 if err != nil { 150 s.cleanup() 151 } 152 }() 153 154 if err = s.setupEnforcer(&payload); err != nil { 155 resp.Status = err.Error() 156 return fmt.Errorf(resp.Status) 157 } 158 159 if err = s.setupSupervisor(&payload); err != nil { 160 resp.Status = err.Error() 161 return fmt.Errorf(resp.Status) 162 } 163 164 if err = s.enforcer.Run(s.ctx); err != nil { 165 resp.Status = err.Error() 166 return fmt.Errorf(resp.Status) 167 } 168 169 if err = s.statsClient.Run(s.ctx); err != nil { 170 resp.Status = err.Error() 171 return fmt.Errorf(resp.Status) 172 } 173 174 if err = s.supervisor.Run(s.ctx); err != nil { 175 resp.Status = err.Error() 176 return fmt.Errorf(resp.Status) 177 } 178 179 if err = s.reportsClient.Run(s.ctx); err != nil { 180 resp.Status = "ReportsClient" + err.Error() 181 return fmt.Errorf(resp.Status) 182 } 183 184 if err = s.tokenIssuer.Run(s.ctx); err != nil { 185 resp.Status = "TokenIssuer" + err.Error() 186 return fmt.Errorf(resp.Status) 187 } 188 189 resp.Status = "" 190 191 return nil 192 } 193 194 // Enforce this method calls the enforce method on the enforcer created during initenforcer 195 func (s *RemoteEnforcer) Enforce(req rpcwrapper.Request, resp *rpcwrapper.Response) error { 196 197 if !s.rpcHandle.CheckValidity(&req, s.rpcSecret) { 198 resp.Status = "enforce message auth failed" 199 return fmt.Errorf(resp.Status) 200 } 201 202 cmdLock.Lock() 203 defer cmdLock.Unlock() 204 205 payload, ok := req.Payload.(rpcwrapper.EnforcePayload) 206 if !ok { 207 resp.Status = "invalid enforcer payload" 208 return fmt.Errorf(resp.Status) 209 } 210 211 plc, err := payload.Policy.ToPrivatePolicy(s.ctx, true) 212 if err != nil { 213 return err 214 } 215 216 runtime := policy.NewPURuntimeWithDefaults() 217 218 puInfo := &policy.PUInfo{ 219 ContextID: payload.ContextID, 220 Policy: plc, 221 Runtime: runtime, 222 } 223 224 if s.enforcer == nil || s.supervisor == nil { 225 resp.Status = "enforcer not initialized - cannot enforce" 226 return fmt.Errorf(resp.Status) 227 } 228 229 // If any error happens, cleanup everything on exit so that we can recover 230 // by launcing a new remote. 231 defer func() { 232 if err != nil { 233 s.cleanup() 234 } 235 }() 236 237 if err = s.supervisor.Supervise(payload.ContextID, puInfo); err != nil { 238 resp.Status = err.Error() 239 return err 240 } 241 242 if err = s.enforcer.Enforce(s.ctx, payload.ContextID, puInfo); err != nil { 243 resp.Status = err.Error() 244 return err 245 } 246 247 resp.Status = "" 248 249 return nil 250 } 251 252 // Unenforce this method calls the unenforce method on the enforcer created from initenforcer 253 func (s *RemoteEnforcer) Unenforce(req rpcwrapper.Request, resp *rpcwrapper.Response) error { 254 255 if !s.rpcHandle.CheckValidity(&req, s.rpcSecret) { 256 resp.Status = "unenforce message auth failed" 257 return fmt.Errorf(resp.Status) 258 } 259 260 cmdLock.Lock() 261 defer cmdLock.Unlock() 262 263 s.statsClient.Send() // nolint: errcheck 264 265 payload, ok := req.Payload.(rpcwrapper.UnEnforcePayload) 266 if !ok { 267 resp.Status = "invalid unenforcer payload" 268 return fmt.Errorf(resp.Status) 269 } 270 271 var err error 272 273 // If any error happens, cleanup everything on exit so that we can recover 274 // by launcing a new remote. 275 defer func() { 276 if err != nil { 277 s.cleanup() 278 } 279 }() 280 281 if err = s.supervisor.Unsupervise(payload.ContextID); err != nil { 282 resp.Status = err.Error() 283 return fmt.Errorf("unable to clean supervisor: %s", err) 284 } 285 286 if err = s.enforcer.Unenforce(s.ctx, payload.ContextID); err != nil { 287 resp.Status = err.Error() 288 return fmt.Errorf("unable to stop enforcer: %s", err) 289 } 290 291 return nil 292 } 293 294 // SetTargetNetworks calls the same method on the actual enforcer 295 func (s *RemoteEnforcer) SetTargetNetworks(req rpcwrapper.Request, resp *rpcwrapper.Response) error { 296 297 var err error 298 if !s.rpcHandle.CheckValidity(&req, s.rpcSecret) { 299 resp.Status = "SetTargetNetworks message auth failed" // nolint 300 return fmt.Errorf(resp.Status) 301 } 302 303 cmdLock.Lock() 304 defer cmdLock.Unlock() 305 306 if s.enforcer == nil || s.supervisor == nil { 307 return fmt.Errorf(resp.Status) 308 } 309 310 payload := req.Payload.(rpcwrapper.SetTargetNetworksPayload) 311 312 // If any error happens, cleanup everything on exit so that we can recover 313 // by launcing a new remote. 314 defer func() { 315 if err != nil { 316 s.cleanup() 317 } 318 }() 319 320 if err = s.enforcer.SetTargetNetworks(payload.Configuration); err != nil { 321 return err 322 } 323 324 err = s.supervisor.SetTargetNetworks(payload.Configuration) 325 326 return err 327 } 328 329 // EnforcerExit is processing messages from the remote that are requesting an exit. In this 330 // case we simply cancel the context. 331 func (s *RemoteEnforcer) EnforcerExit(req rpcwrapper.Request, resp *rpcwrapper.Response) error { 332 333 s.cleanup() 334 335 s.exit <- true 336 337 return nil 338 } 339 340 // UpdateSecrets updates the secrets used by the remote enforcer 341 func (s *RemoteEnforcer) UpdateSecrets(req rpcwrapper.Request, resp *rpcwrapper.Response) error { 342 var err error 343 if !s.rpcHandle.CheckValidity(&req, s.rpcSecret) { 344 resp.Status = "updatesecrets auth failed" 345 return fmt.Errorf(resp.Status) 346 } 347 348 cmdLock.Lock() 349 defer cmdLock.Unlock() 350 if s.enforcer == nil { 351 return fmt.Errorf(resp.Status) 352 } 353 354 // If any error happens, cleanup everything on exit so that we can recover 355 // by launcing a new remote. 356 defer func() { 357 if err != nil { 358 s.cleanup() 359 } 360 }() 361 362 payload := req.Payload.(rpcwrapper.UpdateSecretsPayload) 363 s.secrets, err = rpc.NewSecrets(payload.Secrets) 364 if err != nil { 365 return err 366 } 367 368 err = s.enforcer.UpdateSecrets(s.secrets) 369 370 return err 371 } 372 373 // EnableDatapathPacketTracing enable nfq datapath packet tracing 374 func (s *RemoteEnforcer) EnableDatapathPacketTracing(req rpcwrapper.Request, resp *rpcwrapper.Response) error { 375 376 if !s.rpcHandle.CheckValidity(&req, s.rpcSecret) { 377 resp.Status = "enable datapath packet tracing auth failed" 378 return fmt.Errorf(resp.Status) 379 } 380 381 cmdLock.Lock() 382 defer cmdLock.Unlock() 383 384 payload := req.Payload.(rpcwrapper.EnableDatapathPacketTracingPayLoad) 385 386 if err := s.enforcer.EnableDatapathPacketTracing(s.ctx, payload.ContextID, payload.Direction, payload.Interval); err != nil { 387 resp.Status = err.Error() 388 return err 389 } 390 391 resp.Status = "" 392 return nil 393 } 394 395 // EnableIPTablesPacketTracing enables iptables trace packet tracing 396 func (s *RemoteEnforcer) EnableIPTablesPacketTracing(req rpcwrapper.Request, resp *rpcwrapper.Response) error { 397 398 if !s.rpcHandle.CheckValidity(&req, s.rpcSecret) { 399 resp.Status = "enable iptable packet tracing auth failed" 400 return fmt.Errorf(resp.Status) 401 } 402 403 cmdLock.Lock() 404 defer cmdLock.Unlock() 405 406 payload := req.Payload.(rpcwrapper.EnableIPTablesPacketTracingPayLoad) 407 408 if err := s.supervisor.EnableIPTablesPacketTracing(s.ctx, payload.ContextID, payload.Interval); err != nil { 409 resp.Status = err.Error() 410 return err 411 } 412 413 resp.Status = "" 414 return nil 415 } 416 417 // Ping runs ping to the given config 418 func (s *RemoteEnforcer) Ping(req rpcwrapper.Request, resp *rpcwrapper.Response) error { 419 420 if !s.rpcHandle.CheckValidity(&req, s.rpcSecret) { 421 resp.Status = "ping auth failed" 422 return fmt.Errorf(resp.Status) 423 } 424 425 cmdLock.Lock() 426 defer cmdLock.Unlock() 427 428 payload := req.Payload.(rpcwrapper.PingPayload) 429 430 if err := s.enforcer.Ping(s.ctx, payload.ContextID, payload.PingConfig); err != nil { 431 resp.Status = err.Error() 432 return err 433 } 434 435 resp.Status = "" 436 return nil 437 } 438 439 // DebugCollect collects the desired debug information 440 func (s *RemoteEnforcer) DebugCollect(req rpcwrapper.Request, resp *rpcwrapper.Response) error { 441 442 if !s.rpcHandle.CheckValidity(&req, s.rpcSecret) { 443 resp.Status = "debug collect auth failed" 444 return fmt.Errorf(resp.Status) 445 } 446 447 cmdLock.Lock() 448 defer cmdLock.Unlock() 449 450 payload := req.Payload.(rpcwrapper.DebugCollectPayload) 451 452 var commandOutput string 453 var pid int 454 455 if payload.CommandExec != "" { 456 if values := strings.Split(payload.CommandExec, " "); len(values) >= 1 { 457 cmd := exec.CommandContext(s.ctx, values[0], values[1:]...) 458 output, err := cmd.CombinedOutput() 459 if err != nil { 460 resp.Status = err.Error() 461 return err 462 } 463 commandOutput = string(output) 464 } 465 } else if payload.PcapFilePath != "" { 466 cmd, err := diagnostics.StartTcpdump(s.ctx, payload.PcapFilePath, payload.PcapFilter) 467 if err != nil { 468 resp.Status = err.Error() 469 return err 470 } 471 472 // spawn goroutine to call Wait() so we don't have defunct child process 473 go func() { 474 if err := cmd.Wait(); err != nil { 475 zap.L().Warn("DebugCollect Wait failed on tcpdump process", zap.Error(err)) 476 } 477 }() 478 479 pid = cmd.Process.Pid 480 } else { 481 // otherwise, return pid of remote enforcer 482 pid = os.Getpid() 483 } 484 485 resp.Status = "" 486 resp.Payload = rpcwrapper.DebugCollectResponsePayload{ 487 ContextID: payload.ContextID, 488 PID: pid, 489 CommandOutput: commandOutput, 490 } 491 return nil 492 } 493 494 // SetLogLevel sets log level. 495 func (s *RemoteEnforcer) SetLogLevel(req rpcwrapper.Request, resp *rpcwrapper.Response) error { 496 497 if !s.rpcHandle.CheckValidity(&req, s.rpcSecret) { 498 resp.Status = "set log level auth failed" 499 return fmt.Errorf(resp.Status) 500 } 501 502 cmdLock.Lock() 503 defer cmdLock.Unlock() 504 if s.enforcer == nil { 505 return fmt.Errorf(resp.Status) 506 } 507 508 payload := req.Payload.(rpcwrapper.SetLogLevelPayload) 509 510 newLevel := triremeLogLevelToString(payload.Level) 511 if payload.Level != "" && s.config.logLevel != newLevel { 512 remotelog.SetupRemoteLogger(newLevel, s.config.logFormat, s.config.logID) // nolint: errcheck 513 s.config.logLevel = newLevel 514 515 if err := s.enforcer.SetLogLevel(payload.Level); err != nil { 516 resp.Status = err.Error() 517 return err 518 } 519 } 520 521 resp.Status = "" 522 return nil 523 } 524 525 func triremeLogLevelToString(level constants.LogLevel) string { 526 switch level { 527 case constants.Debug: 528 return "debug" 529 case constants.Trace: 530 return "trace" 531 case constants.Error: 532 return "error" 533 case constants.Info: 534 return "info" 535 case constants.Warn: 536 return "warn" 537 default: 538 return "info" 539 } 540 } 541 542 // setup an enforcer 543 func (s *RemoteEnforcer) setupEnforcer(payload *rpcwrapper.InitRequestPayload) error { 544 545 var err error 546 547 s.secrets, err = rpc.NewSecrets(payload.Secrets) 548 if err != nil { 549 return err 550 } 551 552 // we are usually always starting RemoteContainer enforcers, 553 // however, if envoy is requested, we change the mode to RemoteContainerEnvoyAuthorizer 554 mode := constants.RemoteContainer 555 if s.enforcerType == policy.EnvoyAuthorizerEnforcer { 556 mode = constants.RemoteContainerEnvoyAuthorizer 557 } 558 559 if s.enforcer, err = createEnforcer( 560 payload.MutualAuth, 561 s.fqConfig, 562 s.collector, 563 s.secrets, 564 payload.ServerID, 565 payload.Validity, 566 mode, 567 s.procMountPoint, 568 payload.ExternalIPCacheTimeout, 569 payload.PacketLogs, 570 payload.Configuration, 571 s.tokenIssuer, 572 payload.IsBPFEnabled, 573 s.agentVersion, 574 payload.ServiceMeshType, 575 ); err != nil || s.enforcer == nil { 576 return fmt.Errorf("Error while initializing remote enforcer, %s", err) 577 } 578 579 return nil 580 } 581 582 func (s *RemoteEnforcer) setupSupervisor(payload *rpcwrapper.InitRequestPayload) error { 583 584 // we are usually always starting RemoteContainer enforcers, 585 // however, if envoy is requested, we change the mode to RemoteContainerEnvoyAuthorizer 586 mode := constants.RemoteContainer 587 if s.enforcerType == policy.EnvoyAuthorizerEnforcer { 588 mode = constants.RemoteContainerEnvoyAuthorizer 589 } 590 591 h, err := createSupervisor( 592 s.collector, 593 s.enforcer, 594 mode, 595 payload.Configuration, 596 payload.IPv6Enabled, 597 payload.IPTablesLockfile, 598 ) 599 if err != nil { 600 return fmt.Errorf("unable to setup supervisor: %s", err) 601 } 602 s.supervisor = h 603 604 return nil 605 } 606 607 // cleanup cleans all the acls and any state of the local enforcer. 608 func (s *RemoteEnforcer) cleanup() { 609 610 if s.supervisor != nil { 611 if err := s.supervisor.CleanUp(); err != nil { 612 zap.L().Error("unable to clean supervisor state", zap.Error(err)) 613 } 614 } 615 616 if s.enforcer != nil { 617 if err := s.enforcer.CleanUp(); err != nil { 618 zap.L().Error("unable to clean enforcer state", zap.Error(err)) 619 } 620 } 621 622 if s.service != nil { 623 if err := s.service.Stop(); err != nil { 624 zap.L().Error("unable to clean service state", zap.Error(err)) 625 } 626 } 627 } 628 629 // LaunchRemoteEnforcer launches a remote enforcer 630 func LaunchRemoteEnforcer(ctx context.Context, logLevel, logFormat, logID string, numQueues int, agentVersion semver.Version) error { 631 632 // Before doing anything validate that we are in the right namespace. 633 if err := validateNamespace(); err != nil { 634 return err 635 } 636 637 namedPipe := os.Getenv(constants.EnvContextSocket) 638 secret := os.Getenv(constants.EnvRPCClientSecret) 639 if secret == "" { 640 zap.L().Fatal("No secret found") 641 } 642 os.Setenv(constants.EnvRPCClientSecret, "") // nolint: errcheck 643 644 flag := unix.SIGHUP 645 if err := unix.Prctl(unix.PR_SET_PDEATHSIG, uintptr(flag), 0, 0, 0); err != nil { 646 return err 647 } 648 649 enforcerType, err := policy.EnforcerTypeFromString(os.Getenv(constants.EnvEnforcerType)) 650 if err != nil { 651 return err 652 } 653 654 rpcHandle := rpcwrapper.NewRPCServer() 655 re, err := newRemoteEnforcer(ctx, rpcHandle, secret, nil, nil, nil, nil, logLevel, logFormat, logID, numQueues, enforcerType, agentVersion) 656 if err != nil { 657 return err 658 } 659 660 go func() { 661 if err := rpcHandle.StartServer(ctx, "unix", namedPipe, re); err != nil { 662 zap.L().Fatal("Failed to start the RPC server", zap.Error(err)) 663 } 664 }() 665 666 c := make(chan os.Signal, 1) 667 signal.Notify(c, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT) 668 669 select { 670 671 case <-re.exit: 672 zap.L().Info("Remote enforcer exiting ...") 673 674 case sig := <-c: 675 zap.L().Warn("Remote enforcer received a signal. exiting ...", zap.Any("signal", sig)) 676 re.cleanup() 677 // TODO would be useful to set and return an exit code (instead of nil) to indicate the signal received 678 679 case <-ctx.Done(): 680 re.cleanup() 681 } 682 683 return nil 684 } 685 686 // getCEnvVariable returns an environment variable set in the c context 687 func getCEnvVariable(name string) string { 688 689 val := C.getenv(C.CString(name)) 690 if val == nil { 691 return "" 692 } 693 694 return C.GoString(val) 695 } 696 697 func validateNamespace() error { 698 // Check if successfully switched namespace 699 nsEnterState := getCEnvVariable(constants.EnvNsenterErrorState) 700 nsEnterLogMsg := getCEnvVariable(constants.EnvNsenterLogs) 701 if nsEnterState != "" { 702 return fmt.Errorf("nsErr: %s nsLogs: %s", nsEnterState, nsEnterLogMsg) 703 } 704 705 return nil 706 }