diff --git a/Cargo.lock b/Cargo.lock index cff25dd..4e73c9b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,15 +26,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "ansi_colours" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14eec43e0298190790f41679fe69ef7a829d2a2ddd78c8c00339e84710e435fe" -dependencies = [ - "rgb", -] - [[package]] name = "anyhow" version = "1.0.98" @@ -127,11 +118,9 @@ dependencies = [ name = "boxy" version = "0.1.0" dependencies = [ - "ansi_colours", "anyhow", "base64", "bcrypt", - "colour", "http-body-util", "hyper", "hyper-util", @@ -154,12 +143,6 @@ version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" -[[package]] -name = "bytemuck" -version = "1.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" - [[package]] name = "byteorder" version = "1.5.0" @@ -197,15 +180,6 @@ dependencies = [ "inout", ] -[[package]] -name = "colour" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b536eebcabe54980476d120a182f7da2268fe02d22575cca99cee5fdda178280" -dependencies = [ - "winapi", -] - [[package]] name = "cpufeatures" version = "0.2.17" @@ -856,15 +830,6 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" -[[package]] -name = "rgb" -version = "0.8.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" -dependencies = [ - "bytemuck", -] - [[package]] name = "ring" version = "0.17.14" @@ -1321,22 +1286,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - [[package]] name = "winapi-util" version = "0.1.9" @@ -1346,12 +1295,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-sys" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index e6bf1d9..0c511d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,4 @@ serde = "1.0.219" base64 = "0.22.1" string-builder = "0.2.0" json = "0.12.4" -ansi_colours = "1.2.3" -colour = "2.1.0" diff --git a/config.yaml b/config.yaml index fa4c12b..b3d0032 100644 --- a/config.yaml +++ b/config.yaml @@ -1,4 +1,7 @@ -database: 'postgresql://postgres:trust@127.0.0.1' +db: + host: '127.0.0.1' + user: 'postgres' + password: 'trust' proxy: listen: 127.0.0.1 @@ -8,6 +11,10 @@ api: listen: 127.0.0.1 port: 8006 +hosts: # ignore this it doesn't function + - hostname: localhost:8005 + address: localhost:8000 + clients: - name: 'eu-central-1' # Example Client right here (the client in this case would be for example the stereo.cat backend) - hashed_secret: '$2b$12$5wH/0p702PPqVp7fCpVS4.1GA2/wAbk89w2nMjwuS8439OhjCUGbK' # password123 + secret: '$2b$12$5wH/0p702PPqVp7fCpVS4.1GA2/wAbk89w2nMjwuS8439OhjCUGbK' # password123 diff --git a/src/config.rs b/src/config.rs index a15575f..1e055a0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -27,34 +27,44 @@ pub struct Api { #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] pub struct Client { pub name: String, - pub hashed_secret: String, + pub secret: String, } -impl Client { - pub async fn verify(us: String, config: Config) -> bool { - // us stands for user:secret btw - let us_split: Vec<&str> = us.split(':').collect(); - - let name = us_split.first().unwrap(); - let secret = us_split.last().unwrap(); - - let client: Client = config - .clients - .into_iter() - .filter(|x| x.name.eq(name)) - .nth(0) - .unwrap(); - - bcrypt::verify(secret, client.hashed_secret.as_str()).unwrap() - } +#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] +pub struct Host { + pub hostname: String, + pub address: String, } #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] pub struct Config { - pub database: String, + pub db: Db, pub proxy: Proxy, pub api: Api, pub clients: Vec, + pub hosts: Vec, +} + +impl Db { + pub async fn to_string(&self) -> String { + let mut builder = String::new(); + + builder += format!( + "host={} port={} user={} dbname={}", + self.host, + self.port.unwrap_or(5432), + self.user, + self.database.clone().unwrap_or(self.user.clone()), + ) + .as_str(); + + match &self.password { + Some(x) => builder += format!(" password={}", x).as_str(), + None => {} + } + + builder + } } impl Config { diff --git a/src/db.rs b/src/db.rs index 0fdccba..b880979 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,9 +1,6 @@ -use std::{ - error::Error, - net::IpAddr, -}; +use std::{error::Error, net::{IpAddr, SocketAddr}}; -use tokio_postgres::Client; +use tokio_postgres::{Client, Socket, tls::MakeTlsConnect}; const ENDPOINT_TABLE: &str = "endpoints"; const HOSTS_RELATION_TABLE: &str = "hosts"; diff --git a/src/main.rs b/src/main.rs index 8fd2d2c..9c9cf9e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,28 +1,26 @@ mod config; mod db; -mod server; mod services; +mod types; -use std::{env, process::exit, sync::Arc}; +use std::{env, sync::Arc}; -use bcrypt::DEFAULT_COST; +use bcrypt::{DEFAULT_COST, bcrypt}; use config::Config; use db::BoxyDatabase; -use log::{debug, error, info}; -use server::Server; +use log::{error, info}; +use nanoid::nanoid; +use ring::rand::SystemRandom; use services::{api::ApiService, controller::ControllerService}; -use tokio::sync::Mutex; -use tokio_postgres::NoTls; - -const VERSION: &str = "v0.1a"; +use tokio::{fs::File, io::AsyncReadExt, sync::Mutex}; +use tokio_postgres::{NoTls, tls::NoTlsError}; +use types::Server; #[tokio::main] async fn main() -> Result<(), Box> { - if env::var("RUST_LOG").is_err() { - unsafe { env::set_var("RUST_LOG", "info") }; - } - - pretty_env_logger::init(); + pretty_env_logger::formatted_builder() + .filter(None, log::LevelFilter::Info) + .init(); let args: Vec = env::args().collect(); @@ -39,31 +37,26 @@ async fn main() -> Result<(), Box> { return Ok(()); } - "version" => { - info!("Version: {}", VERSION); - return Ok(()); - } _ => {} } } let config = Config::get().await.unwrap(); - debug!("Database URI: {}", config.database); + let db_string = config.db.to_string().await; - let (client, conn) = tokio_postgres::connect(config.database.as_str(), NoTls) + info!("Database string: {}", db_string); + + let (client, conn) = tokio_postgres::connect(db_string.as_str(), NoTls) .await .unwrap(); tokio::spawn(async move { if let Err(e) = conn.await { error!("Error while connecting to database: {}", e); - exit(1); } }); - info!("Connected to database."); - let database = Box::new(BoxyDatabase::new(client).await.unwrap()); let database_shared = Arc::new(Mutex::new(Box::leak(database))); @@ -71,7 +64,7 @@ async fn main() -> Result<(), Box> { let api_svc = ApiService { database: database_shared.clone(), config: config.clone(), - _address: None, + _address: None }; let svc = ControllerService { @@ -87,12 +80,10 @@ async fn main() -> Result<(), Box> { .unwrap(); tokio::task::spawn(async move { - info!("Starting API server..."); api_server.handle().await; }); // We don't put this on a separate thread because we'd be wasting the main thread. - info!("Starting proxy server..."); proxy_server.handle().await; Ok(()) diff --git a/src/services.rs b/src/services.rs index 168dbf8..e8b28e6 100644 --- a/src/services.rs +++ b/src/services.rs @@ -1,3 +1,3 @@ -pub mod api; -pub mod controller; pub mod proxy; +pub mod controller; +pub mod api; diff --git a/src/services/api.rs b/src/services/api.rs index cab3cac..b8a107a 100644 --- a/src/services/api.rs +++ b/src/services/api.rs @@ -1,19 +1,16 @@ -use std::{net::IpAddr, pin::Pin, sync::Arc}; +use std::{hash, net::{IpAddr, SocketAddr}, pin::Pin, sync::Arc}; use base64::{Engine, prelude::BASE64_STANDARD}; +use bcrypt::DEFAULT_COST; use http_body_util::{BodyExt, Full}; use hyper::{ - Method, Request, Response, StatusCode, - body::{Bytes, Incoming}, - service::Service, + body::{Bytes, Incoming}, service::Service, Method, Request, Response, StatusCode, Uri }; -use log::{debug, warn}; +use log::{info, trace}; use tokio::{net::TcpStream, sync::Mutex}; use crate::{ - config::{Client, Config}, - db::{BoxyDatabase, Endpoint}, - server::{GeneralBody, GeneralResponse, TcpIntercept}, + config::{Client, Config}, db::{BoxyDatabase, Endpoint}, types::{GeneralBody, GeneralResponse, TcpIntercept} }; #[derive(Debug, Clone)] @@ -23,7 +20,7 @@ pub struct ApiService { pub _address: Option, } -async fn default_response() -> GeneralResponse { +async fn default_response() -> Response>> { Response::builder() .status(404) .body(GeneralBody::Right(Full::from(Bytes::from( @@ -32,7 +29,7 @@ async fn default_response() -> GeneralResponse { .unwrap() } -async fn custom_resp(e: StatusCode, m: &'static str) -> GeneralResponse { +async fn custom_resp(e: StatusCode, m: String) -> Response>> { Response::builder() .status(e) .body(GeneralBody::Right(Full::from(Bytes::from(m)))) @@ -40,7 +37,7 @@ async fn custom_resp(e: StatusCode, m: &'static str) -> GeneralResponse { } impl TcpIntercept for ApiService { - fn stream(&mut self, stream: &TcpStream) { + fn handle(&mut self, stream: &TcpStream) { self._address = Some(stream.peer_addr().unwrap().ip()); } } @@ -54,73 +51,37 @@ impl Service> for ApiService { let database = self.database.clone(); let config = self.config.clone(); let address = self._address.clone().unwrap(); - Box::pin(async move { match *req.method() { Method::POST => match req.uri().path() { "/register" => { - debug!("new api register request from {}", address); + let encoded_header = req.headers().get(hyper::header::AUTHORIZATION).unwrap().to_str().unwrap(); + + let auth_string = String::from_utf8(BASE64_STANDARD.decode(&encoded_header[6..]).unwrap()).unwrap(); + + let auth_string_split: Vec<&str> = auth_string.split(':').collect(); - let encoded_header = req - .headers() - .get(hyper::header::AUTHORIZATION) - .unwrap() - .to_str() - .unwrap(); + let name = auth_string_split.first().unwrap(); + let secret = auth_string_split.get(1).unwrap(); - debug!("authorization header: {}", encoded_header); + let matched_clients: Vec<&Client> = config.clients.iter().filter(|x| x.name.eq(name)).collect(); - let auth_string = String::from_utf8( - BASE64_STANDARD.decode(&encoded_header[6..]).unwrap(), - ) - .unwrap(); + let client = matched_clients.first().unwrap(); - debug!("decoded auth string: {}", auth_string); - - if !Client::verify(auth_string.clone(), config).await { - warn!( - "Authentication for string {} from {} failed.", - auth_string, address - ); - - return Ok(custom_resp( - StatusCode::UNAUTHORIZED, - "Invalid credentials.", - ) - .await); + if !bcrypt::verify(secret, client.secret.as_str()).unwrap() { + return Ok(custom_resp(StatusCode::UNAUTHORIZED, "Invalid credentials.".to_string()).await); } - let body = String::from_utf8( - req.collect() - .await - .unwrap() - .to_bytes() - .iter() - .cloned() - .collect::>(), - ) - .unwrap(); + let body = String::from_utf8(req.collect().await.unwrap().to_bytes().iter().cloned().collect::>()).unwrap(); let json = json::parse(body.as_str()).unwrap(); - debug!("body: {}", body); + info!("body: {}", body); - let mut endpoint = Endpoint::new( - None, - address, - json["port"].as_u16().unwrap(), - json["callback"].as_str().unwrap_or("/").to_string(), - ) - .await; + let mut endpoint = Endpoint::new(None, address, json["port"].as_u16().unwrap(), json["callback"].as_str().unwrap_or("/").to_string()).await; - endpoint - .register( - *database.lock().await, - json["hostname"].as_str().unwrap().to_string(), - ) - .await - .unwrap(); + endpoint.register(*database.lock().await, json["hostname"].as_str().unwrap().to_string()).await.unwrap(); - Ok(custom_resp(StatusCode::OK, "").await) + Ok(custom_resp(StatusCode::OK, "yay".to_string()).await) } _ => Ok(default_response().await), }, diff --git a/src/services/controller.rs b/src/services/controller.rs index 7665ac7..eee4b1c 100644 --- a/src/services/controller.rs +++ b/src/services/controller.rs @@ -1,15 +1,18 @@ use std::{pin::Pin, sync::Arc}; +use http_body_util::Full; use hyper::{ - Request, - body::Incoming, + Request, Response, + body::{Bytes, Incoming}, service::Service, }; +use log::error; use tokio::sync::Mutex; use crate::{ + config::{self, Client, Config, Host}, db::{BoxyDatabase, Endpoint}, - server::{GeneralResponse, TcpIntercept}, + types::{GeneralBody, GeneralResponse, TcpIntercept}, }; use super::proxy::ProxyService; @@ -20,7 +23,8 @@ pub struct ControllerService { } impl TcpIntercept for ControllerService { - fn stream(&mut self, _: &tokio::net::TcpStream) {} + fn handle(&mut self, stream: &tokio::net::TcpStream) { + } } impl Service> for ControllerService { diff --git a/src/services/proxy.rs b/src/services/proxy.rs index e4641e1..c0e7e7d 100644 --- a/src/services/proxy.rs +++ b/src/services/proxy.rs @@ -5,7 +5,7 @@ use hyper_util::rt::TokioIo; use log::error; use tokio::net::TcpStream; -use crate::server::{GeneralResponse, to_general_response}; +use crate::types::{GeneralResponse, to_general_response}; #[derive(Debug, Clone)] pub struct ProxyService { diff --git a/src/server.rs b/src/types.rs similarity index 83% rename from src/server.rs rename to src/types.rs index 0eb7c63..c96f5ae 100644 --- a/src/server.rs +++ b/src/types.rs @@ -1,4 +1,4 @@ -use std::{any::type_name_of_val, error::Error}; +use std::error::Error; use http_body_util::{Either, Full}; use hyper::{ @@ -8,7 +8,7 @@ use hyper::{ service::{HttpService, Service}, }; use hyper_util::rt::TokioIo; -use log::{error, info}; +use log::error; use tokio::net::{TcpListener, TcpStream}; pub type GeneralResponse = Response; @@ -25,7 +25,7 @@ pub struct Server { } pub trait TcpIntercept { - fn stream(&mut self, stream: &TcpStream); + fn handle(&mut self, stream: &TcpStream); } impl Server @@ -39,20 +39,13 @@ where >::Future: Send, { pub async fn handle(&self) { - info!( - "Server started at http://{} for service: {}", - self.listener.local_addr().unwrap(), - type_name_of_val(&self.service) - ); - loop { let (stream, _) = self.listener.accept().await.unwrap(); let mut svc_clone = self.service.clone(); - svc_clone.stream(&stream); + svc_clone.handle(&stream); let io = TokioIo::new(stream); - tokio::task::spawn(async move { if let Err(err) = http1::Builder::new() .writev(false)