reverse proxy ip checking
This commit is contained in:
parent
b58654fd70
commit
9cbeee4b67
4 changed files with 104 additions and 61 deletions
47
Cargo.lock
generated
47
Cargo.lock
generated
|
@ -64,6 +64,16 @@ dependencies = [
|
||||||
"tower-service",
|
"tower-service",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "axum-client-ip"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ddfb5a3ddd6367075d50629546fb46710584016ae7704cd03b6d41cb5be82e5a"
|
||||||
|
dependencies = [
|
||||||
|
"axum",
|
||||||
|
"forwarded-header-value",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum-core"
|
name = "axum-core"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
|
@ -187,6 +197,16 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "forwarded-header-value"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9"
|
||||||
|
dependencies = [
|
||||||
|
"nonempty",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.25"
|
version = "0.3.25"
|
||||||
|
@ -463,6 +483,12 @@ dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nonempty"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-ansi-term"
|
name = "nu-ansi-term"
|
||||||
version = "0.46.0"
|
version = "0.46.0"
|
||||||
|
@ -780,6 +806,26 @@ version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8"
|
checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.38"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.38"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thread_local"
|
name = "thread_local"
|
||||||
version = "1.1.4"
|
version = "1.1.4"
|
||||||
|
@ -1134,6 +1180,7 @@ name = "xssbook"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
|
"axum-client-ip",
|
||||||
"bytes",
|
"bytes",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"rand",
|
"rand",
|
||||||
|
|
|
@ -10,6 +10,7 @@ tower-http = { version = "0.3.5", features = ["fs"] }
|
||||||
tower-cookies = "0.8.0"
|
tower-cookies = "0.8.0"
|
||||||
tower = "0.4.13"
|
tower = "0.4.13"
|
||||||
bytes = "1.3.0"
|
bytes = "1.3.0"
|
||||||
|
axum-client-ip = "0.3.1"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = { version = "1.0", features = ["std"] }
|
serde_json = { version = "1.0", features = ["std"] }
|
||||||
rusqlite = { version = "0.28.0", features = ["bundled"] }
|
rusqlite = { version = "0.28.0", features = ["bundled"] }
|
||||||
|
|
|
@ -15,7 +15,7 @@ The project is written in rust, so you can build it by running
|
||||||
|
|
||||||
`cargo build --release`
|
`cargo build --release`
|
||||||
|
|
||||||
Make sure where you run the binary from, you also move the public folder to.
|
Next, make sure where you are runing the binary from, that you copy the sources public folder to the same directory. The public folder is needed to server html, css, js, and font files.
|
||||||
|
|
||||||
Finally, the site runs on port `8080`, so its recommended you put it behind a reverse proxy, or you could use a docker container and remap the outsite port (see below).
|
Finally, the site runs on port `8080`, so its recommended you put it behind a reverse proxy, or you could use a docker container and remap the outsite port (see below).
|
||||||
|
|
||||||
|
@ -37,6 +37,10 @@ to make the database persistant. Finally, before running the container run
|
||||||
|
|
||||||
since docker will create a folder there otherwise and it won't work.
|
since docker will create a folder there otherwise and it won't work.
|
||||||
|
|
||||||
|
**reverse proxy**
|
||||||
|
|
||||||
|
Finally if you are using docker by itself, a reverse proxy, or both, the ip send to the container likily will not be the correct ip. xssbook looks for headers `x-forwarded-for`, `x-real-ip`, and `forwarded` to check for proxies. So make sure to have those headers set. Or if your running just docker, you could also run the docker container on the host network instead of on the bridge network.
|
||||||
|
|
||||||
**license**
|
**license**
|
||||||
|
|
||||||
This amazing project is licensed under the WTFPL
|
This amazing project is licensed under the WTFPL
|
|
@ -1,14 +1,15 @@
|
||||||
use std::{io::Read, net::SocketAddr};
|
use std::io::Read;
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
async_trait,
|
async_trait,
|
||||||
body::HttpBody,
|
body::HttpBody,
|
||||||
extract::{ConnectInfo, FromRequest, FromRequestParts},
|
extract::{FromRequest, FromRequestParts},
|
||||||
headers::Cookie,
|
headers::Cookie,
|
||||||
http::{request::Parts, Request},
|
http::{request::Parts, Request},
|
||||||
response::Response,
|
response::Response,
|
||||||
BoxError, RequestExt, TypedHeader,
|
BoxError, RequestExt, TypedHeader,
|
||||||
};
|
};
|
||||||
|
use axum_client_ip::ClientIp;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
|
|
||||||
|
@ -53,6 +54,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Log;
|
pub struct Log;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<S, B> FromRequest<S, B> for Log
|
impl<S, B> FromRequest<S, B> for Log
|
||||||
where
|
where
|
||||||
|
@ -63,36 +65,8 @@ where
|
||||||
{
|
{
|
||||||
type Rejection = Response;
|
type Rejection = Response;
|
||||||
|
|
||||||
async fn from_request(mut req: Request<B>, state: &S) -> Result<Self> {
|
async fn from_request(req: Request<B>, state: &S) -> Result<Self> {
|
||||||
let Ok(ConnectInfo(info)) = req.extract_parts::<ConnectInfo<SocketAddr>>().await else {
|
parse_body(req, state).await?;
|
||||||
return Ok(Self)
|
|
||||||
};
|
|
||||||
let method = req.method().clone();
|
|
||||||
let path = req
|
|
||||||
.extensions()
|
|
||||||
.get::<RouterURI>()
|
|
||||||
.map_or("", |path| path.0);
|
|
||||||
let uri = req.uri().clone();
|
|
||||||
|
|
||||||
let Ok(bytes) = Bytes::from_request(req, state).await else {
|
|
||||||
console::log(info.ip(), method.clone(), uri.clone(), Some(path.to_string()), None).await;
|
|
||||||
return Ok(Self)
|
|
||||||
};
|
|
||||||
|
|
||||||
let Ok(body) = String::from_utf8(bytes.bytes().flatten().collect()) else {
|
|
||||||
console::log(info.ip(), method.clone(), uri.clone(), Some(path.to_string()), None).await;
|
|
||||||
return Ok(Self)
|
|
||||||
};
|
|
||||||
|
|
||||||
console::log(
|
|
||||||
info.ip(),
|
|
||||||
method.clone(),
|
|
||||||
uri.clone(),
|
|
||||||
Some(path.to_string()),
|
|
||||||
Some(body.to_string()),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
Ok(Self)
|
Ok(Self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,36 +84,13 @@ where
|
||||||
{
|
{
|
||||||
type Rejection = Response;
|
type Rejection = Response;
|
||||||
|
|
||||||
async fn from_request(mut req: Request<B>, state: &S) -> Result<Self> {
|
async fn from_request(req: Request<B>, state: &S) -> Result<Self> {
|
||||||
let Ok(ConnectInfo(info)) = req.extract_parts::<ConnectInfo<SocketAddr>>().await else {
|
|
||||||
tracing::error!("Failed to read connection info");
|
|
||||||
return Err(ResponseCode::InternalServerError.text("Failed to read connection info"));
|
|
||||||
};
|
|
||||||
let method = req.method().clone();
|
|
||||||
let path = req
|
|
||||||
.extensions()
|
|
||||||
.get::<RouterURI>()
|
|
||||||
.map_or("", |path| path.0);
|
|
||||||
let uri = req.uri().clone();
|
|
||||||
|
|
||||||
let Ok(bytes) = Bytes::from_request(req, state).await else {
|
let body = match parse_body(req, state).await {
|
||||||
tracing::error!("Failed to read request body");
|
Ok(body) => body,
|
||||||
return Err(ResponseCode::InternalServerError.text("Failed to read request body"));
|
Err(err) => return Err(err)
|
||||||
};
|
};
|
||||||
|
|
||||||
let Ok(body) = String::from_utf8(bytes.bytes().flatten().collect()) else {
|
|
||||||
return Err(ResponseCode::BadRequest.text("Invalid utf8 body"))
|
|
||||||
};
|
|
||||||
|
|
||||||
console::log(
|
|
||||||
info.ip(),
|
|
||||||
method.clone(),
|
|
||||||
uri.clone(),
|
|
||||||
Some(path.to_string()),
|
|
||||||
Some(body.to_string()),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let Ok(value) = serde_json::from_str::<T>(&body) else {
|
let Ok(value) = serde_json::from_str::<T>(&body) else {
|
||||||
return Err(ResponseCode::BadRequest.text("Invalid request body"))
|
return Err(ResponseCode::BadRequest.text("Invalid request body"))
|
||||||
};
|
};
|
||||||
|
@ -172,5 +123,45 @@ pub trait Check {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn parse_body<S, B>(mut req: Request<B>, state: &S) -> Result<String>
|
||||||
|
where
|
||||||
|
B: HttpBody + Sync + Send + 'static,
|
||||||
|
B::Data: Send,
|
||||||
|
B::Error: Into<BoxError>,
|
||||||
|
S: Send + Sync
|
||||||
|
{
|
||||||
|
let Ok(ClientIp(ip)) = req.extract_parts::<ClientIp>().await else {
|
||||||
|
tracing::error!("Failed to read client ip");
|
||||||
|
return Err(ResponseCode::InternalServerError.text("Failed to read client ip"));
|
||||||
|
};
|
||||||
|
|
||||||
|
let method = req.method().clone();
|
||||||
|
let uri = req.uri().clone();
|
||||||
|
let path = req
|
||||||
|
.extensions()
|
||||||
|
.get::<RouterURI>()
|
||||||
|
.map_or("", |path| path.0);
|
||||||
|
|
||||||
|
let Ok(bytes) = Bytes::from_request(req, state).await else {
|
||||||
|
tracing::error!("Failed to read request body");
|
||||||
|
return Err(ResponseCode::InternalServerError.text("Failed to read request body"));
|
||||||
|
};
|
||||||
|
|
||||||
|
let Ok(body) = String::from_utf8(bytes.bytes().flatten().collect()) else {
|
||||||
|
return Err(ResponseCode::BadRequest.text("Invalid utf8 body"))
|
||||||
|
};
|
||||||
|
|
||||||
|
console::log(
|
||||||
|
ip,
|
||||||
|
method,
|
||||||
|
uri,
|
||||||
|
Some(path.to_string()),
|
||||||
|
Some(body.to_string()),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(body)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RouterURI(pub &'static str);
|
pub struct RouterURI(pub &'static str);
|
||||||
|
|
Loading…
Reference in a new issue