github.com/castai/kvisor@v1.7.1-0.20240516114728-b3572a2607b5/pkg/ebpftracer/signature/signature.go (about)

     1  package signature
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"time"
     7  
     8  	castpb "github.com/castai/kvisor/api/v1/runtime"
     9  	"github.com/castai/kvisor/pkg/ebpftracer/events"
    10  	"github.com/castai/kvisor/pkg/ebpftracer/types"
    11  	"github.com/castai/kvisor/pkg/logging"
    12  )
    13  
    14  type SignatureMetadata struct {
    15  	ID           castpb.SignatureEventID
    16  	Name         string
    17  	Version      string
    18  	TargetEvents []events.ID
    19  }
    20  
    21  type SignatureEngineConfig struct {
    22  	InputChanSize          int `validate:"required"`
    23  	OutputChanSize         int `validate:"required"`
    24  	DefaultSignatureConfig DefaultSignatureConfig
    25  }
    26  
    27  type Signature interface {
    28  	GetMetadata() SignatureMetadata
    29  
    30  	OnEvent(event *types.Event) *castpb.SignatureFinding
    31  }
    32  
    33  type SignatureEngine struct {
    34  	log         *logging.Logger
    35  	inputEvents chan *types.Event
    36  	eventsChan  chan *castpb.Event
    37  	signatures  []Signature
    38  	// Map of precalculated singature metadata to reduce object churn in event handling loop
    39  	signaturesMetadata      map[Signature]*castpb.SignatureMetadata
    40  	eventsSignatureTriggers map[events.ID][]Signature
    41  }
    42  
    43  func NewEngine(signatures []Signature, log *logging.Logger, cfg SignatureEngineConfig) *SignatureEngine {
    44  	eventsSignatureTriggers, signaturesMetadata := buildLookupMaps(signatures)
    45  
    46  	return &SignatureEngine{
    47  		log:                     log.WithField("component", "signature_engine"),
    48  		inputEvents:             make(chan *types.Event, cfg.InputChanSize),
    49  		eventsChan:              make(chan *castpb.Event, cfg.OutputChanSize),
    50  		signatures:              signatures,
    51  		eventsSignatureTriggers: eventsSignatureTriggers,
    52  		signaturesMetadata:      signaturesMetadata,
    53  	}
    54  }
    55  
    56  func buildLookupMaps(signatures []Signature) (map[events.ID][]Signature, map[Signature]*castpb.SignatureMetadata) {
    57  	signaturesByEvent := map[events.ID][]Signature{}
    58  	signatureMetadata := map[Signature]*castpb.SignatureMetadata{}
    59  
    60  	for _, signature := range signatures {
    61  		metadata := signature.GetMetadata()
    62  		signatureMetadata[signature] = &castpb.SignatureMetadata{
    63  			Id:      metadata.ID,
    64  			Version: metadata.Version,
    65  		}
    66  
    67  		for _, event := range metadata.TargetEvents {
    68  			signaturesByEvent[event] = append(signaturesByEvent[event], signature)
    69  		}
    70  	}
    71  
    72  	return signaturesByEvent, signatureMetadata
    73  }
    74  
    75  func (e *SignatureEngine) TargetEvents() []events.ID {
    76  	result := make([]events.ID, 0, len(e.eventsSignatureTriggers))
    77  
    78  	for event := range e.eventsSignatureTriggers {
    79  		result = append(result, event)
    80  	}
    81  
    82  	return result
    83  }
    84  
    85  func (e *SignatureEngine) EventInput() chan<- *types.Event {
    86  	return e.inputEvents
    87  }
    88  
    89  func (e *SignatureEngine) Events() <-chan *castpb.Event {
    90  	return e.eventsChan
    91  }
    92  
    93  func (e *SignatureEngine) QueueEvent(event *types.Event) {
    94  	select {
    95  	case e.inputEvents <- event:
    96  	default:
    97  	}
    98  }
    99  
   100  func (e *SignatureEngine) Run(ctx context.Context) error {
   101  	e.log.Infof("running")
   102  	defer e.log.Infof("stopping")
   103  
   104  	for {
   105  		select {
   106  		case <-ctx.Done():
   107  			return ctx.Err()
   108  
   109  		case event := <-e.inputEvents:
   110  			e.handleEvent(event)
   111  		}
   112  	}
   113  }
   114  
   115  func (e *SignatureEngine) handleEvent(event *types.Event) {
   116  	signatures := e.eventsSignatureTriggers[event.Context.EventID]
   117  
   118  	for _, signature := range signatures {
   119  		finding := signature.OnEvent(event)
   120  
   121  		if finding == nil {
   122  			continue
   123  		}
   124  
   125  		metadata := e.signaturesMetadata[signature]
   126  
   127  		e.eventsChan <- &castpb.Event{
   128  			EventType:     castpb.EventType_EVENT_SIGNATURE,
   129  			Timestamp:     uint64(time.Now().UTC().UnixNano()),
   130  			ProcessName:   string(bytes.Trim(event.Context.Comm[:], "\x00")),
   131  			Namespace:     event.Container.PodNamespace,
   132  			PodName:       event.Container.PodName,
   133  			ContainerName: event.Container.Name,
   134  			PodUid:        event.Container.PodUID,
   135  			ContainerId:   event.Container.ID,
   136  			CgroupId:      event.Context.CgroupID,
   137  			HostPid:       event.Context.HostPid,
   138  			Data: &castpb.Event_Signature{
   139  				Signature: &castpb.SignatureEvent{
   140  					Metadata: metadata,
   141  					Finding:  finding,
   142  				},
   143  			},
   144  		}
   145  	}
   146  }