add on demand tls

This commit is contained in:
slonkazoid 2024-10-30 18:14:22 +03:00
parent 4d70986d2e
commit 3c4db952ec
Signed by: slonk
SSH key fingerprint: SHA256:tbZfJX4IOvZ0LGWOWu5Ijo8jfMPi78TU7x1VoEeCIjM
4 changed files with 384 additions and 45 deletions

212
Cargo.lock generated
View file

@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 4
[[package]] [[package]]
name = "addr2line" name = "addr2line"
@ -17,6 +17,15 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.77" version = "0.1.77"
@ -57,6 +66,7 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
"rustversion", "rustversion",
"serde", "serde",
"serde_urlencoded",
"sync_wrapper", "sync_wrapper",
"tokio", "tokio",
"tower", "tower",
@ -99,6 +109,27 @@ dependencies = [
"rustc-demangle", "rustc-demangle",
] ]
[[package]]
name = "bit-set"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
dependencies = [
"bit-vec",
]
[[package]]
name = "bit-vec"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]]
name = "bitflags"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.5.0" version = "1.5.0"
@ -117,18 +148,75 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "color-eyre"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5"
dependencies = [
"backtrace",
"color-spantrace",
"eyre",
"indenter",
"once_cell",
"owo-colors",
"tracing-error",
]
[[package]]
name = "color-spantrace"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2"
dependencies = [
"once_cell",
"owo-colors",
"tracing-core",
"tracing-error",
]
[[package]] [[package]]
name = "equivalent" name = "equivalent"
version = "1.0.1" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "eyre"
version = "0.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
dependencies = [
"indenter",
"once_cell",
]
[[package]]
name = "fancy-regex"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298"
dependencies = [
"bit-set",
"regex-automata 0.4.8",
"regex-syntax 0.8.5",
]
[[package]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.7" version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "form_urlencoded"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [
"percent-encoding",
]
[[package]] [[package]]
name = "futures-channel" name = "futures-channel"
version = "0.3.30" version = "0.3.30"
@ -287,6 +375,12 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "indenter"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.2.4" version = "2.2.4"
@ -321,6 +415,15 @@ version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "matchers"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
dependencies = [
"regex-automata 0.1.10",
]
[[package]] [[package]]
name = "matchit" name = "matchit"
version = "0.7.3" version = "0.7.3"
@ -400,6 +503,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "owo-colors"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.3.1" version = "2.3.1"
@ -411,11 +520,14 @@ name = "phone"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"axum", "axum",
"color-eyre",
"fancy-regex",
"serde", "serde",
"thiserror", "thiserror",
"tokio", "tokio",
"toml", "toml",
"tower", "tower",
"tower-http",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
] ]
@ -470,6 +582,50 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "regex"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata 0.4.8",
"regex-syntax 0.8.5",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
dependencies = [
"regex-syntax 0.6.29",
]
[[package]]
name = "regex-automata"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax 0.8.5",
]
[[package]]
name = "regex-syntax"
version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "regex-syntax"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.23" version = "0.1.23"
@ -482,6 +638,12 @@ version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
[[package]]
name = "ryu"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.197" version = "1.0.197"
@ -511,6 +673,18 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
"itoa",
"ryu",
"serde",
]
[[package]] [[package]]
name = "sharded-slab" name = "sharded-slab"
version = "0.1.7" version = "0.1.7"
@ -696,10 +870,26 @@ dependencies = [
] ]
[[package]] [[package]]
name = "tower-layer" name = "tower-http"
version = "0.3.2" version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" checksum = "8437150ab6bbc8c5f0f519e3d5ed4aa883a83dd4cdd3d1b21f9482936046cb97"
dependencies = [
"bitflags",
"bytes",
"http",
"http-body",
"pin-project-lite",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower-layer"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
[[package]] [[package]]
name = "tower-service" name = "tower-service"
@ -739,6 +929,16 @@ dependencies = [
"valuable", "valuable",
] ]
[[package]]
name = "tracing-error"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e"
dependencies = [
"tracing",
"tracing-subscriber",
]
[[package]] [[package]]
name = "tracing-log" name = "tracing-log"
version = "0.2.0" version = "0.2.0"
@ -756,10 +956,14 @@ version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
dependencies = [ dependencies = [
"matchers",
"nu-ansi-term", "nu-ansi-term",
"once_cell",
"regex",
"sharded-slab", "sharded-slab",
"smallvec", "smallvec",
"thread_local", "thread_local",
"tracing",
"tracing-core", "tracing-core",
"tracing-log", "tracing-log",
] ]

