forked from navos/sheepd
✨ osquery
This commit is contained in:
parent
1c3a136730
commit
5c53c57d74
6 changed files with 86 additions and 4 deletions
|
@ -46,6 +46,11 @@ pub struct ShellParam {
|
||||||
pub cwd: String,
|
pub cwd: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct QueryParam {
|
||||||
|
pub query: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug)]
|
#[derive(Deserialize, Serialize, Debug)]
|
||||||
pub struct ShellResponse {
|
pub struct ShellResponse {
|
||||||
pub stdout: String,
|
pub stdout: String,
|
||||||
|
|
|
@ -14,7 +14,7 @@ mod herd_core;
|
||||||
use crate::herd_core::mqtt::{handle_mqtt, listen_to_devices};
|
use crate::herd_core::mqtt::{handle_mqtt, listen_to_devices};
|
||||||
use herd_core::{
|
use herd_core::{
|
||||||
config::Config,
|
config::Config,
|
||||||
route::{device_get_api, device_shell_cmd, join_device, login_user},
|
route::{device_get_api, device_osquery, device_shell_cmd, join_device, login_user},
|
||||||
};
|
};
|
||||||
use herd_core::{model::Machine, route::devices_list};
|
use herd_core::{model::Machine, route::devices_list};
|
||||||
use rumqttc::AsyncClient;
|
use rumqttc::AsyncClient;
|
||||||
|
@ -69,6 +69,7 @@ async fn main() {
|
||||||
.route("/login", post(login_user))
|
.route("/login", post(login_user))
|
||||||
.route("/device/{device_id}", get(device_get_api))
|
.route("/device/{device_id}", get(device_get_api))
|
||||||
.route("/device/{device_id}/shell", post(device_shell_cmd))
|
.route("/device/{device_id}/shell", post(device_shell_cmd))
|
||||||
|
.route("/device/{device_id}/osquery", post(device_osquery))
|
||||||
.route("/devices", get(devices_list));
|
.route("/devices", get(devices_list));
|
||||||
|
|
||||||
let app = Router::new().merge(device).merge(user);
|
let app = Router::new().merge(device).merge(user);
|
||||||
|
|
|
@ -42,6 +42,39 @@ macro_rules! check_admin {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn device_osquery(
|
||||||
|
Path(device_id): Path<String>,
|
||||||
|
APIUser(user): APIUser,
|
||||||
|
Json(payload): Json<api::QueryParam>,
|
||||||
|
) -> (StatusCode, Json<api::Result<String>>) {
|
||||||
|
check_admin!(user);
|
||||||
|
|
||||||
|
let machine: Option<Model<Machine>> = get!(device_id);
|
||||||
|
|
||||||
|
if let Some(machine) = machine {
|
||||||
|
let resp = send_msg(
|
||||||
|
crate::MQTT.get().unwrap(),
|
||||||
|
&machine,
|
||||||
|
ClientAction::new(ClientActions::OSQuery(payload.query)),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
if let Some(resp) = resp.wait_for(std::time::Duration::from_secs(60)).await {
|
||||||
|
let r = match resp.response {
|
||||||
|
api::ServerResponses::OSQuery(res) => res,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
(StatusCode::OK, Json(api::Result::OkVal(r)))
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
StatusCode::BAD_GATEWAY,
|
||||||
|
Json(api::Result::Err("Did not receive response from device")),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(StatusCode::NOT_FOUND, Json(api::Result::Err("Not Found")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn device_shell_cmd(
|
pub async fn device_shell_cmd(
|
||||||
Path(device_id): Path<String>,
|
Path(device_id): Path<String>,
|
||||||
APIUser(user): APIUser,
|
APIUser(user): APIUser,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use sheepctl_core::{
|
use sheepctl_core::{
|
||||||
args::{DeviceCommands, SheepctlArgs, SheepctlCommand},
|
args::{DeviceCommands, SheepctlArgs, SheepctlCommand},
|
||||||
cmd::{interactive_shell, list_devices, login},
|
cmd::{interactive_shell, list_devices, login, run_osquery},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod api;
|
mod api;
|
||||||
|
@ -15,5 +15,6 @@ fn main() {
|
||||||
},
|
},
|
||||||
SheepctlCommand::Login(login_command) => login(login_command),
|
SheepctlCommand::Login(login_command) => login(login_command),
|
||||||
SheepctlCommand::Shell(shell_command) => interactive_shell(shell_command),
|
SheepctlCommand::Shell(shell_command) => interactive_shell(shell_command),
|
||||||
|
SheepctlCommand::Query(query_command) => run_osquery(query_command),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ pub enum SheepctlCommand {
|
||||||
Login(LoginCommand),
|
Login(LoginCommand),
|
||||||
Device(DeviceCommand),
|
Device(DeviceCommand),
|
||||||
Shell(ShellCommand),
|
Shell(ShellCommand),
|
||||||
|
Query(QueryCommand),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(FromArgs, PartialEq, Debug)]
|
#[derive(FromArgs, PartialEq, Debug)]
|
||||||
|
@ -59,3 +60,15 @@ pub struct ShellCommand {
|
||||||
/// device ID
|
/// device ID
|
||||||
pub device: String,
|
pub device: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(FromArgs, PartialEq, Debug)]
|
||||||
|
#[argh(subcommand, name = "query")]
|
||||||
|
/// Run an osquery
|
||||||
|
pub struct QueryCommand {
|
||||||
|
#[argh(positional)]
|
||||||
|
/// device ID
|
||||||
|
pub device: String,
|
||||||
|
#[argh(positional)]
|
||||||
|
/// query
|
||||||
|
pub query: String,
|
||||||
|
}
|
||||||
|
|
|
@ -3,8 +3,8 @@ use std::{io::Write, path::PathBuf};
|
||||||
use owl::{Deserialize, Serialize};
|
use owl::{Deserialize, Serialize};
|
||||||
use sheepd::{DeviceList, LoginParam, ShellResponse};
|
use sheepd::{DeviceList, LoginParam, ShellResponse};
|
||||||
|
|
||||||
use super::args::{ListDevicesCommand, LoginCommand, ShellCommand};
|
use super::args::{ListDevicesCommand, LoginCommand, QueryCommand, ShellCommand};
|
||||||
use crate::api::{DeviceEntry, ShellParam, domain};
|
use crate::api::{DeviceEntry, QueryParam, ShellParam, domain};
|
||||||
|
|
||||||
/// Make an POST API call to `path` with `data` returning `Result<T>`
|
/// Make an POST API call to `path` with `data` returning `Result<T>`
|
||||||
pub fn api_call<T: Serialize + for<'a> Deserialize<'a>, I: Serialize>(
|
pub fn api_call<T: Serialize + for<'a> Deserialize<'a>, I: Serialize>(
|
||||||
|
@ -147,6 +147,35 @@ pub fn interactive_shell(arg: ShellCommand) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn run_osquery(args: QueryCommand) {
|
||||||
|
// TODO : sanity checks
|
||||||
|
|
||||||
|
let conf = CtlConfig::load().unwrap();
|
||||||
|
let machine = args.device;
|
||||||
|
|
||||||
|
if let Some(machine) = get_machine_api(&conf.home, &conf.token, &machine) {
|
||||||
|
if !machine.online {
|
||||||
|
println!("Device not online.");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = api_call_post_auth::<String, _>(
|
||||||
|
&conf.home,
|
||||||
|
&format!("device/{}/osquery", machine.id),
|
||||||
|
&conf.token,
|
||||||
|
QueryParam { query: args.query },
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Ok(res) = res.as_result() {
|
||||||
|
println!("{res}");
|
||||||
|
} else {
|
||||||
|
println!("Error doing query");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("No device with ID {machine}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn login(arg: LoginCommand) {
|
pub fn login(arg: LoginCommand) {
|
||||||
if let Some(conf) = CtlConfig::load() {
|
if let Some(conf) = CtlConfig::load() {
|
||||||
println!("You are already logged in to {}", conf.home);
|
println!("You are already logged in to {}", conf.home);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue