feat: added hmac validated proxy route

This commit is contained in:
əlemi 2024-06-23 03:55:03 +02:00
parent ff2570e961
commit 89f5b200a8
Signed by: alemi
GPG key ID: A4895B84D311642C
5 changed files with 39 additions and 2 deletions

3
Cargo.lock generated
View file

@ -4864,7 +4864,9 @@ version = "0.2.0"
dependencies = [ dependencies = [
"apb", "apb",
"axum", "axum",
"base64 0.22.1",
"chrono", "chrono",
"hmac",
"httpsign", "httpsign",
"jrd", "jrd",
"mastodon-async-entities", "mastodon-async-entities",
@ -4874,6 +4876,7 @@ dependencies = [
"sea-orm", "sea-orm",
"serde", "serde",
"serde_json", "serde_json",
"sha2",
"sha256", "sha256",
"thiserror", "thiserror",
"time", "time",

View file

@ -68,6 +68,9 @@ pub struct SecurityConfig {
#[serde(default)] #[serde(default)]
pub allow_public_debugger: bool, pub allow_public_debugger: bool,
#[serde_inline_default("changeme".to_string())]
pub proxy_secret: String,
#[serde_inline_default(true)] #[serde_inline_default(true)]
pub show_reply_ids: bool, pub show_reply_ids: bool,

View file

@ -13,7 +13,10 @@ readme = "README.md"
[dependencies] [dependencies]
thiserror = "1" thiserror = "1"
rand = "0.8" rand = "0.8"
sha256 = "1.5" sha2 = "0.10"
sha256 = "1.5" # TODO ughhh
hmac = "0.12"
base64 = "0.22"
chrono = { version = "0.4", features = ["serde"] } chrono = { version = "0.4", features = ["serde"] }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"

View file

@ -1,9 +1,11 @@
use apb::{LD, ActorMut, BaseMut, ObjectMut, PublicKeyMut}; use apb::{LD, ActorMut, BaseMut, ObjectMut, PublicKeyMut};
use axum::{extract::{Path, Query, State}, http::HeaderMap, response::{IntoResponse, Redirect, Response}, Form}; use axum::{extract::{Path, Query, State}, http::HeaderMap, response::{IntoResponse, Redirect, Response}, Form};
use hmac::{Hmac, Mac};
use reqwest::Method; use reqwest::Method;
use base64::{engine::general_purpose::URL_SAFE, Engine as _};
use upub::{traits::Fetcher, Context}; use upub::{traits::Fetcher, Context};
use crate::{builders::JsonLD, AuthIdentity, Identity}; use crate::{builders::JsonLD, ApiError, AuthIdentity, Identity};
pub async fn view( pub async fn view(
@ -74,6 +76,31 @@ pub async fn proxy_form(
proxy(ctx, query, auth).await proxy(ctx, query, auth).await
} }
pub async fn proxy_hmac(
State(ctx): State<Context>,
AuthIdentity(auth): AuthIdentity,
Path(hmac): Path<String>,
Path(uri): Path<String>,
) -> crate::ApiResult<impl IntoResponse> {
let bytes = URL_SAFE.decode(hmac).map_err(|_| ApiError::bad_request())?;
let uri =
std::str::from_utf8(
&URL_SAFE.decode(uri).map_err(|_| ApiError::bad_request())?
)
.map_err(|_| ApiError::bad_request())?
.to_string();
type HmacSha256 = Hmac<sha2::Sha256>;
let mut mac = HmacSha256::new_from_slice(ctx.cfg().security.proxy_secret.as_bytes())
.map_err(|_| ApiError::internal_server_error())?;
mac.update(uri.as_bytes());
mac.verify_slice(&bytes)
.map_err(|_| ApiError::forbidden())?;
proxy(ctx, uri, auth).await
}
async fn proxy(ctx: Context, query: String, auth: Identity) -> crate::ApiResult<impl IntoResponse> { async fn proxy(ctx: Context, query: String, auth: Identity) -> crate::ApiResult<impl IntoResponse> {
// only local users can request fetches // only local users can request fetches
if !ctx.cfg().security.allow_public_debugger && !auth.is_local() { if !ctx.cfg().security.allow_public_debugger && !auth.is_local() {

View file

@ -25,6 +25,7 @@ impl ActivityPubRouter for Router<upub::Context> {
.route("/proxy", post(ap::application::proxy_form)) .route("/proxy", post(ap::application::proxy_form))
.route("/proxy", get(ap::application::proxy_get)) .route("/proxy", get(ap::application::proxy_get))
.route("/proxy/:uri", get(ap::application::proxy_path)) .route("/proxy/:uri", get(ap::application::proxy_path))
.route("/proxy/:hmac/:uri", get(ap::application::proxy_hmac))
.route("/inbox", post(ap::inbox::post)) .route("/inbox", post(ap::inbox::post))
.route("/inbox", get(ap::inbox::get)) .route("/inbox", get(ap::inbox::get))
.route("/inbox/page", get(ap::inbox::page)) .route("/inbox/page", get(ap::inbox::page))