Skip to content

Commit

Permalink
chore: add hardening
Browse files Browse the repository at this point in the history
  • Loading branch information
FlakM committed Dec 20, 2023
1 parent c1bb0a3 commit 417804e
Show file tree
Hide file tree
Showing 16 changed files with 385 additions and 136 deletions.
245 changes: 165 additions & 80 deletions backend/Cargo.lock

Large diffs are not rendered by default.

12 changes: 9 additions & 3 deletions backend/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "quick-start"
name = "backend"
version = "0.1.0"
edition = "2021"

Expand All @@ -12,7 +12,7 @@ url = { version = "2.5.0", features = ["serde"] }
serde_json = { version = "1.0.108", features = ["preserve_order", "raw_value"] }
reqwest = { version = "0.11.22", features = ["json", "stream"] }
reqwest-middleware = "0.2.4"
tracing = "0.1.40"
tracing = "0.1"
base64 = "0.21.5"
openssl = "0.10.60"
once_cell = "1.18.0"
Expand Down Expand Up @@ -48,11 +48,17 @@ rand = "0.8.5"
env_logger = "0.10.1"
tower-http = { version = "0.4.0", features = ["map-request-body", "util", "trace"] }
axum-macros = "0.3.8"
activitypub_federation = "0.5.0-beta.5"

#activitypub_federation = "0.5.0-beta.5"
# take from git git@github.com:FlakM/activitypub-federation-rust.git
activitypub_federation = { git = "https://github.com/FlakM/activitypub-federation-rust.git"}


tokio = { version = "1.35.0", features = ["full"] }



sqlx = { version = "0.7", features = [ "runtime-tokio", "migrate", "sqlite", "chrono" , "json"] }
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }


6 changes: 2 additions & 4 deletions backend/flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Restart = "on-failure";
ExecStart = "${server}/bin/quick-start";
ExecStart = "${server}/bin/backend";
DynamicUser = true;
TemporaryFileSystem = "/:ro";
BindPaths = "/var/lib/backend";
Expand Down Expand Up @@ -154,9 +154,7 @@
default = server;
};

devShell = with pkgs; mkShell {

};
devShell = with pkgs; mkShell { };
});

}
2 changes: 0 additions & 2 deletions backend/src/activities/follow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ impl ActivityHandler for Follow {
Ok(())
}

// Ignore clippy false positive: https://github.com/rust-lang/rust-clippy/issues/6446
#[allow(clippy::await_holding_lock)]
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
tracing::info!("Received follow from {}", self.actor.inner());

Expand Down
1 change: 1 addition & 0 deletions backend/src/activities/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod accept;
pub mod create_post;
pub mod follow;
pub mod undo_follow;
64 changes: 64 additions & 0 deletions backend/src/activities/undo_follow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use crate::{
activities::accept::Accept, database::Database, generate_object_id, objects::person::DbUser,
};
use activitypub_federation::traits::Actor;
use activitypub_federation::{
config::Data, fetch::object_id::ObjectId, kinds::activity::FollowType, traits::ActivityHandler,
};
use activitystreams_kinds::activity::UndoType;
use serde::{Deserialize, Serialize};
use url::Url;

use super::follow::Follow;


// {"@context":"https://www.w3.org/ns/activitystreams","id":"https://hachyderm.io/users/flakm#follows/3466630/undo",
// "type":"Undo","actor":"https://hachyderm.io/users/flakm",
// "object":{"id":"https://hachyderm.io/d6e9a487-9a5d-45f0-846b-bf2d90947e38","type":"Follow","actor":"https://hachyderm.io/users/flakm","object":"https://fedi.flakm.com/blog_test2"}}
#[derive(Deserialize, Serialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Unfollow {
pub(crate) actor: ObjectId<DbUser>,
pub(crate) object: Follow,
#[serde(rename = "type")]
kind: UndoType,
id: Url,
}


