From 2a93bc2cd6e61a977ce1a7793d688bacb6d1391f Mon Sep 17 00:00:00 2001 From: Ara Sadoyan Date: Mon, 26 May 2025 12:42:01 +0200 Subject: [PATCH] http to https redirect cleanup --- README.md | 11 ++++++++--- src/utils/parceyaml.rs | 8 ++++++-- src/utils/structs.rs | 6 ++++-- src/web/bgservice.rs | 3 ++- src/web/proxyhttp.rs | 42 +++++++++++++----------------------------- src/web/start.rs | 3 ++- 6 files changed, 35 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 8fc1f81..bea6dfb 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,8 @@ A sample `upstreams.yaml` entry: ```yaml provider: "file" -stickysessions: false +sticky_sessions: false +to_ssl: false globals: headers: - "Access-Control-Allow-Origin:*" @@ -135,6 +136,7 @@ globals: myhost.mydomain.com: paths: "/": + to_https: false headers: - "X-Some-Thing:Yaaaaaaaaaaaaaaa" - "X-Proxy-From:Hopaaaaaaaaaaaar" @@ -142,6 +144,7 @@ myhost.mydomain.com: - "127.0.0.1:8000" - "127.0.0.2:8000" "/foo": + to_https: true headers: - "X-Another-Header:Hohohohoho" servers: @@ -151,15 +154,17 @@ myhost.mydomain.com: **This means:** -- Sticky sessions are disabled globally. This setting applies to all upstreams. +- Sticky sessions are disabled globally. This setting applies to all upstreams. If enabled all requests will be 301 redirected to HTTPS. +- HTTP to HTTPS redirect disabled globally, but can be overridden by `to_https` setting per upstream. - Requests to `myhost.mydomain.com/` will be proxied to `127.0.0.1` and `127.0.0.2`. +- Plain HTTP to `myhost.mydomain.com/foo` will get 301 redirect to configured TLS port of Gazan. - Requests to `myhost.mydomain.com/foo` will be proxied to `127.0.0.4` and `127.0.0.5`. - SSL/TLS for upstreams is detected automatically, no need to set any config parameter. - Assuming the `127.0.0.5:8443` is SSL protected. The inner traffic will use TLS. - Self signed certificates are silently accepted. - Global headers (CORS for this case) will be injected to all upstreams - Additional headers will be injected into the request for `myhost.mydomain.com`. -- You can choose any path, deep nested paths are supported, the best match is chosen. +- You can choose any path, deep nested paths are supported, the best match chosen. - All requests to servers will require JWT token authentication (You can comment out the authorization to disable it), - Firs parameter specifies the mechanism of authorisation `jwt` - Second is the secret key for validating `jwt` tokens diff --git a/src/utils/parceyaml.rs b/src/utils/parceyaml.rs index 0d1517c..8a2d991 100644 --- a/src/utils/parceyaml.rs +++ b/src/utils/parceyaml.rs @@ -13,7 +13,8 @@ pub fn load_configuration(d: &str, kind: &str) -> Option { consul: None, typecfg: "".to_string(), extraparams: Extraparams { - stickysessions: false, + sticky_sessions: false, + to_ssl: None, authentication: DashMap::new(), }, }; @@ -56,7 +57,10 @@ pub fn load_configuration(d: &str, kind: &str) -> Option { } global_headers.insert("/".to_string(), hl); toreturn.headers.insert("GLOBAL_HEADERS".to_string(), global_headers); - toreturn.extraparams.stickysessions = parsed.stickysessions; + + toreturn.extraparams.sticky_sessions = parsed.sticky_sessions; + toreturn.extraparams.to_ssl = parsed.to_ssl; + let cfg = DashMap::new(); if let Some(k) = globals.get("authorization") { cfg.insert("authorization".to_string(), k.to_owned()); diff --git a/src/utils/structs.rs b/src/utils/structs.rs index 9e8bcd4..7957ce9 100644 --- a/src/utils/structs.rs +++ b/src/utils/structs.rs @@ -16,7 +16,8 @@ pub struct ServiceMapping { #[derive(Clone, Debug)] pub struct Extraparams { - pub stickysessions: bool, + pub sticky_sessions: bool, + pub to_ssl: Option, pub authentication: DashMap>, } @@ -29,7 +30,8 @@ pub struct Consul { #[derive(Debug, Serialize, Deserialize)] pub struct Config { pub provider: String, - pub stickysessions: bool, + pub sticky_sessions: bool, + pub to_ssl: Option, pub upstreams: Option>, pub globals: Option>>, pub consul: Option, diff --git a/src/web/bgservice.rs b/src/web/bgservice.rs index 06f0546..852d18c 100644 --- a/src/web/bgservice.rs +++ b/src/web/bgservice.rs @@ -56,7 +56,8 @@ impl BackgroundService for LB { clone_dashmap_into(&ss.upstreams, &self.ump_upst); let current = self.extraparams.load_full(); let mut new = (*current).clone(); - new.stickysessions = ss.extraparams.stickysessions; + new.sticky_sessions = ss.extraparams.sticky_sessions; + new.to_ssl = ss.extraparams.to_ssl; new.authentication = ss.extraparams.authentication.clone(); self.extraparams.store(Arc::new(new)); self.headers.clear(); diff --git a/src/web/proxyhttp.rs b/src/web/proxyhttp.rs index 3548bf7..2fe8e21 100644 --- a/src/web/proxyhttp.rs +++ b/src/web/proxyhttp.rs @@ -47,22 +47,17 @@ impl ProxyHttp for LB { return Ok(true); } }; - - // if session.req_header().uri.path().starts_with("/denied") { - // let _ = session.respond_error(403).await; - // warn!("Forbidden: {:?}, {}", session.client_addr(), session.req_header().uri.path().to_string()); - // return Ok(true); - // }; Ok(false) } - async fn upstream_peer(&self, session: &mut Session, _ctx: &mut Self::CTX) -> Result> { + async fn upstream_peer(&self, session: &mut Session, ctx: &mut Self::CTX) -> Result> { let host_name = return_header_host(&session); match host_name { Some(hostname) => { // session.req_header_mut().headers.insert("X-Host-Name", host.to_string().parse().unwrap()); let mut backend_id = None; - if self.extraparams.load().stickysessions { + + if self.extraparams.load().sticky_sessions { if let Some(cookies) = session.req_header().headers.get("cookie") { if let Ok(cookie_str) = cookies.to_str() { for cookie in cookie_str.split(';') { @@ -91,33 +86,22 @@ impl ProxyHttp for LB { peer.options.verify_hostname = false; } // println!("{}, {}, alpn {}, h2 {:?}, to_https {}", hostname, address.as_str(), peer.options.alpn, is_h2, _to_https); - // let mut gogo = false; - // let mut location = String::new(); - if to_https { + if self.extraparams.load().to_ssl.unwrap_or(false) || to_https { if let Some(stream) = session.stream() { if stream.get_ssl().is_none() { if let Some(addr) = session.server_addr() { if let Some((host, _)) = addr.to_string().split_once(':') { let uri = session.req_header().uri.path_and_query().map_or("/", |pq| pq.as_str()); let port = self.config.proxy_port_tls.unwrap_or(443); - // let location = format!("https://{}:{}{}", host, port, uri); - // println!("Redirect: {} => {}", hostname, location); - // gogo = true; - _ctx.to_https = true; - _ctx.redirect_to = format!("https://{}:{}{}", host, port, uri); + ctx.to_https = true; + ctx.redirect_to = format!("https://{}:{}{}", host, port, uri); } } } } } - // if gogo { - // println!("Redirect: {} => {}", hostname, location); - // let _ = session.respond_error(301).await; - // warn!("Forbidden: {:?}, {}", session.client_addr(), session.req_header().uri.path().to_string()); - // } - _ctx.backend_id = format!("{}:{}:{}", address.clone(), port.clone(), ssl); - // println!("{:?}, {:?}", session.request_summary(), session.server_addr()); + ctx.backend_id = format!("{}:{}:{}", address.clone(), port.clone(), ssl); Ok(peer) } None => { @@ -153,20 +137,20 @@ impl ProxyHttp for LB { Ok(()) } - async fn response_filter(&self, session: &mut Session, _upstream_response: &mut ResponseHeader, _ctx: &mut Self::CTX) -> Result<()> { + async fn response_filter(&self, session: &mut Session, _upstream_response: &mut ResponseHeader, ctx: &mut Self::CTX) -> Result<()> { // _upstream_response.insert_header("X-Proxied-From", "Fooooooooooooooo").unwrap(); - if self.extraparams.load().stickysessions { - let backend_id = _ctx.backend_id.clone(); + if self.extraparams.load().sticky_sessions { + let backend_id = ctx.backend_id.clone(); if let Some(bid) = self.ump_byid.get(&backend_id) { // let _ = _upstream_response.insert_header("set-cookie", format!("backend {}", bid.0)); let _ = _upstream_response.insert_header("set-cookie", format!("backend_id={}; Path=/; Max-Age=600; HttpOnly; SameSite=Lax", bid.0)); } } - if _ctx.to_https { - // println!("{} => {}", _ctx.to_https, _ctx.redirect_to); + if ctx.to_https { + // println!("{} => {}", ctx.to_https, ctx.redirect_to); let mut redirect_response = ResponseHeader::build(StatusCode::MOVED_PERMANENTLY, None)?; - redirect_response.insert_header("Location", _ctx.redirect_to.clone())?; + redirect_response.insert_header("Location", ctx.redirect_to.clone())?; redirect_response.insert_header("Content-Length", "0")?; session.write_response_header(Box::new(redirect_response), false).await?; // return Ok(()); diff --git a/src/web/start.rs b/src/web/start.rs index dc9516b..5d6566a 100644 --- a/src/web/start.rs +++ b/src/web/start.rs @@ -23,7 +23,8 @@ pub fn run() { let im_config = Arc::new(DashMap::new()); let hh_config = Arc::new(DashMap::new()); let ec_config = Arc::new(ArcSwap::from_pointee(Extraparams { - stickysessions: false, + sticky_sessions: false, + to_ssl: None, authentication: DashMap::new(), }));