From 8b95ea3abd7f5016991bd4b9518d1b4fe1220149 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Wed, 16 Jun 2021 00:45:51 -0400 Subject: [PATCH] ip filter middleware (not rejecting yet) --- Cargo.lock | 1 + Cargo.toml | 1 + .../middleware/ip_filter_middleware.rs | 84 +++++++++++++++++++ src/http_server/middleware/mod.rs | 1 + src/http_server/mod.rs | 1 + src/main.rs | 5 ++ src/threads/ip_banlist_set.rs | 10 +++ src/threads/poll_ip_banlist_thread.rs | 7 -- 8 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 src/http_server/middleware/ip_filter_middleware.rs create mode 100644 src/http_server/middleware/mod.rs diff --git a/Cargo.lock b/Cargo.lock index f67cb88333..b396b54398 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3168,6 +3168,7 @@ dependencies = [ "actix-http", "actix-multipart", "actix-rt 2.2.0", + "actix-service", "actix-utils 3.0.0", "actix-web", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index 117e26bfb3..33dc410197 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,6 +86,7 @@ actix-files= "0.5" actix-http = "2.2.0" actix-multipart = "0.3.0" # TODO: Ready for 0.4 upgrade actix-rt = "2.2.0" +actix-service = "1.0.6" actix-utils = "3.0.0" actix-web = "3.3.2" jwt = "0.13.0" diff --git a/src/http_server/middleware/ip_filter_middleware.rs b/src/http_server/middleware/ip_filter_middleware.rs new file mode 100644 index 0000000000..7544d98dfa --- /dev/null +++ b/src/http_server/middleware/ip_filter_middleware.rs @@ -0,0 +1,84 @@ +use std::pin::Pin; +use std::task::{Context, Poll}; + +use actix_service::{Service, Transform}; +use actix_web::{dev::ServiceRequest, dev::ServiceResponse, Error}; +use futures::future::{ok, Ready}; +use futures::Future; +use crate::threads::ip_banlist_set::IpBanlistSet; +use log::warn; +use log::info; + +// There are two steps in middleware processing. +// 1. Middleware initialization, middleware factory gets called with +// next service in chain as parameter. +// 2. Middleware's call method gets called with normal request. +#[derive(Clone)] // NB: Clone is safe because IpBanlist is clone-safe (internal Arc) +pub struct IpFilter { + ip_banlist: IpBanlistSet, +} + +impl IpFilter { + pub fn new(ip_banlist: IpBanlistSet) -> Self { + Self { + ip_banlist, + } + } +} + +// Middleware factory is `Transform` trait from actix-service crate +// `S` - type of the next service +// `B` - type of response's body +impl Transform for IpFilter + where + S: Service, Error = Error>, + S::Future: 'static, + B: 'static, +{ + type Request = ServiceRequest; + type Response = ServiceResponse; + type Error = Error; + type InitError = (); + type Transform = IpFilterMiddleware; + type Future = Ready>; + + fn new_transform(&self, service: S) -> Self::Future { + // NB: IpBanlist is clone-safe due to internal Arc. + ok(IpFilterMiddleware { service, ip_banlist: self.ip_banlist.clone() }) + } +} + +pub struct IpFilterMiddleware { + service: S, + ip_banlist: IpBanlistSet, +} + +impl Service for IpFilterMiddleware + where + S: Service, Error = Error>, + S::Future: 'static, + B: 'static, +{ + type Request = ServiceRequest; + type Response = ServiceResponse; + type Error = Error; + type Future = Pin>>>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.service.poll_ready(cx) + } + + 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 fut = self.service.call(req); + + Box::pin(async move { + let res = fut.await?; + + println!("Hi from response"); + Ok(res) + }) + } +} \ No newline at end of file diff --git a/src/http_server/middleware/mod.rs b/src/http_server/middleware/mod.rs new file mode 100644 index 0000000000..a2369086a3 --- /dev/null +++ b/src/http_server/middleware/mod.rs @@ -0,0 +1 @@ +pub mod ip_filter_middleware; \ No newline at end of file diff --git a/src/http_server/mod.rs b/src/http_server/mod.rs index 9b6c9f4477..43daf6085b 100644 --- a/src/http_server/mod.rs +++ b/src/http_server/mod.rs @@ -1,2 +1,3 @@ pub mod endpoints; +pub mod middleware; pub mod web_utils; diff --git a/src/main.rs b/src/main.rs index a436cc29e0..8e3eaa376b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -97,6 +97,7 @@ use sqlx::MySqlPool; use sqlx::mysql::MySqlPoolOptions; use std::sync::Arc; use std::time::Duration; +use crate::http_server::middleware::ip_filter_middleware::IpFilter; // TODO TODO TODO TODO // TODO TODO TODO TODO @@ -270,6 +271,9 @@ pub async fn serve(server_state: ServerState) -> AnyhowResult<()> let log_format = "[%{HOSTNAME}e] %a \"%r\" %s %b \"%{Referer}i\" \"%{User-Agent}i\" %T"; HttpServer::new(move || { + // NB: Safe to clone due to internal arc + let ip_banlist = server_state_arc.ip_banlist.clone(); + App::new() .app_data(server_state_arc.clone()) .wrap(Cors::default() @@ -310,6 +314,7 @@ pub async fn serve(server_state: ServerState) -> AnyhowResult<()> .wrap(DefaultHeaders::new() .header("X-Backend-Hostname", &hostname) .header("X-Build-Sha", "")) + .wrap(IpFilter::new(ip_banlist)) // ==================== ACCOUNT CREATION / SESSION MANAGEMENT ==================== .service( web::resource("/create_account") diff --git a/src/threads/ip_banlist_set.rs b/src/threads/ip_banlist_set.rs index a585c1e64a..40023f83db 100644 --- a/src/threads/ip_banlist_set.rs +++ b/src/threads/ip_banlist_set.rs @@ -36,4 +36,14 @@ impl IpBanlistSet { }, } } + + pub fn num_bans(&self) -> AnyhowResult { + match self.ip_banlist.read() { + Err(_) => Err(anyhow!("Can't read lock")), + Ok(read) => { + let result = read.len(); + Ok(result) + }, + } + } } \ No newline at end of file diff --git a/src/threads/poll_ip_banlist_thread.rs b/src/threads/poll_ip_banlist_thread.rs index 0dcf47bcab..81ff51d471 100644 --- a/src/threads/poll_ip_banlist_thread.rs +++ b/src/threads/poll_ip_banlist_thread.rs @@ -5,15 +5,8 @@ use log::info; use log::warn; use sqlx::MySqlPool; use std::collections::HashSet; -use std::sync::Arc; -use std::sync::atomic::{AtomicBool, Ordering}; use std::thread; use std::time::Duration; -use tokio::signal::ctrl_c; -use tokio::signal::unix::signal; -use tokio::signal::unix::{SignalKind, Signal}; -use tokio::sync::oneshot::{Sender, Receiver}; -use tokio::task::JoinHandle; pub async fn poll_ip_bans( ip_banlist_set: IpBanlistSet,