Skip to main content

Sensors and Virtual Sensors

Introduction

This module handles physical sensor registration, one-time host enrollment, interface inventory management, host heartbeat and metrics ingestion, and virtual sensor lifecycle management. A physical sensor is the hardware host, while a virtual sensor is a deployable Suricata workload that consumes two eligible interfaces on that host.

The sensor orchestration has been split into focused services:

  • SensorsService — Core CRUD and sensor management (480 lines)
  • SensorInterfaceAllocationService — Network interface pool validation
  • ResilientHttpService — Circuit breaker + retry for external HTTP calls
  • VirtualSensorsService — Virtual sensor lifecycle management
  • SensorHeartbeatService — Heartbeat processing

All controllers delegate to service interfaces. Repositories (SensorRepository, SensorHeartbeatRepository, VirtualSensorRepository) handle data access.


Controllers

ControllerRouteDescription
SensorsControllerapi/sensorsSensor CRUD and lifecycle
VirtualSensorsControllerapi/virtual-sensorsVirtual sensor operations
SensorHeartbeatControllerapi/sensor-heartbeatHeartbeat processing

Files:

  • API/Controllers/Sensors/SensorsController.cs
  • API/Controllers/Sensors/VirtualSensorsController.cs
  • API/Controllers/Sensors/SensorHeartbeatController.cs

Sensor Entity

File: Domain/Entities/Sensor.cs Table: sensors

ColumnTypeConstraintsDescription
iduuidPKPrimary key (UUIDv7)
namevarchar(255)RequiredSensor display name
organization_iduuidFK, RequiredOwning organization
statusvarchar(50)Requiredunknown, off, on
operational_statusvarchar(50)active, inactive, maintenance
sensor_ipvarchar(50)NullableSensor IP address
sensor_portintNullableSensor port number
sudo_uservarchar(100)NullableSSH sudo user for deployment
hostnamevarchar(255)NullableHostname reported by sensor agent
os_infovarchar(255)NullableOperating system information
architecturevarchar(100)NullableReported CPU architecture
agent_versionvarchar(100)NullableInstalled host agent version
enrollment_statusvarchar(50)Requiredpending, enrolled
management_interfacevarchar(255)NullableManagement network interface
last_heartbeat_attimestampNullableLast agent heartbeat received
last_inventory_attimestampNullableLast interface inventory update
created_attimestampCreation timestamp
updated_attimestampLast update timestamp
[ForeignKey("OrganizationId")]
public Organization Organization { get; set; } = null!;
public ICollection<SensorHeartbeat> Heartbeats { get; set; }
public ICollection<VirtualSensor> VirtualSensors { get; set; }
public ICollection<SensorNetworkInterface> Interfaces { get; set; }

Status Model

Sensors have two status dimensions:

Status (connectivity):

ValueMeaning
unknownInitial state, never connected
offSensor is offline
onSensor is online and reporting

Operational Status (operational):

ValueMeaning
activeNormal operation
inactiveDisabled by administrator
maintenanceUnder maintenance

Virtual Sensor Entity

File: Domain/Entities/VirtualSensor.cs Table: virtual_sensors

ColumnTypeConstraintsDescription
iduuidPKPrimary key (UUIDv7)
sensor_iduuidFK, RequiredParent sensor
namevarchar(255)RequiredVirtual sensor name
interface1varchar(255)RequiredFirst network interface
interface2varchar(255)RequiredSecond network interface
home_netvarchar(255)Home network CIDR (default: 192.168.0.0/16)
statusvarchar(50)RequiredLifecycle status
activation_statusvarchar(50)NullableAnsible activation status
activation_errortextNullableError message from failed activation
execution_idvarchar(255)NullableAnsible execution ID
deployment_pathvarchar(255)NullableDeployment target path
created_attimestampCreation timestamp
updated_attimestampLast update timestamp

Virtual Sensor Status Model

Status (lifecycle):

ValueMeaning
inactiveCreated but not yet activated
activatingActivation in progress via Ansible
activeSuccessfully activated and running
failedActivation failed

Activation Status (Ansible execution):