#[async_trait::async_trait]
impl ActivityHandler for Unfollow {
type DataType = Database;
type Error = crate::error::Error;

fn id(&self) -> &Url {
&self.id
}

fn actor(&self) -> &Url {
self.actor.inner()
}

async fn verify(&self, _data: &Data<Self::DataType>) -> Result<(), Self::Error> {
Ok(())
}

async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
tracing::info!("Received unfollow from {}", self.actor.inner());

let local_user = data.local_user().await?;
let follower = self.actor.dereference(data).await?;

// add to followers
data.remove_follower(&local_user, &follower).await?;

// send back an accept
let id = generate_object_id(data.domain())?;
let accept = Accept::new(local_user.ap_id.clone(), self.object, id.clone());
tracing::info!("Sending unfollow accept to {}", follower.ap_id);
local_user
.send(accept, vec![follower.shared_inbox_or_inbox()], data)
.await?;
Ok(())
}
}
67 changes: 51 additions & 16 deletions backend/src/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ pub struct Database {
pub pool: sqlx::SqlitePool,
}

#[derive(sqlx::FromRow, Debug)]
pub struct SavedUser {
pub id: i64,
}

impl Database {
pub async fn local_user(&self) -> Result<DbUser, Error> {
let user = self.read_user(LOCAL_USER_NAME).await?;
Expand All @@ -30,33 +35,34 @@ impl Database {
user.map(|u| Ok(u.into())).transpose()
}

pub async fn save_user(&self, user: &DbUser) -> Result<(), Error> {
pub async fn save_user(&self, user: &DbUser) -> Result<SavedUser, Error> {
let user_json = serde_json::to_string(user)?;
let object_id = user.ap_id.inner().to_string();
sqlx::query!(
"INSERT INTO users (name, user, object_id) VALUES (?, ?, ?)",

//"INSERT INTO users (name, user, object_id) VALUES (?, ?, ?) returning id",
//
let id: SavedUser = sqlx::query_as!(
SavedUser,
r#"INSERT INTO users (name, user, object_id) VALUES (?, ?, ?) returning id"#,
user.name,
user_json,
object_id
)
.execute(&self.pool)
.fetch_one(&self.pool)
.await?;
Ok(())
Ok(id)
}

pub async fn find_by_object_id(&self, object_id: &str) -> Result<DbUser, Error> {
pub async fn find_by_object_id(&self, object_id: &str) -> Result<Option<DbUser>, Error> {
tracing::debug!("find_by_object_id: {}", object_id);
let user: Option<SqliteUser> = sqlx::query_as!(
SqliteUser,
r#"SELECT id, name, object_id, user AS "user: sqlx::types::Json<DbUser>" FROM users WHERE object_id = ?"#,
object_id
)
.fetch_optional(&self.pool)
.await?;

match user {
Some(user) => Ok(user.into()),
None => Err(anyhow!("User not found").into()),
}
user.map(|u| Ok(u.into())).transpose()
}

pub async fn get_followers(&self, user: &DbUser) -> Result<Vec<Follower>, Error> {
Expand All @@ -68,16 +74,45 @@ impl Database {
Ok(followers)
}

pub async fn remove_follower(&self, user: &DbUser, follower: &DbUser) -> Result<(), Error> {
let follower_url = follower.ap_id.inner().to_string();

tracing::debug!("remove_follower: {} {}", user.name, follower_url);

sqlx::query!(
r#"DELETE FROM followers WHERE user_id = (SELECT id FROM users WHERE name = ?) AND follower_id = (SELECT id FROM users WHERE name = ?)"#,
user.name,
follower.name
)
.execute(&self.pool)
.await?;
Ok(())
}

pub async fn save_follower(&self, user: &DbUser, follower: &DbUser) -> Result<(), Error> {
// todo: check if follower already exists
self.save_user(follower).await?;
if let Some(_user) = self.read_user(&follower.name).await? {
// follower user already exists, do nothing
}
else {
// save new user in the database
self.save_user(follower).await?;
};

let follower_url = follower.ap_id.inner().to_string();

tracing::debug!("save_follower: {} {}", user.name, follower_url);

sqlx::query!(
r#"INSERT INTO followers (user_id, follower_url) VALUES ((SELECT id FROM users WHERE name = ?), ?)"#,
r#"INSERT INTO followers (user_id, follower_id, follower_url) VALUES (
(SELECT id FROM users WHERE name = ?),
(SELECT id FROM users WHERE name = ?)
, ?)"#,
user.name,
follower.name,
follower_url
).execute(&self.pool).await?;

)
.execute(&self.pool)
.await?;
Ok(())
}
}
2 changes: 1 addition & 1 deletion backend/src/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub async fn http_get_user(
) -> Result<FederationJson<WithContext<Person>>, Error> {
let db_user = data.read_user(&name).await?;
let json_user = db_user
.ok_or(Error(anyhow::anyhow!("User not found")))?
.ok_or(Error(anyhow::anyhow!("User not found http_get_user")))?
.into_json(&data)
.await?;
Ok(FederationJson(WithContext::new_default(json_user)))
Expand Down
27 changes: 19 additions & 8 deletions backend/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,25 @@ use crate::{
objects::{person::DbUser, post::DbPost},
utils::generate_object_id,
};
use ::http::HeaderMap;
use activitypub_federation::config::{FederationConfig, FederationMiddleware};
use axum::response::Response;
use axum::{
http::Request,
routing::{get, post},
Router,
};
use bytes::Bytes;
use error::Error;
use sqlx::sqlite::SqliteConnectOptions;
use sqlx::SqlitePool;
use std::net::ToSocketAddrs;
use tower_http::classify::ServerErrorsFailureClass;
use tracing::log::info;

use axum::extract::MatchedPath;
use std::time::Duration;
use tower_http::trace::TraceLayer;
use tracing::{info_span, Span};
use tracing::{field, info_span, Span};

use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};

mod activities;
mod database;
Expand All @@ -40,7 +39,16 @@ const BIND_ADDRESS: &str = "127.0.0.1:3000";

#[tokio::main]
async fn main() -> Result<(), Error> {
env_logger::builder().format_timestamp(None).init();
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| {
// axum logs rejections from built-in extractors with the `axum::rejection`
// target, at `TRACE` level. `axum::rejection=trace` enables showing those events
"backend=debug,tower_http=debug,axum::rejection=trace".into()
}),
)
.with(tracing_subscriber::fmt::layer())
.init();

let database_path = std::env::var("DATABASE_PATH").unwrap_or_else(|_| "./db.sqlite".into());

Expand Down Expand Up @@ -100,17 +108,20 @@ async fn main() -> Result<(), Error> {
info_span!(
"http_request",
method = ?request.method(),
path = ?request.uri().path(),
matched_path,
some_other_field = tracing::field::Empty,
latency = tracing::field::Empty,
status_code = tracing::field::Empty,
)
})
.on_request(|_request: &Request<_>, _span: &Span| {
// You can use `_span.record("some_other_field", value)` in one of these
// closures to attach a value to the initially empty field in the info_span
// created above.
})
.on_response(|_response: &Response, _latency: Duration, _span: &Span| {
// ...
.on_response(|response: &Response, latency: Duration, span: &Span| {
span.record("latency", field::debug(&latency));
span.record("status_code", response.status().as_u16());
tracing::info!("response");
}),
)
Expand Down
5 changes: 3 additions & 2 deletions backend/src/objects/person.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
activities::{accept::Accept, create_post::CreatePost, follow::Follow},
activities::{accept::Accept, create_post::CreatePost, follow::Follow, undo_follow::Unfollow},
database::Database,
error::Error,
utils::generate_object_id,
Expand Down Expand Up @@ -118,6 +118,7 @@ pub enum PersonAcceptedActivities {
CreateNote(CreatePost),
Follow(Follow),
Accept(Accept),
UndoFollow(Unfollow),
}

impl DbUser {
Expand Down Expand Up @@ -207,7 +208,7 @@ impl Object for DbUser {
object_id: Url,
data: &Data<Self::DataType>,
) -> Result<Option<Self>, Self::Error> {
data.find_by_object_id(object_id.as_str()).await.map(Some)
data.find_by_object_id(object_id.as_str()).await
}

async fn into_json(self, _data: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
Expand Down
20 changes: 20 additions & 0 deletions blog-static/content/posts/nixos_rust/04_hardening.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
+++
draft = true
date = 2023-12-20T08:37:45+01:00
title = ""
description = "A blog entry about hardening my nixos host"
slug = ""
authors = []
tags = []
categories = []
externalLink = ""

series = ["Simple personal blog"]
+++


## The goals




Loading

0 comments on commit 417804e

Please sign in to comment.