diff --git a/Cargo.lock b/Cargo.lock index d1126a2..3c670a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -63,6 +63,12 @@ dependencies = [ "serde", ] +[[package]] +name = "arc-swap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" + [[package]] name = "argon2" version = "0.4.1" @@ -312,6 +318,26 @@ dependencies = [ "tower-service", ] +[[package]] +name = "axum-server" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8456dab8f11484979a86651da8e619b355ede5d61a160755155f6c344bd18c47" +dependencies = [ + "arc-swap", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "tokio", + "tokio-rustls", + "tower-service", +] + [[package]] name = "base64" version = "0.13.0" @@ -1156,9 +1182,9 @@ checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" [[package]] name = "httparse" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" @@ -1168,9 +1194,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.20" +version = "0.14.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" dependencies = [ "bytes", "futures-channel", @@ -1285,6 +1311,7 @@ dependencies = [ "async-trait", "axum", "axum-auth", + "axum-server", "chrono", "ciborium", "clap", @@ -1994,9 +2021,9 @@ checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" [[package]] name = "socket2" -version = "0.4.4" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" dependencies = [ "libc", "winapi", @@ -2357,9 +2384,32 @@ dependencies = [ "once_cell", "pin-project-lite", "socket2", + "tokio-macros", "winapi", ] +[[package]] +name = "tokio-macros" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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]] name = "tokio-util" version = "0.7.3" diff --git a/kitchen/Cargo.toml b/kitchen/Cargo.toml index 1412d1b..7d69393 100644 --- a/kitchen/Cargo.toml +++ b/kitchen/Cargo.toml @@ -29,6 +29,10 @@ version = "0.4.1" version = "0.8.0" features = ["serde"] +[dependencies.axum-server] +version = "0.4.4" +features = [ "tls-rustls" ] + [dependencies.axum-auth] version = "0.3.0" features = ["auth-basic"] diff --git a/kitchen/src/main.rs b/kitchen/src/main.rs index 465b61a..cb8a490 100644 --- a/kitchen/src/main.rs +++ b/kitchen/src/main.rs @@ -45,7 +45,10 @@ fn create_app<'a>() -> clap::App<'a> { (@subcommand serve => (about: "Serve the interface via the web") (@arg recipe_dir: -d --dir +takes_value "Directory containing recipe files to use") - (@arg session_dir: --session_dir + takes_value "Session store directory to use") + (@arg session_dir: --session_dir +takes_value "Session store directory to use") + (@arg tls: --tls "Use TLS to serve.") + (@arg cert_path: --cert +takes_value "Certificate path. Required if you specified --tls.") + (@arg key_path: --cert_key +takes_value "Certificate key path. Required if you specified --tls") (@arg listen: --listen +takes_value "address and port to listen on 0.0.0.0:3030") ) (@subcommand add_user => @@ -136,7 +139,22 @@ fn main() { }; info!(listen=%listen_socket, "Launching web interface..."); async_std::task::block_on(async { - web::ui_main(recipe_dir_path, session_store_path, listen_socket).await + if matches.contains_id("tls") { + web::ui_main_tls( + recipe_dir_path, + session_store_path, + listen_socket, + matches + .value_of("cert_path") + .expect("You must provide a cert path with --cert"), + matches + .value_of("key_path") + .expect("You must provide a key path with --cert_key"), + ) + .await + } else { + web::ui_main(recipe_dir_path, session_store_path, listen_socket).await + } }); } else if let Some(matches) = matches.subcommand_matches("add_user") { let recipe_dir_path = matches.value_of("recipe_dir").map(|dir| PathBuf::from(dir)); diff --git a/kitchen/src/web/mod.rs b/kitchen/src/web/mod.rs index 5eda923..613ca9f 100644 --- a/kitchen/src/web/mod.rs +++ b/kitchen/src/web/mod.rs @@ -355,8 +355,8 @@ fn mk_v2_routes() -> Router { ) } -#[instrument(fields(recipe_dir=?recipe_dir_path,listen=?listen_socket), skip_all)] -pub async fn ui_main(recipe_dir_path: PathBuf, store_path: PathBuf, listen_socket: SocketAddr) { +#[instrument(fields(recipe_dir=?recipe_dir_path), skip_all)] +pub async fn make_router(recipe_dir_path: PathBuf, store_path: PathBuf) -> Router { let store = Arc::new(storage::file_store::AsyncFileStore::new( recipe_dir_path.clone(), )); @@ -369,7 +369,7 @@ pub async fn ui_main(recipe_dir_path: PathBuf, store_path: PathBuf, listen_socke .run_migrations() .await .expect("Failed to run database migrations"); - let router = Router::new() + Router::new() .route("/", get(|| async { Redirect::temporary("/ui/plan") })) .route("/favicon.ico", get(|| async { StaticFile("favicon.ico") })) .route("/ui/*path", get(ui_static_assets)) @@ -390,12 +390,39 @@ pub async fn ui_main(recipe_dir_path: PathBuf, store_path: PathBuf, listen_socke .layer(TraceLayer::new_for_http()) .layer(Extension(store)) .layer(Extension(app_store)), - ); + ) +} + +#[instrument(fields(recipe_dir=?recipe_dir_path,listen=?listen_socket), skip_all)] +pub async fn ui_main_tls( + recipe_dir_path: PathBuf, + store_path: PathBuf, + listen_socket: SocketAddr, + cert_path: &str, + key_path: &str, +) { + let router = make_router(recipe_dir_path, store_path).await; info!( http = format!("http://{}", listen_socket), "Starting server" ); - axum::Server::bind(&listen_socket) + let config = axum_server::tls_rustls::RustlsConfig::from_pem_file(cert_path, key_path) + .await + .expect("Failed to parse config from pem files"); + axum_server::bind_rustls(listen_socket, config) + .serve(router.into_make_service()) + .await + .expect("Failed to start tls service"); +} + +#[instrument(fields(recipe_dir=?recipe_dir_path,listen=?listen_socket), skip_all)] +pub async fn ui_main(recipe_dir_path: PathBuf, store_path: PathBuf, listen_socket: SocketAddr) { + let router = make_router(recipe_dir_path, store_path).await; + info!( + http = format!("http://{}", listen_socket), + "Starting server" + ); + axum_server::bind(listen_socket) .serve(router.into_make_service()) .await .expect("Failed to start service"); diff --git a/run.sh b/run.sh index 0d07a9b..0bb52d0 100755 --- a/run.sh +++ b/run.sh @@ -14,5 +14,5 @@ EXAMPLES=${EXAMPLES:-../examples} make clean wasm kitchen echo Starting api server serving ${EXAMPLES} -cargo run -- --verbose debug serve --dir ${EXAMPLES} +cargo run -- --verbose debug serve --dir ${EXAMPLES} --tls --cert ~/tls-certs/localhost+2.pem --cert_key ~/tls-certs/localhost+2-key.pem # This is ghetto but I'm doing it anyway