diff --git a/Cargo.lock b/Cargo.lock index 99d4e80..2656666 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,6 +70,17 @@ dependencies = [ "tower-service", ] +[[package]] +name = "axum-client-ip" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d719fabd6813392bbc10e1fe67f2977fad52791a836e51236f7e02f2482e017" +dependencies = [ + "axum", + "forwarded-header-value", + "serde", +] + [[package]] name = "axum-core" version = "0.3.2" @@ -170,6 +181,43 @@ dependencies = [ "phf_codegen", ] +[[package]] +name = "clap" +version = "4.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0b0588d44d4d63a87dbd75c136c166bbfd9a86a31cb89e09906521c7d3f5e3" +dependencies = [ + "bitflags", + "clap_derive", + "clap_lex", + "is-terminal", + "once_cell", + "strsim", + "termcolor", +] + +[[package]] +name = "clap_derive" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -283,6 +331,27 @@ dependencies = [ "syn", ] +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "fnv" version = "1.0.7" @@ -298,6 +367,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "forwarded-header-value" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9" +dependencies = [ + "nonempty", + "thiserror", +] + [[package]] name = "futures-channel" version = "0.3.25" @@ -383,6 +462,12 @@ dependencies = [ "walkdir", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "heck" version = "0.4.1" @@ -398,6 +483,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "hostname" version = "0.3.1" @@ -540,6 +631,26 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" +dependencies = [ + "libc", + "windows-sys 0.45.0", +] + [[package]] name = "ipconfig" version = "0.3.1" @@ -558,6 +669,18 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +[[package]] +name = "is-terminal" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.45.0", +] + [[package]] name = "itoa" version = "1.0.5" @@ -600,6 +723,12 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "lock_api" version = "0.4.9" @@ -677,9 +806,24 @@ dependencies = [ "libc", "log", "wasi", - "windows-sys", + "windows-sys 0.42.0", ] +[[package]] +name = "nom8" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" +dependencies = [ + "memchr", +] + +[[package]] +name = "nonempty" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" + [[package]] name = "num-integer" version = "0.1.45" @@ -705,7 +849,7 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "libc", ] @@ -715,6 +859,12 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +[[package]] +name = "os_str_bytes" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" + [[package]] name = "parking_lot" version = "0.12.1" @@ -735,7 +885,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -874,6 +1024,30 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.50" @@ -969,14 +1143,30 @@ name = "rust-web-thingy" version = "0.1.0" dependencies = [ "axum", + "axum-client-ip", + "clap", "maxminddb", - "rand", "serde", "tera", "tokio", + "toml", "trust-dns-resolver", ] +[[package]] +name = "rustix" +version = "0.36.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.45.0", +] + [[package]] name = "rustversion" version = "1.0.11" @@ -1050,6 +1240,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1122,6 +1321,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "syn" version = "1.0.107" @@ -1231,7 +1436,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -1245,6 +1450,40 @@ dependencies = [ "syn", ] +[[package]] +name = "toml" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7afcae9e3f0fe2c370fd4657108972cbb2fa9db1b9f84849cefd80741b01cb6" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6a7712b49e1775fb9a7b998de6635b299237f48b404dde71704f2e0e7f37e5" +dependencies = [ + "indexmap", + "nom8", + "serde", + "serde_spanned", + "toml_datetime", +] + [[package]] name = "tower" version = "0.4.13" @@ -1624,6 +1863,30 @@ dependencies = [ "windows_x86_64_msvc", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.1" diff --git a/Cargo.toml b/Cargo.toml index a5cc548..31eaf65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,14 +2,17 @@ name = "rust-web-thingy" version = "0.1.0" edition = "2021" +authors = ["Slatian "] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] axum = "0.6" -rand = "0.8" +axum-client-ip = "0.4" +clap = { version = "4", features = ["derive"] } serde = { version = "1", features = ["derive"] } tokio = { version = "1", features = ["full"] } tera = "1" +toml = "0.7" trust-dns-resolver = "0.22" maxminddb = "0.17" diff --git a/README.md b/README.md new file mode 100644 index 0000000..2175f49 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# echoip-slatecave + +## Building + +Simply run `cargo build` after cloning. + +NOTE: As of 2023-02-18 You need at least version 1.65 of the rust compiler. Consider using rustup. diff --git a/echoip_config.toml b/echoip_config.toml new file mode 100644 index 0000000..2d49b0c --- /dev/null +++ b/echoip_config.toml @@ -0,0 +1,42 @@ +[server] +# What port to listen on and where request are supposed to come from +#listen_on: "127.0.0.1:3000" + +# What header your reverse proxy sets that contains the real ip-address +# Possible Values: Every Vaiation of SecureClientIpSource in the axum_client_ip package +# https://docs.rs/axum-client-ip/latest/axum_client_ip/enum.SecureClientIpSource.html +#ip_header = "RightmostXForwardedFor" +# When you don't want to use a proxy server: +#ip_header = "ConnectInfo" + +[dns] +# Enable the /dig enpoint +#allow_forward_lookup = true + +# Enable reverse lookup, make sure to configure the hidden_suffixes +# to contain your locally used domains, to prevent onformation leakage +#allow_reverse_lookup = false + +# Hide anything that has to do with private ip ranges +# Useful dor public services, disable if you want it +# on your internal network for some reason +#hide_private_range_ips = true + +# echoip-sltecave will pretend that domains +# that end with one of these suffixes don't exist +#hidden_suffixes = [".local"] + +[geoip] +# Path to geoip databses +# Currently only the mmdb format is supported +# Official databases can be obtained from maxmind.com +#asn_database = "mmdb/GeoLite2-ASN.mmdb" +#location_database = "mmdb/GeoLite2-City.mmdb" + +# If anyone knows a free (as in freedom) groip database +# please open an issue so I can integrate it +# https://codeberg.org/slatian/service.echoip-slatecave + +[template] +# Path to the template directory, can contain glob patterns +#template_location = "templates" diff --git a/src/config.rs b/src/config.rs index 6d238cc..8419049 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,45 +1,44 @@ -use std::path::Path; use axum_client_ip::SecureClientIpSource; use std::net::SocketAddr; -#[derive(serde::Deserialize)] +#[derive(serde::Deserialize, Default)] pub struct EchoIpServiceConfig { - server: ServerConfig, - dns: DnsConfig, - geoip: GeoIpConfig, - template: TemplateConfig, + pub server: ServerConfig, + pub dns: DnsConfig, + pub geoip: GeoIpConfig, + pub template: TemplateConfig, } #[derive(serde::Deserialize)] pub struct ServerConfig { - listen_on: SocketAddr, - ip_header: SecureClientIpSource, + pub listen_on: SocketAddr, + pub ip_header: SecureClientIpSource, } #[derive(serde::Deserialize)] pub struct DnsConfig { - allow_forward_lookup: bool, - allow_reverse_lookup: bool, - hide_private_range_ips: bool, - hidden_suffixes: Vec, + pub allow_forward_lookup: bool, + pub allow_reverse_lookup: bool, + pub hide_private_range_ips: bool, + pub hidden_suffixes: Vec, //Future Idea: allow custom resolver } #[derive(serde::Deserialize)] pub struct GeoIpConfig { - asn_database: Option, - location_database: Option, + pub asn_database: Option, + pub location_database: Option, } #[derive(serde::Deserialize)] pub struct TemplateConfig { - template_location: String, + pub template_location: String, } impl Default for ServerConfig { fn default() -> Self { ServerConfig { - listen_on: "127.0.0.1:3000", + listen_on: "127.0.0.1:3000".parse().unwrap(), ip_header: SecureClientIpSource::RightmostXForwardedFor, } } @@ -48,10 +47,27 @@ impl Default for ServerConfig { impl Default for DnsConfig { fn default() -> Self { DnsConfig { - allow_forward_lookup: true; - allow_reverse_lookup: false; - hide_private_range_ips: true; - hidden_suffixes: Vec::new(); + allow_forward_lookup: true, + allow_reverse_lookup: false, + hide_private_range_ips: true, + hidden_suffixes: Vec::new(), + } + } +} + +impl Default for GeoIpConfig { + fn default() -> Self { + GeoIpConfig { + asn_database: Some("mmdb/GeoLite2-ASN.mmdb".to_string()), + location_database: Some("mmdb/GeoLite2-City.mmdb".to_string()), + } + } +} + +impl Default for TemplateConfig { + fn default() -> Self { + TemplateConfig { + template_location: "templates/".to_string(), } } } diff --git a/src/main.rs b/src/main.rs index fce82ef..51aaf7a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ use axum::{ routing::get, }; use axum_client_ip::{SecureClientIp, SecureClientIpSource}; - +use clap::Parser; use tera::Tera; use trust_dns_resolver::{ TokioAsyncResolver, @@ -15,6 +15,7 @@ use trust_dns_resolver::{ config::ResolverConfig, }; +use std::fs; use std::net::IpAddr; use std::sync::Arc; use std::path::Path; @@ -64,15 +65,50 @@ struct ServiceSharedState { dns_resolver: TokioAsyncResolver, asn_db: geoip::MMDBCarrier, location_db: geoip::MMDBCarrier, + config: config::EchoIpServiceConfig, } +#[derive(Parser)] +#[command(author, version, long_about="None")] +struct CliArgs { + #[arg(short, long)] + config: Option, + #[arg(short, long)] + listen_on: Option, + #[arg(short, long)] + templates: Option, +} #[tokio::main] async fn main() { + // Parse Command line arguments + let cli_args = CliArgs::parse(); + + // Read configuration file + let config: config::EchoIpServiceConfig = match cli_args.config { + Some(config_path) => { + let config_text = fs::read_to_string(config_path) + .expect("Can't read configuration file!"); + match toml::from_str(&config_text) { + Ok(c) => c, + Err(e) => { + panic!("Unable to parse configuration file: {e}"); + } + } + }, + None => Default::default(), + }; + + + // Initalize Tera templates - // TODO: don't hardcode template directory - println!("Parsing Templates ..."); - let res = Tera::new("templates/*.html"); + let mut template_base_dir = (&config.template.template_location).to_owned(); + if !template_base_dir.ends_with("/") { + template_base_dir = template_base_dir + "/"; + } + let template_glob = template_base_dir+"*.html"; + println!("Parsing Templates from '{}' ...", &template_glob); + let res = Tera::new((template_glob).as_str()); let tera = match res { Ok(t) => t, Err(e) => { @@ -87,13 +123,19 @@ async fn main() { mmdb: None, name: "GeoIP ASN Database".to_string(), }; - asn_db.load_database_from_path(Path::new("mmdb/GeoLite2-ASN.mmdb")).ok(); + match &config.geoip.asn_database { + Some(path) => { asn_db.load_database_from_path(Path::new(&path)).ok(); }, + None => {}, + } let mut location_db = geoip::MMDBCarrier { mmdb: None, name: "GeoIP Location Database".to_string(), }; - location_db.load_database_from_path(Path::new("mmdb/GeoLite2-City.mmdb")).ok(); + match &config.geoip.location_database { + Some(path) => { location_db.load_database_from_path(Path::new(&path)).ok(); }, + None => {}, + } // Initalize DNS resolver with os defaults println!("Initalizing dns resolver ..."); @@ -105,16 +147,19 @@ async fn main() { ::std::process::exit(1); } }; + + let listen_on = config.server.listen_on; // Initialize shared state let shared_state = Arc::new( - ServiceSharedState{ + ServiceSharedState { templating_engine: templating_engine::Engine{ tera: tera, }, dns_resolver: dns_resolver, asn_db: asn_db, location_db: location_db, + config: config, }); // Initalize axum server @@ -131,7 +176,7 @@ async fn main() { println!("Starting Server ..."); - axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) + axum::Server::bind(&listen_on) .serve(app.into_make_service()) .await .unwrap(); diff --git a/templates/dig.html b/templates/dig.html index d599ae9..b731446 100644 --- a/templates/dig.html +++ b/templates/dig.html @@ -8,30 +8,36 @@

Lookup for: {{ q.name }}

{% if r.a %} -

A (IPv4) records:

+

A (IPv4) records:

    {% for address in r.a%} -
  • {{address}}
  • +
  • {{address}}
  • {% endfor %}
+ {% else %} +

No A (IPv4) Records.

{% endif %} {% if r.aaaa %} -

AAAA (IPv6) records:

+

AAAA (IPv6) records:

    {% for address in r.aaaa%} -
  • {{address}}
  • +
  • {{address}}
  • {% endfor %}
+ {% else %} +

No AAAA (IPv6) Records.

{% endif %} {% if r.mx %} -

MX (Mail Exchange) records:

+

MX (Mail Exchange) records:

+ {% else %} +

No MX (Mail Exchange) records.

{% endif %} diff --git a/templates/ip.html b/templates/ip.html new file mode 100644 index 0000000..89dcae1 --- /dev/null +++ b/templates/ip.html @@ -0,0 +1,57 @@ +{% import "helpers.html" as helper %} + + + + {{ data.query.ip }} + + + {% set r = data.result %} +

About IP-Address: {{ data.query.ip }}

+
+

Network Information

+
+ {% if r.hostname %} + Hostname +
{{r.hostname}}
+ {% endif %} + {% if r.asn %} + ASN +
AS{{r.asn.asn}}
+ AS Name +
{{ r.asn.name }}
+
+ {% endif %} +
+ {% if r.location %} +
+

Geolocation

+
+ {{ helper::place_dl(place=r.location.continent, label="Continent") }} + {{ helper::place_dl(place=r.location.country, label="Country") }} + {{ helper::place_dl(place=r.location.registered_country, label="Registred in") }} + {{ helper::place_dl(place=r.location.represented_country, label="Represents") }} + {% if r.location.subdivisions %} + {% for sd in r.location.subdivisions %} + {{ helper::place_dl(place=sd, label="Subdivision", iso_code_prefix=r.location.country.iso_code|default(value="")) }} + {% endfor %} + {% endif %} + {{ helper::place_dl(place=r.location.city, label="City") }} + {% if r.location.postal_code %} + Postal Code +
{{r.location.postal_code}}
+ {% endif %} + {% if r.location.time_zone %} + Timezone +
{{r.location.time_zone}}
+ {% endif %} +
+ +

+ The GeopIP and ASN information is provided by the GeoLite2 database created by + MaxMind. +

+
+ {% endif %} + + +