From 0c6ffa5e771995b0d9c0c91d27837653d49cd2b1 Mon Sep 17 00:00:00 2001 From: Jun Kurihara Date: Sun, 10 Jul 2022 01:42:54 +0900 Subject: [PATCH] x-forwarded-* as rfc and nginx --- src/proxy/proxy_handler.rs | 6 ++--- src/proxy/proxy_tls.rs | 8 +++--- src/proxy/utils_headers.rs | 54 +++++++++++++++++++++++++++++++++----- src/proxy/utils_request.rs | 27 ------------------- 4 files changed, 56 insertions(+), 39 deletions(-) diff --git a/src/proxy/proxy_handler.rs b/src/proxy/proxy_handler.rs index 9893525..3276ee1 100644 --- a/src/proxy/proxy_handler.rs +++ b/src/proxy/proxy_handler.rs @@ -157,7 +157,7 @@ where let headers = response.headers_mut(); remove_hop_header(headers); remove_connection_header(headers); - append_header_entry( + append_header_entry_with_comma( headers, "server", &format!("{}/{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")), @@ -166,7 +166,7 @@ where { if self.globals.http3 { if let Some(port) = self.globals.https_port { - append_header_entry( + append_header_entry_with_comma( headers, header::ALT_SVC.as_str(), &format!( @@ -208,7 +208,7 @@ where // delete hop headers including header.connection remove_hop_header(headers); // X-Forwarded-For - add_forwarding_header(headers, client_addr, self.tls_enabled)?; + add_forwarding_header(headers, client_addr, self.tls_enabled, &self.globals)?; // Add te: trailer if te_trailer if te_trailers { diff --git a/src/proxy/proxy_tls.rs b/src/proxy/proxy_tls.rs index 684be0d..586f0c7 100644 --- a/src/proxy/proxy_tls.rs +++ b/src/proxy/proxy_tls.rs @@ -9,6 +9,8 @@ use futures::{future::FutureExt, select}; use hyper::{client::connect::Connect, server::conn::Http}; use rustc_hash::FxHashMap as HashMap; use rustls::ServerConfig; +#[cfg(feature = "h3")] +use std::pin::Pin; use std::sync::Arc; use tokio::{net::TcpListener, sync::watch, time::Duration}; @@ -147,7 +149,7 @@ where let initial_app_name = next.ok_or_else(|| anyhow!(""))?; debug!( "HTTP/3 SNI multiplexer initial app_name: {:?}", - String::from_utf8(initial_app_name.to_vec()) + std::str::from_utf8(initial_app_name) ); let backend_serve = self .backends @@ -167,7 +169,7 @@ where select! { // TODO: Not sure if this properly works to handle multiple "server_name"s to host multiple hosts. // peek() should work for that. - peeked_conn = std::pin::Pin::new(&mut p).peek_mut().fuse() => { + peeked_conn = Pin::new(&mut p).peek_mut().fuse() => { if server_crypto_map.is_none() || peeked_conn.is_none() { continue; } @@ -181,7 +183,7 @@ where false }; // Then acquire actual connection - let peekable_incoming = std::pin::Pin::new(&mut p); + let peekable_incoming = Pin::new(&mut p); if let Some(conn) = peekable_incoming.get_mut().next().await { if is_acceptable { self.clone().client_serve_h3(conn).await; diff --git a/src/proxy/utils_headers.rs b/src/proxy/utils_headers.rs index 272d7ba..ca1d478 100644 --- a/src/proxy/utils_headers.rs +++ b/src/proxy/utils_headers.rs @@ -1,10 +1,11 @@ use super::{Upstream, UpstreamOption}; -use crate::{error::*, log::*, utils::*}; +use crate::{error::*, globals::Globals, log::*, utils::*}; +use bytes::BufMut; use hyper::{ header::{self, HeaderMap, HeaderName, HeaderValue}, Uri, }; -use std::net::SocketAddr; +use std::{net::SocketAddr, sync::Arc}; //////////////////////////////////////////////////// // Functions to manipulate headers @@ -29,36 +30,77 @@ pub(super) fn apply_upstream_options_to_header( Ok(()) } -pub(super) fn append_header_entry(headers: &mut HeaderMap, key: &str, value: &str) -> Result<()> { +// https://datatracker.ietf.org/doc/html/rfc9110 +pub(super) fn append_header_entry_with_comma( + headers: &mut HeaderMap, + key: &str, + value: &str, +) -> Result<()> { match headers.entry(HeaderName::from_bytes(key.as_bytes())?) { header::Entry::Vacant(entry) => { entry.insert(value.parse::()?); } header::Entry::Occupied(mut entry) => { - entry.append(value.parse::()?); + // entry.append(value.parse::()?); + let mut new_value = Vec::::with_capacity(entry.get().as_bytes().len() + 2 + value.len()); + new_value.put_slice(entry.get().as_bytes()); + new_value.put_slice(&[b',', b' ']); + new_value.put_slice(value.as_bytes()); + entry.insert(HeaderValue::from_bytes(&new_value)?); } } Ok(()) } +pub(super) fn add_header_entry_if_not_exist( + headers: &mut HeaderMap, + key: impl Into>, + value: impl Into>, +) -> Result<()> { + match headers.entry(HeaderName::from_bytes(key.into().as_bytes())?) { + header::Entry::Vacant(entry) => { + entry.insert(value.into().parse::()?); + } + header::Entry::Occupied(_) => (), + }; + + Ok(()) +} + pub(super) fn add_forwarding_header( headers: &mut HeaderMap, client_addr: &SocketAddr, tls: bool, + globals: &Arc, // TODO: Fix ) -> Result<()> { // default process // optional process defined by upstream_option is applied in fn apply_upstream_options - append_header_entry( + append_header_entry_with_comma( headers, "x-forwarded-for", &client_addr.to_canonical().ip().to_string(), )?; - append_header_entry( + + /////////// As Nginx + // If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the + // scheme used to connect to this server + add_header_entry_if_not_exist( headers, "x-forwarded-proto", if tls { "https" } else { "http" }, )?; + // If we receive X-Forwarded-Port, pass it through; otherwise, pass along the + // server port the client connected to + add_header_entry_if_not_exist( + headers, + "x-forwarded-port", + if tls { + globals.https_port.unwrap().to_string() + } else { + globals.http_port.unwrap().to_string() + }, + )?; Ok(()) } diff --git a/src/proxy/utils_request.rs b/src/proxy/utils_request.rs index 163f31c..1374339 100644 --- a/src/proxy/utils_request.rs +++ b/src/proxy/utils_request.rs @@ -76,30 +76,3 @@ impl ParseHost for Request { ) } } - -// pub(super) fn parse_host_port( -// req: &Request, -// ) -> Result<(String, Option)> { -// let headers_host = req.headers().get("host"); -// let uri_host = req.uri().host(); -// let uri_port = req.uri().port_u16(); - -// ensure!( -// !(headers_host.is_none() && uri_host.is_none()), -// "No host in request header" -// ); - -// // prioritize server_name in uri -// if let Some(v) = uri_host { -// Ok((v.to_string(), uri_port)) -// } else { -// let uri_from_host = headers_host.unwrap().to_str()?.parse::()?; -// Ok(( -// uri_from_host -// .host() -// .ok_or_else(|| anyhow!("Failed to parse host"))? -// .to_string(), -// uri_from_host.port_u16(), -// )) -// } -// }