Merge pull request 'dev' (#1) from dev into main

Reviewed-on: #1
This commit is contained in:
serxoz 2024-04-18 13:47:09 +02:00
commit 5f3489dbc5
10 changed files with 288 additions and 8 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
/target
*.log
*.db

115
Cargo.lock generated
View File

@ -2,6 +2,24 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "ahash"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "allocator-api2"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
[[package]]
name = "android-tzdata"
version = "0.1.1"
@ -41,6 +59,12 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "bumpalo"
version = "3.16.0"
@ -106,6 +130,18 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "fallible-iterator"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
[[package]]
name = "fallible-streaming-iterator"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "fnv"
version = "1.0.7"
@ -128,6 +164,19 @@ name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
dependencies = [
"ahash",
"allocator-api2",
]
[[package]]
name = "hashlink"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692eaaf7f7607518dd3cef090f1474b61edc5301d8012f09579920df68b725ee"
dependencies = [
"hashbrown",
]
[[package]]
name = "hermit-abi"
@ -198,6 +247,17 @@ version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "libsqlite3-sys"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f"
dependencies = [
"cc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "lock_api"
version = "0.4.9"
@ -332,6 +392,12 @@ version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
[[package]]
name = "pkg-config"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
@ -392,7 +458,21 @@ version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags",
"bitflags 1.3.2",
]
[[package]]
name = "rusqlite"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b838eba278d213a8beaf485bd313fd580ca4505a00d5871caeb1457c55322cae"
dependencies = [
"bitflags 2.5.0",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink",
"libsqlite3-sys",
"smallvec",
]
[[package]]
@ -486,6 +566,7 @@ dependencies = [
"log",
"log4rs",
"rand",
"rusqlite",
"serde",
"tokio",
"toml",
@ -654,6 +735,18 @@ version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
@ -874,3 +967,23 @@ checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352"
dependencies = [
"memchr",
]
[[package]]
name = "zerocopy"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.59",
]

View File

@ -9,6 +9,7 @@ edition = "2021"
log = "0.4.21"
log4rs = "1.3.0"
rand = "0.8.5"
rusqlite = { version = "0.31.0", features = ["bundled"] }
serde = { version = "1.0.197", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
toml = "0.8.12"

View File

@ -18,3 +18,8 @@ This way attacker will be entertained for some time... :D
## Configure
Read config.toml and adapt it to your preferences. Keep in mind that for docker
use you want to keep log_file="CONSOLE".
## Action Script
You can configure action script by adding it to config.toml.
When a new suspicious IP address is detected, the script will be executed. See
action_script.sh for an example.

18
action_script.sh Executable file
View File

@ -0,0 +1,18 @@
#!/bin/sh
# Script to run when a new suspicious IP address is detected.
# Example for Linux:
# iptables -I INPUT -s $1 -j DROP
#
# Example for OpenBSD:
# Having a table in pf.conf:
# table <blacklist> persist file "/etc/blacklist"
# block drop in quick from <blacklist> to any
# then add/delete dinamically:
# pfctl -t badhosts -T add $1
# pfctl -t badhosts -T delete $1
# Example for "debugging":
echo $1 > /tmp/foo

View File

@ -14,3 +14,10 @@ level = "INFO"
[tarpit]
# delay in milliseconds
delay = 1500
[sqlite]
location = "tarpit.db"
[action]
# path to action script, remember give it execution permission
script = "./action_script.sh"

View File

@ -4,7 +4,9 @@ use serde::{Deserialize, Serialize};
struct ConfigToml {
listen: Option<ConfigTomlListen>,
log: Option<ConfigTomlLog>,
tarpit: Option<ConfigTomlTarpit>
tarpit: Option<ConfigTomlTarpit>,
sqlite: Option<ConfigTomlSqlite>,
action: Option<ConfigTomlAction>,
}
#[derive(Serialize, Deserialize, Debug)]
@ -24,6 +26,16 @@ struct ConfigTomlTarpit {
delay: Option<u64>,
}
#[derive(Serialize, Deserialize, Debug)]
struct ConfigTomlSqlite {
location: Option<String>,
}
#[derive(Serialize, Deserialize, Debug)]
struct ConfigTomlAction {
script: Option<String>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Config {
pub bind_addr: String,
@ -31,6 +43,8 @@ pub struct Config {
pub log_file: String,
pub log_level: String,
pub delay: u64,
pub database_location: String,
pub action_script: String
}
impl Config {
@ -57,7 +71,9 @@ impl Config {
ConfigToml {
listen: None,
log: None,
tarpit: None
tarpit: None,
sqlite: None,
action: None
}
});
@ -82,12 +98,24 @@ impl Config {
None => 0
};
let database_location = match config_toml.sqlite {
Some(ConfigTomlSqlite { location }) => location.unwrap_or("./tarpit.db".to_owned()),
None => "./tarpit.db".to_owned()
};
let action_script = match config_toml.action {
Some(ConfigTomlAction { script }) => script.unwrap_or("".to_owned()),
None => "".to_owned()
};
Config {
bind_addr,
bind_port,
log_file,
log_level,
delay
delay,
database_location,
action_script
}
}
}

72
src/database.rs Normal file
View File

@ -0,0 +1,72 @@
use rusqlite::{Connection, Result};
use std::time::SystemTime;
#[allow(dead_code)]
pub struct Suspicious {
ip_addr: String,
banned: u8,
create_time: u64,
}
pub fn init_db(path: &str) -> Result<Connection> {
let conn = Connection::open(path)?;
conn.execute(
"CREATE TABLE IF NOT EXISTS suspicious (
ip_addr VARCHAR(128) PRIMARY KEY,
banned INTEGER NOT NULL,
create_time INTEGER NOT NULL
)", [])?;
Ok(conn)
}
#[allow(dead_code)]
pub fn get_all_suspicious(conn: &Connection) -> Result<Vec<Suspicious>, rusqlite::Error> {
let mut stmt = conn.prepare("SELECT * FROM suspicious")?;
let rows = stmt.query_map([], |row| {
Ok(Suspicious {
ip_addr: row.get(0)?,
banned: row.get(1)?,
create_time: row.get(2)?,
})
})?;
Ok(rows.collect::<Result<Vec<Suspicious>, _>>()?)
}
pub fn get_suspicious_by_ip_addr(conn: &Connection, ip_addr: &str) -> Result<Suspicious, rusqlite::Error> {
let mut stmt = conn.prepare("SELECT * FROM suspicious WHERE ip_addr = ?")?;
let row = stmt.query_row([ip_addr], |row| {
Ok(Suspicious {
ip_addr: row.get(0)?,
banned: row.get(1)?,
create_time: row.get(2)?,
})
})?;
Ok(row)
}
pub fn add_suspicious(conn: &Connection, mut ip_addr: &str) -> Result<(), rusqlite::Error> {
//if ip_addr has ":" in it means it has port, we insert only IP addr
if ip_addr.contains(":") {
ip_addr = ip_addr.split(":").collect::<Vec<&str>>()[0];
}
let epoch = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs()
.to_string();
let mut stmt = conn.prepare("INSERT OR IGNORE INTO suspicious (ip_addr, banned, create_time) VALUES (?, ?, ?)")?;
stmt.execute([ip_addr, "0", epoch.as_str()])?;
Ok(())
}
#[allow(dead_code)]
pub fn delete_suspicious(conn: &Connection, ip_addr: &str) -> Result<(), rusqlite::Error> {
let mut stmt = conn.prepare("DELETE FROM suspicious WHERE ip_addr = ?")?;
stmt.execute([ip_addr])?;
Ok(())
}

View File

@ -5,12 +5,14 @@
*/
mod sip;
mod config;
mod database;
use crate::sip::generic::*;
use crate::sip::not_found::*;
use crate::sip::options::*;
use crate::sip::register::*;
use crate::sip::unauthorized::*;
use crate::database::*;
use rand::Rng;
use std::error::Error;
use std::net::SocketAddr;
@ -22,12 +24,15 @@ use log4rs::append::file::FileAppender;
use log4rs::append::console::ConsoleAppender;
use log4rs::config::{Appender, Config, Root};
use log4rs::encode::pattern::PatternEncoder;
use rusqlite::Connection;
use std::process::Command;
struct Server {
socket: UdpSocket,
buf: Vec<u8>,
to_send: Option<(usize, SocketAddr)>,
config: config::Config
config: config::Config,
db_con: Connection
}
impl Server {
@ -36,7 +41,8 @@ impl Server {
socket,
mut buf,
mut to_send,
config
config,
db_con
} = self;
loop {
@ -44,8 +50,33 @@ impl Server {
// If so then we try to send it back to the original source, waiting
// until it's writable and we're able to do so.
if let Some((size, peer)) = to_send {
log::info!("Suspicious peer: {}", peer);
//
// save peer
let str_peer_ip = peer.ip().to_string();
match get_suspicious_by_ip_addr(&db_con, str_peer_ip.as_str()) {
Ok(_) => {
// was added to database, do nothing
// log::info!("Peer already added to database");
},
Err(_e) => {
// was not in database
// log::info!("Error getting suspicious peer from database: {}", _e);
log::info!("Suspicious peer: {}", peer.ip());
match add_suspicious(&db_con, str_peer_ip.as_str()) {
Ok(_) => {
// launch action script
let output = Command::new(config.action_script.to_owned())
.arg(str_peer_ip.as_str())
.output();
match output {
Ok(_) => log::info!("Action script executed"),
Err(e) => log::info!("Error executing action script {}", e)
}
},
Err(_) => return Err(io::Error::new(io::ErrorKind::Other, "Error adding suspicious peer to database")),
}
}
}
// test type of message received
let msg = String::from_utf8(buf[..size].to_vec()).unwrap();
match msg {
@ -206,6 +237,8 @@ async fn main() -> Result<(), Box<dyn Error>> {
log4rs::init_config(logconfig).unwrap();
let db_con = init_db(config.database_location.as_str())?;
let addr = format!("{}:{}", config.bind_addr, config.bind_port);
let socket = UdpSocket::bind(&addr).await?;
log::info!("Listening on: {}", socket.local_addr()?);
@ -215,6 +248,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
buf: vec![0; 1024],
to_send: None,
config,
db_con
};
// This starts the server task.

View File

@ -19,6 +19,7 @@ pub struct Forbidden {
pub content_length: Option<i32>, // 0
}
#[allow(dead_code)]
impl Forbidden {
pub fn serialize(&self) -> Vec<u8> {
let mut preout = format!(