mirror of
https://github.com/therealaleph/MasterHttpRelayVPN-RUST.git
synced 2026-05-19 08:04:39 +03:00
v0.6.1: fix OpenWRT CA install + replace --user-less CI + perf pack artifacts
v0.6.0's release CI was cancelled before it could produce artifacts. This is a clean re-cut that also fixes a user-reported bug on OpenWRT. === OpenWRT CA install fix === User on issue #2 reported that --install-cert fails on an OpenWRT router with 'install failed on this platform'. Two problems: 1. Misclassification. The old distro detector did a substring-match over all of /etc/os-release, and OpenWRT's file contains lines like OPENWRT_DEVICE_ARCH=x86_64 and OPENWRT_ARCH=x86_64 — which contain the substring 'arch' — so we classified OpenWRT as Arch Linux and tried to install into /etc/ca-certificates/trust-source/ anchors/ (which doesn't exist there) and then run 'trust' (also missing). Predictable failure. 2. Even with correct classification, OpenWRT doesn't need the CA on the router itself. LAN clients are the ones terminating TLS through mhrv-rs's MITM; they're the ones that need to trust our root. The router is just forwarding packets. So emitting an error for the no-op case is misleading. Fixes: - Detect OpenWRT explicitly (/etc/openwrt_release marker file + ID=openwrt in os-release). - Rewrite the fallback os-release parser to look at ID / ID_LIKE token-wise instead of substring-matching the whole file. Added support for raspbian / rocky / almalinux / endeavouros while we're there. - For OpenWRT: install_linux returns Ok() with a clear message explaining that the CA needs to be installed on LAN clients, not on the router. No-op success instead of confusing error. - For unknown distros: the error message now points at the CA file path and the two most common anchor dirs so the user can install manually. - Extracted classify_os_release(&str) as a pure function and added 8 unit tests, including a regression guard with a real OpenWRT 23.05 os-release file so this specific substring-match bug can't return. === v0.6.0 perf pack (same as what cancelled CI was meant to ship) === - Connection pool pre-warm on startup (skip handshake on first request) - Per-connection SNI rotation across known Google-edge subdomains - Expanded SNI-rewrite suffix list (gstatic, googleusercontent, googleapis, ggpht, ytimg, blogspot, blogger) - Per-site stats tracker + UI drill-down table - Optional parallel script-ID dispatch (config field parallel_relay) - TCP_NODELAY audit + fix on SNI-rewrite outbound All 36 unit tests pass. Closes-via-fix #2 follow-up.
This commit is contained in:
Generated
+1
-1
@@ -1317,7 +1317,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "mhrv-rs"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mhrv-rs"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
edition = "2021"
|
||||
description = "Rust port of MasterHttpRelayVPN -- DPI bypass via Google Apps Script relay with domain fronting"
|
||||
license = "MIT"
|
||||
|
||||
+153
-10
@@ -142,8 +142,27 @@ fn install_linux(cert_path: &str) -> bool {
|
||||
let dest = format!("/etc/ca-certificates/trust-source/anchors/{}.crt", safe_name);
|
||||
try_copy_and_run(cert_path, &dest, &[&["trust", "extract-compat"]])
|
||||
}
|
||||
"openwrt" => {
|
||||
// OpenWRT itself doesn't open HTTPS connections through the proxy —
|
||||
// LAN clients do. The CA needs to be trusted on the CLIENTS, not on
|
||||
// the router. So this is a no-op success with guidance rather than
|
||||
// an error.
|
||||
tracing::info!(
|
||||
"OpenWRT detected: the router doesn't need to trust the MITM CA. \
|
||||
Copy {} to each LAN client (browser / OS trust store) instead. \
|
||||
Example: scp root@<router>:{} ./ and import from there.",
|
||||
cert_path, cert_path
|
||||
);
|
||||
true
|
||||
}
|
||||
_ => {
|
||||
tracing::warn!("Unknown Linux distro — install {} manually.", cert_path);
|
||||
tracing::warn!(
|
||||
"Unknown Linux distro — CA file is at {}. Copy it into your system's \
|
||||
trust anchors dir (e.g. /usr/local/share/ca-certificates/ for \
|
||||
Debian-like, /etc/pki/ca-trust/source/anchors/ for RHEL-like) and \
|
||||
run the corresponding refresh command.",
|
||||
cert_path
|
||||
);
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -198,6 +217,10 @@ fn run_cmd(args: &[&str]) -> bool {
|
||||
}
|
||||
|
||||
fn detect_linux_distro() -> String {
|
||||
// Marker-file shortcuts (most reliable).
|
||||
if Path::new("/etc/openwrt_release").exists() {
|
||||
return "openwrt".into();
|
||||
}
|
||||
if Path::new("/etc/debian_version").exists() {
|
||||
return "debian".into();
|
||||
}
|
||||
@@ -208,17 +231,50 @@ fn detect_linux_distro() -> String {
|
||||
return "arch".into();
|
||||
}
|
||||
if let Ok(content) = std::fs::read_to_string("/etc/os-release") {
|
||||
let lc = content.to_lowercase();
|
||||
if lc.contains("debian") || lc.contains("ubuntu") || lc.contains("mint") {
|
||||
return "debian".into();
|
||||
}
|
||||
if lc.contains("fedora") || lc.contains("rhel") || lc.contains("centos") {
|
||||
return "rhel".into();
|
||||
}
|
||||
if lc.contains("arch") || lc.contains("manjaro") {
|
||||
return "arch".into();
|
||||
return classify_os_release(&content);
|
||||
}
|
||||
"unknown".into()
|
||||
}
|
||||
|
||||
/// Parse /etc/os-release content and return a distro family.
|
||||
///
|
||||
/// We specifically look at the `ID` and `ID_LIKE` fields (not a substring
|
||||
/// search over the whole file) because random other fields like
|
||||
/// `OPENWRT_DEVICE_ARCH=x86_64` contain substrings that false-positive on
|
||||
/// "arch". Exposed for unit testing.
|
||||
fn classify_os_release(content: &str) -> String {
|
||||
let mut id = String::new();
|
||||
let mut id_like = String::new();
|
||||
for line in content.lines() {
|
||||
let (k, v) = match line.split_once('=') {
|
||||
Some(x) => x,
|
||||
None => continue,
|
||||
};
|
||||
let v = v.trim().trim_matches('"').trim_matches('\'').to_ascii_lowercase();
|
||||
match k.trim() {
|
||||
"ID" => id = v,
|
||||
"ID_LIKE" => id_like = v,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let tokens: Vec<&str> = id
|
||||
.split(|c: char| c.is_whitespace() || c == ',')
|
||||
.chain(id_like.split(|c: char| c.is_whitespace() || c == ','))
|
||||
.filter(|t| !t.is_empty())
|
||||
.collect();
|
||||
let has = |needle: &str| tokens.iter().any(|t| *t == needle);
|
||||
if has("openwrt") {
|
||||
return "openwrt".into();
|
||||
}
|
||||
if has("debian") || has("ubuntu") || has("mint") || has("raspbian") {
|
||||
return "debian".into();
|
||||
}
|
||||
if has("fedora") || has("rhel") || has("centos") || has("rocky") || has("almalinux") {
|
||||
return "rhel".into();
|
||||
}
|
||||
if has("arch") || has("manjaro") || has("endeavouros") {
|
||||
return "arch".into();
|
||||
}
|
||||
"unknown".into()
|
||||
}
|
||||
|
||||
@@ -409,3 +465,90 @@ fn firefox_profile_dirs() -> Vec<std::path::PathBuf> {
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn openwrt_os_release_is_not_arch() {
|
||||
// Real OpenWRT 23.05 /etc/os-release. Contains OPENWRT_DEVICE_ARCH
|
||||
// which substring-matches "arch" — the old detector would mis-classify
|
||||
// this as Arch Linux. Regression guard for issue #2.
|
||||
let content = r#"
|
||||
NAME="OpenWrt"
|
||||
VERSION="23.05.3"
|
||||
ID="openwrt"
|
||||
ID_LIKE="lede openwrt"
|
||||
PRETTY_NAME="OpenWrt 23.05.3"
|
||||
VERSION_ID="23.05.3"
|
||||
HOME_URL="https://openwrt.org/"
|
||||
BUG_URL="https://bugs.openwrt.org/"
|
||||
SUPPORT_URL="https://forum.openwrt.org/"
|
||||
BUILD_ID="r23809-234f1a2efa"
|
||||
OPENWRT_BOARD="x86/64"
|
||||
OPENWRT_ARCH="x86_64"
|
||||
OPENWRT_TAINTS=""
|
||||
OPENWRT_DEVICE_MANUFACTURER="OpenWrt"
|
||||
OPENWRT_DEVICE_MANUFACTURER_URL="https://openwrt.org/"
|
||||
OPENWRT_DEVICE_PRODUCT="Generic"
|
||||
OPENWRT_DEVICE_REVISION="v0"
|
||||
OPENWRT_RELEASE="OpenWrt 23.05.3 r23809-234f1a2efa"
|
||||
"#;
|
||||
assert_eq!(classify_os_release(content), "openwrt");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn debian_bullseye_classified_as_debian() {
|
||||
let content = r#"
|
||||
PRETTY_NAME="Debian GNU/Linux 11 (bullseye)"
|
||||
NAME="Debian GNU/Linux"
|
||||
VERSION_ID="11"
|
||||
VERSION="11 (bullseye)"
|
||||
VERSION_CODENAME=bullseye
|
||||
ID=debian
|
||||
"#;
|
||||
assert_eq!(classify_os_release(content), "debian");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ubuntu_classified_as_debian_via_id_like() {
|
||||
let content = r#"
|
||||
NAME="Ubuntu"
|
||||
VERSION="22.04.3 LTS (Jammy Jellyfish)"
|
||||
ID=ubuntu
|
||||
ID_LIKE=debian
|
||||
"#;
|
||||
assert_eq!(classify_os_release(content), "debian");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fedora_classified_as_rhel() {
|
||||
let content = "ID=fedora\nVERSION_ID=39\n";
|
||||
assert_eq!(classify_os_release(content), "rhel");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arch_classified_as_arch() {
|
||||
let content = "ID=arch\nID_LIKE=\n";
|
||||
assert_eq!(classify_os_release(content), "arch");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manjaro_classified_as_arch() {
|
||||
let content = "ID=manjaro\nID_LIKE=arch\n";
|
||||
assert_eq!(classify_os_release(content), "arch");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_os_release_is_unknown() {
|
||||
assert_eq!(classify_os_release(""), "unknown");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn random_file_with_arch_substring_does_not_match() {
|
||||
// Make sure we don't regress to the old substring-match bug.
|
||||
let content = "SOMEFIELD=maybearchived\nFOO=bar\n";
|
||||
assert_eq!(classify_os_release(content), "unknown");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user