From 98b71277ac9ec82cbdf3a2c7c4fd74dc6b33711c Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Wed, 16 Jun 2021 01:30:51 -0400 Subject: [PATCH] IP bans work! --- .../middleware/ip_filter_middleware.rs | 13 ++++++-- src/http_server/web_utils/ip_address.rs | 31 +++++++++++++++++++ src/threads/ip_banlist_set.rs | 4 +-- 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/http_server/middleware/ip_filter_middleware.rs b/src/http_server/middleware/ip_filter_middleware.rs index 7544d98dfa..5815dfeb8e 100644 --- a/src/http_server/middleware/ip_filter_middleware.rs +++ b/src/http_server/middleware/ip_filter_middleware.rs @@ -8,6 +8,8 @@ use futures::Future; use crate::threads::ip_banlist_set::IpBanlistSet; use log::warn; use log::info; +use crate::http_server::web_utils::ip_address::get_service_request_ip; +use actix_web::error::ErrorForbidden; // There are two steps in middleware processing. // 1. Middleware initialization, middleware factory gets called with @@ -69,8 +71,15 @@ impl Service for IpFilterMiddleware } fn call(&mut self, req: ServiceRequest) -> Self::Future { - println!("Hi from start. You requested: {}", req.path()); - info!("Checking IP. There are {} bans currently.", self.ip_banlist.num_bans().unwrap_or(0)); + let ip_address = get_service_request_ip(&req); + + // NB: Fail open. + let is_banned = self.ip_banlist.is_banned(&ip_address).unwrap_or(false); + + if is_banned { + warn!("Ip is banned: {}", &ip_address); + return Box::pin(ok(req.error_response(ErrorForbidden("Forbidden")))) + } let fut = self.service.call(req); diff --git a/src/http_server/web_utils/ip_address.rs b/src/http_server/web_utils/ip_address.rs index bb500e129b..5372c5f26e 100644 --- a/src/http_server/web_utils/ip_address.rs +++ b/src/http_server/web_utils/ip_address.rs @@ -1,6 +1,7 @@ use actix_web::HttpRequest; use actix_web::http::HeaderName; use log::{info}; +use actix_web::dev::ServiceRequest; pub fn get_request_ip(request: &HttpRequest) -> String { match request.headers().get(HeaderName::from_static("x-voder-proxy-for")) { @@ -30,3 +31,33 @@ pub fn get_request_ip(request: &HttpRequest) -> String { }, } } + +// TODO: De-duplicate +pub fn get_service_request_ip(request: &ServiceRequest) -> String { + match request.headers().get(HeaderName::from_static("x-voder-proxy-for")) { + Some(ip_address) => { + // Unfortunately the upstream Rust proxy is replacing the `forwarded` and `x-forwarded-for` + // headers, so we populate this custom header as a workaround. + info!("Proxied IP address: {:?}", ip_address); + ip_address.to_str() + .unwrap_or("") + .to_string() + }, + None => { + // If we're running without the upstream Rust proxy, we can grab 'x-forarded-for', which is + // populated by the DigitalOcean load balancer. + let ip_address_and_port = request.connection_info() + .remote_addr() + .unwrap_or("") + .to_string(); + let ip_address = ip_address_and_port.split(":") + .collect::>() + .get(0) + .copied() + .unwrap_or("") + .to_string(); + info!("Forwarded IP address: {}", &ip_address); + ip_address + }, + } +} diff --git a/src/threads/ip_banlist_set.rs b/src/threads/ip_banlist_set.rs index 40023f83db..f0ec2611bf 100644 --- a/src/threads/ip_banlist_set.rs +++ b/src/threads/ip_banlist_set.rs @@ -27,11 +27,11 @@ impl IpBanlistSet { } } - pub fn is_banned(&self, ip_address: String) -> AnyhowResult { + pub fn is_banned>(&self, ip_address: S) -> AnyhowResult { match self.ip_banlist.read() { Err(_) => Err(anyhow!("Can't read lock")), Ok(read) => { - let result = read.contains(&ip_address); + let result = read.contains(ip_address.as_ref()); Ok(result) }, }