| Crates.io | osquery-rust-ng |
| lib.rs | osquery-rust-ng |
| version | 2.0.0 |
| created_at | 2025-07-01 23:33:17.926379+00 |
| updated_at | 2025-12-07 16:34:59.021753+00 |
| description | Rust bindings for Osquery |
| homepage | https://github.com/withzombies |
| repository | https://github.com/withzombies/osquery-rust |
| max_upload_size | |
| id | 1734219 |
| size | 198,477 |
By providing Rust bindings for Osquery this crate facilitates the implementation of Osquery extensions.
Clone the repository and build the workspace:
git clone https://github.com/withzombies/osquery-rust.git
cd osquery-rust
cargo build --workspace
Run tests:
cargo test --workspace
The project uses a workspace structure with the main library and several examples. All examples are built automatically when you build the workspace.
Here's a simple example of creating a read-only table plugin:
use osquery_rust_ng::prelude::*;
use osquery_rust_ng::plugin::{ColumnDef, ColumnType, ColumnOptions, Plugin, ReadOnlyTable};
use std::collections::BTreeMap;
struct MyTable;
impl ReadOnlyTable for MyTable {
fn name(&self) -> String {
"my_table".to_string()
}
fn columns(&self) -> Vec<ColumnDef> {
vec![
ColumnDef::new("greeting", ColumnType::Text, ColumnOptions::DEFAULT),
ColumnDef::new("count", ColumnType::Integer, ColumnOptions::DEFAULT),
]
}
fn generate(&self, _req: ExtensionPluginRequest) -> ExtensionResponse {
let row = BTreeMap::from([
("greeting".to_string(), "Hello, osquery!".to_string()),
("count".to_string(), "42".to_string()),
]);
ExtensionResponse::new(ExtensionStatus::default(), vec![row])
}
fn shutdown(&self) {
// Called when the extension is shutting down
}
}
fn main() -> std::io::Result<()> {
let mut server = Server::new(None, "/path/to/osquery/socket")?;
server.register_plugin(Plugin::readonly_table(MyTable));
server.run().map_err(std::io::Error::other)
}
Version 2.0 simplifies the shutdown API by removing ShutdownReason. If upgrading from 1.x:
Before (1.x):
fn shutdown(&self, reason: ShutdownReason) {
println!("Shutting down: {reason}");
}
After (2.0):
fn shutdown(&self) {
println!("Shutting down");
}
This affects all plugin traits: ReadOnlyTable, Table, LoggerPlugin, and ConfigPlugin.
The Server::stop() and ServerStopHandle::stop() methods also no longer take a reason parameter.
Table plugins allow you to expose data as SQL tables in osquery. There are two types:
ReadOnlyTable traitTable trait for full CRUD operationsLogger plugins receive log data from osquery and can forward it to various backends:
use osquery_rust_ng::plugin::{LoggerPlugin, LogStatus, LoggerFeatures};
struct MyLogger;
impl LoggerPlugin for MyLogger {
fn name(&self) -> String {
"my_logger".to_string()
}
fn log_string(&self, message: &str) -> Result<(), String> {
println!("Log: {}", message);
Ok(())
}
fn log_status(&self, status: &LogStatus) -> Result<(), String> {
println!("[{}] {}:{} - {}",
status.severity, status.filename, status.line, status.message);
Ok(())
}
// Advertise support for status logs (enabled by default)
fn features(&self) -> i32 {
LoggerFeatures::LOG_STATUS
}
}
The features() method tells osquery what log types your plugin supports. By default, loggers receive status logs (INFO/WARNING/ERROR from osquery internals). Available features:
LoggerFeatures::BLANK - Query results onlyLoggerFeatures::LOG_STATUS - Status logs (default)LoggerFeatures::LOG_EVENT - Event logsConfig plugins provide configuration data to osquery, allowing dynamic configuration management:
use osquery_rust_ng::plugin::ConfigPlugin;
use std::collections::HashMap;
struct MyConfig;
impl ConfigPlugin for MyConfig {
fn name(&self) -> String {
"my_config".to_string()
}
fn gen_config(&self) -> Result<HashMap<String, String>, String> {
let mut config_map = HashMap::new();
// Provide JSON configuration
let config = r#"{
"options": {
"host_identifier": "hostname",
"schedule_splay_percent": 10
},
"schedule": {
"heartbeat": {
"query": "SELECT version FROM osquery_info;",
"interval": 3600
}
}
}"#;
config_map.insert("main".to_string(), config.to_string());
Ok(config_map)
}
fn gen_pack(&self, name: &str, _value: &str) -> Result<String, String> {
// Optionally provide query packs
Err(format!("Pack '{}' not found", name))
}
}
There are three ways to run your extension:
osqueryi --extension /path/to/extension--socket /path/to/osquery.sockSee the examples directory for complete implementations.
Extensions support graceful shutdown through multiple mechanisms:
Signal Handling (recommended for production)
Use run_with_signal_handling() to automatically handle SIGTERM and SIGINT:
fn main() -> std::io::Result<()> {
let mut server = Server::new(None, "/path/to/socket")?;
server.register_plugin(Plugin::readonly_table(MyTable));
// Handles SIGTERM (systemd) and SIGINT (Ctrl+C)
server.run_with_signal_handling().map_err(std::io::Error::other)
}
Programmatic Shutdown
Use ServerStopHandle to stop the server from another thread:
let mut server = Server::new(None, "/path/to/socket")?;
let handle = server.get_stop_handle();
// In another thread or signal handler:
std::thread::spawn(move || {
// ... wait for condition ...
handle.stop();
});
server.run()?;
Shutdown Lifecycle
When shutdown is triggered (via signal, osquery RPC, or stop()):
shutdown() callbackrun() returnsThe repository includes several complete examples:
/proc/meminfo as a queryable tableEach example includes its own README with specific build and usage instructions.
We welcome contributions! Here's how to get started:
cp .hooks/pre-commit .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
This project maintains high code quality standards:
cargo fmtThe pre-commit hook automatically runs these checks.
Run the full test suite:
cargo test --workspace
mainPlease report issues on GitHub with:
The project is organized as a Cargo workspace:
table-proc-meminfo/ - Read-only table examplewriteable-table/ - Full CRUD table exampletwo-tables/ - Multiple tables in one extensionlogger-file/ - File logger pluginlogger-syslog/ - Syslog logger pluginconfig-file/ - An example that loads a config from a json fileconfig-static/ - An example that provides a static configThis project contributed the support for Unix Domain Sockets to Apache Thrift's Rust crate.
This project was initially forked from polarlab's osquery-rust project.