ValueMeaning
pendingQueued for activation
runningAnsible playbook executing
successActivation completed successfully
failedActivation failed (see error)

Sensor Heartbeat Entity

File: Domain/Entities/SensorHeartbeat.cs Table: sensor_heartbeats

ColumnTypeConstraintsDescription
iduuidPKPrimary key (UUIDv7)
sensor_iduuidFK, RequiredReporting sensor
last_seentimestampNullableLast seen timestamp
isActiveboolNullableActive flag
created_attimestampCreation timestamp
updated_attimestampLast update timestamp

Sensor Responsibilities

Registration and Enrollment

  • Create a sensor draft with name and organization
  • Generate a one-time install command from the UI
  • Install one host agent on the physical sensor
  • Enroll the sensor and persist host metadata plus a structured interface inventory

Activation Orchestration

  • Activate or deactivate virtual sensors via Ansible service
  • Track activation progress and status
  • Deploy only Suricata and sensor-client for each virtual sensor

Heartbeat Processing

  • Receive heartbeat reports from the host agent
  • Update last_heartbeat_at and last_inventory_at on sensor
  • Store online status in InfluxDB sensor_status
  • Store host CPU, memory, and eligible interface traffic in InfluxDB sensor_metrics

Virtual Sensor Responsibilities

Lifecycle Management

  • Create virtual sensors attached to a physical sensor
  • Configure interface pairs (interface1, interface2) and home network
  • Track deployment path and execution state

Activation via Ansible

  • Submit activation request to Ansible service at AnsibleSettings:ServiceUrl
  • Pass configuration including:
    • Interface assignments
    • InfluxDB connection details
    • Data collector endpoint
    • Docker registry credentials
    • Backend URL for callbacks

Interface Allocation

  • Interfaces are stored as structured records on the parent sensor
  • Virtual sensor creation requires two explicit interfaces
  • Only interfaces marked isEligible=true can be assigned
  • Assignment state is tracked on the interface record itself

External Dependencies

DependencyConfig KeyPurpose
Ansible ServiceAnsibleSettings:ServiceUrlVirtual sensor deployment
Sensor API KeySensorApiSettings:ApiKeySensor trigger authentication
Backend URLBackendUrlCallback URL for Ansible
Data CollectorDataCollector:Endpoint/PortSensor data destination
Docker RegistryDockerRegistry:Registry/Username/PasswordContainer image source
InfluxDBInfluxDb:*Metrics and heartbeat streams

These settings are aggregated into SensorRuntimeOptions for convenience:

public sealed class SensorRuntimeOptions
{
public string BackendUrl { get; set; }
public string SensorApiKey { get; set; }
public string AnsibleServiceUrl { get; set; }
public string InfluxDbUrl { get; set; }
public string InfluxDbToken { get; set; }
public string InfluxDbOrg { get; set; }
public string InfluxDbBucket { get; set; }
public string DataCollectorEndpoint { get; set; }
public string DataCollectorPort { get; set; }
public string DockerRegistry { get; set; }
public string DockerUsername { get; set; }
public string DockerPassword { get; set; }
}

gRPC Healthcheck Service

File: Infrastructure/Services/SensorHealthcheckService.cs Proto: Protos/sensor_healthcheck.proto

Provides a gRPC endpoint for sensors to report health status. Mapped in the middleware pipeline:

app.MapGrpcService<SensorHealthcheckService>();

Cascade Delete Rules

Parent → ChildBehavior
Organization → SensorCascade
Sensor → SensorHeartbeatCascade
Sensor → VirtualSensorCascade

Key Risk Areas

AreaRisk
Activation orchestrationMulti-step flow relies on external Ansible response contracts
No integration testsCritical activation paths lack test coverage
MonitorExecutionAsyncFire-and-forget Task.Run for monitoring Ansible execution status — no error reporting if monitoring fails

Refactoring Status

Completed

  1. Split sensor orchestration service into focused services.
  2. Added dedicated VirtualSensorsService lifecycle handling.
  3. Added resilient outbound execution path for Ansible interactions.
  4. Moved interface allocation logic into dedicated service.
  5. Kept controllers focused on transport and delegation.

Open

  1. Add integration tests for activation and rollback flows.