diff --git a/.env b/.env new file mode 100644 index 0000000..001cab7 --- /dev/null +++ b/.env @@ -0,0 +1,3 @@ +ADMIN_AUTH_TOKEN=464af63dbd241969baa1e94b2461d94d +POSTGRES_PASSWORD=12348756 +DATABASE_URL=postgres://postgres:${POSTGRES_PASSWORD}@u_db/u_db diff --git a/Makefile b/Makefile index 523cf6b..8c51d78 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,18 @@ -.PHONY: build run clean +.PHONY: _pre_build debug release run clean CARGO=./scripts/cargo_musl.sh clean: ${CARGO} clean -build: - cd muslrust +_pre_build: docker build -t unki/musllibs ./muslrust - cd - - ${CARGO} build $@ + +debug: _pre_build + ${CARGO} build + +release: _pre_build + ${CARGO} build --release run: build ${CARGO} run \ No newline at end of file diff --git a/bin/u_panel/src/main.rs b/bin/u_panel/src/main.rs index e6b8990..d072d56 100644 --- a/bin/u_panel/src/main.rs +++ b/bin/u_panel/src/main.rs @@ -1,5 +1,6 @@ +use std::env; use structopt::StructOpt; -use u_lib::{api::ClientHandler, models::JobMeta, UError}; +use u_lib::{api::ClientHandler, models::JobMeta, utils::init_env, UError}; use uuid::Uuid; const DELIM: &'static str = "*************\n"; @@ -72,7 +73,8 @@ fn parse_uuid(src: &str) -> Result { } async fn process_cmd(cmd: Cmd) -> Result<(), UError> { - let cli_handler = ClientHandler::new(None).password("123qwe".to_string()); + let token = env::var("ADMIN_AUTH_TOKEN").unwrap(); + let cli_handler = ClientHandler::new(None).password(token); match cmd { Cmd::Agents(action) => match action { LD::List { uid } => cli_handler @@ -123,6 +125,7 @@ async fn process_cmd(cmd: Cmd) -> Result<(), UError> { #[tokio::main] async fn main() -> Result<(), UError> { + init_env(); let args: Args = Args::from_args(); process_cmd(args.cmd).await } diff --git a/bin/u_server/Cargo.toml b/bin/u_server/Cargo.toml index 2b53483..28c5467 100644 --- a/bin/u_server/Cargo.toml +++ b/bin/u_server/Cargo.toml @@ -5,7 +5,6 @@ name = "u_server" version = "0.1.0" [dependencies] -dotenv = "0.15.0" env_logger = "0.7.1" log = "0.4.11" thiserror = "*" @@ -34,4 +33,12 @@ path = "../../lib/u_lib" version = "*" [dev-dependencies] -test-case = "1.1.0" \ No newline at end of file +test-case = "1.1.0" + +[lib] +name = "u_server_lib" +path = "src/lib.rs" + +[[bin]] +name = "u_server" +path = "src/main.rs" \ No newline at end of file diff --git a/bin/u_server/src/db.rs b/bin/u_server/src/db.rs index 9fa0d0c..a3ab7fb 100644 --- a/bin/u_server/src/db.rs +++ b/bin/u_server/src/db.rs @@ -1,5 +1,4 @@ use diesel::{pg::PgConnection, prelude::*, result::Error as DslError}; -use dotenv::dotenv; use once_cell::sync::OnceCell; use std::{ env, @@ -21,7 +20,6 @@ static DB: OnceCell>> = OnceCell::new(); impl UDB { pub fn lock_db() -> MutexGuard<'static, UDB> { DB.get_or_init(|| { - dotenv().ok(); let db_path = env::var("DATABASE_URL").unwrap(); let conn = PgConnection::establish(&db_path).unwrap(); let instance = UDB { conn }; diff --git a/bin/u_server/src/handlers.rs b/bin/u_server/src/handlers.rs index 8b0d235..76d26de 100644 --- a/bin/u_server/src/handlers.rs +++ b/bin/u_server/src/handlers.rs @@ -4,7 +4,7 @@ use hyper::Body; use serde::Serialize; use u_lib::{ messaging::{AsMsg, BaseMessage}, - models::{Agent, AssignedJob, ExecResult, JobMeta, JobState}, + models::{Agent, AgentState, AssignedJob, ExecResult, JobMeta, JobState}, ULocalError, }; use uuid::Uuid; @@ -76,9 +76,11 @@ impl Endpoints { let result = UDB::lock_db().get_exact_jobs(uid, personal); match result { Ok(r) => { - let db = UDB::lock_db(); - for j in r.iter() { - db.update_job_status(j.id, JobState::Running).ok(); + if personal { + let db = UDB::lock_db(); + for j in r.iter() { + db.update_job_status(j.id, JobState::Running).ok(); + } } Ok(build_message(r)) } @@ -140,7 +142,8 @@ impl Endpoints { failed.push(e.to_string()) } } - ExecResult::Agent(a) => { + ExecResult::Agent(mut a) => { + a.state = AgentState::Active; Self::add_agent(a).await?; } } diff --git a/bin/u_server/src/lib.rs b/bin/u_server/src/lib.rs index b2b3406..c17717d 100644 --- a/bin/u_server/src/lib.rs +++ b/bin/u_server/src/lib.rs @@ -12,14 +12,22 @@ extern crate mockall; #[macro_use] extern crate mockall_double; +// because of linking errors +extern crate openssl; +#[macro_use] +extern crate diesel; +// + use db::UDB; #[double] use handlers::Endpoints; use serde::de::DeserializeOwned; +use std::env; use u_lib::{ config::MASTER_PORT, messaging::{AsMsg, BaseMessage}, models::*, + utils::init_env, }; use uuid::Uuid; @@ -40,6 +48,7 @@ fn prefill_jobs() { } fn init() { + init_env(); env_logger::init(); prefill_jobs(); } @@ -99,11 +108,12 @@ fn make_filters() -> impl Filter + C .and(warp::path("report")) .and(get_content::>().and_then(Endpoints::report)); - let auth_token = warp::header::exact("authorization", "Bearer 123qwe"); + let auth_token = format!("Bearer {}", env::var("ADMIN_AUTH_TOKEN").unwrap()).into_boxed_str(); + let authenticated = warp::header::exact("authorization", Box::leak(auth_token)); let agent_zone = get_jobs.clone().or(get_personal_jobs).or(report); - let auth_zone = auth_token.and( + let auth_zone = authenticated.and( get_agents .or(get_jobs) .or(upload_jobs) diff --git a/bin/u_server/src/main.rs b/bin/u_server/src/main.rs index a6ac83b..05ec4e0 100644 --- a/bin/u_server/src/main.rs +++ b/bin/u_server/src/main.rs @@ -1,8 +1,4 @@ -extern crate openssl; -#[macro_use] -extern crate diesel; - -use u_server::serve; +use u_server_lib::serve; #[tokio::main] async fn main() { diff --git a/integration/.env b/integration/.env deleted file mode 100644 index f22b345..0000000 --- a/integration/.env +++ /dev/null @@ -1,2 +0,0 @@ -PG_PASSWORD=12348756 -DATABASE_URL=postgres://postgres:${PG_PASSWORD}@u_db/u_db diff --git a/integration/Cargo.toml b/integration/Cargo.toml index c5cc48c..fb5000f 100644 --- a/integration/Cargo.toml +++ b/integration/Cargo.toml @@ -13,6 +13,7 @@ env_logger = "0.8.3" uuid = { version = "0.6.5", features = ["serde", "v4"] } reqwest = { version = "0.11", features = ["json"] } serde_json = "1.0" +serde = { version = "1.0.114", features = ["derive"] } futures = "0.3.5" diff --git a/integration/docker-compose.yml b/integration/docker-compose.yml index e82e0c9..27d7530 100644 --- a/integration/docker-compose.yml +++ b/integration/docker-compose.yml @@ -22,7 +22,7 @@ services: environment: RUST_LOG: warp env_file: - - .env + - ../.env healthcheck: test: /bin/ss -tlpn | grep 63714 interval: 5s @@ -35,8 +35,8 @@ services: - u_net expose: - '5432' - environment: - - POSTGRES_PASSWORD=${PG_PASSWORD} + env_file: + - ../.env healthcheck: test: /bin/ss -tlpn | grep 5432 interval: 5s @@ -71,6 +71,7 @@ services: - u_net volumes: - ./:/tests/ + - ../target/x86_64-unknown-linux-musl/release/u_panel:/u_panel - ~/.cargo/registry:/root/.cargo/registry working_dir: /tests/ @@ -80,4 +81,6 @@ services: u_agent_2: condition: service_started u_server: - condition: service_healthy \ No newline at end of file + condition: service_healthy + environment: + RUST_BACKTRACE: 1 \ No newline at end of file diff --git a/integration/docker_compose.py b/integration/docker_compose.py index 9ae0fc5..47e3916 100644 --- a/integration/docker_compose.py +++ b/integration/docker_compose.py @@ -47,7 +47,7 @@ class Compose: log(f'Running command "{cmd}" in container {container}') result = docker([ 'exec', - '-i', + '-ti', container ] + cmd) log('Ok') diff --git a/integration/tests/behaviour.rs b/integration/tests/behaviour.rs index 2d46457..617f329 100644 --- a/integration/tests/behaviour.rs +++ b/integration/tests/behaviour.rs @@ -1,34 +1,38 @@ -use futures::Future; -use reqwest::{Response, Result as RResult, Url}; -use serde_json::{from_str, Value}; +use crate::helpers::client::AgentClient; +use serde_json::json; use uuid::Uuid; -const SERVER: &str = "u_server"; -const PORT: &str = "63714"; - type TestResult = Result>; -fn url>(url: S) -> Url { - Url::parse(&format!("http://{}:{}/{}", SERVER, PORT, url.as_ref())).unwrap() -} - -async fn unpack(req: impl Future>) -> Value { - let resp = req.await.unwrap().text().await.unwrap(); - let resp: Value = from_str(&resp).unwrap(); - resp.get("inner").unwrap().get(0).unwrap().clone() -} - -async fn get>(_url: S) -> Value { - let req = reqwest::get(url(_url)); - unpack(req).await +async fn register_agent() -> Uuid { + let cli = AgentClient::new(); + let agent_uid = Uuid::new_v4(); + let resp = cli.get(format!("get_agent_jobs/{}", agent_uid)).await; + let job_id = &resp["job_id"]; + let resp = cli.get(format!("get_jobs/{}", job_id)).await; + assert_eq!(&resp["alias"], "agent_hello"); + let agent_data = json! { + {"id": &agent_uid,"inner":[ + {"Agent": + {"alias":null, + "hostname":"3b1030fa6324", + "id":&agent_uid, + "is_root":false, + "is_root_allowed":false, + "last_active":{"secs_since_epoch":1625271265,"nanos_since_epoch":92814921}, + "platform":"x86_64-unknown-linux-gnu", + "regtime":{"secs_since_epoch":1625271265,"nanos_since_epoch":92814945}, + "state":"New", + "token":null, + "username":"root"} + }]} + }; + cli.post("report", &agent_data).await; + agent_uid } #[tokio::test] async fn test_first_connection() -> TestResult { - let agent_uid = Uuid::new_v4(); - let resp = get(format!("get_agent_jobs/{}", agent_uid)).await; - let job_id = &resp["job_id"]; - let resp = get(format!("get_jobs/{}", job_id)).await; - assert_eq!(&resp["alias"], "agent_hello"); + register_agent().await; Ok(()) } diff --git a/integration/tests/helpers/client.rs b/integration/tests/helpers/client.rs new file mode 100644 index 0000000..01c5c89 --- /dev/null +++ b/integration/tests/helpers/client.rs @@ -0,0 +1,48 @@ +use reqwest::{Client, RequestBuilder, Url}; +use serde::Serialize; +use serde_json::{from_str, json, Value}; + +const SERVER: &str = "u_server"; +const PORT: &str = "63714"; + +pub struct AgentClient { + client: Client, + base_url: Url, +} + +impl AgentClient { + pub fn new() -> Self { + Self { + client: Client::new(), + base_url: Url::parse(&format!("http://{}:{}", SERVER, PORT)).unwrap(), + } + } + + async fn process_request(&self, req: RequestBuilder, resp_needed: bool) -> Value { + let resp = req.send().await.unwrap(); + if let Err(e) = resp.error_for_status_ref() { + panic!( + "Server responded with code {}\nError: {}", + e.status() + .map(|s| s.to_string()) + .unwrap_or(String::from("")), + e.to_string() + ); + } + if !resp_needed { + return json!([]); + } + let resp: Value = from_str(&resp.text().await.unwrap()).unwrap(); + resp.get("inner").unwrap().get(0).unwrap().clone() + } + + pub async fn get>(&self, url: S) -> Value { + let req = self.client.get(self.base_url.join(url.as_ref()).unwrap()); + self.process_request(req, true).await + } + + pub async fn post, B: Serialize>(&self, url: S, body: &B) -> Value { + let req = self.client.post(self.base_url.join(url.as_ref()).unwrap()); + self.process_request(req.json(body), false).await + } +} diff --git a/integration/tests/helpers/mod.rs b/integration/tests/helpers/mod.rs new file mode 100644 index 0000000..b9babe5 --- /dev/null +++ b/integration/tests/helpers/mod.rs @@ -0,0 +1 @@ +pub mod client; diff --git a/integration/tests/helpers/panel.rs b/integration/tests/helpers/panel.rs new file mode 100644 index 0000000..fe7eeab --- /dev/null +++ b/integration/tests/helpers/panel.rs @@ -0,0 +1,3 @@ +const BINARY: &str = "/u_panel"; + +pub struct Panel; diff --git a/integration/tests/tests.rs b/integration/tests/tests.rs index b3a413f..bb44d7e 100644 --- a/integration/tests/tests.rs +++ b/integration/tests/tests.rs @@ -1 +1,2 @@ mod behaviour; +mod helpers; diff --git a/lib/u_lib/Cargo.toml b/lib/u_lib/Cargo.toml index 70b0743..dad63a9 100644 --- a/lib/u_lib/Cargo.toml +++ b/lib/u_lib/Cargo.toml @@ -7,6 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +dotenv = "0.15.0" serde = { version = "1.0.114", features = ["derive"] } uuid = { version = "0.6.5", features = ["serde", "v4"] } nix = "0.17" diff --git a/lib/u_lib/src/api.rs b/lib/u_lib/src/api.rs index 13e6d4e..136861d 100644 --- a/lib/u_lib/src/api.rs +++ b/lib/u_lib/src/api.rs @@ -7,7 +7,6 @@ use crate::{ UError, UResult, }; use reqwest::{Client, RequestBuilder, Url}; -use std::{net::Ipv4Addr, str::FromStr}; use u_api_proc_macro::api_route; use uuid::Uuid; @@ -59,7 +58,7 @@ impl ClientHandler { #[api_route("POST")] fn report(&self, payload: &M) {} - //#/////////#// Admin area //#////////#// + //##########// Admin area //##########// /// client listing #[api_route("GET")] fn get_agents(&self, url_param: Option) -> Vec {} diff --git a/lib/u_lib/src/lib.rs b/lib/u_lib/src/lib.rs index a5be8b0..00d1c18 100644 --- a/lib/u_lib/src/lib.rs +++ b/lib/u_lib/src/lib.rs @@ -20,7 +20,6 @@ pub mod schema_exports { #[macro_use] extern crate lazy_static; -extern crate openssl; #[macro_use] extern crate diesel; diff --git a/lib/u_lib/src/models/agent.rs b/lib/u_lib/src/models/agent.rs index fe213b9..2c66f1e 100644 --- a/lib/u_lib/src/models/agent.rs +++ b/lib/u_lib/src/models/agent.rs @@ -79,10 +79,9 @@ impl Agent { .await; let decoder = |job_result: ExecResult| { let assoc_job = unwrap_enum!(job_result, ExecResult::Assigned); - assoc_job.to_string_result().unwrap() + assoc_job.to_string_result().unwrap().trim().to_string() }; - #[cfg(unix)] Self { hostname: decoder(builder.pop("hostname")), is_root: &decoder(builder.pop("is_root")) == "0", diff --git a/lib/u_lib/src/utils.rs b/lib/u_lib/src/utils.rs index ebf3ebe..b43c968 100644 --- a/lib/u_lib/src/utils.rs +++ b/lib/u_lib/src/utils.rs @@ -26,55 +26,6 @@ impl OneOrMany for Vec { } } -pub fn daemonize() { - if getppid().as_raw() != 1 { - setsig(Signal::SIGTTOU, SigHandler::SigIgn); - setsig(Signal::SIGTTIN, SigHandler::SigIgn); - setsig(Signal::SIGTSTP, SigHandler::SigIgn); - } - for fd in 0..=2 { - match fdclose(fd) { - _ => (), - } - } - match chdir("/") { - _ => (), - }; - - match fork() { - Ok(ForkResult::Parent { .. }) => { - exit(0); - } - Ok(ForkResult::Child) => match setsid() { - _ => (), - }, - Err(_) => exit(255), - } -} - -pub fn setsig(sig: Signal, hnd: SigHandler) { - unsafe { - signal(sig, hnd).unwrap(); - } -} - -pub fn vec_to_string(v: &[u8]) -> String { - String::from_utf8_lossy(v).to_string() -} - -pub fn opt_to_string(item: Option) -> String { - match item { - Some(s) => s.to_string(), - None => String::new(), - } -} - -pub fn systime_to_string(time: &SystemTime) -> String { - DateTime::::from(*time) - .format("%d/%m/%Y %T") - .to_string() -} - pub struct TempFile { path: PathBuf, } @@ -159,3 +110,59 @@ macro_rules! unwrap_enum { } }; } + +pub fn daemonize() { + if getppid().as_raw() != 1 { + setsig(Signal::SIGTTOU, SigHandler::SigIgn); + setsig(Signal::SIGTTIN, SigHandler::SigIgn); + setsig(Signal::SIGTSTP, SigHandler::SigIgn); + } + for fd in 0..=2 { + match fdclose(fd) { + _ => (), + } + } + match chdir("/") { + _ => (), + }; + + match fork() { + Ok(ForkResult::Parent { .. }) => { + exit(0); + } + Ok(ForkResult::Child) => match setsid() { + _ => (), + }, + Err(_) => exit(255), + } +} + +pub fn setsig(sig: Signal, hnd: SigHandler) { + unsafe { + signal(sig, hnd).unwrap(); + } +} + +pub fn vec_to_string(v: &[u8]) -> String { + String::from_utf8_lossy(v).to_string() +} + +pub fn opt_to_string(item: Option) -> String { + match item { + Some(s) => s.to_string(), + None => String::new(), + } +} + +pub fn systime_to_string(time: &SystemTime) -> String { + DateTime::::from(*time) + .format("%d/%m/%Y %T") + .to_string() +} + +pub fn init_env() { + let envs = [".env"]; + for envfile in &envs { + dotenv::from_filename(envfile).ok(); + } +}