mirror of
https://github.com/TrustTunnel/TrustTunnel.git
synced 2026-07-09 17:18:36 +00:00
Squashed commit of the following:
commit 6eae1e962a27b2c3bcb6362f53bb1d7d92a66983
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Fri Dec 26 11:46:12 2025 +0400
Run lint on both macos and linux
commit 94254caec3ea166db80c6b3f4004b4126605a1b7
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 22:44:10 2025 +0400
Fix note again by adding lint hint
commit 5a67ae358a5676a22e85798683674607d2788a51
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 22:42:02 2025 +0400
Fix note
commit 937b178302244fe237d06b6f38ba0f29db6e0d7e
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 22:39:45 2025 +0400
Fix README
commit 769c5d9ebdc03e8500f9fc00d7f2b6f316924557
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 22:39:15 2025 +0400
Cargo update
commit 1e932e4037c2b9ffc4b12f398f1ef14c32b5481e
Merge: dcf6a53 2041edc
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 22:37:24 2025 +0400
Merge remote-tracking branch 'origin/master' into feature/TRUST-235
commit dcf6a53410e59411a3e05f798ed4be7f7c9994ce
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 22:24:56 2025 +0400
Get rid of rustls-pemfile and update sentry
commit cb2e26e47d4612d65ae990ec887875bb1ac94456
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 19:08:23 2025 +0400
Fix tests
commit a3cde3fdf16edfe2e2a574b8d729c2b9d59daf84
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 18:33:46 2025 +0400
Fix vulnerabilities
commit 35cb9c699a0ddf2eb344c7c475be3c36a26dbf83
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 17:13:18 2025 +0400
Don't install cargo-audit manually
commit 71a5411ac4fe31fc08c3bacb83d327bf6b7ab8c3
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 17:07:30 2025 +0400
Install stable rust for cargo-audit
commit b7f38a90054cda39d72760b0ebc3ce295fba95d2
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 16:54:13 2025 +0400
Fix yaml
commit fbbe78f68b2987280874f23d4ed05ef75ed42f46
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 16:53:15 2025 +0400
Try to lock cargo-audit version
commit 08f31734b49c70d9dc03c7977ac6182198d1cbde
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 16:46:06 2025 +0400
Update audit workflow
commit c202f186cd1610439a13928fc1fabac88e83097b
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 16:30:55 2025 +0400
Install rust tools and better rust cache
commit eccf2fa91efcc4c6e5684960e368892bc68e67cd
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 16:30:25 2025 +0400
Name for job
commit dccc19f13180e767b8390c8ea32fde4285c0cab8
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 16:30:17 2025 +0400
Update checkout step version
commit edbb4404bf6fc1927f0184433df9982767a9c762
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 16:30:02 2025 +0400
Run lint only on linux
commit b59ed893fa55edf030f9ffee2e442c8b947fa28f
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 16:09:43 2025 +0400
Lint in the same workflow as testing to avoid rebuilds
commit 8d8ecd51859c825d0437361f8c51bde6b46994bc
Author: Andrey Yakushin <a.yakushin@adguard.com>
Date: Thu Dec 25 15:27:23 2025 +0400
More clippy fixes
... and 6 more commits
199 lines
6.9 KiB
Rust
199 lines
6.9 KiB
Rust
use crate::user_interaction::{
|
|
ask_for_agreement, ask_for_input, ask_for_password, checked_overwrite, select_variant,
|
|
};
|
|
use crate::Mode;
|
|
use std::fs;
|
|
use toml_edit::{ArrayOfTables, Item, Key, Table};
|
|
use trusttunnel::settings::{
|
|
Http1Settings, Http2Settings, ListenProtocolSettings, QuicSettings, Settings,
|
|
};
|
|
|
|
pub const DEFAULT_CREDENTIALS_PATH: &str = "credentials.toml";
|
|
pub const DEFAULT_RULES_PATH: &str = "rules.toml";
|
|
|
|
pub struct Built {
|
|
pub settings: Settings,
|
|
pub credentials_path: String,
|
|
pub rules_path: String,
|
|
}
|
|
|
|
pub fn build() -> Built {
|
|
let builder = Settings::builder()
|
|
.listen_address(
|
|
crate::get_predefined_params()
|
|
.listen_address
|
|
.clone()
|
|
.unwrap_or_else(|| {
|
|
ask_for_input(
|
|
&format!(
|
|
"{} (use 0.0.0.0:443 for all interfaces on HTTPS port)",
|
|
Settings::doc_listen_address()
|
|
),
|
|
Some(Settings::default_listen_address().to_string()),
|
|
)
|
|
}),
|
|
)
|
|
.unwrap();
|
|
|
|
Built {
|
|
settings: builder
|
|
.listen_protocols(ListenProtocolSettings {
|
|
http1: Some(Http1Settings::builder().build()),
|
|
http2: Some(Http2Settings::builder().build()),
|
|
quic: Some(QuicSettings::builder().build()),
|
|
})
|
|
.build()
|
|
.expect("Couldn't build the library settings"),
|
|
credentials_path: build_authenticator(),
|
|
rules_path: build_rules(),
|
|
}
|
|
}
|
|
|
|
fn build_authenticator() -> String {
|
|
if crate::get_mode() != Mode::NonInteractive
|
|
&& check_file_exists(".", DEFAULT_CREDENTIALS_PATH)
|
|
&& ask_for_agreement(&format!(
|
|
"Reuse the existing credentials file: {DEFAULT_CREDENTIALS_PATH}?"
|
|
))
|
|
{
|
|
DEFAULT_CREDENTIALS_PATH.into()
|
|
} else {
|
|
let path = ask_for_input::<String>(
|
|
"Path to the credentials file",
|
|
Some(DEFAULT_CREDENTIALS_PATH.into()),
|
|
);
|
|
|
|
if checked_overwrite(&path, "Overwrite the existing credentials file?") {
|
|
println!("Let's create user credentials");
|
|
let users = build_user_list();
|
|
fs::write(&path, compose_credentials_content(users.into_iter()))
|
|
.expect("Couldn't write the credentials into a file");
|
|
println!("The user credentials are written to the file: {}", path);
|
|
}
|
|
|
|
path
|
|
}
|
|
}
|
|
|
|
fn build_rules() -> String {
|
|
if crate::get_mode() != Mode::NonInteractive
|
|
&& check_file_exists(".", DEFAULT_RULES_PATH)
|
|
&& ask_for_agreement(&format!(
|
|
"Reuse the existing rules file: {DEFAULT_RULES_PATH}?"
|
|
))
|
|
{
|
|
DEFAULT_RULES_PATH.into()
|
|
} else {
|
|
let path =
|
|
ask_for_input::<String>("Path to the rules file", Some(DEFAULT_RULES_PATH.into()));
|
|
|
|
if checked_overwrite(&path, "Overwrite the existing rules file?") {
|
|
println!("Let's create connection filtering rules");
|
|
let rules_config = crate::rules_settings::build();
|
|
let rules_content = generate_rules_toml_content(&rules_config);
|
|
fs::write(&path, rules_content).expect("Couldn't write the rules into a file");
|
|
println!("The rules configuration is written to the file: {}", path);
|
|
}
|
|
|
|
path
|
|
}
|
|
}
|
|
|
|
fn build_user_list() -> Vec<(String, String)> {
|
|
if let Some(x) = crate::get_predefined_params().credentials.clone() {
|
|
return vec![x];
|
|
}
|
|
|
|
let mut list = vec![(
|
|
ask_for_input::<String>("Username", None),
|
|
ask_for_password("Password"),
|
|
)];
|
|
|
|
loop {
|
|
if "no" == select_variant("Add one more user?", &["yes", "no"], Some(1)) {
|
|
break;
|
|
}
|
|
|
|
list.push((
|
|
ask_for_input::<String>("Username", None),
|
|
ask_for_password("Password"),
|
|
));
|
|
}
|
|
|
|
list
|
|
}
|
|
|
|
fn compose_credentials_content(clients: impl Iterator<Item = (String, String)>) -> String {
|
|
let mut doc = toml_edit::Document::new();
|
|
|
|
let x = clients
|
|
.map(|(u, p)| {
|
|
Table::from_iter(
|
|
std::iter::once(("username", u)).chain(std::iter::once(("password", p))),
|
|
)
|
|
})
|
|
.collect::<ArrayOfTables>();
|
|
|
|
doc.insert_formatted(&Key::new("client"), Item::ArrayOfTables(x));
|
|
|
|
doc.to_string()
|
|
}
|
|
|
|
fn generate_rules_toml_content(rules_config: &trusttunnel::rules::RulesConfig) -> String {
|
|
let mut content = String::new();
|
|
|
|
// Add header comments explaining the format
|
|
content.push_str("# Rules configuration for VPN endpoint connection filtering\n");
|
|
content.push_str("# \n");
|
|
content.push_str("# This file defines filter rules for incoming connections.\n");
|
|
content.push_str(
|
|
"# Rules are evaluated in order, and the first matching rule's action is applied.\n",
|
|
);
|
|
content.push_str("# If no rules match, the connection is allowed by default.\n");
|
|
content.push_str("#\n");
|
|
content.push_str("# Each rule can specify:\n");
|
|
content.push_str("# - cidr: IP address range in CIDR notation\n");
|
|
content.push_str("# - client_random_prefix: Hex-encoded prefix of TLS client random data\n");
|
|
content.push_str(
|
|
"# Can optionally include a mask in format \"prefix[/mask]\" for bitwise matching\n",
|
|
);
|
|
content.push_str("# - action: \"allow\" or \"deny\"\n");
|
|
content.push_str("#\n");
|
|
content.push_str("# All fields except 'action' are optional - if specified, all conditions must match for the rule to apply.\n");
|
|
content.push_str("#\n");
|
|
content.push_str("# client_random_prefix formats:\n");
|
|
content.push_str("# 1. Simple prefix matching:\n");
|
|
content.push_str("# client_random_prefix = \"aabbcc\"\n");
|
|
content.push_str("# → matches client_random starting with 0xaabbcc\n");
|
|
content.push_str("#\n");
|
|
content.push_str("# 2. Bitwise matching with mask:\n");
|
|
content.push_str("# client_random_prefix = \"a0b0/f0f0\"\n");
|
|
content.push_str("# → prefix=a0b0, mask=f0f0\n");
|
|
content.push_str(
|
|
"# → matches client_random where (client_random & 0xf0f0) == (0xa0b0 & 0xf0f0)\n",
|
|
);
|
|
content.push_str("# → e.g., 0xa5b5, 0xa9bf match, but 0xb0b0, 0xa0c0 don't match\n\n");
|
|
|
|
// Serialize the actual rules (usually empty)
|
|
if !rules_config.rule.is_empty() {
|
|
content.push_str(&toml::ser::to_string(rules_config).unwrap());
|
|
content.push('\n');
|
|
}
|
|
|
|
content
|
|
}
|
|
|
|
fn check_file_exists(path: &str, name: &str) -> bool {
|
|
match fs::read_dir(path) {
|
|
Ok(x) => x
|
|
.filter_map(Result::ok)
|
|
.filter(|entry| {
|
|
entry
|
|
.metadata()
|
|
.map(|meta| meta.is_file())
|
|
.unwrap_or_default()
|
|
})
|
|
.any(|entry| Ok(name) == entry.file_name().into_string().as_ref().map(String::as_str)),
|
|
Err(_) => false,
|
|
}
|
|
}
|