Enforce network IP restriction

This commit is contained in:
Pierre HUBERT 2025-01-30 22:06:17 +01:00
parent 501520a9df
commit af1dd4d122
4 changed files with 41 additions and 4 deletions

12
Cargo.lock generated
View File

@ -68,6 +68,17 @@ dependencies = [
"syn",
]
[[package]]
name = "actix-remote-ip"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7629b357d4705cf3f1e31f989f48ecd56027112f7d52dcf06dd96ee197065f8e"
dependencies = [
"actix-web",
"futures-util",
"log",
]
[[package]]
name = "actix-router"
version = "0.5.3"
@ -1887,6 +1898,7 @@ checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
name = "matrix_gateway"
version = "0.1.0"
dependencies = [
"actix-remote-ip",
"actix-session",
"actix-web",
"anyhow",

View File

@ -26,3 +26,4 @@ ipnet = { version = "2.11.0", features = ["serde"] }
chrono = "0.4.39"
futures-util = "0.3.31"
jwt-simple = { version = "0.12.11", default-features=false, features=["pure-rust"] }
actix-remote-ip = "0.1.0"

View File

@ -1,9 +1,11 @@
use crate::user::{APIClient, APIClientID, UserConfig, UserID};
use crate::utils::curr_time;
use actix_remote_ip::RemoteIP;
use actix_web::dev::Payload;
use actix_web::{FromRequest, HttpRequest};
use jwt_simple::common::VerificationOptions;
use jwt_simple::prelude::{Duration, HS256Key, MACLike};
use std::net::IpAddr;
use std::str::FromStr;
pub struct APIClientAuth {
@ -19,7 +21,7 @@ pub struct TokenClaims {
}
impl APIClientAuth {
async fn extract_auth(req: &HttpRequest) -> Result<Self, actix_web::Error> {
async fn extract_auth(req: &HttpRequest, remote_ip: IpAddr) -> Result<Self, actix_web::Error> {
let Some(token) = req.headers().get("x-client-auth") else {
return Err(actix_web::error::ErrorBadRequest(
"Missing authentication header!",
@ -99,6 +101,19 @@ impl APIClientAuth {
));
}
// Check IP restriction
if let Some(net) = client.network {
if !net.contains(&remote_ip) {
log::error!(
"Trying to use client {} from unauthorized IP address: {remote_ip}",
client.id.0
);
return Err(actix_web::error::ErrorForbidden(
"This client cannot be used from this IP address!",
));
}
}
// Check URI & verb
if claims.custom.uri != req.uri().to_string() {
return Err(actix_web::error::ErrorBadRequest("URI mismatch!"));
@ -115,7 +130,6 @@ impl APIClientAuth {
}
// TODO : handle payload
// TODO : check for IP restriction
// Update last use (if needed)
if client.need_update_last_used() {
@ -143,6 +157,12 @@ impl FromRequest for APIClientAuth {
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
let req = req.clone();
Box::pin(async move { Self::extract_auth(&req).await })
let remote_ip = match RemoteIP::from_request(&req, &mut Payload::None).into_inner() {
Ok(ip) => ip,
Err(e) => return Box::pin(async { Err(e) }),
};
Box::pin(async move { Self::extract_auth(&req, remote_ip.0).await })
}
}

View File

@ -1,3 +1,4 @@
use actix_remote_ip::RemoteIPConfig;
use actix_session::config::SessionLifecycle;
use actix_session::{storage::RedisSessionStore, SessionMiddleware};
use actix_web::cookie::Key;
@ -35,6 +36,9 @@ async fn main() -> std::io::Result<()> {
.session_lifecycle(SessionLifecycle::BrowserSession(Default::default()))
.build(),
)
.app_data(web::Data::new(RemoteIPConfig {
proxy: AppConfig::get().proxy_ip.clone(),
}))
// Web configuration routes
.route("/assets/{tail:.*}", web::get().to(web_ui::static_file))
.route("/", web::get().to(web_ui::home))