diff --git a/virtweb_backend/Cargo.lock b/virtweb_backend/Cargo.lock index 75a0527..ba6f0e3 100644 --- a/virtweb_backend/Cargo.lock +++ b/virtweb_backend/Cargo.lock @@ -30,7 +30,7 @@ dependencies = [ "actix-service", "actix-utils", "ahash", - "base64", + "base64 0.21.3", "bitflags 2.4.0", "brotli", "bytes", @@ -58,6 +58,22 @@ dependencies = [ "zstd", ] +[[package]] +name = "actix-identity" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1224c9f9593dc27c9077b233ce04adedc1d7febcfc35ee9f53ea3c24df180bec" +dependencies = [ + "actix-service", + "actix-session", + "actix-utils", + "actix-web", + "anyhow", + "futures-core", + "serde", + "tracing", +] + [[package]] name = "actix-macros" version = "0.2.4" @@ -130,6 +146,23 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "actix-session" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da8b818ae1f11049a4d218975345fe8e56ce5a5f92c11f972abcff5ff80e87" +dependencies = [ + "actix-service", + "actix-utils", + "actix-web", + "anyhow", + "async-trait", + "derive_more", + "serde", + "serde_json", + "tracing", +] + [[package]] name = "actix-utils" version = "3.0.1" @@ -207,6 +240,41 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "ahash" version = "0.8.3" @@ -291,6 +359,23 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "async-trait" +version = "0.1.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.29", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -312,6 +397,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" + [[package]] name = "base64" version = "0.21.3" @@ -397,6 +488,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "clap" version = "4.4.2" @@ -455,7 +556,14 @@ version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" dependencies = [ + "aes-gcm", + "base64 0.20.0", + "hkdf", + "hmac", "percent-encoding", + "rand", + "sha2", + "subtle", "time", "version_check", ] @@ -501,9 +609,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core", "typenum", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "deranged" version = "0.3.8" @@ -531,6 +649,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -695,6 +814,16 @@ dependencies = [ "wasi", ] +[[package]] +name = "ghash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "gimli" version = "0.28.0" @@ -738,6 +867,24 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "http" version = "0.2.9" @@ -835,6 +982,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "ipnet" version = "2.8.0" @@ -900,7 +1056,7 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "608aa1b7148a6eeab631c6267deca33407ff851ab50eea115e52c13a9bb184ee" dependencies = [ - "base64", + "base64 0.21.3", "log", "reqwest", "serde", @@ -1014,6 +1170,12 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "openssl" version = "0.10.57" @@ -1111,6 +1273,18 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "polyval" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1209,7 +1383,7 @@ version = "0.11.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" dependencies = [ - "base64", + "base64 0.21.3", "bytes", "encoding_rs", "futures-core", @@ -1372,6 +1546,17 @@ dependencies = [ "digest", ] +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -1422,6 +1607,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "syn" version = "1.0.109" @@ -1610,6 +1801,16 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "url" version = "2.4.1" @@ -1649,7 +1850,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "virtweb_backend" version = "0.1.0" dependencies = [ + "actix-identity", "actix-remote-ip", + "actix-session", "actix-web", "clap", "env_logger", diff --git a/virtweb_backend/Cargo.toml b/virtweb_backend/Cargo.toml index 1a32c01..7f7cf77 100644 --- a/virtweb_backend/Cargo.toml +++ b/virtweb_backend/Cargo.toml @@ -13,4 +13,6 @@ light-openid = "1.0.1" lazy_static = "1.4.0" actix-web = "4" actix-remote-ip = "0.1.0" +actix-session = { version = "0.7.2", features = ["cookie-session"] } +actix-identity = "0.5.2" serde = { version = "1.0.175", features = ["derive"] } \ No newline at end of file diff --git a/virtweb_backend/src/app_config.rs b/virtweb_backend/src/app_config.rs index dadfd20..76f553a 100644 --- a/virtweb_backend/src/app_config.rs +++ b/virtweb_backend/src/app_config.rs @@ -20,6 +20,10 @@ pub struct AppConfig { #[clap(long, env, default_value = "")] secret: String, + /// Specify whether the cookie should be transmitted only over secure connections + #[clap(long, env)] + pub cookie_secure: bool, + /// Auth username #[arg(long, env, default_value = "admin")] pub auth_username: String, @@ -38,13 +42,12 @@ pub struct AppConfig { /// URL where the OpenID configuration can be found #[arg( - long, - env, - default_value = "http://localhost:9001/.well-known/openid-configuration" + long, + env, + default_value = "http://localhost:9001/.well-known/openid-configuration" )] pub oidc_configuration_url: String, - /// Disable OpenID authentication #[arg(long, env)] pub disable_oidc: bool, @@ -83,11 +86,11 @@ impl AppConfig { let mut secret = self.secret.as_str(); if cfg!(debug_assertions) && secret.is_empty() { - secret = "DEBUGKEYDEBUGKEYDEBUGKEYDEBUGKEYDEBUGKEY"; + secret = "DEBUGKEYDEBUGKEYDEBUGKEYDEBUGKEYDEBUGKEYDEBUGKEYDEBUGKEYDEBUGKEY"; } if secret.is_empty() { - panic!("SECRET is undefined or too short (min 30 chars)!") + panic!("SECRET is undefined or too short (min 64 chars)!") } secret diff --git a/virtweb_backend/src/constants.rs b/virtweb_backend/src/constants.rs new file mode 100644 index 0000000..87507d7 --- /dev/null +++ b/virtweb_backend/src/constants.rs @@ -0,0 +1,8 @@ +/// Name of the cookie that contains session information +pub const SESSION_COOKIE_NAME: &str = "X-auth-token"; + +/// Maximum session duration after inactivity, in seconds +pub const MAX_INACTIVITY_DURATION: u64 = 60 * 30; + +/// Maximum session duration (6 hours) +pub const MAX_SESSION_DURATION: u64 = 3600 * 6; diff --git a/virtweb_backend/src/controllers/mod.rs b/virtweb_backend/src/controllers/mod.rs index 977ab49..98a0949 100644 --- a/virtweb_backend/src/controllers/mod.rs +++ b/virtweb_backend/src/controllers/mod.rs @@ -1 +1 @@ -pub mod server_controller; \ No newline at end of file +pub mod server_controller; diff --git a/virtweb_backend/src/lib.rs b/virtweb_backend/src/lib.rs index 09b2b57..11c3b98 100644 --- a/virtweb_backend/src/lib.rs +++ b/virtweb_backend/src/lib.rs @@ -1,2 +1,3 @@ pub mod app_config; -pub mod controllers; \ No newline at end of file +pub mod constants; +pub mod controllers; diff --git a/virtweb_backend/src/main.rs b/virtweb_backend/src/main.rs index a851b18..4c2df23 100644 --- a/virtweb_backend/src/main.rs +++ b/virtweb_backend/src/main.rs @@ -1,7 +1,16 @@ +use actix_identity::config::LogoutBehaviour; +use actix_identity::IdentityMiddleware; use actix_remote_ip::RemoteIPConfig; -use actix_web::{App, HttpServer, web}; -use virtweb_backend::app_config::AppConfig; +use actix_session::storage::CookieSessionStore; +use actix_session::SessionMiddleware; +use actix_web::cookie::{Key, SameSite}; use actix_web::middleware::Logger; +use actix_web::{web, App, HttpServer}; +use std::time::Duration; +use virtweb_backend::app_config::AppConfig; +use virtweb_backend::constants::{ + MAX_INACTIVITY_DURATION, MAX_SESSION_DURATION, SESSION_COOKIE_NAME, +}; use virtweb_backend::controllers::server_controller; #[actix_web::main] @@ -11,14 +20,31 @@ async fn main() -> std::io::Result<()> { log::info!("Start to listen on {}", AppConfig::get().listen_address); HttpServer::new(|| { + let session_mw = SessionMiddleware::builder( + CookieSessionStore::default(), + Key::from(AppConfig::get().secret().as_bytes()), + ) + .cookie_name(SESSION_COOKIE_NAME.to_string()) + .cookie_secure(AppConfig::get().cookie_secure) + .cookie_same_site(SameSite::Strict) + .build(); + + let identity_middleware = IdentityMiddleware::builder() + .logout_behaviour(LogoutBehaviour::PurgeSession) + .visit_deadline(Some(Duration::from_secs(MAX_INACTIVITY_DURATION))) + .login_deadline(Some(Duration::from_secs(MAX_SESSION_DURATION))) + .build(); + App::new() .wrap(Logger::default()) + .wrap(identity_middleware) + .wrap(session_mw) .app_data(web::Data::new(RemoteIPConfig { - proxy: AppConfig::get().proxy_ip.clone() + proxy: AppConfig::get().proxy_ip.clone(), })) .route("/", web::get().to(server_controller::root_index)) }) - .bind(&AppConfig::get().listen_address)? - .run() - .await + .bind(&AppConfig::get().listen_address)? + .run() + .await }