feat: retrieve client IP from header in config.toml

If the config.toml has defined a HTTP header to look for a remote IP,
that will be logged.  Otherwise, the socket address IP will be used.

closes: https://todo.sr.ht/~gheartsfield/nostr-rs-relay/47
This commit is contained in:
Greg Heartsfield 2022-11-04 18:05:01 -05:00
parent 1cf9d719f0
commit ae5bf98d87
3 changed files with 21 additions and 18 deletions

View File

@ -46,6 +46,11 @@ address = "0.0.0.0"
# Listen on this port # Listen on this port
port = 8080 port = 8080
# If present, read this HTTP header for logging client IP addresses.
# Examples for common proxies, cloudflare:
#remote_ip_header = "x-forwarded-for"
#remote_ip_header = "cf-connecting-ip"
[options] [options]
# Reject events that have timestamps greater than this many seconds in # Reject events that have timestamps greater than this many seconds in
# the future. Recommended to reject anything greater than 30 minutes # the future. Recommended to reject anything greater than 30 minutes

View File

@ -28,9 +28,9 @@ pub struct Database {
pub struct Network { pub struct Network {
pub port: u16, pub port: u16,
pub address: String, pub address: String,
pub remote_ip_header: Option<String>, // retrieve client IP from this HTTP header if present
} }
//
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[allow(unused)] #[allow(unused)]
pub struct Options { pub struct Options {
@ -208,6 +208,7 @@ impl Default for Settings {
network: Network { network: Network {
port: 8080, port: 8080,
address: "0.0.0.0".to_owned(), address: "0.0.0.0".to_owned(),
remote_ip_header: None,
}, },
limits: Limits { limits: Limits {
messages_per_sec: None, messages_per_sec: None,

View File

@ -85,9 +85,17 @@ async fn handle_web_request(
Some(config), Some(config),
) )
.await; .await;
// spawn server with info... but include IP here.
// determine the remote IP from headers if the exist
let header_ip = settings
.network
.remote_ip_header
.as_ref()
.and_then(|x| get_header_remote_ip(x, request.headers()));
// use the socket addr as a backup
let remote_ip = let remote_ip =
get_remote_ip_string(&remote_addr, request.headers()); header_ip.unwrap_or_else(|| remote_addr.ip().to_string());
// spawn a nostr server with our websocket
tokio::spawn(nostr_server( tokio::spawn(nostr_server(
pool, remote_ip, settings, ws_stream, broadcast, event_tx, pool, remote_ip, settings, ws_stream, broadcast, event_tx,
shutdown, shutdown,
@ -153,21 +161,10 @@ async fn handle_web_request(
} }
} }
fn get_remote_ip_string(remote_addr: &SocketAddr, headers: &HeaderMap) -> String { fn get_header_remote_ip(header: &str, headers: &HeaderMap) -> Option<String> {
if let Some(ip) = get_cloudflare_remote_ip(headers) { headers
return ip; .get(header)
} .and_then(|x| x.to_str().ok().map(|x| x.to_string()))
return remote_addr.ip().to_string();
}
fn get_cloudflare_remote_ip(headers: &HeaderMap) -> Option<String> {
if let Some(val) = headers.get("CF-Connecting-IP") {
if let Ok(s) = val.to_str() {
return Some(s.to_string());
}
}
return None;
} }
// return on a control-c or internally requested shutdown signal // return on a control-c or internally requested shutdown signal