TrustTunnel/tools/setup_wizard/library_settings.rs
Andrey Yakushin 783908b315 Pull request 121: Clippy and github actions
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
2025-12-26 12:45:09 +03:00

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,
}
}