Add ability to drop privileges after binding to port
This commit is contained in:
		
							parent
							
								
									3b596de06f
								
							
						
					
					
						commit
						7ed3a9e52d
					
				
					 7 changed files with 79 additions and 4 deletions
				
			
		
							
								
								
									
										1
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							| 
						 | 
					@ -902,6 +902,7 @@ dependencies = [
 | 
				
			||||||
 "chrono",
 | 
					 "chrono",
 | 
				
			||||||
 "config",
 | 
					 "config",
 | 
				
			||||||
 "futures",
 | 
					 "futures",
 | 
				
			||||||
 | 
					 "libc",
 | 
				
			||||||
 "libsqlite3-sys",
 | 
					 "libsqlite3-sys",
 | 
				
			||||||
 "log",
 | 
					 "log",
 | 
				
			||||||
 "serde",
 | 
					 "serde",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,10 @@
 | 
				
			||||||
port = 4419
 | 
					port = 4419
 | 
				
			||||||
host = "127.0.0.1"
 | 
					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 "*")
 | 
					# Leave empty to allow all origins (will always respond with Origin and not "*")
 | 
				
			||||||
#origins = ["http://localhost", "https://emgauwa.app"]
 | 
					#origins = ["http://localhost", "https://emgauwa.app"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,11 @@
 | 
				
			||||||
use actix_cors::Cors;
 | 
					use actix_cors::Cors;
 | 
				
			||||||
 | 
					use std::net::TcpListener;
 | 
				
			||||||
use std::str::FromStr;
 | 
					use std::str::FromStr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use actix_web::middleware::TrailingSlash;
 | 
					use actix_web::middleware::TrailingSlash;
 | 
				
			||||||
use actix_web::{middleware, web, App, HttpServer};
 | 
					use actix_web::{middleware, web, App, HttpServer};
 | 
				
			||||||
use emgauwa_lib::handlers;
 | 
					use emgauwa_lib::handlers;
 | 
				
			||||||
 | 
					use emgauwa_lib::utils::drop_privileges;
 | 
				
			||||||
use log::{trace, LevelFilter};
 | 
					use log::{trace, LevelFilter};
 | 
				
			||||||
use simple_logger::SimpleLogger;
 | 
					use simple_logger::SimpleLogger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,6 +24,18 @@ async fn main() -> std::io::Result<()> {
 | 
				
			||||||
		.init()
 | 
							.init()
 | 
				
			||||||
		.expect("Error initializing logger.");
 | 
							.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;
 | 
						let pool = emgauwa_lib::db::init(&settings.database).await;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log::info!("Starting server on {}:{}", settings.host, settings.port);
 | 
						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::schedules::delete)
 | 
				
			||||||
			.service(handlers::v1::ws::controllers::index)
 | 
								.service(handlers::v1::ws::controllers::index)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	.bind(format!("{}:{}", settings.host, settings.port))?
 | 
						.listen(listener)?
 | 
				
			||||||
	.run()
 | 
						.run()
 | 
				
			||||||
	.await
 | 
						.await
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,9 +14,14 @@ pub struct Logging {
 | 
				
			||||||
#[allow(unused)]
 | 
					#[allow(unused)]
 | 
				
			||||||
pub struct Settings {
 | 
					pub struct Settings {
 | 
				
			||||||
	pub database: String,
 | 
						pub database: String,
 | 
				
			||||||
	pub port: u16,
 | 
					
 | 
				
			||||||
	pub host: String,
 | 
						pub host: String,
 | 
				
			||||||
 | 
						pub port: u16,
 | 
				
			||||||
	pub origins: Vec<String>,
 | 
						pub origins: Vec<String>,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pub user: String,
 | 
				
			||||||
 | 
						pub group: String,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pub logging: Logging,
 | 
						pub logging: Logging,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,9 +29,14 @@ impl Default for Settings {
 | 
				
			||||||
	fn default() -> Self {
 | 
						fn default() -> Self {
 | 
				
			||||||
		Settings {
 | 
							Settings {
 | 
				
			||||||
			database: String::from("sqlite://emgauwa-core.sqlite"),
 | 
								database: String::from("sqlite://emgauwa-core.sqlite"),
 | 
				
			||||||
			port: constants::DEFAULT_PORT,
 | 
					
 | 
				
			||||||
			host: String::from("127.0.0.1"),
 | 
								host: String::from("127.0.0.1"),
 | 
				
			||||||
 | 
								port: constants::DEFAULT_PORT,
 | 
				
			||||||
			origins: Vec::new(),
 | 
								origins: Vec::new(),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								user: String::from(""),
 | 
				
			||||||
 | 
								group: String::from(""),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			logging: Logging::default(),
 | 
								logging: Logging::default(),
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,3 +24,4 @@ libsqlite3-sys = { version = "*", features = ["bundled"] }
 | 
				
			||||||
log = "0.4"
 | 
					log = "0.4"
 | 
				
			||||||
uuid = "1.6"
 | 
					uuid = "1.6"
 | 
				
			||||||
futures = "0.3"
 | 
					futures = "0.3"
 | 
				
			||||||
 | 
					libc = "0.2"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,6 @@ use actix::{Actor, StreamHandler};
 | 
				
			||||||
use actix_web::{get, web, HttpRequest, HttpResponse};
 | 
					use actix_web::{get, web, HttpRequest, HttpResponse};
 | 
				
			||||||
use actix_web_actors::ws;
 | 
					use actix_web_actors::ws;
 | 
				
			||||||
use actix_web_actors::ws::ProtocolError;
 | 
					use actix_web_actors::ws::ProtocolError;
 | 
				
			||||||
use futures::FutureExt;
 | 
					 | 
				
			||||||
use serde_derive::{Deserialize, Serialize};
 | 
					use serde_derive::{Deserialize, Serialize};
 | 
				
			||||||
use sqlx::pool::PoolConnection;
 | 
					use sqlx::pool::PoolConnection;
 | 
				
			||||||
use sqlx::{Pool, Sqlite};
 | 
					use sqlx::{Pool, Sqlite};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,6 @@
 | 
				
			||||||
 | 
					use std::ffi::CString;
 | 
				
			||||||
 | 
					use std::io::{Error, ErrorKind};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn load_settings<T>(config_name: &str, env_prefix: &str) -> T
 | 
					pub fn load_settings<T>(config_name: &str, env_prefix: &str) -> T
 | 
				
			||||||
where
 | 
					where
 | 
				
			||||||
	for<'de> T: serde::Deserialize<'de>,
 | 
						for<'de> T: serde::Deserialize<'de>,
 | 
				
			||||||
| 
						 | 
					@ -16,3 +19,46 @@ where
 | 
				
			||||||
		.try_deserialize::<T>()
 | 
							.try_deserialize::<T>()
 | 
				
			||||||
		.expect("Error reading settings")
 | 
							.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(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue