Refacto: Use proper type for WsServer
This commit is contained in:
parent
a33a889b3d
commit
7d30e0f462
6 changed files with 422 additions and 411 deletions
57
src/main.rs
57
src/main.rs
|
@ -11,6 +11,7 @@ use crate::tunnel::connectors::{Socks5TunnelConnector, TcpTunnelConnector, UdpTu
|
|||
use crate::tunnel::listeners::{
|
||||
new_stdio_listener, new_udp_listener, HttpProxyTunnelListener, Socks5TunnelListener, TcpTunnelListener,
|
||||
};
|
||||
use crate::tunnel::server::{TlsServerConfig, WsServer, WsServerConfig};
|
||||
use crate::tunnel::{to_host_port, RemoteAddr, TransportAddr, TransportScheme};
|
||||
use base64::Engine;
|
||||
use clap::Parser;
|
||||
|
@ -20,16 +21,16 @@ use log::debug;
|
|||
use parking_lot::{Mutex, RwLock};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::fmt::Debug;
|
||||
use std::io;
|
||||
use std::io::ErrorKind;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::{fmt, io};
|
||||
use tokio::select;
|
||||
use tokio_rustls::rustls::pki_types::{CertificateDer, DnsName, PrivateKeyDer};
|
||||
use tokio_rustls::rustls::pki_types::DnsName;
|
||||
use tracing::{error, info};
|
||||
use tracing_subscriber::filter::Directive;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
@ -690,49 +691,6 @@ fn parse_server_url(arg: &str) -> Result<Url, io::Error> {
|
|||
Ok(url)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TlsServerConfig {
|
||||
pub tls_certificate: Mutex<Vec<CertificateDer<'static>>>,
|
||||
pub tls_key: Mutex<PrivateKeyDer<'static>>,
|
||||
pub tls_client_ca_certificates: Option<Mutex<Vec<CertificateDer<'static>>>>,
|
||||
pub tls_certificate_path: Option<PathBuf>,
|
||||
pub tls_key_path: Option<PathBuf>,
|
||||
pub tls_client_ca_certs_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
pub struct WsServerConfig {
|
||||
pub socket_so_mark: Option<u32>,
|
||||
pub bind: SocketAddr,
|
||||
pub websocket_ping_frequency: Option<Duration>,
|
||||
pub timeout_connect: Duration,
|
||||
pub websocket_mask_frame: bool,
|
||||
pub tls: Option<TlsServerConfig>,
|
||||
pub dns_resolver: DnsResolver,
|
||||
pub restriction_config: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl Debug for WsServerConfig {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("WsServerConfig")
|
||||
.field("socket_so_mark", &self.socket_so_mark)
|
||||
.field("bind", &self.bind)
|
||||
.field("websocket_ping_frequency", &self.websocket_ping_frequency)
|
||||
.field("timeout_connect", &self.timeout_connect)
|
||||
.field("websocket_mask_frame", &self.websocket_mask_frame)
|
||||
.field("restriction_config", &self.restriction_config)
|
||||
.field("tls", &self.tls.is_some())
|
||||
.field(
|
||||
"mTLS",
|
||||
&self
|
||||
.tls
|
||||
.as_ref()
|
||||
.map(|x| x.tls_client_ca_certificates.is_some())
|
||||
.unwrap_or(false),
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let args = Wstunnel::parse();
|
||||
|
@ -1194,16 +1152,15 @@ async fn main() -> anyhow::Result<()> {
|
|||
.expect("Cannot create DNS resolver"),
|
||||
restriction_config: args.restrict_config,
|
||||
};
|
||||
let server = WsServer::new(server_config);
|
||||
|
||||
info!(
|
||||
"Starting wstunnel server v{} with config {:?}",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
server_config
|
||||
server.config
|
||||
);
|
||||
debug!("Restriction rules: {:#?}", restrictions);
|
||||
tunnel::server::run_server(Arc::new(server_config), restrictions)
|
||||
.await
|
||||
.unwrap_or_else(|err| {
|
||||
server.serve(restrictions).await.unwrap_or_else(|err| {
|
||||
panic!("Cannot start wstunnel server: {:?}", err);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use crate::TlsServerConfig;
|
||||
use anyhow::{anyhow, Context};
|
||||
use std::fs::File;
|
||||
|
||||
|
@ -10,6 +9,7 @@ use tokio::net::TcpStream;
|
|||
use tokio_rustls::client::TlsStream;
|
||||
|
||||
use crate::tunnel::client::WsClientConfig;
|
||||
use crate::tunnel::server::TlsServerConfig;
|
||||
use crate::tunnel::TransportAddr;
|
||||
use tokio_rustls::rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
|
||||
use tokio_rustls::rustls::pki_types::{CertificateDer, PrivateKeyDer, ServerName, UnixTime};
|
||||
|
|
|
@ -2,7 +2,7 @@ pub mod client;
|
|||
pub mod connectors;
|
||||
pub mod listeners;
|
||||
pub mod server;
|
||||
pub mod tls_reloader;
|
||||
mod tls_reloader;
|
||||
mod transport;
|
||||
|
||||
use crate::{LocalProtocol, TlsClientConfig};
|
||||
|
|
6
src/tunnel/server/mod.rs
Normal file
6
src/tunnel/server/mod.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
#![allow(clippy::module_inception)]
|
||||
mod server;
|
||||
|
||||
pub use server::TlsServerConfig;
|
||||
pub use server::WsServer;
|
||||
pub use server::WsServerConfig;
|
|
@ -5,16 +5,19 @@ use futures_util::{pin_mut, FutureExt, StreamExt};
|
|||
use http_body_util::combinators::BoxBody;
|
||||
use http_body_util::{BodyStream, Either, StreamBody};
|
||||
use std::cmp::min;
|
||||
use std::fmt;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::future::Future;
|
||||
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::ops::Deref;
|
||||
use std::path::PathBuf;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use super::{tunnel_to_jwt_token, JwtTunnelConfig, RemoteAddr, JWT_DECODE, JWT_HEADER_PREFIX};
|
||||
use crate::{protocols, LocalProtocol, TlsServerConfig, WsServerConfig};
|
||||
use crate::tunnel::{transport, tunnel_to_jwt_token, JwtTunnelConfig, RemoteAddr, JWT_DECODE, JWT_HEADER_PREFIX};
|
||||
use crate::{protocols, LocalProtocol};
|
||||
use hyper::body::{Frame, Incoming};
|
||||
use hyper::header::{CONTENT_TYPE, COOKIE, SEC_WEBSOCKET_PROTOCOL};
|
||||
use hyper::http::HeaderValue;
|
||||
|
@ -27,6 +30,7 @@ use once_cell::sync::Lazy;
|
|||
use parking_lot::Mutex;
|
||||
use socket2::SockRef;
|
||||
|
||||
use crate::protocols::dns::DnsResolver;
|
||||
use crate::protocols::tls;
|
||||
use crate::protocols::udp::{UdpStream, UdpStreamWriter};
|
||||
use crate::restrictions::config_reloader::RestrictionsRulesReloader;
|
||||
|
@ -44,26 +48,60 @@ use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
|
|||
use tokio::net::TcpListener;
|
||||
use tokio::select;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
use tokio_rustls::rustls::pki_types::{CertificateDer, PrivateKeyDer};
|
||||
use tokio_rustls::TlsAcceptor;
|
||||
use tokio_stream::wrappers::ReceiverStream;
|
||||
use tracing::{error, info, span, warn, Instrument, Level, Span};
|
||||
use url::Host;
|
||||
use uuid::Uuid;
|
||||
|
||||
async fn run_tunnel(
|
||||
server_config: &WsServerConfig,
|
||||
#[derive(Debug)]
|
||||
pub struct TlsServerConfig {
|
||||
pub tls_certificate: Mutex<Vec<CertificateDer<'static>>>,
|
||||
pub tls_key: Mutex<PrivateKeyDer<'static>>,
|
||||
pub tls_client_ca_certificates: Option<Mutex<Vec<CertificateDer<'static>>>>,
|
||||
pub tls_certificate_path: Option<PathBuf>,
|
||||
pub tls_key_path: Option<PathBuf>,
|
||||
pub tls_client_ca_certs_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
pub struct WsServerConfig {
|
||||
pub socket_so_mark: Option<u32>,
|
||||
pub bind: SocketAddr,
|
||||
pub websocket_ping_frequency: Option<Duration>,
|
||||
pub timeout_connect: Duration,
|
||||
pub websocket_mask_frame: bool,
|
||||
pub tls: Option<TlsServerConfig>,
|
||||
pub dns_resolver: DnsResolver,
|
||||
pub restriction_config: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WsServer {
|
||||
pub config: Arc<WsServerConfig>,
|
||||
}
|
||||
|
||||
impl WsServer {
|
||||
pub fn new(config: WsServerConfig) -> Self {
|
||||
Self {
|
||||
config: Arc::new(config),
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_tunnel(
|
||||
&self,
|
||||
restriction: &RestrictionConfig,
|
||||
remote: RemoteAddr,
|
||||
client_address: SocketAddr,
|
||||
) -> anyhow::Result<(RemoteAddr, Pin<Box<dyn AsyncRead + Send>>, Pin<Box<dyn AsyncWrite + Send>>)> {
|
||||
) -> anyhow::Result<(RemoteAddr, Pin<Box<dyn AsyncRead + Send>>, Pin<Box<dyn AsyncWrite + Send>>)> {
|
||||
match remote.protocol {
|
||||
LocalProtocol::Udp { timeout, .. } => {
|
||||
let (rx, tx) = UdpTunnelConnector::new(
|
||||
&remote.host,
|
||||
remote.port,
|
||||
server_config.socket_so_mark,
|
||||
self.config.socket_so_mark,
|
||||
timeout.unwrap_or(Duration::from_secs(10)),
|
||||
&server_config.dns_resolver,
|
||||
&self.config.dns_resolver,
|
||||
)
|
||||
.connect(&None)
|
||||
.await?;
|
||||
|
@ -74,9 +112,9 @@ async fn run_tunnel(
|
|||
let (rx, mut tx) = TcpTunnelConnector::new(
|
||||
&remote.host,
|
||||
remote.port,
|
||||
server_config.socket_so_mark,
|
||||
self.config.socket_so_mark,
|
||||
Duration::from_secs(10),
|
||||
&server_config.dns_resolver,
|
||||
&self.config.dns_resolver,
|
||||
)
|
||||
.connect(&None)
|
||||
.await?;
|
||||
|
@ -192,6 +230,226 @@ async fn run_tunnel(
|
|||
Err(anyhow::anyhow!("Invalid upgrade request"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn serve(self, restrictions: RestrictionsRules) -> anyhow::Result<()> {
|
||||
info!("Starting wstunnel server listening on {}", self.config.bind);
|
||||
|
||||
// setup upgrade request handler
|
||||
let mk_websocket_upgrade_fn = |server: WsServer,
|
||||
restrictions: Arc<RestrictionsRules>,
|
||||
restrict_path: Option<String>,
|
||||
client_addr: SocketAddr| {
|
||||
move |req: Request<Incoming>| {
|
||||
ws_server_upgrade(server.clone(), restrictions.clone(), restrict_path.clone(), client_addr, req)
|
||||
.map::<anyhow::Result<_>, _>(Ok)
|
||||
}
|
||||
};
|
||||
|
||||
let mk_http_upgrade_fn = |server: WsServer,
|
||||
restrictions: Arc<RestrictionsRules>,
|
||||
restrict_path: Option<String>,
|
||||
client_addr: SocketAddr| {
|
||||
move |req: Request<Incoming>| {
|
||||
http_server_upgrade(server.clone(), restrictions.clone(), restrict_path.clone(), client_addr, req)
|
||||
.map::<anyhow::Result<_>, _>(Ok)
|
||||
}
|
||||
};
|
||||
|
||||
let mk_auto_upgrade_fn = |server: WsServer,
|
||||
restrictions: Arc<RestrictionsRules>,
|
||||
restrict_path: Option<String>,
|
||||
client_addr: SocketAddr| {
|
||||
move |req: Request<Incoming>| {
|
||||
let server = server.clone();
|
||||
let restrictions = restrictions.clone();
|
||||
let restrict_path = restrict_path.clone();
|
||||
async move {
|
||||
if fastwebsockets::upgrade::is_upgrade_request(&req) {
|
||||
ws_server_upgrade(server.clone(), restrictions.clone(), restrict_path, client_addr, req)
|
||||
.map(|response| Ok::<_, anyhow::Error>(response.map(Either::Left)))
|
||||
.await
|
||||
} else if req.version() == Version::HTTP_2 {
|
||||
http_server_upgrade(
|
||||
server.clone(),
|
||||
restrictions.clone(),
|
||||
restrict_path.clone(),
|
||||
client_addr,
|
||||
req,
|
||||
)
|
||||
.map::<anyhow::Result<_>, _>(Ok)
|
||||
.await
|
||||
} else {
|
||||
error!("Invalid protocol version request, got {:?} while expecting either websocket http1 upgrade or http2", req.version());
|
||||
Ok(http::Response::builder()
|
||||
.status(StatusCode::BAD_REQUEST)
|
||||
.body(Either::Left("Invalid protocol request".to_string()))
|
||||
.unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Init TLS if needed
|
||||
let mut tls_context = if let Some(tls_config) = &self.config.tls {
|
||||
let tls_context = TlsContext {
|
||||
tls_acceptor: Arc::new(tls::tls_acceptor(
|
||||
tls_config,
|
||||
Some(vec![b"h2".to_vec(), b"http/1.1".to_vec()]),
|
||||
)?),
|
||||
tls_reloader: TlsReloader::new_for_server(self.config.clone())?,
|
||||
tls_config,
|
||||
};
|
||||
Some(tls_context)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Bind server and run forever to serve incoming connections.
|
||||
let mut restrictions = RestrictionsRulesReloader::new(restrictions, self.config.restriction_config.clone())?;
|
||||
let mut await_config_reload = Box::pin(restrictions.reload_notifier());
|
||||
let listener = TcpListener::bind(&self.config.bind).await?;
|
||||
|
||||
loop {
|
||||
let cnx = select! {
|
||||
biased;
|
||||
|
||||
_ = &mut await_config_reload => {
|
||||
drop(await_config_reload);
|
||||
restrictions.reload_restrictions_config();
|
||||
await_config_reload = Box::pin(restrictions.reload_notifier());
|
||||
continue;
|
||||
},
|
||||
|
||||
cnx = listener.accept() => { cnx }
|
||||
};
|
||||
|
||||
let (stream, peer_addr) = match cnx {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => {
|
||||
warn!("Error while accepting connection {:?}", err);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(err) = protocols::tcp::configure_socket(SockRef::from(&stream), &None) {
|
||||
warn!("Error while configuring server socket {:?}", err);
|
||||
}
|
||||
|
||||
let span = span!(
|
||||
Level::INFO,
|
||||
"tunnel",
|
||||
id = tracing::field::Empty,
|
||||
remote = tracing::field::Empty,
|
||||
peer = peer_addr.to_string(),
|
||||
forwarded_for = tracing::field::Empty
|
||||
);
|
||||
|
||||
info!("Accepting connection");
|
||||
let server = self.clone();
|
||||
let restrictions = restrictions.restrictions_rules().clone();
|
||||
|
||||
// Check if we need to enable TLS or not
|
||||
match tls_context.as_mut() {
|
||||
Some(tls) => {
|
||||
// Reload TLS certificate if needed
|
||||
let tls_acceptor = tls.tls_acceptor().clone();
|
||||
let fut = async move {
|
||||
info!("Doing TLS handshake");
|
||||
let tls_stream = match tls_acceptor.accept(stream).await {
|
||||
Ok(tls_stream) => hyper_util::rt::TokioIo::new(tls_stream),
|
||||
Err(err) => {
|
||||
error!("error while accepting TLS connection {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let tls_ctx = tls_stream.inner().get_ref().1;
|
||||
// extract client certificate common name if any
|
||||
let restrict_path = tls_ctx
|
||||
.peer_certificates()
|
||||
.and_then(tls::find_leaf_certificate)
|
||||
.and_then(|c| tls::cn_from_certificate(&c));
|
||||
match tls_ctx.alpn_protocol() {
|
||||
// http2
|
||||
Some(b"h2") => {
|
||||
let mut conn_builder = http2::Builder::new(TokioExecutor::new());
|
||||
if let Some(ping) = server.config.websocket_ping_frequency {
|
||||
conn_builder.keep_alive_interval(ping);
|
||||
}
|
||||
|
||||
let http_upgrade_fn =
|
||||
mk_http_upgrade_fn(server, restrictions.clone(), restrict_path, peer_addr);
|
||||
let con_fut = conn_builder.serve_connection(tls_stream, service_fn(http_upgrade_fn));
|
||||
if let Err(e) = con_fut.await {
|
||||
error!("Error while upgrading cnx to http: {:?}", e);
|
||||
}
|
||||
}
|
||||
// websocket
|
||||
_ => {
|
||||
let websocket_upgrade_fn =
|
||||
mk_websocket_upgrade_fn(server, restrictions.clone(), restrict_path, peer_addr);
|
||||
let conn_fut = http1::Builder::new()
|
||||
.serve_connection(tls_stream, service_fn(websocket_upgrade_fn))
|
||||
.with_upgrades();
|
||||
|
||||
if let Err(e) = conn_fut.await {
|
||||
error!("Error while upgrading cnx: {:?}", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
.instrument(span);
|
||||
|
||||
tokio::spawn(fut);
|
||||
// Normal
|
||||
}
|
||||
// HTTP without TLS
|
||||
None => {
|
||||
let fut = async move {
|
||||
let stream = hyper_util::rt::TokioIo::new(stream);
|
||||
let mut conn_fut = hyper_util::server::conn::auto::Builder::new(TokioExecutor::new());
|
||||
if let Some(ping) = server.config.websocket_ping_frequency {
|
||||
conn_fut.http2().keep_alive_interval(ping);
|
||||
}
|
||||
|
||||
let websocket_upgrade_fn = mk_auto_upgrade_fn(server, restrictions.clone(), None, peer_addr);
|
||||
let upgradable =
|
||||
conn_fut.serve_connection_with_upgrades(stream, service_fn(websocket_upgrade_fn));
|
||||
|
||||
if let Err(e) = upgradable.await {
|
||||
error!("Error while upgrading cnx to websocket: {:?}", e);
|
||||
}
|
||||
}
|
||||
.instrument(span);
|
||||
|
||||
tokio::spawn(fut);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for WsServerConfig {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("WsServerConfig")
|
||||
.field("socket_so_mark", &self.socket_so_mark)
|
||||
.field("bind", &self.bind)
|
||||
.field("websocket_ping_frequency", &self.websocket_ping_frequency)
|
||||
.field("timeout_connect", &self.timeout_connect)
|
||||
.field("websocket_mask_frame", &self.websocket_mask_frame)
|
||||
.field("restriction_config", &self.restriction_config)
|
||||
.field("tls", &self.tls.is_some())
|
||||
.field(
|
||||
"mTLS",
|
||||
&self
|
||||
.tls
|
||||
.as_ref()
|
||||
.map(|x| x.tls_client_ca_certificates.is_some())
|
||||
.unwrap_or(false),
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the requested (remote) port has been mapped in the configuration to another port.
|
||||
|
@ -396,7 +654,7 @@ fn validate_tunnel<'a>(
|
|||
}
|
||||
|
||||
async fn ws_server_upgrade(
|
||||
server_config: Arc<WsServerConfig>,
|
||||
server: WsServer,
|
||||
restrictions: Arc<RestrictionsRules>,
|
||||
restrict_path_prefix: Option<String>,
|
||||
mut client_addr: SocketAddr,
|
||||
|
@ -466,7 +724,7 @@ async fn ws_server_upgrade(
|
|||
};
|
||||
|
||||
let req_protocol = remote.protocol.clone();
|
||||
let tunnel = match run_tunnel(&server_config, restriction, remote, client_addr).await {
|
||||
let tunnel = match server.run_tunnel(restriction, remote, client_addr).await {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => {
|
||||
warn!("Rejecting connection with bad upgrade request: {} {}", err, req.uri());
|
||||
|
@ -500,19 +758,15 @@ async fn ws_server_upgrade(
|
|||
}
|
||||
};
|
||||
let (close_tx, close_rx) = oneshot::channel::<()>();
|
||||
ws_tx.set_auto_apply_mask(server_config.websocket_mask_frame);
|
||||
ws_tx.set_auto_apply_mask(server.config.websocket_mask_frame);
|
||||
|
||||
tokio::task::spawn(
|
||||
super::transport::io::propagate_remote_to_local(local_tx, WebsocketTunnelRead::new(ws_rx), close_rx)
|
||||
transport::io::propagate_remote_to_local(local_tx, WebsocketTunnelRead::new(ws_rx), close_rx)
|
||||
.instrument(Span::current()),
|
||||
);
|
||||
|
||||
let _ = super::transport::io::propagate_local_to_remote(
|
||||
local_rx,
|
||||
WebsocketTunnelWrite::new(ws_tx),
|
||||
close_tx,
|
||||
None,
|
||||
)
|
||||
let _ =
|
||||
transport::io::propagate_local_to_remote(local_rx, WebsocketTunnelWrite::new(ws_tx), close_tx, None)
|
||||
.await;
|
||||
}
|
||||
.instrument(Span::current()),
|
||||
|
@ -539,7 +793,7 @@ async fn ws_server_upgrade(
|
|||
}
|
||||
|
||||
async fn http_server_upgrade(
|
||||
server_config: Arc<WsServerConfig>,
|
||||
server: WsServer,
|
||||
restrictions: Arc<RestrictionsRules>,
|
||||
restrict_path_prefix: Option<String>,
|
||||
mut client_addr: SocketAddr,
|
||||
|
@ -600,7 +854,7 @@ async fn http_server_upgrade(
|
|||
};
|
||||
|
||||
let req_protocol = remote.protocol.clone();
|
||||
let tunnel = match run_tunnel(&server_config, restriction, remote, client_addr).await {
|
||||
let tunnel = match server.run_tunnel(restriction, remote, client_addr).await {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => {
|
||||
warn!("Rejecting connection with bad upgrade request: {} {}", err, req.uri());
|
||||
|
@ -630,13 +884,12 @@ async fn http_server_upgrade(
|
|||
async move {
|
||||
let (close_tx, close_rx) = oneshot::channel::<()>();
|
||||
tokio::task::spawn(
|
||||
super::transport::io::propagate_remote_to_local(local_tx, Http2TunnelRead::new(ws_rx), close_rx)
|
||||
transport::io::propagate_remote_to_local(local_tx, Http2TunnelRead::new(ws_rx), close_rx)
|
||||
.instrument(Span::current()),
|
||||
);
|
||||
|
||||
let _ =
|
||||
super::transport::io::propagate_local_to_remote(local_rx, Http2TunnelWrite::new(ws_tx), close_tx, None)
|
||||
.await;
|
||||
transport::io::propagate_local_to_remote(local_rx, Http2TunnelWrite::new(ws_tx), close_tx, None).await;
|
||||
}
|
||||
.instrument(Span::current()),
|
||||
);
|
||||
|
@ -678,211 +931,6 @@ impl TlsContext<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn run_server(server_config: Arc<WsServerConfig>, restrictions: RestrictionsRules) -> anyhow::Result<()> {
|
||||
info!("Starting wstunnel server listening on {}", server_config.bind);
|
||||
|
||||
// setup upgrade request handler
|
||||
let mk_websocket_upgrade_fn = |server_config: Arc<WsServerConfig>,
|
||||
restrictions: Arc<RestrictionsRules>,
|
||||
restrict_path: Option<String>,
|
||||
client_addr: SocketAddr| {
|
||||
move |req: Request<Incoming>| {
|
||||
ws_server_upgrade(
|
||||
server_config.clone(),
|
||||
restrictions.clone(),
|
||||
restrict_path.clone(),
|
||||
client_addr,
|
||||
req,
|
||||
)
|
||||
.map::<anyhow::Result<_>, _>(Ok)
|
||||
}
|
||||
};
|
||||
|
||||
let mk_http_upgrade_fn = |server_config: Arc<WsServerConfig>,
|
||||
restrictions: Arc<RestrictionsRules>,
|
||||
restrict_path: Option<String>,
|
||||
client_addr: SocketAddr| {
|
||||
move |req: Request<Incoming>| {
|
||||
http_server_upgrade(
|
||||
server_config.clone(),
|
||||
restrictions.clone(),
|
||||
restrict_path.clone(),
|
||||
client_addr,
|
||||
req,
|
||||
)
|
||||
.map::<anyhow::Result<_>, _>(Ok)
|
||||
}
|
||||
};
|
||||
|
||||
let mk_auto_upgrade_fn = |server_config: Arc<WsServerConfig>,
|
||||
restrictions: Arc<RestrictionsRules>,
|
||||
restrict_path: Option<String>,
|
||||
client_addr: SocketAddr| {
|
||||
move |req: Request<Incoming>| {
|
||||
let server_config = server_config.clone();
|
||||
let restrictions = restrictions.clone();
|
||||
let restrict_path = restrict_path.clone();
|
||||
async move {
|
||||
if fastwebsockets::upgrade::is_upgrade_request(&req) {
|
||||
ws_server_upgrade(server_config.clone(), restrictions.clone(), restrict_path, client_addr, req)
|
||||
.map(|response| Ok::<_, anyhow::Error>(response.map(Either::Left)))
|
||||
.await
|
||||
} else if req.version() == Version::HTTP_2 {
|
||||
http_server_upgrade(
|
||||
server_config.clone(),
|
||||
restrictions.clone(),
|
||||
restrict_path.clone(),
|
||||
client_addr,
|
||||
req,
|
||||
)
|
||||
.map::<anyhow::Result<_>, _>(Ok)
|
||||
.await
|
||||
} else {
|
||||
error!("Invalid protocol version request, got {:?} while expecting either websocket http1 upgrade or http2", req.version());
|
||||
Ok(http::Response::builder()
|
||||
.status(StatusCode::BAD_REQUEST)
|
||||
.body(Either::Left("Invalid protocol request".to_string()))
|
||||
.unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Init TLS if needed
|
||||
let mut tls_context = if let Some(tls_config) = &server_config.tls {
|
||||
let tls_context = TlsContext {
|
||||
tls_acceptor: Arc::new(tls::tls_acceptor(tls_config, Some(vec![b"h2".to_vec(), b"http/1.1".to_vec()]))?),
|
||||
tls_reloader: TlsReloader::new_for_server(server_config.clone())?,
|
||||
tls_config,
|
||||
};
|
||||
Some(tls_context)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Bind server and run forever to serve incoming connections.
|
||||
let mut restrictions = RestrictionsRulesReloader::new(restrictions, server_config.restriction_config.clone())?;
|
||||
let mut await_config_reload = Box::pin(restrictions.reload_notifier());
|
||||
let listener = TcpListener::bind(&server_config.bind).await?;
|
||||
|
||||
loop {
|
||||
let cnx = select! {
|
||||
biased;
|
||||
|
||||
_ = &mut await_config_reload => {
|
||||
drop(await_config_reload);
|
||||
restrictions.reload_restrictions_config();
|
||||
await_config_reload = Box::pin(restrictions.reload_notifier());
|
||||
continue;
|
||||
},
|
||||
|
||||
cnx = listener.accept() => { cnx }
|
||||
};
|
||||
|
||||
let (stream, peer_addr) = match cnx {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => {
|
||||
warn!("Error while accepting connection {:?}", err);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(err) = protocols::tcp::configure_socket(SockRef::from(&stream), &None) {
|
||||
warn!("Error while configuring server socket {:?}", err);
|
||||
}
|
||||
|
||||
let span = span!(
|
||||
Level::INFO,
|
||||
"tunnel",
|
||||
id = tracing::field::Empty,
|
||||
remote = tracing::field::Empty,
|
||||
peer = peer_addr.to_string(),
|
||||
forwarded_for = tracing::field::Empty
|
||||
);
|
||||
|
||||
info!("Accepting connection");
|
||||
let server_config = server_config.clone();
|
||||
let restrictions = restrictions.restrictions_rules().clone();
|
||||
|
||||
// Check if we need to enable TLS or not
|
||||
match tls_context.as_mut() {
|
||||
Some(tls) => {
|
||||
// Reload TLS certificate if needed
|
||||
let tls_acceptor = tls.tls_acceptor().clone();
|
||||
let fut = async move {
|
||||
info!("Doing TLS handshake");
|
||||
let tls_stream = match tls_acceptor.accept(stream).await {
|
||||
Ok(tls_stream) => hyper_util::rt::TokioIo::new(tls_stream),
|
||||
Err(err) => {
|
||||
error!("error while accepting TLS connection {}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let tls_ctx = tls_stream.inner().get_ref().1;
|
||||
// extract client certificate common name if any
|
||||
let restrict_path = tls_ctx
|
||||
.peer_certificates()
|
||||
.and_then(tls::find_leaf_certificate)
|
||||
.and_then(|c| tls::cn_from_certificate(&c));
|
||||
match tls_ctx.alpn_protocol() {
|
||||
// http2
|
||||
Some(b"h2") => {
|
||||
let mut conn_builder = http2::Builder::new(TokioExecutor::new());
|
||||
if let Some(ping) = server_config.websocket_ping_frequency {
|
||||
conn_builder.keep_alive_interval(ping);
|
||||
}
|
||||
|
||||
let http_upgrade_fn =
|
||||
mk_http_upgrade_fn(server_config, restrictions.clone(), restrict_path, peer_addr);
|
||||
let con_fut = conn_builder.serve_connection(tls_stream, service_fn(http_upgrade_fn));
|
||||
if let Err(e) = con_fut.await {
|
||||
error!("Error while upgrading cnx to http: {:?}", e);
|
||||
}
|
||||
}
|
||||
// websocket
|
||||
_ => {
|
||||
let websocket_upgrade_fn =
|
||||
mk_websocket_upgrade_fn(server_config, restrictions.clone(), restrict_path, peer_addr);
|
||||
let conn_fut = http1::Builder::new()
|
||||
.serve_connection(tls_stream, service_fn(websocket_upgrade_fn))
|
||||
.with_upgrades();
|
||||
|
||||
if let Err(e) = conn_fut.await {
|
||||
error!("Error while upgrading cnx: {:?}", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
.instrument(span);
|
||||
|
||||
tokio::spawn(fut);
|
||||
// Normal
|
||||
}
|
||||
// HTTP without TLS
|
||||
None => {
|
||||
let fut = async move {
|
||||
let stream = hyper_util::rt::TokioIo::new(stream);
|
||||
let mut conn_fut = hyper_util::server::conn::auto::Builder::new(TokioExecutor::new());
|
||||
if let Some(ping) = server_config.websocket_ping_frequency {
|
||||
conn_fut.http2().keep_alive_interval(ping);
|
||||
}
|
||||
|
||||
let websocket_upgrade_fn = mk_auto_upgrade_fn(server_config, restrictions.clone(), None, peer_addr);
|
||||
let upgradable = conn_fut.serve_connection_with_upgrades(stream, service_fn(websocket_upgrade_fn));
|
||||
|
||||
if let Err(e) = upgradable.await {
|
||||
error!("Error while upgrading cnx to websocket: {:?}", e);
|
||||
}
|
||||
}
|
||||
.instrument(span);
|
||||
|
||||
tokio::spawn(fut);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
async fn run_listening_server<T>(
|
||||
local_srv: &(Host, u16),
|
|
@ -1,7 +1,7 @@
|
|||
use crate::protocols::tls;
|
||||
use crate::tunnel::client::WsClientConfig;
|
||||
use crate::tunnel::server::WsServerConfig;
|
||||
use crate::tunnel::tls_reloader::TlsReloaderState::{Client, Server};
|
||||
use crate::WsServerConfig;
|
||||
use anyhow::Context;
|
||||
use log::trace;
|
||||
use notify::{EventKind, RecommendedWatcher, Watcher};
|
||||
|
|
Loading…
Reference in a new issue