diff --git a/Cargo.lock b/Cargo.lock index 82ca689..1e11455 100644 Binary files a/Cargo.lock and b/Cargo.lock differ diff --git a/emgauwa-core.toml b/emgauwa-core.toml index 4caa781..6b879a4 100644 --- a/emgauwa-core.toml +++ b/emgauwa-core.toml @@ -1,6 +1,10 @@ port = 4419 host = "127.0.0.1" +# Set to a user AND a group to drop privileges to after binding to the port +#user = "emgauwa" +#group = "emgauwa" + # Leave empty to allow all origins (will always respond with Origin and not "*") #origins = ["http://localhost", "https://emgauwa.app"] diff --git a/emgauwa-core/src/main.rs b/emgauwa-core/src/main.rs index 1c17a3e..e310aa6 100644 --- a/emgauwa-core/src/main.rs +++ b/emgauwa-core/src/main.rs @@ -1,9 +1,11 @@ use actix_cors::Cors; +use std::net::TcpListener; use std::str::FromStr; use actix_web::middleware::TrailingSlash; use actix_web::{middleware, web, App, HttpServer}; use emgauwa_lib::handlers; +use emgauwa_lib::utils::drop_privileges; use log::{trace, LevelFilter}; use simple_logger::SimpleLogger; @@ -22,6 +24,18 @@ async fn main() -> std::io::Result<()> { .init() .expect("Error initializing logger."); + let listener = TcpListener::bind(format!("{}:{}", settings.host, settings.port)) + .expect("Error creating listener"); + + if !settings.user.is_empty() && !settings.group.is_empty() { + log::info!( + "Dropping privileges to {}:{}", + settings.user, + settings.group + ); + drop_privileges(&settings.user, &settings.group).expect("Error dropping privileges"); + } + let pool = emgauwa_lib::db::init(&settings.database).await; log::info!("Starting server on {}:{}", settings.host, settings.port); @@ -55,7 +69,7 @@ async fn main() -> std::io::Result<()> { .service(handlers::v1::schedules::delete) .service(handlers::v1::ws::controllers::index) }) - .bind(format!("{}:{}", settings.host, settings.port))? + .listen(listener)? .run() .await } diff --git a/emgauwa-core/src/settings.rs b/emgauwa-core/src/settings.rs index 388d1d9..5c4e353 100644 --- a/emgauwa-core/src/settings.rs +++ b/emgauwa-core/src/settings.rs @@ -14,9 +14,14 @@ pub struct Logging { #[allow(unused)] pub struct Settings { pub database: String, - pub port: u16, + pub host: String, + pub port: u16, pub origins: Vec, + + pub user: String, + pub group: String, + pub logging: Logging, } @@ -24,9 +29,14 @@ impl Default for Settings { fn default() -> Self { Settings { database: String::from("sqlite://emgauwa-core.sqlite"), - port: constants::DEFAULT_PORT, + host: String::from("127.0.0.1"), + port: constants::DEFAULT_PORT, origins: Vec::new(), + + user: String::from(""), + group: String::from(""), + logging: Logging::default(), } } diff --git a/emgauwa-lib/Cargo.toml b/emgauwa-lib/Cargo.toml index f79008e..dad6bfe 100644 --- a/emgauwa-lib/Cargo.toml +++ b/emgauwa-lib/Cargo.toml @@ -24,3 +24,4 @@ libsqlite3-sys = { version = "*", features = ["bundled"] } log = "0.4" uuid = "1.6" futures = "0.3" +libc = "0.2" diff --git a/emgauwa-lib/src/handlers/v1/ws/controllers.rs b/emgauwa-lib/src/handlers/v1/ws/controllers.rs index ba5ad35..60b7174 100644 --- a/emgauwa-lib/src/handlers/v1/ws/controllers.rs +++ b/emgauwa-lib/src/handlers/v1/ws/controllers.rs @@ -6,7 +6,6 @@ use actix::{Actor, StreamHandler}; use actix_web::{get, web, HttpRequest, HttpResponse}; use actix_web_actors::ws; use actix_web_actors::ws::ProtocolError; -use futures::FutureExt; use serde_derive::{Deserialize, Serialize}; use sqlx::pool::PoolConnection; use sqlx::{Pool, Sqlite}; diff --git a/emgauwa-lib/src/utils.rs b/emgauwa-lib/src/utils.rs index b74f68b..c3901fd 100644 --- a/emgauwa-lib/src/utils.rs +++ b/emgauwa-lib/src/utils.rs @@ -1,3 +1,6 @@ +use std::ffi::CString; +use std::io::{Error, ErrorKind}; + pub fn load_settings(config_name: &str, env_prefix: &str) -> T where for<'de> T: serde::Deserialize<'de>, @@ -16,3 +19,46 @@ where .try_deserialize::() .expect("Error reading settings") } + +// https://blog.lxsang.me/post/id/28.0 +pub fn drop_privileges(user: &str, group: &str) -> Result<(), Error> { + // the group id need to be set first, otherwise, + // when the user privileges drop, it is unable to + // drop the group privileges + // get the gid from username + if let Ok(cstr) = CString::new(group.as_bytes()) { + let p = unsafe { libc::getgrnam(cstr.as_ptr()) }; + if p.is_null() { + eprintln!("Unable to getgrnam of group: {}", group); + return Err(Error::last_os_error()); + } + if unsafe { libc::setgid((*p).gr_gid) } != 0 { + eprintln!("Unable to setgid of group: {}", group); + return Err(Error::last_os_error()); + } + } else { + return Err(Error::new( + ErrorKind::Other, + "Cannot create CString from String (group)!", + )); + } + // drop the user privileges + // get the uid from username + if let Ok(cstr) = CString::new(user.as_bytes()) { + let p = unsafe { libc::getpwnam(cstr.as_ptr()) }; + if p.is_null() { + eprintln!("Unable to getpwnam of user: {}", user); + return Err(Error::last_os_error()); + } + if unsafe { libc::setuid((*p).pw_uid) } != 0 { + eprintln!("Unable to setuid of user: {}", user); + return Err(Error::last_os_error()); + } + } else { + return Err(Error::new( + ErrorKind::Other, + "Cannot create CString from String (user)!", + )); + } + Ok(()) +}