Compare commits

...

2 Commits

Author SHA1 Message Date
meeg_leeto 0768507036 minor: fixed debug message on config.json version mismatch 2024-03-01 21:44:16 +00:00
meeg_leeto a3fc446dbc feat!: links are logged to a separate file
This should help monitor the use of the link shortener, in particular
for nefarious uses.
Prompted to do this by https://app.y.gy/blog/honeypot
2024-03-01 21:43:53 +00:00
4 changed files with 49 additions and 25 deletions

2
Cargo.lock generated
View File

@ -838,7 +838,7 @@ dependencies = [
[[package]] [[package]]
name = "lonk" name = "lonk"
version = "1.0.1" version = "2.0.1"
dependencies = [ dependencies = [
"argh", "argh",
"async-object-pool", "async-object-pool",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "lonk" name = "lonk"
version = "1.0.1" version = "2.0.1"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,5 +1,5 @@
{ {
"version": 1, "version": 2,
"db": { "db": {
"address": "redis://redis:6379", "address": "redis://redis:6379",
"expire_seconds": 259200 "expire_seconds": 259200
@ -10,7 +10,8 @@
}, },
"log_rules": { "log_rules": {
"error_log_file": "/data/log/error.log", "error_log_file": "/data/log/error.log",
"access_log_file": "/data/log/access.log" "access_log_file": "/data/log/access.log",
"link_log_file": "/data/log/link.log"
}, },
"serve_rules": { "serve_rules": {
"dir": { "dir": {

View File

@ -142,8 +142,10 @@ mod conf {
pub struct LogRules { pub struct LogRules {
/// Where to write error logs to. The file will be appended to. /// Where to write error logs to. The file will be appended to.
pub error_log_file: PathBuf, pub error_log_file: PathBuf,
/// Where to write access ogs to. The file will be appended to. /// Where to write access logs to. The file will be appended to.
pub access_log_file: PathBuf, pub access_log_file: PathBuf,
/// Where to write link shortening logs to. The file will be appended to.
pub link_log_file: PathBuf,
} }
#[derive(Deserialize, Serialize, Debug, Clone)] #[derive(Deserialize, Serialize, Debug, Clone)]
@ -183,6 +185,7 @@ mod conf {
ServeDirNotExists(PathBuf), ServeDirNotExists(PathBuf),
AccessLogDirectoryNotExists(PathBuf), AccessLogDirectoryNotExists(PathBuf),
ErrorLogDirectoryNotExists(PathBuf), ErrorLogDirectoryNotExists(PathBuf),
LinkLogDirectoryNotExists(PathBuf),
} }
impl Config { impl Config {
@ -223,24 +226,27 @@ mod conf {
} }
// Check access and error log parent directories // Check access and error log parent directories
fn check_exists<E>(path: &PathBuf, to_err: impl FnOnce(PathBuf) -> E) -> Result<(), E> {
let weak_canonical = normalize_path(path);
if let Some(parent) = weak_canonical.parent() {
if !parent.exists() {
return Err(to_err(parent.to_path_buf()));
}
}
Ok(())
}
// - Access log file // - Access log file
let weak_canonical = normalize_path(&self.log_rules.access_log_file); check_exists(&self.log_rules.access_log_file, |path| {
if let Some(parent) = weak_canonical.parent() { ConfigParseError::AccessLogDirectoryNotExists(path)
if !parent.exists() { })?;
return Err(ConfigParseError::AccessLogDirectoryNotExists(
parent.to_path_buf(),
));
}
}
// - Error log file // - Error log file
let weak_canonical = normalize_path(&self.log_rules.error_log_file); check_exists(&self.log_rules.error_log_file, |path| {
if let Some(parent) = weak_canonical.parent() { ConfigParseError::ErrorLogDirectoryNotExists(path)
if !parent.exists() { })?;
return Err(ConfigParseError::ErrorLogDirectoryNotExists( // - Link log file
parent.to_path_buf(), check_exists(&self.log_rules.link_log_file, |path| {
)); ConfigParseError::LinkLogDirectoryNotExists(path)
} })?;
}
Ok(self) Ok(self)
} }
@ -308,8 +314,8 @@ mod conf {
eprintln!( eprintln!(
concat!("Configuration file has outdated version.\n", concat!("Configuration file has outdated version.\n",
"Expected version field to be {} but got {}."), "Expected version field to be {} but got {}."),
config_version(),
old_version, old_version,
config_version()
); );
} }
ConfigParseError::ServeDirNotExists(dir) => { ConfigParseError::ServeDirNotExists(dir) => {
@ -342,6 +348,9 @@ mod conf {
ConfigParseError::ErrorLogDirectoryNotExists(dir) => { ConfigParseError::ErrorLogDirectoryNotExists(dir) => {
eprintln!("Error log file should have parent directory {}, but this directory does not exist.", dir.to_string_lossy()) eprintln!("Error log file should have parent directory {}, but this directory does not exist.", dir.to_string_lossy())
} }
ConfigParseError::LinkLogDirectoryNotExists(dir) => {
eprintln!("Link registration log file should have parent directory {}, but this directory does not exist.", dir.to_string_lossy())
}
} }
std::process::exit(1); std::process::exit(1);
} }
@ -406,6 +415,7 @@ mod conf {
Self { Self {
error_log_file: "/etc/lonk/log/error.log".into(), error_log_file: "/etc/lonk/log/error.log".into(),
access_log_file: "/etc/lonk/log/access.log".into(), access_log_file: "/etc/lonk/log/access.log".into(),
link_log_file: "/etc/lonk/log/links.log".into(),
} }
} }
} }
@ -924,6 +934,7 @@ mod service {
pub struct Logger { pub struct Logger {
access_tx: sync::mpsc::UnboundedSender<String>, access_tx: sync::mpsc::UnboundedSender<String>,
error_tx: sync::mpsc::UnboundedSender<String>, error_tx: sync::mpsc::UnboundedSender<String>,
link_tx: sync::mpsc::UnboundedSender<String>,
} }
impl Logger { impl Logger {
@ -931,6 +942,7 @@ mod service {
// Create the communication channels // Create the communication channels
let (access_tx, access_rx) = sync::mpsc::unbounded_channel::<String>(); let (access_tx, access_rx) = sync::mpsc::unbounded_channel::<String>();
let (error_tx, error_rx) = sync::mpsc::unbounded_channel::<String>(); let (error_tx, error_rx) = sync::mpsc::unbounded_channel::<String>();
let (link_tx, link_rx) = sync::mpsc::unbounded_channel::<String>();
// Start the logging tasks // Start the logging tasks
tokio::spawn(Self::logging_task( tokio::spawn(Self::logging_task(
@ -938,11 +950,13 @@ mod service {
config.access_log_file.clone(), config.access_log_file.clone(),
)); ));
tokio::spawn(Self::logging_task(error_rx, config.error_log_file.clone())); tokio::spawn(Self::logging_task(error_rx, config.error_log_file.clone()));
tokio::spawn(Self::logging_task(link_rx, config.link_log_file.clone()));
// Done // Done
Logger { Logger {
access_tx, access_tx,
error_tx, error_tx,
link_tx,
} }
} }
@ -965,6 +979,16 @@ mod service {
pub fn error(&self, msg: String) -> Result<(), ()> { pub fn error(&self, msg: String) -> Result<(), ()> {
self.error_tx.send(msg).map_err(|_| ()) self.error_tx.send(msg).map_err(|_| ())
} }
/// Log a message into the link log file.
///
/// Returns a Result with empty type; if posting the log message
/// failed for any reason, it's unlikely to recover, and the user
/// should decide either to stop logging, ignore these errors, or
/// halt the program.
pub fn link(&self, msg: String) -> Result<(), ()> {
self.link_tx.send(msg).map_err(|_| ())
}
/// The task responsible for receiving the log messages and actually /// The task responsible for receiving the log messages and actually
/// writing them into the corresponding files. One task is created /// writing them into the corresponding files. One task is created
@ -1068,7 +1092,7 @@ async fn shorten(
Ok(result) => match result { Ok(result) => match result {
service::db::AddResult::Success(slug) => { service::db::AddResult::Success(slug) => {
logger logger
.access(format!("{} -> {}\n", slug.inner_str(), url)) .link(format!("{} -> {}\n", slug.inner_str(), url))
.ok(); .ok();
Ok(slug) Ok(slug)
} }
@ -1237,8 +1261,7 @@ async fn serve() {
// GET / // GET /
// This should be the last thing matched, so that anything that doesn't // This should be the last thing matched, so that anything that doesn't
// match another filter will try to match a file. // match another filter will try to match a file.
let homepage = warp::get() let homepage = warp::get().and(config.serve_rules.dir.to_filter());
.and(config.serve_rules.dir.to_filter());
let get_routes = warp::get().and(link.or(homepage)); let get_routes = warp::get().and(link.or(homepage));
let post_routes = warp::post().and(shorten); let post_routes = warp::post().and(shorten);