View file

@ -11,7 +11,10 @@ lto = "fat"
axum = { version = "0.7.2", default-features = false, features = [ axum = { version = "0.7.2", default-features = false, features = [
"http1", "http1",
"tokio", "tokio",
"query",
] } ] }
color-eyre = "0.6.3"
fancy-regex = "0.14.0"
serde = { version = "1.0.195", features = ["derive"] } serde = { version = "1.0.195", features = ["derive"] }
thiserror = "1.0.51" thiserror = "1.0.51"
tokio = { version = "1.35.1", features = [ tokio = { version = "1.35.1", features = [
@ -22,5 +25,6 @@ tokio = { version = "1.35.1", features = [
] } ] }
toml = "0.8.8" toml = "0.8.8"
tower = { version = "0.4.13", default-features = false, features = ["limit"] } tower = { version = "0.4.13", default-features = false, features = ["limit"] }
tower-http = { version = "0.6.1", features = ["trace"] }
tracing = "0.1.40" tracing = "0.1.40"
tracing-subscriber = "0.3.18" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }

View file

@ -1,12 +1,21 @@
use std::{ use std::collections::HashMap;
collections::HashMap, use std::env;
error::Error, use std::net::{IpAddr, Ipv4Addr};
net::{IpAddr, Ipv4Addr}, use std::path::PathBuf;
path::{Path, PathBuf},
};
use color_eyre::eyre::{self, bail, Context};
use fancy_regex::Regex;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio::io::AsyncReadExt; use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tracing::{error, info, instrument};
#[derive(Deserialize, Serialize, Debug)]
#[repr(transparent)]
pub struct DomainMatcher(#[serde(with = "regex")] Regex);
pub fn cast_matcher(v: &[DomainMatcher]) -> &[Regex] {
unsafe { std::mem::transmute(v) }
}
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(default)] #[serde(default)]
@ -15,6 +24,7 @@ pub struct Config {
pub port: u16, pub port: u16,
pub concurrency: usize, pub concurrency: usize,
pub repos: HashMap<String, RepoConfig>, pub repos: HashMap<String, RepoConfig>,
pub domains: Vec<DomainMatcher>,
} }
impl Default for Config { impl Default for Config {
@ -24,6 +34,7 @@ impl Default for Config {
port: 5000, port: 5000,
concurrency: 8, concurrency: 8,
repos: HashMap::new(), repos: HashMap::new(),
domains: Vec::new(),
} }
} }
} }
@ -46,15 +57,87 @@ pub struct SecretRule {
pub value: String, pub value: String,
} }
pub async fn load(file: impl AsRef<Path>) -> Result<Config, Box<dyn Error>> { #[instrument(name = "config")]
let mut buf = String::new(); pub async fn load() -> eyre::Result<Config> {
let config_file = env::var(format!(
tokio::fs::OpenOptions::new() "{}_CONFIG",
env!("CARGO_BIN_NAME").to_uppercase().replace('-', "_")
))
.unwrap_or(String::from("config.toml"));
match tokio::fs::OpenOptions::new()
.read(true) .read(true)
.open(&file) .open(&config_file)
.await? .await
.read_to_string(&mut buf) {
.await?; Ok(mut file) => {
let mut buf = String::new();
Ok(toml::from_str(&buf)?) file.read_to_string(&mut buf)
.await
.context("couldn't read configuration file")?;
toml::from_str(&buf).context("couldn't parse configuration")
}
Err(err) => match err.kind() {
std::io::ErrorKind::NotFound => {
let config = Config::default();
info!("configuration file doesn't exist, creating");
match tokio::fs::OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&config_file)
.await
{
Ok(mut file) => file
.write_all(
toml::to_string_pretty(&config)
.context("couldn't serialize configuration")?
.as_bytes(),
)
.await
.unwrap_or_else(|err| error!("couldn't write configuration: {}", err)),
Err(err) => {
error!("couldn't open file {:?} for writing: {}", &config_file, err)
}
}
Ok(config)
}
_ => bail!("couldn't open config file: {}", err),
},
}
}
mod regex {
use fancy_regex::Regex;
use serde::{de::Visitor, Deserializer, Serialize, Serializer};
pub fn deserialize<'de, D>(d: D) -> Result<Regex, D::Error>
where
D: Deserializer<'de>,
{
struct RegexVisitor;
impl<'v> Visitor<'v> for RegexVisitor {
type Value = Regex;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "a regex string")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
let regex = Regex::new(v);
regex.map_err(|err| serde::de::Error::custom(err))
}
}
d.deserialize_string(RegexVisitor)
}
pub fn serialize<S>(value: &Regex, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
value.as_str().serialize(serializer)
}
} }

