Skip to content
Snippets Groups Projects
Commit 3580852b authored by Éloïs's avatar Éloïs
Browse files

[fix] gva:anti-spam: handle reverse proxy case

parent 7cf78091
No related branches found
No related tags found
1 merge request!1335Gva proto 2
...@@ -82,9 +82,9 @@ impl AntiSpam { ...@@ -82,9 +82,9 @@ impl AntiSpam {
true true
} }
} }
pub(crate) async fn verify(&self, remote_addr_opt: Option<std::net::SocketAddr>) -> bool { pub(crate) async fn verify(&self, remote_addr_opt: Option<std::net::IpAddr>) -> bool {
if let Some(remote_addr) = remote_addr_opt { if let Some(ip) = remote_addr_opt {
let ip = remote_addr.ip(); log::trace!("GVA: receive request from {}", ip);
if self.whitelist.contains(&ip) { if self.whitelist.contains(&ip) {
true true
} else { } else {
...@@ -123,7 +123,7 @@ impl AntiSpam { ...@@ -123,7 +123,7 @@ impl AntiSpam {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr}; use std::net::{Ipv4Addr, Ipv6Addr};
use tokio::time::delay_for; use tokio::time::delay_for;
const LOCAL_IP4: IpAddr = IpAddr::V4(Ipv4Addr::LOCALHOST); const LOCAL_IP4: IpAddr = IpAddr::V4(Ipv4Addr::LOCALHOST);
...@@ -135,33 +135,33 @@ mod tests { ...@@ -135,33 +135,33 @@ mod tests {
assert!(!anti_spam.verify(None).await); assert!(!anti_spam.verify(None).await);
for _ in 0..(COUNT_INTERVAL * 2) { for _ in 0..(COUNT_INTERVAL * 2) {
assert!(anti_spam.verify(Some(SocketAddr::new(LOCAL_IP4, 0))).await); assert!(anti_spam.verify(Some(LOCAL_IP4)).await);
assert!(anti_spam.verify(Some(SocketAddr::new(LOCAL_IP6, 0))).await); assert!(anti_spam.verify(Some(LOCAL_IP6)).await);
} }
let extern_ip = IpAddr::V4(Ipv4Addr::UNSPECIFIED); let extern_ip = IpAddr::V4(Ipv4Addr::UNSPECIFIED);
// Consume max queries // Consume max queries
for _ in 0..COUNT_INTERVAL { for _ in 0..COUNT_INTERVAL {
assert!(anti_spam.verify(Some(SocketAddr::new(extern_ip, 0))).await); assert!(anti_spam.verify(Some(extern_ip)).await);
} }
// Should be banned // Should be banned
assert!(!anti_spam.verify(Some(SocketAddr::new(extern_ip, 0))).await); assert!(!anti_spam.verify(Some(extern_ip)).await);
// Should be un-banned after one second // Should be un-banned after one second
delay_for(Duration::from_millis(1_100)).await; delay_for(Duration::from_millis(1_100)).await;
// Re-consume max queries // Re-consume max queries
for _ in 0..COUNT_INTERVAL { for _ in 0..COUNT_INTERVAL {
assert!(anti_spam.verify(Some(SocketAddr::new(extern_ip, 0))).await); assert!(anti_spam.verify(Some(extern_ip)).await);
} }
// Should be banned for 2 seconds this time // Should be banned for 2 seconds this time
delay_for(Duration::from_millis(1_100)).await; delay_for(Duration::from_millis(1_100)).await;
// Attempting a request when I'm banned must be twice my banning time // Attempting a request when I'm banned must be twice my banning time
assert!(!anti_spam.verify(Some(SocketAddr::new(extern_ip, 0))).await); assert!(!anti_spam.verify(Some(extern_ip)).await);
delay_for(Duration::from_millis(4_100)).await; delay_for(Duration::from_millis(4_100)).await;
// Re-consume max queries // Re-consume max queries
for _ in 0..COUNT_INTERVAL { for _ in 0..COUNT_INTERVAL {
assert!(anti_spam.verify(Some(SocketAddr::new(extern_ip, 0))).await); assert!(anti_spam.verify(Some(extern_ip)).await);
} }
} }
} }
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use std::net::{IpAddr, SocketAddr};
use crate::anti_spam::AntiSpam; use crate::anti_spam::AntiSpam;
use crate::*; use crate::*;
...@@ -58,24 +60,29 @@ pub(crate) fn graphql( ...@@ -58,24 +60,29 @@ pub(crate) fn graphql(
let anti_spam = AntiSpam::from(conf); let anti_spam = AntiSpam::from(conf);
let opts = Arc::new(opts); let opts = Arc::new(opts);
warp::path::path(conf.get_path()) warp::path::path(conf.get_path())
.and(warp::addr::remote())
.and(warp::method()) .and(warp::method())
.and(warp::query::raw().or(warp::any().map(String::new)).unify()) .and(warp::query::raw().or(warp::any().map(String::new)).unify())
.and(warp::addr::remote())
.and(warp::header::optional::<IpAddr>("X-Real-IP"))
.and(warp::header::optional::<String>("content-type")) .and(warp::header::optional::<String>("content-type"))
.and(warp::body::stream()) .and(warp::body::stream())
.and(warp::any().map(move || opts.clone())) .and(warp::any().map(move || opts.clone()))
.and(warp::any().map(move || schema.clone())) .and(warp::any().map(move || schema.clone()))
.and(warp::any().map(move || anti_spam.clone())) .and(warp::any().map(move || anti_spam.clone()))
.and_then( .and_then(
|remote_addr, |method,
method,
query: String, query: String,
remote_addr: Option<SocketAddr>,
x_real_ip: Option<IpAddr>,
content_type, content_type,
body, body,
opts: Arc<async_graphql::http::MultipartOptions>, opts: Arc<async_graphql::http::MultipartOptions>,
schema, schema,
anti_spam: AntiSpam| async move { anti_spam: AntiSpam| async move {
if anti_spam.verify(remote_addr).await { if anti_spam
.verify(x_real_ip.or_else(|| remote_addr.map(|ra| ra.ip())))
.await
{
if method == http::Method::GET { if method == http::Method::GET {
let request: async_graphql::Request = serde_urlencoded::from_str(&query) let request: async_graphql::Request = serde_urlencoded::from_str(&query)
.map_err(|err| warp::reject::custom(BadRequest(err.into())))?; .map_err(|err| warp::reject::custom(BadRequest(err.into())))?;
...@@ -96,7 +103,7 @@ pub(crate) fn graphql( ...@@ -96,7 +103,7 @@ pub(crate) fn graphql(
} }
} else { } else {
Err(warp::reject::custom(BadRequest(anyhow::Error::msg( Err(warp::reject::custom(BadRequest(anyhow::Error::msg(
"too many requests", r#"{ "error": "too many requests" }"#,
)))) ))))
} }
}, },
...@@ -115,16 +122,24 @@ pub(crate) fn graphql_ws( ...@@ -115,16 +122,24 @@ pub(crate) fn graphql_ws(
let anti_spam = AntiSpam::from(conf); let anti_spam = AntiSpam::from(conf);
warp::path::path(conf.get_subscriptions_path()) warp::path::path(conf.get_subscriptions_path())
.and(warp::addr::remote()) .and(warp::addr::remote())
.and(warp::header::optional::<IpAddr>("X-Real-IP"))
.and(warp::ws()) .and(warp::ws())
.and(warp::any().map(move || schema.clone())) .and(warp::any().map(move || schema.clone()))
.and(warp::any().map(move || anti_spam.clone())) .and(warp::any().map(move || anti_spam.clone()))
.and_then( .and_then(
|remote_addr, ws: warp::ws::Ws, schema: GraphQlSchema, anti_spam: AntiSpam| async move { |remote_addr: Option<SocketAddr>,
if anti_spam.verify(remote_addr).await { x_real_ip: Option<IpAddr>,
ws: warp::ws::Ws,
schema: GraphQlSchema,
anti_spam: AntiSpam| async move {
if anti_spam
.verify(x_real_ip.or_else(|| remote_addr.map(|ra| ra.ip())))
.await
{
Ok((ws, schema)) Ok((ws, schema))
} else { } else {
Err(warp::reject::custom(BadRequest(anyhow::Error::msg( Err(warp::reject::custom(BadRequest(anyhow::Error::msg(
"too many requests", r#"{ "error": "too many requests" }"#,
)))) ))))
} }
}, },
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment