131 lines
4.2 KiB
Rust
131 lines
4.2 KiB
Rust
use std::sync::Arc;
|
|
|
|
use actix_web::web::Data;
|
|
use actix_web::{middleware, web, App, HttpRequest, HttpResponse, HttpServer, Responder};
|
|
|
|
use crate::base::err_utils::{encpasulate_error, new_err};
|
|
use crate::base::{cert_utils, RelayedPort};
|
|
use crate::tcp_relay_server::relay_ws::relay_ws;
|
|
use crate::tcp_relay_server::server_config::ServerConfig;
|
|
use crate::tcp_relay_server::tls_cert_client_verifier::CustomCertClientVerifier;
|
|
|
|
mod relay_ws;
|
|
pub mod server_config;
|
|
mod tls_cert_client_verifier;
|
|
|
|
pub async fn hello_route() -> &'static str {
|
|
"Hello world!"
|
|
}
|
|
|
|
pub async fn config_route(req: HttpRequest, data: Data<Arc<ServerConfig>>) -> impl Responder {
|
|
if data.has_token_auth() {
|
|
let token = req
|
|
.headers()
|
|
.get("Authorization")
|
|
.map(|t| t.to_str().unwrap_or_default())
|
|
.unwrap_or_default()
|
|
.strip_prefix("Bearer ")
|
|
.unwrap_or_default();
|
|
|
|
if !data.tokens.iter().any(|t| t.eq(token)) {
|
|
return HttpResponse::Unauthorized().json("Missing / invalid token");
|
|
}
|
|
}
|
|
|
|
HttpResponse::Ok().json(
|
|
data.ports
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(id, port)| RelayedPort {
|
|
id,
|
|
port: port + data.increment_ports,
|
|
})
|
|
.collect::<Vec<_>>(),
|
|
)
|
|
}
|
|
|
|
pub async fn run_app(mut config: ServerConfig) -> std::io::Result<()> {
|
|
// Check if no port are to be forwarded
|
|
if config.ports.is_empty() {
|
|
return Err(new_err("No port to forward!"));
|
|
}
|
|
|
|
// Read tokens from file, if any
|
|
if let Some(file) = &config.tokens_file {
|
|
std::fs::read_to_string(file)
|
|
.map_err(|e| encpasulate_error(e, "Failed to read tokens file!"))?
|
|
.split('\n')
|
|
.filter(|l| !l.is_empty())
|
|
.for_each(|t| config.tokens.push(t.to_string()));
|
|
}
|
|
|
|
if !config.has_auth() {
|
|
return Err(new_err("No authentication method specified!"));
|
|
}
|
|
|
|
if config.tls_cert.is_some() != config.tls_key.is_some() {
|
|
return Err(new_err("Incomplete server TLS configuration!"));
|
|
}
|
|
|
|
if config.has_tls_client_auth() && !config.has_tls_config() {
|
|
return Err(new_err(
|
|
"Cannot provide client auth without TLS configuration!",
|
|
));
|
|
}
|
|
|
|
let args = Arc::new(config);
|
|
|
|
// Load TLS configuration, if any
|
|
let tls_config = if let (Some(cert), Some(key)) = (&args.tls_cert, &args.tls_key) {
|
|
// Load TLS certificate & private key
|
|
let cert_file = std::fs::read(cert)
|
|
.map_err(|e| encpasulate_error(e, "Failed to read certificate file"))?;
|
|
let key_file = std::fs::read(key)
|
|
.map_err(|e| encpasulate_error(e, "Failed to read server private key"))?;
|
|
|
|
// Get certificates chain
|
|
let cert_chain = cert_utils::parse_pem_certificates(&cert_file)
|
|
.map_err(|e| encpasulate_error(e, "Failed to extract certificates"))?;
|
|
|
|
// Get private key
|
|
let key = cert_utils::parse_pem_private_key(&key_file)
|
|
.map_err(|e| encpasulate_error(e, "Failed to extract private key!"))?;
|
|
|
|
let config = rustls::ServerConfig::builder().with_safe_defaults();
|
|
|
|
let config = match args.has_tls_client_auth() {
|
|
true => config
|
|
.with_client_cert_verifier(Arc::new(CustomCertClientVerifier::new(args.clone())?)),
|
|
false => config.with_no_client_auth(),
|
|
};
|
|
|
|
let config = config
|
|
.with_single_cert(cert_chain, key)
|
|
.expect("Failed to load TLS certificate!");
|
|
|
|
Some(config)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
log::info!("Starting relay on http://{}", args.listen_address);
|
|
|
|
let args_clone = args.clone();
|
|
let server = HttpServer::new(move || {
|
|
App::new()
|
|
.wrap(middleware::Logger::default())
|
|
.app_data(Data::new(args_clone.clone()))
|
|
.route("/", web::get().to(hello_route))
|
|
.route("/config", web::get().to(config_route))
|
|
.route("/ws", web::get().to(relay_ws))
|
|
});
|
|
|
|
if let Some(tls_conf) = tls_config {
|
|
server.bind_rustls_021(&args.listen_address, tls_conf)?
|
|
} else {
|
|
server.bind(&args.listen_address)?
|
|
}
|
|
.run()
|
|
.await
|
|
}
|