Merge pull request 'web_server.rs: Log X-Forwarded-For IP' (#504) from jpds/garage:web_server-log-x-forwarded-for into main
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #504
This commit is contained in:
commit
00dcfc97a5
4 changed files with 82 additions and 10 deletions
|
@ -19,6 +19,7 @@ use opentelemetry::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use garage_util::error::Error as GarageError;
|
use garage_util::error::Error as GarageError;
|
||||||
|
use garage_util::forwarded_headers;
|
||||||
use garage_util::metrics::{gen_trace_id, RecordDuration};
|
use garage_util::metrics::{gen_trace_id, RecordDuration};
|
||||||
|
|
||||||
pub(crate) trait ApiEndpoint: Send + Sync + 'static {
|
pub(crate) trait ApiEndpoint: Send + Sync + 'static {
|
||||||
|
@ -126,15 +127,9 @@ impl<A: ApiHandler> ApiServer<A> {
|
||||||
) -> Result<Response<Body>, GarageError> {
|
) -> Result<Response<Body>, GarageError> {
|
||||||
let uri = req.uri().clone();
|
let uri = req.uri().clone();
|
||||||
|
|
||||||
let has_forwarded_for_header = req.headers().contains_key("x-forwarded-for");
|
if let Ok(forwarded_for_ip_addr) =
|
||||||
if has_forwarded_for_header {
|
forwarded_headers::handle_forwarded_for_headers(&req.headers())
|
||||||
let forwarded_for_ip_addr = &req
|
{
|
||||||
.headers()
|
|
||||||
.get("x-forwarded-for")
|
|
||||||
.expect("Could not parse X-Forwarded-For header")
|
|
||||||
.to_str()
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"{} (via {}) {} {}",
|
"{} (via {}) {} {}",
|
||||||
forwarded_for_ip_addr,
|
forwarded_for_ip_addr,
|
||||||
|
|
63
src/util/forwarded_headers.rs
Normal file
63
src/util/forwarded_headers.rs
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
use http::{HeaderMap, HeaderValue};
|
||||||
|
use std::net::IpAddr;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use crate::error::{Error, OkOrMessage};
|
||||||
|
|
||||||
|
pub fn handle_forwarded_for_headers(headers: &HeaderMap<HeaderValue>) -> Result<String, Error> {
|
||||||
|
let forwarded_for_header = headers
|
||||||
|
.get("x-forwarded-for")
|
||||||
|
.ok_or_message("X-Forwarded-For header not provided")?;
|
||||||
|
|
||||||
|
let forwarded_for_ip_str = forwarded_for_header
|
||||||
|
.to_str()
|
||||||
|
.ok_or_message("Error parsing X-Forwarded-For header")?;
|
||||||
|
|
||||||
|
let client_ip = IpAddr::from_str(&forwarded_for_ip_str)
|
||||||
|
.ok_or_message("Valid IP address not found in X-Forwarded-For header")?;
|
||||||
|
|
||||||
|
Ok(client_ip.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_handle_forwarded_for_headers_ipv4_client() {
|
||||||
|
let mut test_headers = HeaderMap::new();
|
||||||
|
test_headers.insert("X-Forwarded-For", "192.0.2.100".parse().unwrap());
|
||||||
|
|
||||||
|
if let Ok(forwarded_ip) = handle_forwarded_for_headers(&test_headers) {
|
||||||
|
assert_eq!(forwarded_ip, "192.0.2.100");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_handle_forwarded_for_headers_ipv6_client() {
|
||||||
|
let mut test_headers = HeaderMap::new();
|
||||||
|
test_headers.insert("X-Forwarded-For", "2001:db8::f00d:cafe".parse().unwrap());
|
||||||
|
|
||||||
|
if let Ok(forwarded_ip) = handle_forwarded_for_headers(&test_headers) {
|
||||||
|
assert_eq!(forwarded_ip, "2001:db8::f00d:cafe");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_handle_forwarded_for_headers_invalid_ip() {
|
||||||
|
let mut test_headers = HeaderMap::new();
|
||||||
|
test_headers.insert("X-Forwarded-For", "www.example.com".parse().unwrap());
|
||||||
|
|
||||||
|
let result = handle_forwarded_for_headers(&test_headers);
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_handle_forwarded_for_headers_missing() {
|
||||||
|
let mut test_headers = HeaderMap::new();
|
||||||
|
test_headers.insert("Host", "www.deuxfleurs.fr".parse().unwrap());
|
||||||
|
|
||||||
|
let result = handle_forwarded_for_headers(&test_headers);
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ pub mod data;
|
||||||
pub mod encode;
|
pub mod encode;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod formater;
|
pub mod formater;
|
||||||
|
pub mod forwarded_headers;
|
||||||
pub mod metrics;
|
pub mod metrics;
|
||||||
pub mod migrate;
|
pub mod migrate;
|
||||||
pub mod persister;
|
pub mod persister;
|
||||||
|
|
|
@ -29,6 +29,7 @@ use garage_model::garage::Garage;
|
||||||
|
|
||||||
use garage_table::*;
|
use garage_table::*;
|
||||||
use garage_util::error::Error as GarageError;
|
use garage_util::error::Error as GarageError;
|
||||||
|
use garage_util::forwarded_headers;
|
||||||
use garage_util::metrics::{gen_trace_id, RecordDuration};
|
use garage_util::metrics::{gen_trace_id, RecordDuration};
|
||||||
|
|
||||||
struct WebMetrics {
|
struct WebMetrics {
|
||||||
|
@ -104,7 +105,19 @@ impl WebServer {
|
||||||
req: Request<Body>,
|
req: Request<Body>,
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
) -> Result<Response<Body>, Infallible> {
|
) -> Result<Response<Body>, Infallible> {
|
||||||
|
if let Ok(forwarded_for_ip_addr) =
|
||||||
|
forwarded_headers::handle_forwarded_for_headers(&req.headers())
|
||||||
|
{
|
||||||
|
info!(
|
||||||
|
"{} (via {}) {} {}",
|
||||||
|
forwarded_for_ip_addr,
|
||||||
|
addr,
|
||||||
|
req.method(),
|
||||||
|
req.uri()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
info!("{} {} {}", addr, req.method(), req.uri());
|
info!("{} {} {}", addr, req.method(), req.uri());
|
||||||
|
}
|
||||||
|
|
||||||
// Lots of instrumentation
|
// Lots of instrumentation
|
||||||
let tracer = opentelemetry::global::tracer("garage");
|
let tracer = opentelemetry::global::tracer("garage");
|
||||||
|
|
Loading…
Reference in a new issue