By providing Rust bindings for Osquery this crate facilitates the implementation of Osquery extensions.
- âś… Table plugins - Create custom tables to query system information
- âś… Logger plugins - Implement custom logging backends for osquery
- âś… Config plugins - Provide custom configuration sources for osquery
- âś… Writable tables - Support for INSERT, UPDATE, and DELETE operations
- 🦀 Pure Rust - No C/C++ dependencies, just safe Rust code
- 🚀 High performance - Minimal overhead for extensions
- 📦 Easy to use - Simple API with examples to get started quickly
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 table plugin that reports system uptime:
use osquery_rust_ng::prelude::*;
#[derive(Default)]
struct UptimeTable;
impl ReadOnlyTable for UptimeTable {
fn name(&self) -> &str {
"uptime"
}
fn columns(&self) -> Vec<ColumnDef> {
vec![
ColumnDef::new("days", ColumnType::Integer),
ColumnDef::new("hours", ColumnType::Integer),
ColumnDef::new("minutes", ColumnType::Integer),
ColumnDef::new("seconds", ColumnType::Integer),
]
}
fn generate(&self, _constraints: &QueryConstraints) -> Result<Vec<Row>, String> {
let uptime_seconds = std::fs::read_to_string("/proc/uptime")
.map_err(|e| e.to_string())?
.split_whitespace()
.next()
.ok_or("Failed to parse uptime")?
.parse::<f64>()
.map_err(|e| e.to_string())? as u64;
let days = uptime_seconds / 86400;
let hours = (uptime_seconds % 86400) / 3600;
let minutes = (uptime_seconds % 3600) / 60;
let seconds = uptime_seconds % 60;
Ok(vec![Row::from_iter([
("days", days.to_string()),
("hours", hours.to_string()),
("minutes", minutes.to_string()),
("seconds", seconds.to_string()),
])])
}
}
fn main() {
let mut server = Server::new(None, "/path/to/osquery/socket").unwrap();
server.register_plugin(Plugin::table(UptimeTable::default()));
server.run().unwrap();
}
Table plugins allow you to expose data as SQL tables in osquery. There are two types:
- Read-only tables - Implement the
ReadOnlyTable
trait - Writable tables - Implement the
Table
trait for full CRUD operations
See the examples directory for complete implementations.
Logger plugins receive log data from osquery and can forward it to various backends:
use osquery_rust_ng::plugin::{LoggerPlugin, LogStatus};
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(())
}
}
Config 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:
- Direct loading:
osqueryi --extension /path/to/extension
- Socket connection: Run extension separately with
--socket /path/to/osquery.sock
- Auto-loading: Place extension in osquery's autoload directory
See the examples README for detailed integration instructions.
The repository includes several complete examples:
- table-proc-meminfo - Exposes
/proc/meminfo
as a queryable table - writeable-table - Demonstrates INSERT, UPDATE, DELETE operations
- two-tables - Shows how to register multiple tables in one extension
- logger-file - Logger plugin that writes to files
- logger-syslog - Logger plugin that sends logs to syslog
- config-file - Config plugin that loads configuration from JSON files
- config-static - Config plugin that provides static configuration
Each example includes its own README with specific build and usage instructions.
We welcome contributions! Here's how to get started:
- Fork and clone the repository
- Install the pre-commit hook:
cp .hooks/pre-commit .git/hooks/pre-commit chmod +x .git/hooks/pre-commit
This project maintains high code quality standards:
- All code must pass
cargo fmt
- No clippy warnings allowed (enforced by CI)
- All tests must pass
- Unsafe code must be documented
The pre-commit hook automatically runs these checks.
Run the full test suite:
cargo test --workspace
- Create a feature branch from
main
- Write tests for new functionality
- Ensure all checks pass
- Submit a PR with a clear description
- Address review feedback
Please report issues on GitHub with:
- osquery version
- Rust version
- Operating system
- Steps to reproduce
- Expected vs actual behavior
The project is organized as a Cargo workspace:
- osquery-rust/ - The main library crate with Thrift bindings and plugin framework
- examples/ - Working examples demonstrating different plugin types:
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 config
- Tutorial: osquery-rust tutorial
- Examples: osquery-rust by example
- Documentation: docs.rs/osquery-rust
This 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.