index : static-web-server.git

ascending towards madness

author Dominik Dzienia <dlvoy@users.noreply.github.com> 2022-09-15 18:41:21.0 +00:00:00
committer GitHub <noreply@github.com> 2022-09-15 18:41:21.0 +00:00:00
commit
5f107714434fd1ae57f6ca2148b26b6fcad933dd [patch]
tree
d729d3022343675cb1d9aef4ce334ee746f0147c
parent
e9a4aa3809c5a77362227cf38adab8236ff5f8df
download
5f107714434fd1ae57f6ca2148b26b6fcad933dd.tar.gz

feat: add logging of real remote ip in case of proxies (#138)

* Simplify code
* Header parsing and documentation
* Fixed code formatting

Co-authored-by: Jose Quintana <1700322+joseluisq@users.noreply.github.com>

Diff

 docs/content/features/logging.md | 32 ++++++++++++++++++++++++++++++++
 src/handler.rs                   | 12 +++++++++++-
 2 files changed, 43 insertions(+), 1 deletion(-)

diff --git a/docs/content/features/logging.md b/docs/content/features/logging.md
index d9c8ed8..31ccd24 100644
--- a/docs/content/features/logging.md
+++ b/docs/content/features/logging.md
@@ -47,3 +47,35 @@ static-web-server -a "0.0.0.0" -p 8080 -d docker/public/ -g info --log-remote-ad
# 2022-05-23T22:24:50.519540Z  INFO static_web_server::handler: incoming request: method=GET uri=/ remote_addr=192.168.1.126:57625
# 2022-05-23T22:25:26.516841Z  INFO static_web_server::handler: incoming request: method=GET uri=/favicon.ico remote_addr=192.168.1.126:57625
```
## Log Real Remote IP

When used behind reverse proxy, reported `remote_addr` indicate proxy internal IP address and port, and not client real remote IP.
Proxy server can be configured to provide [X-Forwarded-For header]https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For, containing comma-separated list of IP addresses, starting with *client real remote IP*, and all following intermediate proxies (if any).

When *Remote Address (IP) logging* [is enabled]#log-remote-addresses, and `X-Forwarded-For` header is present and correctly formated, then log entries for requests will contain a `real_remote_ip` section with IP of remote client, **as reported by this header**. 

We can simulate request as from behind reverse proxy with additional intermediate-proxy with following command:

```sh
curl --header "X-Forwarded-For: 203.0.113.195, 2001:db8:85a3:8d3:1319:8a2e:370:7348" http://0.0.0.0:8080
```

Log entry for such case will look like:

```log
2022-05-23T22:24:50.519540Z  INFO static_web_server::handler: incoming request: method=GET uri=/ remote_addr=192.168.1.126:57625 real_remote_ip=203.0.113.195
```

**`SWS`** will parse `X-Forwarded-For` header, and if format of provided IP is invalid - it will be ignored to prevent log poisoning attacks. In such case `real_remote_ip` section will not be added.

Example from above, but with invalid header:

```sh
curl --header "X-Forwarded-For: <iframe src=//malware.attack>" http://0.0.0.0:8080
```

```log
2022-05-23T22:24:50.519540Z  INFO static_web_server::handler: incoming request: method=GET uri=/ remote_addr=192.168.1.126:57625
```

Be aware, that contents of `X-Forwarded-For` header can be augumented by all proxies in the chain, and as such - remote IP address reported by it may not be trusted.
\ No newline at end of file
diff --git a/src/handler.rs b/src/handler.rs
index fc0cb90..07f7f13 100644
--- a/src/handler.rs
+++ b/src/handler.rs
@@ -1,6 +1,6 @@
use headers::HeaderValue;
use hyper::{header::WWW_AUTHENTICATE, Body, Method, Request, Response, StatusCode};
use std::{future::Future, net::SocketAddr, path::PathBuf, sync::Arc};
use std::{future::Future, net::IpAddr, net::SocketAddr, path::PathBuf, sync::Arc};

use crate::{
    basic_auth, compression, control_headers, cors, custom_headers, error_page, fallback_page,
@@ -62,6 +62,16 @@ impl RequestHandler {
        if log_remote_addr {
            remote_addr_str.push_str(" remote_addr=");
            remote_addr_str.push_str(&remote_addr.map_or("".to_owned(), |v| v.to_string()));

            if let Some(client_ip_address) = headers
                .get("X-Forwarded-For")
                .and_then(|v| v.to_str().ok())
                .and_then(|s| s.split(',').next())
                .and_then(|s| s.trim().parse::<IpAddr>().ok())
            {
                remote_addr_str.push_str(" real_remote_ip=");
                remote_addr_str.push_str(&client_ip_address.to_string())
            }
        }
        tracing::info!(
            "incoming request: method={} uri={}{}",