Add embedded TLS server
This commit is contained in:
parent
884018a90d
commit
3b2866fa6a
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +1,4 @@
|
|||||||
target
|
target
|
||||||
.idea
|
.idea
|
||||||
|
*.crt
|
||||||
|
*.key
|
||||||
|
111
Cargo.lock
generated
111
Cargo.lock
generated
@ -52,6 +52,7 @@ dependencies = [
|
|||||||
"actix-codec",
|
"actix-codec",
|
||||||
"actix-rt",
|
"actix-rt",
|
||||||
"actix-service",
|
"actix-service",
|
||||||
|
"actix-tls",
|
||||||
"actix-utils",
|
"actix-utils",
|
||||||
"ahash",
|
"ahash",
|
||||||
"base64",
|
"base64",
|
||||||
@ -143,6 +144,24 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-tls"
|
||||||
|
version = "3.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9fde0cf292f7cdc7f070803cb9a0d45c018441321a78b1042ffbbb81ec333297"
|
||||||
|
dependencies = [
|
||||||
|
"actix-codec",
|
||||||
|
"actix-rt",
|
||||||
|
"actix-service",
|
||||||
|
"actix-utils",
|
||||||
|
"futures-core",
|
||||||
|
"log",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio-rustls",
|
||||||
|
"tokio-util",
|
||||||
|
"webpki-roots",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "actix-utils"
|
name = "actix-utils"
|
||||||
version = "3.0.0"
|
version = "3.0.0"
|
||||||
@ -166,6 +185,7 @@ dependencies = [
|
|||||||
"actix-rt",
|
"actix-rt",
|
||||||
"actix-server",
|
"actix-server",
|
||||||
"actix-service",
|
"actix-service",
|
||||||
|
"actix-tls",
|
||||||
"actix-utils",
|
"actix-utils",
|
||||||
"actix-web-codegen",
|
"actix-web-codegen",
|
||||||
"ahash",
|
"ahash",
|
||||||
@ -1281,6 +1301,21 @@ dependencies = [
|
|||||||
"winreg",
|
"winreg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ring"
|
||||||
|
version = "0.16.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
"spin",
|
||||||
|
"untrusted",
|
||||||
|
"web-sys",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@ -1290,6 +1325,27 @@ dependencies = [
|
|||||||
"semver",
|
"semver",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls"
|
||||||
|
version = "0.20.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"ring",
|
||||||
|
"sct",
|
||||||
|
"webpki",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pemfile"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55"
|
||||||
|
dependencies = [
|
||||||
|
"base64",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.11"
|
version = "1.0.11"
|
||||||
@ -1312,6 +1368,16 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sct"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
|
||||||
|
dependencies = [
|
||||||
|
"ring",
|
||||||
|
"untrusted",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "security-framework"
|
name = "security-framework"
|
||||||
version = "2.7.0"
|
version = "2.7.0"
|
||||||
@ -1440,6 +1506,12 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spin"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
@ -1477,6 +1549,7 @@ name = "tcp_relay_server"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix",
|
"actix",
|
||||||
|
"actix-tls",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"actix-web-actors",
|
"actix-web-actors",
|
||||||
"base",
|
"base",
|
||||||
@ -1484,6 +1557,8 @@ dependencies = [
|
|||||||
"env_logger",
|
"env_logger",
|
||||||
"futures",
|
"futures",
|
||||||
"log",
|
"log",
|
||||||
|
"rustls",
|
||||||
|
"rustls-pemfile",
|
||||||
"serde",
|
"serde",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
@ -1612,6 +1687,17 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-rustls"
|
||||||
|
version = "0.23.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59"
|
||||||
|
dependencies = [
|
||||||
|
"rustls",
|
||||||
|
"tokio",
|
||||||
|
"webpki",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-tungstenite"
|
name = "tokio-tungstenite"
|
||||||
version = "0.17.2"
|
version = "0.17.2"
|
||||||
@ -1717,6 +1803,12 @@ dependencies = [
|
|||||||
"tinyvec",
|
"tinyvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "untrusted"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.2.2"
|
version = "2.2.2"
|
||||||
@ -1845,6 +1937,25 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webpki"
|
||||||
|
version = "0.22.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
|
||||||
|
dependencies = [
|
||||||
|
"ring",
|
||||||
|
"untrusted",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webpki-roots"
|
||||||
|
version = "0.22.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1c760f0d366a6c24a02ed7816e23e691f5d92291f94d15e836006fd11b04daf"
|
||||||
|
dependencies = [
|
||||||
|
"webpki",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
@ -9,8 +9,11 @@ clap = { version = "3.2.18", features = ["derive", "env"] }
|
|||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
env_logger = "0.9.0"
|
env_logger = "0.9.0"
|
||||||
actix = "0.13.0"
|
actix = "0.13.0"
|
||||||
actix-web = "4"
|
actix-web = { version = "4", features = ["rustls"] }
|
||||||
actix-web-actors = "4.1.0"
|
actix-web-actors = "4.1.0"
|
||||||
|
actix-tls = "3.0.3"
|
||||||
serde = { version = "1.0.144", features = ["derive"] }
|
serde = { version = "1.0.144", features = ["derive"] }
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
futures = "0.3.24"
|
futures = "0.3.24"
|
||||||
|
rustls = "0.20.6"
|
||||||
|
rustls-pemfile = "1.0.1"
|
@ -2,8 +2,9 @@ use clap::Parser;
|
|||||||
|
|
||||||
/// TCP relay server
|
/// TCP relay server
|
||||||
#[derive(Parser, Debug, Clone)]
|
#[derive(Parser, Debug, Clone)]
|
||||||
#[clap(author, version, about, long_about = None)]
|
#[clap(author, version, about,
|
||||||
pub struct Args {
|
long_about = "TCP-over-HTTP server. This program might be configured behind a reverse-proxy.")]
|
||||||
|
pub struct ProgramArgs {
|
||||||
/// Access tokens
|
/// Access tokens
|
||||||
#[clap(short, long)]
|
#[clap(short, long)]
|
||||||
pub tokens: Vec<String>,
|
pub tokens: Vec<String>,
|
||||||
@ -28,4 +29,12 @@ pub struct Args {
|
|||||||
/// on the same machine
|
/// on the same machine
|
||||||
#[clap(short, long, default_value_t = 0)]
|
#[clap(short, long, default_value_t = 0)]
|
||||||
pub increment_ports: u16,
|
pub increment_ports: u16,
|
||||||
|
|
||||||
|
/// TLS certificate. Specify also private key to use HTTPS/TLS instead of HTTP
|
||||||
|
#[clap(long)]
|
||||||
|
pub tls_cert: Option<String>,
|
||||||
|
|
||||||
|
/// TLS private key. Specify also certificate to use HTTPS/TLS instead of HTTP
|
||||||
|
#[clap(long)]
|
||||||
|
pub tls_key: Option<String>,
|
||||||
}
|
}
|
@ -1,18 +1,22 @@
|
|||||||
|
use std::fs::File;
|
||||||
|
use std::io::BufReader;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use actix_web::{App, HttpRequest, HttpResponse, HttpServer, middleware, Responder, web};
|
use actix_web::{App, HttpRequest, HttpResponse, HttpServer, middleware, Responder, web};
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use rustls::{Certificate, PrivateKey, ServerConfig};
|
||||||
|
use rustls_pemfile::{certs, Item, read_one};
|
||||||
|
|
||||||
use base::RelayedPort;
|
use base::RelayedPort;
|
||||||
use tcp_relay_server::args::Args;
|
use tcp_relay_server::args::ProgramArgs;
|
||||||
use tcp_relay_server::relay_ws::relay_ws;
|
use tcp_relay_server::relay_ws::relay_ws;
|
||||||
|
|
||||||
pub async fn hello_route() -> &'static str {
|
pub async fn hello_route() -> &'static str {
|
||||||
"Hello world!"
|
"Hello world!"
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn config_route(req: HttpRequest, data: Data<Arc<Args>>) -> impl Responder {
|
pub async fn config_route(req: HttpRequest, data: Data<Arc<ProgramArgs>>) -> impl Responder {
|
||||||
let token = req.headers().get("Authorization")
|
let token = req.headers().get("Authorization")
|
||||||
.map(|t| t.to_str().unwrap_or_default())
|
.map(|t| t.to_str().unwrap_or_default())
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
@ -35,13 +39,49 @@ pub async fn config_route(req: HttpRequest, data: Data<Arc<Args>>) -> impl Respo
|
|||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
|
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
|
||||||
|
|
||||||
let mut args: Args = Args::parse();
|
let mut args: ProgramArgs = ProgramArgs::parse();
|
||||||
|
|
||||||
if args.ports.is_empty() {
|
if args.ports.is_empty() {
|
||||||
log::error!("No port to forward!");
|
log::error!("No port to forward!");
|
||||||
std::process::exit(2);
|
std::process::exit(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 = &mut BufReader::new(File::open(cert).unwrap());
|
||||||
|
let key_file = &mut BufReader::new(File::open(key).unwrap());
|
||||||
|
|
||||||
|
// Get certificates chain
|
||||||
|
let cert_chain = certs(cert_file).unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.map(Certificate)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Get private key
|
||||||
|
let key = match read_one(key_file).expect("Failed to read private key!") {
|
||||||
|
None => {
|
||||||
|
log::error!("Failed to extract private key!");
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
Some(Item::PKCS8Key(key)) => key,
|
||||||
|
Some(Item::RSAKey(key)) => key,
|
||||||
|
_ => {
|
||||||
|
log::error!("Unsupported private key type!");
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let config = ServerConfig::builder()
|
||||||
|
.with_safe_defaults()
|
||||||
|
.with_no_client_auth()
|
||||||
|
.with_single_cert(cert_chain, PrivateKey(key))
|
||||||
|
.expect("Failed to load TLS certificate!");
|
||||||
|
|
||||||
|
Some(config)
|
||||||
|
} else { None };
|
||||||
|
|
||||||
// Read tokens from file, if any
|
// Read tokens from file, if any
|
||||||
if let Some(file) = &args.tokens_file {
|
if let Some(file) = &args.tokens_file {
|
||||||
std::fs::read_to_string(file)
|
std::fs::read_to_string(file)
|
||||||
@ -60,15 +100,19 @@ async fn main() -> std::io::Result<()> {
|
|||||||
|
|
||||||
let args = Arc::new(args);
|
let args = Arc::new(args);
|
||||||
let args_clone = args.clone();
|
let args_clone = args.clone();
|
||||||
HttpServer::new(move || {
|
let server = HttpServer::new(move || {
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(middleware::Logger::default())
|
.wrap(middleware::Logger::default())
|
||||||
.app_data(Data::new(args_clone.clone()))
|
.app_data(Data::new(args_clone.clone()))
|
||||||
.route("/", web::get().to(hello_route))
|
.route("/", web::get().to(hello_route))
|
||||||
.route("/config", web::get().to(config_route))
|
.route("/config", web::get().to(config_route))
|
||||||
.route("/ws", web::get().to(relay_ws))
|
.route("/ws", web::get().to(relay_ws))
|
||||||
})
|
});
|
||||||
.bind(&args.listen_address)?
|
|
||||||
.run()
|
if let Some(tls_conf) = tls_config {
|
||||||
|
server.bind_rustls(&args.listen_address, tls_conf)?
|
||||||
|
} else {
|
||||||
|
server.bind(&args.listen_address)?
|
||||||
|
}.run()
|
||||||
.await
|
.await
|
||||||
}
|
}
|
@ -9,7 +9,7 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
|||||||
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
|
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
|
|
||||||
use crate::args::Args;
|
use crate::args::ProgramArgs;
|
||||||
|
|
||||||
/// How often heartbeat pings are sent
|
/// How often heartbeat pings are sent
|
||||||
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
|
const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5);
|
||||||
@ -153,7 +153,7 @@ pub struct WebSocketQuery {
|
|||||||
|
|
||||||
pub async fn relay_ws(req: HttpRequest, stream: web::Payload,
|
pub async fn relay_ws(req: HttpRequest, stream: web::Payload,
|
||||||
query: web::Query<WebSocketQuery>,
|
query: web::Query<WebSocketQuery>,
|
||||||
conf: web::Data<Arc<Args>>) -> Result<HttpResponse, Error> {
|
conf: web::Data<Arc<ProgramArgs>>) -> Result<HttpResponse, Error> {
|
||||||
if !conf.tokens.contains(&query.token) {
|
if !conf.tokens.contains(&query.token) {
|
||||||
log::error!("Rejected WS request from {:?} due to invalid token!", req.peer_addr());
|
log::error!("Rejected WS request from {:?} due to invalid token!", req.peer_addr());
|
||||||
return Ok(HttpResponse::Unauthorized().json("Invalid / missing token!"));
|
return Ok(HttpResponse::Unauthorized().json("Invalid / missing token!"));
|
||||||
|
Loading…
Reference in New Issue
Block a user