Skip to content

Commit 134db39

Browse files
authored
feat: optional log from X-Real-IP header via new --log-x-real-ip option (#521)
* feat(logger): --log-x-real-ip option. * docs: Updates for --log-x-real-ip.
1 parent 99aa74d commit 134db39

File tree

10 files changed

+79
-9
lines changed

10 files changed

+79
-9
lines changed

docs/content/configuration/command-line-arguments.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ The server can be configured via the following command-line arguments.
99

1010
```
1111
$ static-web-server -h
12-
1312
A cross-platform, high-performance and asynchronous web server for static files-serving.
1413
1514
Usage: static-web-server [OPTIONS] [COMMAND]
@@ -85,8 +84,10 @@ Options:
8584
Server TOML configuration file path [env: SERVER_CONFIG_FILE=] [default: ./config.toml]
8685
--log-remote-address [<LOG_REMOTE_ADDRESS>]
8786
Log incoming requests information along with its remote address if available using the `info` log level [env: SERVER_LOG_REMOTE_ADDRESS=] [default: false] [possible values: true, false]
87+
--log-x-real-ip [<LOG_X_REAL_IP>]
88+
Log the X-Real-IP header for remote IP information [env: SERVER_LOG_X_REAL_IP=] [default: false] [possible values: true, false]
8889
--log-forwarded-for [<LOG_FORWARDED_FOR>]
89-
Log real IP from X-Forwarded-For header [env: SERVER_LOG_FORWARDED_FOR] [default: false] [possible values: true, false]
90+
Log the X-Forwarded-For header for remote IP information [env: SERVER_LOG_FORWARDED_FOR=] [default: false] [possible values: true, false]
9091
--trusted-proxies <TRUSTED_PROXIES>
9192
A comma separated list of IP addresses to accept the X-Forwarded-For header from. Empty means trust all IPs [env: SERVER_TRUSTED_PROXIES] [default: ""]
9293
--redirect-trailing-slash [<REDIRECT_TRAILING_SLASH>]

docs/content/configuration/environment-variables.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,11 @@ Specify a logging level in lowercase. Possible values are `error`, `warn`, `info
3030
### SERVER_LOG_REMOTE_ADDRESS
3131
Log incoming request information along with its Remote Address (IP) if available using the `info` log level. Default `false`.
3232

33+
### SERVER_LOG_X_REAL_IP
34+
Log the X-Real-IP header if available using the `info` log level. Default `false`.
35+
3336
### SERVER_LOG_FORWARDED_FOR
34-
Log real IP from X-Forwarded-For header if available using the `info` log level. Default `false`
37+
Log the X-Forwarded-For header if available using the `info` log level. Default `false`.
3538

3639
### SERVER_TRUSTED_PROXIES
3740
A comma separated list of IP addresses to accept the X-Forwarded-For header from. An empty string means trust all IPs. Default `""`

docs/content/features/logging.md

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ static-web-server \
1313
--log-level "trace"
1414
```
1515

16+
> Note: The log format is not well defined and is subject to change.
17+
1618
## Log Remote Addresses
1719

1820
SWS provides *Remote Address (IP)* logging for every request via an `INFO` log level.
@@ -42,17 +44,36 @@ INFO static_web_server::info: log requests with remote IP addresses: enabled=tru
4244
INFO static_web_server::handler: incoming request: method=GET uri=/ remote_addr=192.168.1.126:57625
4345
INFO static_web_server::handler: incoming request: method=GET uri=/favicon.ico remote_addr=192.168.1.126:57625
4446
```
45-
## Log Real Remote IP
4647

47-
When used behind a reverse proxy the reported `remote_addr` indicates the proxies IP address and port, not the clients real IP.
48+
## Logging Client IP from X-Real-IP header
49+
50+
Some upstream proxies will report the client's real IP address in the `X-Real-IP` header.
51+
52+
To enable logging of the X-Real-IP header, enable the `--log-x-real-ip` option or the equivalent [SERVER_LOG_X_REAL_IP](../configuration/environment-variables.md#server_log_x_real_ip) environment variable.
53+
54+
When enabled, the log entries will look like:
55+
56+
```log
57+
INFO static_web_server::handler: incoming request: method=GET uri=/ x_real_ip=203.0.113.195
58+
```
59+
60+
If the value of the `X-Real-IP` header does not parse as an IP address, no value will be logged.
61+
62+
To restrict the logging to only requests that originate from trusted proxy IPs, you can use the `--trusted-proxies` option, or the equivalent [SERVER_TRUSTED_PROXIES](../configuration/environment-variables.md#server_trusted_proxies) env. This should be a list of IPs, separated by commas. An empty list (the default) indicates that all IPs should be trusted.
63+
64+
## Logging Client IP from X-Forwarded-For header
65+
66+
> Note: This header should only be trusted when you know your upstream is handling X-Forwarded-For securely and when using the `--trusted-proxies` option.
67+
68+
When used behind a reverse proxy the reported `remote_addr` indicates the proxies IP address and port, not the client's real IP.
4869
The Proxy server can be configured to provide the [X-Forwarded-For header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For), containing a comma-separated list of IP addresses, starting with the *real remote client IP*, and all following intermediate proxies (if any).
4970

5071

5172
To enable logging of the real remote IP, enable the `--log-forwarded-for` option or the equivalent [SERVER_LOG_FORWARDED_FOR](../configuration/environment-variables.md#server_log_forwarded_for) env. By default this will log all requests which have a correctly formatted `X-Forwarded-For` header.
5273

5374
Since the content of the `X-Forwarded-For` header can be changed by all proxies in the chain, the remote IP address reported may not be trusted.
5475

55-
To restrict the logging to only trusted proxy IPs, you can use the `--trusted-proxies` option, or the equivalent [SERVER_TRUSTED_PROXIES](../configuration/environment-variables.md#server_trusted_proxies) env. This should be a list of IPs, separated by commas. An empty list (the default) indicates that all IPs should be trusted.
76+
To restrict the logging to only requests that originate from trusted proxy IPs, you can use the `--trusted-proxies` option, or the equivalent [SERVER_TRUSTED_PROXIES](../configuration/environment-variables.md#server_trusted_proxies) env. This should be a list of IPs, separated by commas. An empty list (the default) indicates that all IPs should be trusted.
5677

5778
Command used for the following examples:
5879
```sh

src/handler.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ pub struct RequestHandlerOpts {
102102
pub index_files: Vec<String>,
103103
/// Log remote address feature.
104104
pub log_remote_address: bool,
105+
/// Log the X-Real-IP header.
106+
pub log_x_real_ip: bool,
105107
/// Log the X-Forwarded-For header.
106108
pub log_forwarded_for: bool,
107109
/// Trusted IPs for remote addresses.
@@ -161,6 +163,7 @@ impl Default for RequestHandlerOpts {
161163
basic_auth: String::new(),
162164
index_files: vec!["index.html".into()],
163165
log_remote_address: false,
166+
log_x_real_ip: false,
164167
log_forwarded_for: false,
165168
trusted_proxies: Vec::new(),
166169
redirect_trailing_slash: true,

src/log_addr.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ pub(crate) fn init(enabled: bool, handler_opts: &mut RequestHandlerOpts) {
2222

2323
server_info!("log requests with remote IP addresses: enabled={enabled}");
2424
server_info!(
25-
"log X-Forwarded-For real remote IP addresses: enabled={}",
25+
"log X-Real-IP header: enabled={}",
26+
handler_opts.log_forwarded_for
27+
);
28+
server_info!(
29+
"log X-Forwarded-For header: enabled={}",
2630
handler_opts.log_forwarded_for
2731
);
2832
server_info!("trusted IPs for X-Forwarded-For: {trusted}");
@@ -41,18 +45,31 @@ pub(crate) fn pre_process<T>(
4145
remote_addrs.push_str(format!(" remote_addr={addr}").as_str());
4246
}
4347
}
44-
if opts.log_forwarded_for
48+
if opts.log_x_real_ip
4549
&& (opts.trusted_proxies.is_empty()
4650
|| remote_addr.is_some_and(|addr| opts.trusted_proxies.contains(&addr.ip())))
4751
{
4852
if let Some(real_ip) = req
53+
.headers()
54+
.get("X-Real-IP")
55+
.and_then(|h| h.to_str().ok())
56+
.and_then(|s| s.trim().parse::<IpAddr>().ok())
57+
{
58+
remote_addrs.push_str(format!(" x_real_ip={real_ip}").as_str());
59+
}
60+
}
61+
if opts.log_forwarded_for
62+
&& (opts.trusted_proxies.is_empty()
63+
|| remote_addr.is_some_and(|addr| opts.trusted_proxies.contains(&addr.ip())))
64+
{
65+
if let Some(forwarded_for) = req
4966
.headers()
5067
.get("X-Forwarded-For")
5168
.and_then(|h| h.to_str().ok())
5269
.and_then(|s| s.split(',').next())
5370
.and_then(|s| s.trim().parse::<IpAddr>().ok())
5471
{
55-
remote_addrs.push_str(format!(" real_remote_ip={real_ip}").as_str());
72+
remote_addrs.push_str(format!(" real_remote_ip={forwarded_for}").as_str());
5673
}
5774
}
5875

src/server.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,9 @@ impl Server {
225225
// Log remote address option
226226
let log_remote_address = general.log_remote_address;
227227

228+
// Log the X-Real-IP header.
229+
let log_x_real_ip = general.log_x_real_ip;
230+
228231
// Log the X-Forwarded-For header.
229232
let log_forwarded_for = general.log_forwarded_for;
230233

@@ -267,6 +270,7 @@ impl Server {
267270
page404: page404.clone(),
268271
page50x: page50x.clone(),
269272
log_remote_address,
273+
log_x_real_ip,
270274
log_forwarded_for,
271275
trusted_proxies,
272276
redirect_trailing_slash,

src/settings/cli.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,18 @@ pub struct General {
414414
/// Log incoming requests information along with its remote address if available using the `info` log level.
415415
pub log_remote_address: bool,
416416

417+
#[arg(
418+
long,
419+
default_value = "false",
420+
default_missing_value("true"),
421+
num_args(0..=1),
422+
require_equals(false),
423+
action = clap::ArgAction::Set,
424+
env = "SERVER_LOG_X_REAL_IP",
425+
)]
426+
/// Log the X-Real-IP header for remote IP information.
427+
pub log_x_real_ip: bool,
428+
417429
#[arg(
418430
long,
419431
default_value = "false",

src/settings/file.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,9 @@ pub struct General {
357357
/// Log remote address feature.
358358
pub log_remote_address: Option<bool>,
359359

360+
/// Log the X-Real-IP header.
361+
pub log_x_real_ip: Option<bool>,
362+
360363
/// Log the X-Forwarded-For header.
361364
pub log_forwarded_for: Option<bool>,
362365

src/settings/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ impl Settings {
197197
let mut page_fallback = opts.page_fallback;
198198

199199
let mut log_remote_address = opts.log_remote_address;
200+
let mut log_x_real_ip = opts.log_x_real_ip;
200201
let mut log_forwarded_for = opts.log_forwarded_for;
201202
let mut trusted_proxies = opts.trusted_proxies;
202203
let mut redirect_trailing_slash = opts.redirect_trailing_slash;
@@ -365,6 +366,9 @@ impl Settings {
365366
if let Some(v) = general.log_remote_address {
366367
log_remote_address = v
367368
}
369+
if let Some(v) = general.log_x_real_ip {
370+
log_x_real_ip = v
371+
}
368372
if let Some(v) = general.log_forwarded_for {
369373
log_forwarded_for = v
370374
}
@@ -663,6 +667,7 @@ impl Settings {
663667
#[cfg(feature = "fallback-page")]
664668
page_fallback,
665669
log_remote_address,
670+
log_x_real_ip,
666671
log_forwarded_for,
667672
trusted_proxies,
668673
redirect_trailing_slash,

src/testing.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ pub mod fixtures {
9494
#[cfg(feature = "basic-auth")]
9595
basic_auth: general.basic_auth,
9696
log_remote_address: general.log_remote_address,
97+
log_x_real_ip: general.log_x_real_ip,
9798
log_forwarded_for: general.log_forwarded_for,
9899
trusted_proxies: general.trusted_proxies,
99100
redirect_trailing_slash: general.redirect_trailing_slash,

0 commit comments

Comments
 (0)