Compare commits
2 Commits
4055bf24ab
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| e1f3bca708 | |||
| 105deab45d |
@@ -2,3 +2,4 @@
|
|||||||
cert.pem
|
cert.pem
|
||||||
key.pem
|
key.pem
|
||||||
bundle
|
bundle
|
||||||
|
config.toml
|
||||||
|
|||||||
Generated
+682
-249
File diff suppressed because it is too large
Load Diff
@@ -8,9 +8,14 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.86"
|
anyhow = "1.0.86"
|
||||||
axum = "0.7.7"
|
axum = "0.7.7"
|
||||||
|
axum-server = { version = "0.7.1", features = ["tls-rustls"] }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
rustls-pemfile = "2.2.0"
|
||||||
|
serde = { version = "1.0.214", features = ["derive"] }
|
||||||
|
serde_json = "1.0.132"
|
||||||
tokio = { version = "1.37.0", features = ["full"] }
|
tokio = { version = "1.37.0", features = ["full"] }
|
||||||
tokio-rustls = "0.26.0"
|
tokio-rustls = "0.26.0"
|
||||||
|
toml = "0.8.19"
|
||||||
tower = "0.5.1"
|
tower = "0.5.1"
|
||||||
tower-http = { version = "0.6.1", features = ["fs"] }
|
tower-http = { version = "0.6.1", features = ["fs"] }
|
||||||
tracing = { version = "0.1.40", features = ["async-await"] }
|
tracing = { version = "0.1.40", features = ["async-await"] }
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
proxy_listen_address = "127.0.0.1:4433"
|
||||||
|
http_listen_address = "127.0.0.1:4434"
|
||||||
|
cert_path = "./cert.pem"
|
||||||
|
key_path = "./key.pem"
|
||||||
|
mumble_server_url = "voip.ohea.xyz:64738"
|
||||||
|
gui_path = "../mumble-web2/dist"
|
||||||
|
serve_https = true
|
||||||
|
|
||||||
|
[gui]
|
||||||
|
proxy_url = "https://voip2.ohea.xyz"
|
||||||
|
# cert_hash = [...]
|
||||||
+95
-42
@@ -1,12 +1,13 @@
|
|||||||
use anyhow::{Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use axum::http::{Response, StatusCode};
|
use axum::http::{Response, StatusCode};
|
||||||
use axum::response::IntoResponse;
|
use axum::response::IntoResponse;
|
||||||
use std::env;
|
use serde::{Deserialize, Serialize};
|
||||||
use std::future::IntoFuture;
|
use std::future::IntoFuture;
|
||||||
use std::net::{SocketAddr, ToSocketAddrs};
|
use std::net::{SocketAddr, ToSocketAddrs};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use tokio::fs::read_to_string;
|
||||||
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tokio::pin;
|
use tokio::pin;
|
||||||
@@ -87,48 +88,41 @@ impl ServerCertVerifier for NoCertificateVerification {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Deserialize, Serialize)]
|
||||||
struct Config {
|
struct GuiConfig {
|
||||||
proxy_listen_address: String,
|
proxy_url: Option<String>,
|
||||||
http_listen_address: String,
|
cert_hash: Option<Vec<u8>>,
|
||||||
cert_path: String,
|
|
||||||
key_path: String,
|
|
||||||
mumble_server_address: SocketAddr,
|
|
||||||
gui_path: PathBuf,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
#[derive(Clone, Deserialize)]
|
||||||
fn get() -> Result<Config> {
|
struct Config {
|
||||||
Ok(Config {
|
proxy_listen_address: SocketAddr,
|
||||||
proxy_listen_address: env::var("MUMBLE_WEBTRANSPORT_PROXY_LISTEN_ADDRESS")
|
http_listen_address: SocketAddr,
|
||||||
.unwrap_or("127.0.0.1:4433".to_string()),
|
cert_path: String,
|
||||||
http_listen_address: env::var("MUMBLE_WEBTRANSPORT_HTTP_LISTEN_ADDRESS")
|
key_path: String,
|
||||||
.unwrap_or("127.0.0.1:4434".to_string()),
|
#[serde(default)]
|
||||||
cert_path: env::var("MUMBLE_WEBTRANSPORT_CERT_PATH").unwrap_or("cert.pem".to_string()),
|
serve_https: bool,
|
||||||
key_path: env::var("MUMBLE_WEBTRANSPORT_KEY_PATH").unwrap_or("key.pem".to_string()),
|
mumble_server_url: String,
|
||||||
mumble_server_address: env::var("MUMBLE_WEBTRANSPORT_MUMBLE_SERVER_ADDRESS")
|
gui_path: PathBuf,
|
||||||
.with_context(|| "Please set MUMBLE_WEBTRANSPORT_MUMBLE_SERVER_ADDRESS")?
|
gui: GuiConfig,
|
||||||
.to_socket_addrs()?
|
|
||||||
.next()
|
|
||||||
.ok_or(anyhow::anyhow!("Invalid mumble server address"))?,
|
|
||||||
gui_path: PathBuf::from(
|
|
||||||
env::var("MUMBLE_WEBTRANSPORT_GUI_PATH").unwrap_or("gui".to_string()),
|
|
||||||
),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//async fn serve_index_html_with_config(State(config): State<Config>) -> impl IntoResponse {
|
//async fn serve_index_html_with_config(State(config): State<Config>) -> impl IntoResponse {
|
||||||
async fn serve_index_html_with_config(State(config): State<Config>) -> impl IntoResponse {
|
async fn serve_index_html_with_config(State(config): State<Config>) -> impl IntoResponse {
|
||||||
// Load the HTML file
|
// Load the HTML file
|
||||||
let html = match tokio::fs::read_to_string(config.gui_path.join("index.html")).await {
|
let html = match read_to_string(config.gui_path.join("index.html")).await {
|
||||||
Ok(content) => content,
|
Ok(content) => content,
|
||||||
Err(_) => return (StatusCode::NOT_FOUND, "File not found").into_response(),
|
Err(_) => return (StatusCode::NOT_FOUND, "File not found").into_response(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Insert the script tag with configuration
|
// Insert the script tag with configuration
|
||||||
let config_script = "<script>window.config = {\"foo\": \"bar\"}</script>";
|
let modified_html = html.replace(
|
||||||
let modified_html = html.replace("</head>", &format!("{}\n</head>", config_script));
|
"</head>",
|
||||||
|
&format!(
|
||||||
|
"<script>window.config = {}</script>\n</head>",
|
||||||
|
serde_json::to_string(&config.gui).unwrap(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
// Create a response with the modified HTML
|
// Create a response with the modified HTML
|
||||||
Response::builder()
|
Response::builder()
|
||||||
@@ -139,11 +133,57 @@ async fn serve_index_html_with_config(State(config): State<Config>) -> impl Into
|
|||||||
.into_response()
|
.into_response()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn configure_tls(config: &Config) -> Result<rustls::ServerConfig, anyhow::Error> {
|
||||||
|
// Thanks perplexity!
|
||||||
|
use rustls_pemfile::{certs, pkcs8_private_keys};
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::BufReader;
|
||||||
|
|
||||||
|
// Create a new ServerConfig with no client authentication
|
||||||
|
//(rustls::server::NoClientAuth::new());
|
||||||
|
|
||||||
|
// Read the certificate file
|
||||||
|
let cert_file = File::open(&config.cert_path)?;
|
||||||
|
let mut cert_reader = BufReader::new(cert_file);
|
||||||
|
let cert_chain = certs(&mut cert_reader).collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
|
// Read the private key file
|
||||||
|
let key_file = File::open(&config.key_path)?;
|
||||||
|
let mut key_reader = BufReader::new(key_file);
|
||||||
|
let key = pkcs8_private_keys(&mut key_reader)
|
||||||
|
.next()
|
||||||
|
.ok_or(anyhow!("no keys in key.pem"))??;
|
||||||
|
|
||||||
|
// Set the certificate chain and private key
|
||||||
|
let config = rustls::ServerConfig::builder()
|
||||||
|
.with_no_client_auth()
|
||||||
|
.with_single_cert(cert_chain, key.into())?;
|
||||||
|
|
||||||
|
Ok(config)
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
init_logging();
|
init_logging();
|
||||||
|
|
||||||
let proxy_config = Config::get()?;
|
let proxy_config: Config = toml::from_str(
|
||||||
|
&read_to_string("./config.toml")
|
||||||
|
.await
|
||||||
|
.context("reading config.toml (try making a copy of config.toml.example)")?,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mumble_server_addr = proxy_config
|
||||||
|
.mumble_server_url
|
||||||
|
.to_socket_addrs()
|
||||||
|
.context(format!(
|
||||||
|
"parsing mumble_server_url={}",
|
||||||
|
proxy_config.mumble_server_url
|
||||||
|
))?
|
||||||
|
.next()
|
||||||
|
.ok_or(anyhow!(
|
||||||
|
"no socket addrs in mumble_server_url={}",
|
||||||
|
proxy_config.mumble_server_url
|
||||||
|
))?;
|
||||||
|
|
||||||
// Setup HTTP Server
|
// Setup HTTP Server
|
||||||
//let http = axum::Router::new().route("/", axum::routing::get(serve_gui));
|
//let http = axum::Router::new().route("/", axum::routing::get(serve_gui));
|
||||||
@@ -151,17 +191,30 @@ async fn main() -> Result<()> {
|
|||||||
.route("/", axum::routing::get(serve_index_html_with_config))
|
.route("/", axum::routing::get(serve_index_html_with_config))
|
||||||
.fallback_service(tower_http::services::ServeDir::new(&proxy_config.gui_path))
|
.fallback_service(tower_http::services::ServeDir::new(&proxy_config.gui_path))
|
||||||
.with_state(proxy_config.clone());
|
.with_state(proxy_config.clone());
|
||||||
let listener = tokio::net::TcpListener::bind(proxy_config.http_listen_address)
|
if proxy_config.serve_https {
|
||||||
.await
|
tokio::spawn(
|
||||||
.unwrap();
|
axum_server::bind_rustls(
|
||||||
tokio::spawn(axum::serve(listener, app).into_future());
|
proxy_config.http_listen_address,
|
||||||
|
axum_server::tls_rustls::RustlsConfig::from_config(Arc::new(configure_tls(
|
||||||
|
&proxy_config,
|
||||||
|
)?)),
|
||||||
|
)
|
||||||
|
.serve(app.into_make_service())
|
||||||
|
.into_future(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
tokio::spawn(
|
||||||
|
axum_server::bind(proxy_config.http_listen_address)
|
||||||
|
.serve(app.into_make_service())
|
||||||
|
.into_future(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Setup WebTransport proxy listener
|
// Setup WebTransport proxy listener
|
||||||
|
let identity = Identity::load_pemfiles(proxy_config.cert_path, proxy_config.key_path).await?;
|
||||||
let config = ServerConfig::builder()
|
let config = ServerConfig::builder()
|
||||||
.with_bind_address(proxy_config.proxy_listen_address.parse()?)
|
.with_bind_address(proxy_config.proxy_listen_address)
|
||||||
.with_identity(
|
.with_identity(&identity)
|
||||||
&Identity::load_pemfiles(proxy_config.cert_path, proxy_config.key_path).await?,
|
|
||||||
)
|
|
||||||
.keep_alive_interval(Some(Duration::from_secs(20)))
|
.keep_alive_interval(Some(Duration::from_secs(20)))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
@@ -172,7 +225,7 @@ async fn main() -> Result<()> {
|
|||||||
for id in 0.. {
|
for id in 0.. {
|
||||||
let incoming_session = server.accept().await;
|
let incoming_session = server.accept().await;
|
||||||
tokio::spawn(
|
tokio::spawn(
|
||||||
handle_connection(incoming_session, id, proxy_config.mumble_server_address)
|
handle_connection(incoming_session, id, mumble_server_addr)
|
||||||
.instrument(info_span!("Connection", id)),
|
.instrument(info_span!("Connection", id)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user