Skip to content

Commit

Permalink
Ratelimit acct creation (#2933)
Browse files Browse the repository at this point in the history
  • Loading branch information
Geometrically authored Nov 11, 2024
1 parent 648b40a commit 3fa07f6
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 44 deletions.
20 changes: 2 additions & 18 deletions apps/labrinth/src/queue/analytics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use dashmap::{DashMap, DashSet};
use redis::cmd;
use sqlx::PgPool;
use std::collections::HashMap;
use std::net::Ipv6Addr;

const DOWNLOADS_NAMESPACE: &str = "downloads";
const VIEWS_NAMESPACE: &str = "views";
Expand All @@ -33,31 +32,16 @@ impl AnalyticsQueue {
}
}

fn strip_ip(ip: Ipv6Addr) -> u64 {
if let Some(ip) = ip.to_ipv4_mapped() {
let octets = ip.octets();
u64::from_be_bytes([
octets[0], octets[1], octets[2], octets[3], 0, 0, 0, 0,
])
} else {
let octets = ip.octets();
u64::from_be_bytes([
octets[0], octets[1], octets[2], octets[3], octets[4],
octets[5], octets[6], octets[7],
])
}
}

pub fn add_view(&self, page_view: PageView) {
let ip_stripped = Self::strip_ip(page_view.ip);
let ip_stripped = crate::util::ip::strip_ip(page_view.ip);

self.views_queue
.entry((ip_stripped, page_view.project_id))
.or_default()
.push(page_view);
}
pub fn add_download(&self, download: Download) {
let ip_stripped = Self::strip_ip(download.ip);
let ip_stripped = crate::util::ip::strip_ip(download.ip);
self.downloads_queue
.insert((ip_stripped, download.project_id), download);
}
Expand Down
14 changes: 2 additions & 12 deletions apps/labrinth/src/routes/analytics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use actix_web::{HttpRequest, HttpResponse};
use serde::Deserialize;
use sqlx::PgPool;
use std::collections::HashMap;
use std::net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr};
use std::net::Ipv4Addr;
use std::sync::Arc;
use url::Url;

Expand All @@ -39,16 +39,6 @@ pub const FILTERED_HEADERS: &[&str] = &[
"x-vercel-ip-latitude",
"x-vercel-ip-country",
];

pub fn convert_to_ip_v6(src: &str) -> Result<Ipv6Addr, AddrParseError> {
let ip_addr: IpAddr = src.parse()?;

Ok(match ip_addr {
IpAddr::V4(x) => x.to_ipv6_mapped(),
IpAddr::V6(x) => x,
})
}

#[derive(Deserialize)]
pub struct UrlInput {
url: String,
Expand Down Expand Up @@ -101,7 +91,7 @@ pub async fn page_view_ingest(
})
.collect::<HashMap<String, String>>();

let ip = convert_to_ip_v6(
let ip = crate::util::ip::convert_to_ip_v6(
if let Some(header) = headers.get("cf-connecting-ip") {
header
} else {
Expand Down
2 changes: 1 addition & 1 deletion apps/labrinth/src/routes/internal/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ pub async fn count_download(
ApiError::InvalidInput("invalid download URL specified!".to_string())
})?;

let ip = crate::routes::analytics::convert_to_ip_v6(&download_body.ip)
let ip = crate::util::ip::convert_to_ip_v6(&download_body.ip)
.unwrap_or_else(|_| Ipv4Addr::new(127, 0, 0, 1).to_ipv6_mapped());

analytics_queue.add_download(Download {
Expand Down
58 changes: 45 additions & 13 deletions apps/labrinth/src/routes/internal/flows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1468,6 +1468,8 @@ pub struct NewAccount {
pub sign_up_newsletter: Option<bool>,
}

const NEW_ACCOUNT_LIMITER_NAMESPACE: &str = "new_account_ips";

#[post("create")]
pub async fn create_account_with_password(
req: HttpRequest,
Expand Down Expand Up @@ -1533,19 +1535,6 @@ pub async fn create_account_with_password(
));
}

let flow = Flow::ConfirmEmail {
user_id,
confirm_email: new_account.email.clone(),
}
.insert(Duration::hours(24), &redis)
.await?;

send_email_verify(
new_account.email.clone(),
flow,
&format!("Welcome to Modrinth, {}!", new_account.username),
)?;

crate::database::models::User {
id: user_id,
github_id: None,
Expand Down Expand Up @@ -1577,6 +1566,49 @@ pub async fn create_account_with_password(
let session = issue_session(req, user_id, &mut transaction, &redis).await?;
let res = crate::models::sessions::Session::from(session, true, None);

// We limit each ip to creating 5 accounts in a six hour period
let ip = crate::util::ip::convert_to_ip_v6(&res.ip).map_err(|_| {
ApiError::InvalidInput("unable to parse user ip!".to_string())
})?;
let stripped_ip = crate::util::ip::strip_ip(ip).to_string();

let mut conn = redis.connect().await?;
let uses = if let Some(res) = conn
.get(NEW_ACCOUNT_LIMITER_NAMESPACE, &stripped_ip)
.await?
{
res.parse::<u64>().unwrap_or(0)
} else {
0
};

if uses >= 5 {
return Err(ApiError::InvalidInput(
"IP has been rate-limited.".to_string(),
));
}

conn.set(
NEW_ACCOUNT_LIMITER_NAMESPACE,
&stripped_ip,
&(uses + 1).to_string(),
Some(60 * 60 * 6),
)
.await?;

let flow = Flow::ConfirmEmail {
user_id,
confirm_email: new_account.email.clone(),
}
.insert(Duration::hours(24), &redis)
.await?;

send_email_verify(
new_account.email.clone(),
flow,
&format!("Welcome to Modrinth, {}!", new_account.username),
)?;

if new_account.sign_up_newsletter.unwrap_or(false) {
sign_up_beehiiv(&new_account.email).await?;
}
Expand Down
25 changes: 25 additions & 0 deletions apps/labrinth/src/util/ip.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use std::net::{AddrParseError, IpAddr, Ipv6Addr};

pub fn convert_to_ip_v6(src: &str) -> Result<Ipv6Addr, AddrParseError> {
let ip_addr: IpAddr = src.parse()?;

Ok(match ip_addr {
IpAddr::V4(x) => x.to_ipv6_mapped(),
IpAddr::V6(x) => x,
})
}

pub fn strip_ip(ip: Ipv6Addr) -> u64 {
if let Some(ip) = ip.to_ipv4_mapped() {
let octets = ip.octets();
u64::from_be_bytes([
octets[0], octets[1], octets[2], octets[3], 0, 0, 0, 0,
])
} else {
let octets = ip.octets();
u64::from_be_bytes([
octets[0], octets[1], octets[2], octets[3], octets[4], octets[5],
octets[6], octets[7],
])
}
}
1 change: 1 addition & 0 deletions apps/labrinth/src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod env;
pub mod ext;
pub mod guards;
pub mod img;
pub mod ip;
pub mod ratelimit;
pub mod redis;
pub mod routes;
Expand Down

0 comments on commit 3fa07f6

Please sign in to comment.