Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion sensor/common/virtualmachine/index/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import (
)

// Handler provides functionality to send virtual machine index reports to Central.
// It embeds ComplianceComponent (which itself embeds SensorComponent) so that
// compliance channel wiring is part of the compile-time contract.
type Handler interface {
common.SensorComponent
common.ComplianceComponent

Send(ctx context.Context, vm *v1.IndexReport) error
}
Expand All @@ -21,6 +23,7 @@ type Handler interface {
//
//go:generate mockgen-wrapper
type VirtualMachineStore interface {
Get(id virtualmachine.VMID) *virtualmachine.Info
GetFromCID(cid uint32) *virtualmachine.Info
}

Expand Down
91 changes: 78 additions & 13 deletions sensor/common/virtualmachine/index/handler_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/pkg/errors"
"github.com/stackrox/rox/generated/internalapi/central"
"github.com/stackrox/rox/generated/internalapi/sensor"
v1 "github.com/stackrox/rox/generated/internalapi/virtualmachine/v1"
"github.com/stackrox/rox/pkg/centralsensor"
"github.com/stackrox/rox/pkg/concurrency"
Expand All @@ -17,6 +18,7 @@ import (
"github.com/stackrox/rox/sensor/common"
"github.com/stackrox/rox/sensor/common/centralcaps"
"github.com/stackrox/rox/sensor/common/message"
"github.com/stackrox/rox/sensor/common/virtualmachine"
"github.com/stackrox/rox/sensor/common/virtualmachine/metrics"
)

Expand All @@ -34,12 +36,22 @@ type handlerImpl struct {
lock *sync.RWMutex
stopper concurrency.Stopper
toCentral <-chan *message.ExpiringMessage
toCompliance chan common.MessageToComplianceWithAddress
indexReports chan *v1.IndexReport
store VirtualMachineStore
}

func (h *handlerImpl) Capabilities() []centralsensor.SensorCapability {
return nil
return []centralsensor.SensorCapability{centralsensor.SensorACKSupport}
}

func (h *handlerImpl) Stopped() concurrency.ReadOnlyErrorSignal {
return h.stopper.Client().Stopped()
}

// ComplianceC returns a channel with messages destined for Compliance.
func (h *handlerImpl) ComplianceC() <-chan common.MessageToComplianceWithAddress {
return h.toCompliance
}

func (h *handlerImpl) Send(ctx context.Context, vm *v1.IndexReport) error {
Expand Down Expand Up @@ -118,7 +130,7 @@ func (h *handlerImpl) Accepts(msg *central.MsgToSensor) bool {
}

// ProcessMessage handles SensorACK messages for VM index reports.
func (h *handlerImpl) ProcessMessage(_ context.Context, msg *central.MsgToSensor) error {
func (h *handlerImpl) ProcessMessage(ctx context.Context, msg *central.MsgToSensor) error {
sensorAck := msg.GetSensorAck()
if sensorAck == nil || sensorAck.GetMessageType() != central.SensorACK_VM_INDEX_REPORT {
return nil
Expand All @@ -127,17 +139,9 @@ func (h *handlerImpl) ProcessMessage(_ context.Context, msg *central.MsgToSensor
vmID := sensorAck.GetResourceId()
action := sensorAck.GetAction()
reason := sensorAck.GetReason()
h.forwardToCompliance(ctx, vmID, action, reason)

switch action {
case central.SensorACK_ACK:
log.Debugf("Received ACK from Central for VM index report: vm_id=%s", vmID)
metrics.IndexReportAcksReceived.WithLabelValues(action.String()).Inc()
case central.SensorACK_NACK:
log.Warnf("Received NACK from Central for VM index report: vm_id=%s, reason=%s", vmID, reason)
metrics.IndexReportAcksReceived.WithLabelValues(action.String()).Inc()
// TODO(ROX-xxxxx): Implement retry logic or notifying VM relay.
// Currently, the VM relay has its own retry mechanism, but it's not aware of Central's rate limiting.
}
metrics.IndexReportAcksReceived.WithLabelValues(action.String()).Inc()

return nil
}
Expand All @@ -157,10 +161,11 @@ func (h *handlerImpl) Start() error {
log.Debug("Starting virtual machine handler")
h.lock.Lock()
defer h.lock.Unlock()
if h.toCentral != nil || h.indexReports != nil {
if h.toCentral != nil || h.indexReports != nil || h.toCompliance != nil {
return errStartMoreThanOnce
}
h.indexReports = make(chan *v1.IndexReport, env.VirtualMachinesIndexReportsBufferSize.IntegerSetting())
h.toCompliance = make(chan common.MessageToComplianceWithAddress, 1)
h.toCentral = h.run(h.indexReports)
return nil
}
Expand Down Expand Up @@ -212,6 +217,66 @@ func (h *handlerImpl) run(indexReports <-chan *v1.IndexReport) (toCentral <-chan
return ch2Central
}

func (h *handlerImpl) forwardToCompliance(
ctx context.Context,
resourceID string,
action central.SensorACK_Action,
reason string,
) {
if h.toCompliance == nil {
log.Debug("Compliance channel not initialized; skipping forwarding VM ACK/NACK")
return
}

var complianceAction sensor.MsgToCompliance_ComplianceACK_Action
switch action {
case central.SensorACK_ACK:
complianceAction = sensor.MsgToCompliance_ComplianceACK_ACK
case central.SensorACK_NACK:
complianceAction = sensor.MsgToCompliance_ComplianceACK_NACK
default:
log.Warnf("Unknown SensorACK action for VM index report: %v", action)
return
}

// Resolve the VM's host node so the compliance multiplexer can route to
// the correct compliance connection. If the VM is unknown (e.g. deleted
// between send and ACK), broadcast to all connections as a fallback.
var nodeName string
if resourceID != "" {
if vmInfo := h.store.Get(virtualmachine.VMID(resourceID)); vmInfo != nil {
nodeName = vmInfo.NodeName
}
}

msg := common.MessageToComplianceWithAddress{
Msg: &sensor.MsgToCompliance{
Msg: &sensor.MsgToCompliance_ComplianceAck{
ComplianceAck: &sensor.MsgToCompliance_ComplianceACK{
Action: complianceAction,
MessageType: sensor.MsgToCompliance_ComplianceACK_VM_INDEX_REPORT,
ResourceId: resourceID,
Reason: reason,
},
},
},
// Hostname is the node name (not the VM resource ID) used by the
// compliance multiplexer to route to the correct compliance connection.
Hostname: nodeName,
Broadcast: nodeName == "",
}

select {
case <-ctx.Done():
log.Warnf("Dropping VM ACK/NACK (resourceID=%s): %v", resourceID, ctx.Err())
case <-h.stopper.Flow().StopRequested():
log.Debugf("Dropping VM ACK/NACK (resourceID=%s) during shutdown", resourceID)
case h.toCompliance <- msg:
default:
log.Warnf("Dropping VM ACK/NACK (resourceID=%s): compliance queue is full", resourceID)
}
}

func (h *handlerImpl) handleIndexReport(
toCentral chan *message.ExpiringMessage,
indexReport *v1.IndexReport,
Expand Down
Loading
Loading