View file

@ -1,22 +1,27 @@
#![feature(let_chains, fs_try_exists, io_error_more)] #![feature(let_chains, io_error_more)]
mod config; mod config;
mod update_repo; mod update_repo;
use std::{env, sync::Arc}; use std::sync::Arc;
use std::time::Duration;
use axum::{ use axum::extract::{Path, Query, State};
extract::{Path, State}, use axum::http::{HeaderMap, Request, StatusCode};
http::{HeaderMap, StatusCode}, use axum::response::{IntoResponse, Response};
response::{IntoResponse, Response}, use axum::routing::{get, post};
routing::post, use axum::Router;
Router, use color_eyre::eyre::{self, Context};
}; use config::{cast_matcher, Config, SecretRule};
use config::{Config, SecretRule};
use thiserror::Error; use thiserror::Error;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use tower::limit::ConcurrencyLimitLayer; use tower::limit::ConcurrencyLimitLayer;
use tracing::error; use tower_http::trace::TraceLayer;
use tracing::level_filters::LevelFilter;
use tracing::{error, info, info_span, Span};
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::EnvFilter;
use update_repo::update_repo; use update_repo::update_repo;
use crate::config::RepoConfig; use crate::config::RepoConfig;
@ -63,14 +68,37 @@ async fn update_repo_handler(
} }
} }
#[tokio::main] #[derive(serde::Deserialize)]
async fn main() { struct AskQuery {
tracing_subscriber::fmt::init(); domain: String,
}
let mut config: Config = async fn ask(
config::load(env::var("PHONE_CONFIG").unwrap_or(String::from("config.toml"))) State(config): State<Arc<Config>>,
.await Query(AskQuery { domain }): Query<AskQuery>,
.unwrap_or_else(|err| panic!("failed to load config: {}", err)); ) -> StatusCode {
for matcher in cast_matcher(&config.domains) {
if matcher.is_match(&domain).is_ok_and(|x| x) {
return StatusCode::OK;
}
}
StatusCode::NOT_FOUND
}
#[tokio::main]
async fn main() -> eyre::Result<()> {
color_eyre::install()?;
tracing_subscriber::registry()
.with(
EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into())
.from_env_lossy(),
)
.with(tracing_subscriber::fmt::layer())
.init();
let mut config: Config = config::load().await.context("failed to load config")?;
config.repos = config config.repos = config
.repos .repos
@ -98,12 +126,32 @@ async fn main() {
let app = Router::new() let app = Router::new()
.route("/update_repo/:repo", post(update_repo_handler)) .route("/update_repo/:repo", post(update_repo_handler))
.layer(ConcurrencyLimitLayer::new(8)) .layer(ConcurrencyLimitLayer::new(8))
.with_state(Arc::clone(&config)); .route("/ask", get(ask))
.with_state(Arc::clone(&config))
.layer(
TraceLayer::new_for_http()
.make_span_with(|request: &Request<_>| {
info_span!(
"request",
method = ?request.method(),
path = ?request.uri().path(),
)
})
.on_response(|response: &Response<_>, duration: Duration, span: &Span| {
let _ = span.enter();
let status = response.status();
info!(?status, ?duration, "response");
}),
);
let address = (config.host, config.port); let address = (config.host, config.port);
let listener = TcpListener::bind(address).await.unwrap(); let listener = TcpListener::bind(address).await.context("failed to bind")?;
let local_addr = listener.local_addr().unwrap(); let local_addr = listener.local_addr().context("failed to get local addr")?;
tracing::info!("listening on http://{}", local_addr); info!("listening on http://{}", local_addr);
axum::serve(listener, app).await.unwrap(); axum::serve(listener, app)
.await
.context("failed to serve app")?;
Ok(())
} }