Add ProxiFyre firewall rule management and bump version to 1.1.0
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
# `cargo run` / `tauri dev` should control the components installed by ProxyWarden,
|
||||||
|
# not copies that happen to exist beside target\debug\proxywarden.exe.
|
||||||
|
[env]
|
||||||
|
PROXYWARDEN_DEV_INSTALL_ROOT = { value = 'C:\Program Files\ProxyWarden', force = false }
|
||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "proxywarden",
|
"name": "proxywarden",
|
||||||
"version": "1.0.3",
|
"version": "1.1.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "proxywarden",
|
"name": "proxywarden",
|
||||||
"version": "1.0.3",
|
"version": "1.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource-variable/jetbrains-mono": "^5.2.8",
|
"@fontsource-variable/jetbrains-mono": "^5.2.8",
|
||||||
"@tauri-apps/api": "^2.0.0",
|
"@tauri-apps/api": "^2.0.0",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "proxywarden",
|
"name": "proxywarden",
|
||||||
"version": "1.0.3",
|
"version": "1.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Standalone Windows desktop proxy management app for ProxyWarden.",
|
"description": "Standalone Windows desktop proxy management app for ProxyWarden.",
|
||||||
|
|||||||
Generated
+1
-1
@@ -2314,7 +2314,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proxywarden"
|
name = "proxywarden"
|
||||||
version = "1.0.3"
|
version = "1.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "proxywarden"
|
name = "proxywarden"
|
||||||
version = "1.0.3"
|
version = "1.1.0"
|
||||||
description = "Standalone Windows desktop proxy management app for ProxyWarden."
|
description = "Standalone Windows desktop proxy management app for ProxyWarden."
|
||||||
authors = ["ProxyWarden"]
|
authors = ["ProxyWarden"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
@@ -172,6 +172,17 @@ function Remove-SafeDirectory([string]$Path, [string]$Root) {
|
|||||||
return $true
|
return $true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Remove-ManagedFirewallRules {
|
||||||
|
$removed = @()
|
||||||
|
foreach ($name in @("ProxyWarden.ProxiFyre.Inbound", "ProxyWarden.ProxiFyre.Outbound")) {
|
||||||
|
$rule = Get-NetFirewallRule -Name $name -ErrorAction SilentlyContinue
|
||||||
|
if ($null -eq $rule) { continue }
|
||||||
|
$rule | Remove-NetFirewallRule -ErrorAction Stop
|
||||||
|
$removed += $name
|
||||||
|
}
|
||||||
|
return $removed
|
||||||
|
}
|
||||||
|
|
||||||
function Get-InstalledProgram([string]$Pattern) {
|
function Get-InstalledProgram([string]$Pattern) {
|
||||||
$paths = @(
|
$paths = @(
|
||||||
"HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*",
|
"HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*",
|
||||||
@@ -220,6 +231,7 @@ try {
|
|||||||
|
|
||||||
$details.removedProxiFyreServices = Remove-ManagedService -Names @("ProxiFyreService", "ProxiFyre") -InstallRoot $root -UninstallExe (Join-Path $proxifyreDir "ProxiFyre.exe")
|
$details.removedProxiFyreServices = Remove-ManagedService -Names @("ProxiFyreService", "ProxiFyre") -InstallRoot $root -UninstallExe (Join-Path $proxifyreDir "ProxiFyre.exe")
|
||||||
$details.removedSingBoxServices = Remove-ManagedService -Names @("ProxyWardenSingBox") -InstallRoot $root -UninstallExe (Join-Path $singboxDir "ProxyWardenSingBox.exe")
|
$details.removedSingBoxServices = Remove-ManagedService -Names @("ProxyWardenSingBox") -InstallRoot $root -UninstallExe (Join-Path $singboxDir "ProxyWardenSingBox.exe")
|
||||||
|
$details.removedProxiFyreFirewallRules = Remove-ManagedFirewallRules
|
||||||
$details.removedProxiFyreDir = Remove-SafeDirectory $proxifyreDir $root
|
$details.removedProxiFyreDir = Remove-SafeDirectory $proxifyreDir $root
|
||||||
$details.removedSingBoxDir = Remove-SafeDirectory $singboxDir $root
|
$details.removedSingBoxDir = Remove-SafeDirectory $singboxDir $root
|
||||||
|
|
||||||
|
|||||||
@@ -18,13 +18,15 @@ pub use crate::configuration_use_case::{
|
|||||||
};
|
};
|
||||||
pub use crate::proxifyre_runtime::wrap_elevated_package_script;
|
pub use crate::proxifyre_runtime::wrap_elevated_package_script;
|
||||||
use crate::proxifyre_runtime::{
|
use crate::proxifyre_runtime::{
|
||||||
build_proxifyre_setup_status_for_install_dir, control_proxifyre_service,
|
build_proxifyre_setup_status_for_install_dir, configure_proxifyre_firewall,
|
||||||
install_proxifyre_component, proxifyre_install_dir_for_app, read_proxifyre_setup_progress,
|
control_proxifyre_service, install_proxifyre_component, proxifyre_install_dir_for_app,
|
||||||
singbox_install_dir_for_app, uninstall_proxifyre_component, ServiceControlAction,
|
read_proxifyre_setup_progress, singbox_install_dir_for_app, uninstall_proxifyre_component,
|
||||||
|
ServiceControlAction,
|
||||||
};
|
};
|
||||||
pub use crate::proxifyre_scripts::{
|
pub use crate::proxifyre_scripts::{
|
||||||
install_proxifyre_script, install_proxifyre_script_for_target,
|
configure_proxifyre_firewall_script, install_proxifyre_script,
|
||||||
install_proxifyre_script_with_bundle, uninstall_proxifyre_script,
|
install_proxifyre_script_for_target, install_proxifyre_script_with_bundle,
|
||||||
|
uninstall_proxifyre_script,
|
||||||
};
|
};
|
||||||
pub use crate::proxy_apply::{
|
pub use crate::proxy_apply::{
|
||||||
apply_profiles_with_services, apply_profiles_with_services_and_detection,
|
apply_profiles_with_services, apply_profiles_with_services_and_detection,
|
||||||
@@ -288,6 +290,13 @@ pub async fn install_proxifyre(
|
|||||||
.map_err(background_task_error)?
|
.map_err(background_task_error)?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn configure_proxifyre_firewall_rules(app: tauri::AppHandle) -> Result<(), CommandError> {
|
||||||
|
tauri::async_runtime::spawn_blocking(move || configure_proxifyre_firewall(&app))
|
||||||
|
.await
|
||||||
|
.map_err(background_task_error)?
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn uninstall_proxifyre(
|
pub async fn uninstall_proxifyre(
|
||||||
app: tauri::AppHandle,
|
app: tauri::AppHandle,
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ use std::{
|
|||||||
pub const PROXYWARDEN_COMPONENTS_DIR_NAME: &str = "components";
|
pub const PROXYWARDEN_COMPONENTS_DIR_NAME: &str = "components";
|
||||||
pub const PROXIFYRE_COMPONENT_DIR_NAME: &str = "ProxiFyre";
|
pub const PROXIFYRE_COMPONENT_DIR_NAME: &str = "ProxiFyre";
|
||||||
pub const SINGBOX_COMPONENT_DIR_NAME: &str = "sing-box";
|
pub const SINGBOX_COMPONENT_DIR_NAME: &str = "sing-box";
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
const PROXYWARDEN_DEV_INSTALL_ROOT_ENV: &str = "PROXYWARDEN_DEV_INSTALL_ROOT";
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum ProxyfierEngine {
|
pub enum ProxyfierEngine {
|
||||||
@@ -132,11 +134,42 @@ pub fn detect_proxyfier_install() -> Option<DetectedProxyfier> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn app_install_dir_from_current_exe() -> Option<PathBuf> {
|
pub fn app_install_dir_from_current_exe() -> Option<PathBuf> {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
if let Some(path) = env::var_os(PROXYWARDEN_DEV_INSTALL_ROOT_ENV)
|
||||||
|
.filter(|value| !value.is_empty())
|
||||||
|
.map(PathBuf::from)
|
||||||
|
{
|
||||||
|
return Some(path);
|
||||||
|
}
|
||||||
|
|
||||||
env::current_exe()
|
env::current_exe()
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|path| path.parent().map(Path::to_path_buf))
|
.and_then(|path| path.parent().map(Path::to_path_buf))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(test, debug_assertions))]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
fn debug_component_roots_follow_configured_install() {
|
||||||
|
let install_root = PathBuf::from(
|
||||||
|
env::var(PROXYWARDEN_DEV_INSTALL_ROOT_ENV)
|
||||||
|
.expect("Cargo dev config should define the installed ProxyWarden root"),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
default_proxifyre_install_dir(),
|
||||||
|
proxifyre_install_dir_from_app_dir(&install_root)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
default_singbox_install_dir(),
|
||||||
|
singbox_install_dir_from_app_dir(&install_root)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn component_root_from_app_dir(app_dir: &Path) -> PathBuf {
|
pub fn component_root_from_app_dir(app_dir: &Path) -> PathBuf {
|
||||||
app_dir.join(PROXYWARDEN_COMPONENTS_DIR_NAME)
|
app_dir.join(PROXYWARDEN_COMPONENTS_DIR_NAME)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ pub fn run() {
|
|||||||
commands::start_proxifyre_service,
|
commands::start_proxifyre_service,
|
||||||
commands::stop_proxifyre_service,
|
commands::stop_proxifyre_service,
|
||||||
commands::install_proxifyre,
|
commands::install_proxifyre,
|
||||||
|
commands::configure_proxifyre_firewall_rules,
|
||||||
commands::uninstall_proxifyre,
|
commands::uninstall_proxifyre,
|
||||||
commands::start_singbox_service,
|
commands::start_singbox_service,
|
||||||
commands::stop_singbox_service,
|
commands::stop_singbox_service,
|
||||||
|
|||||||
@@ -68,7 +68,8 @@ pub fn verify_managed_proxifyre_install(
|
|||||||
marker_path.display()
|
marker_path.display()
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
let marker: ProxiFyreInstallMarker = serde_json::from_str(&marker_text).map_err(|error| {
|
let marker_text = marker_text.strip_prefix('\u{feff}').unwrap_or(&marker_text);
|
||||||
|
let marker: ProxiFyreInstallMarker = serde_json::from_str(marker_text).map_err(|error| {
|
||||||
format!(
|
format!(
|
||||||
"marker установки {} содержит некорректный JSON: {error}",
|
"marker установки {} содержит некорректный JSON: {error}",
|
||||||
marker_path.display()
|
marker_path.display()
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
use crate::clock::{Clock, SystemClock};
|
use crate::clock::{Clock, SystemClock};
|
||||||
use crate::command_dto::*;
|
use crate::command_dto::*;
|
||||||
use crate::component_detection::{
|
use crate::component_detection::{
|
||||||
detect_proxyfier_install, proxifyre_install_dir_from_app_dir,
|
app_install_dir_from_current_exe, detect_proxyfier_install, proxifyre_install_dir_from_app_dir,
|
||||||
proxyfier_component_from_detection, singbox_install_dir_from_app_dir, DetectedProxyfier,
|
proxyfier_component_from_detection, singbox_install_dir_from_app_dir, DetectedProxyfier,
|
||||||
};
|
};
|
||||||
use crate::elevated_scripts;
|
use crate::elevated_scripts;
|
||||||
@@ -17,12 +17,15 @@ use crate::powershell::{
|
|||||||
};
|
};
|
||||||
use crate::process::command_no_window;
|
use crate::process::command_no_window;
|
||||||
use crate::proxifyre_ownership::verify_managed_proxifyre_install;
|
use crate::proxifyre_ownership::verify_managed_proxifyre_install;
|
||||||
use crate::proxifyre_scripts::{install_proxifyre_script_for_target, uninstall_proxifyre_script};
|
use crate::proxifyre_scripts::{
|
||||||
|
configure_proxifyre_firewall_script, install_proxifyre_script_for_target,
|
||||||
|
uninstall_proxifyre_script,
|
||||||
|
};
|
||||||
use crate::safe_fs;
|
use crate::safe_fs;
|
||||||
use crate::storage::{default_config_root, JsonStorage};
|
use crate::storage::{default_config_root, JsonStorage};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::{env, fs};
|
|
||||||
use tauri::Manager;
|
use tauri::Manager;
|
||||||
|
|
||||||
pub(crate) fn control_proxifyre_service(
|
pub(crate) fn control_proxifyre_service(
|
||||||
@@ -389,6 +392,7 @@ exit 4
|
|||||||
enum ProxiFyrePackageAction {
|
enum ProxiFyrePackageAction {
|
||||||
Install,
|
Install,
|
||||||
Uninstall,
|
Uninstall,
|
||||||
|
ConfigureFirewall,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ProxiFyrePackageAction {
|
impl ProxiFyrePackageAction {
|
||||||
@@ -396,6 +400,7 @@ impl ProxiFyrePackageAction {
|
|||||||
match self {
|
match self {
|
||||||
ProxiFyrePackageAction::Install => "proxifyre_install_failed",
|
ProxiFyrePackageAction::Install => "proxifyre_install_failed",
|
||||||
ProxiFyrePackageAction::Uninstall => "proxifyre_uninstall_failed",
|
ProxiFyrePackageAction::Uninstall => "proxifyre_uninstall_failed",
|
||||||
|
ProxiFyrePackageAction::ConfigureFirewall => "proxifyre_firewall_failed",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -403,6 +408,7 @@ impl ProxiFyrePackageAction {
|
|||||||
match self {
|
match self {
|
||||||
ProxiFyrePackageAction::Install => "установить",
|
ProxiFyrePackageAction::Install => "установить",
|
||||||
ProxiFyrePackageAction::Uninstall => "удалить",
|
ProxiFyrePackageAction::Uninstall => "удалить",
|
||||||
|
ProxiFyrePackageAction::ConfigureFirewall => "настроить Windows Firewall для",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,6 +416,7 @@ impl ProxiFyrePackageAction {
|
|||||||
match self {
|
match self {
|
||||||
ProxiFyrePackageAction::Install => "install",
|
ProxiFyrePackageAction::Install => "install",
|
||||||
ProxiFyrePackageAction::Uninstall => "uninstall",
|
ProxiFyrePackageAction::Uninstall => "uninstall",
|
||||||
|
ProxiFyrePackageAction::ConfigureFirewall => "firewall",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,6 +424,7 @@ impl ProxiFyrePackageAction {
|
|||||||
match self {
|
match self {
|
||||||
ProxiFyrePackageAction::Install => "install",
|
ProxiFyrePackageAction::Install => "install",
|
||||||
ProxiFyrePackageAction::Uninstall => "uninstall",
|
ProxiFyrePackageAction::Uninstall => "uninstall",
|
||||||
|
ProxiFyrePackageAction::ConfigureFirewall => "firewall",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -424,6 +432,9 @@ impl ProxiFyrePackageAction {
|
|||||||
match self {
|
match self {
|
||||||
ProxiFyrePackageAction::Install => "Готовлю установку ProxiFyre.",
|
ProxiFyrePackageAction::Install => "Готовлю установку ProxiFyre.",
|
||||||
ProxiFyrePackageAction::Uninstall => "Готовлю удаление ProxiFyre и сетевого драйвера.",
|
ProxiFyrePackageAction::Uninstall => "Готовлю удаление ProxiFyre и сетевого драйвера.",
|
||||||
|
ProxiFyrePackageAction::ConfigureFirewall => {
|
||||||
|
"Готовлю правила Windows Firewall для ProxiFyre."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,6 +442,9 @@ impl ProxiFyrePackageAction {
|
|||||||
match self {
|
match self {
|
||||||
ProxiFyrePackageAction::Install => "ProxiFyre и сетевой драйвер готовы.",
|
ProxiFyrePackageAction::Install => "ProxiFyre и сетевой драйвер готовы.",
|
||||||
ProxiFyrePackageAction::Uninstall => "ProxiFyre и сетевой драйвер удалены.",
|
ProxiFyrePackageAction::Uninstall => "ProxiFyre и сетевой драйвер удалены.",
|
||||||
|
ProxiFyrePackageAction::ConfigureFirewall => {
|
||||||
|
"Правила Windows Firewall для ProxiFyre добавлены."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -477,6 +491,36 @@ pub(crate) fn install_proxifyre_component(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn configure_proxifyre_firewall(app: &tauri::AppHandle) -> Result<(), CommandError> {
|
||||||
|
let Some(detected) = detect_proxyfier_install() else {
|
||||||
|
return Err(CommandError::new(
|
||||||
|
ProxiFyrePackageAction::ConfigureFirewall.error_code(),
|
||||||
|
"ProxiFyre не найден на компьютере.",
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
let expected_install_dir = proxifyre_install_dir_for_app(app)?;
|
||||||
|
verify_managed_proxifyre_install(
|
||||||
|
&detected.install_dir,
|
||||||
|
&detected.executable_path,
|
||||||
|
&expected_install_dir,
|
||||||
|
)
|
||||||
|
.map_err(|reason| {
|
||||||
|
CommandError::new(
|
||||||
|
ProxiFyrePackageAction::ConfigureFirewall.error_code(),
|
||||||
|
format!("Настройка Windows Firewall заблокирована: {reason}"),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let script = configure_proxifyre_firewall_script(&detected.executable_path);
|
||||||
|
let artifact_dir = default_config_root().join("state");
|
||||||
|
run_elevated_package_script(
|
||||||
|
ProxiFyrePackageAction::ConfigureFirewall,
|
||||||
|
script,
|
||||||
|
&artifact_dir,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn bundled_proxifyre_asset_dir(app: &tauri::AppHandle) -> Option<PathBuf> {
|
fn bundled_proxifyre_asset_dir(app: &tauri::AppHandle) -> Option<PathBuf> {
|
||||||
let mut candidates = Vec::new();
|
let mut candidates = Vec::new();
|
||||||
if let Ok(resource_dir) = app.path().resource_dir() {
|
if let Ok(resource_dir) = app.path().resource_dir() {
|
||||||
@@ -492,10 +536,8 @@ fn bundled_proxifyre_asset_dir(app: &tauri::AppHandle) -> Option<PathBuf> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn app_install_dir(app: &tauri::AppHandle) -> Result<PathBuf, CommandError> {
|
fn app_install_dir(app: &tauri::AppHandle) -> Result<PathBuf, CommandError> {
|
||||||
if let Ok(exe_path) = env::current_exe() {
|
if let Some(app_dir) = app_install_dir_from_current_exe() {
|
||||||
if let Some(parent) = exe_path.parent() {
|
return Ok(app_dir);
|
||||||
return Ok(parent.to_path_buf());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
app.path().resource_dir().map_err(|error| {
|
app.path().resource_dir().map_err(|error| {
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ const NDISAPI_PINNED_RELEASE_TAG: &str = "v3.6.2";
|
|||||||
const NDISAPI_PINNED_INSTALLER_VERSION: &str = "3.6.2.1";
|
const NDISAPI_PINNED_INSTALLER_VERSION: &str = "3.6.2.1";
|
||||||
const VC_REDIST_X64_URL: &str = "https://aka.ms/vc14/vc_redist.x64.exe";
|
const VC_REDIST_X64_URL: &str = "https://aka.ms/vc14/vc_redist.x64.exe";
|
||||||
const VC_REDIST_X86_URL: &str = "https://aka.ms/vc14/vc_redist.x86.exe";
|
const VC_REDIST_X86_URL: &str = "https://aka.ms/vc14/vc_redist.x86.exe";
|
||||||
|
pub const PROXIFYRE_FIREWALL_INBOUND_RULE: &str = "ProxyWarden.ProxiFyre.Inbound";
|
||||||
|
pub const PROXIFYRE_FIREWALL_OUTBOUND_RULE: &str = "ProxyWarden.ProxiFyre.Outbound";
|
||||||
|
|
||||||
pub fn install_proxifyre_script(generated_config_path: &Path) -> String {
|
pub fn install_proxifyre_script(generated_config_path: &Path) -> String {
|
||||||
install_proxifyre_script_with_bundle(generated_config_path, None)
|
install_proxifyre_script_with_bundle(generated_config_path, None)
|
||||||
@@ -453,14 +455,15 @@ pub fn install_proxifyre_script_for_target(
|
|||||||
}
|
}
|
||||||
|
|
||||||
$markerPath = Join-Path $targetDir 'proxywarden-component.json'
|
$markerPath = Join-Path $targetDir 'proxywarden-component.json'
|
||||||
[ordered]@{
|
$markerJson = [ordered]@{
|
||||||
manager = 'ProxyWarden'
|
manager = 'ProxyWarden'
|
||||||
component = 'proxifyre'
|
component = 'proxifyre'
|
||||||
serviceName = 'ProxiFyreService'
|
serviceName = 'ProxiFyreService'
|
||||||
installedAt = (Get-Date).ToString('o')
|
installedAt = (Get-Date).ToString('o')
|
||||||
installRoot = $targetDir
|
installRoot = $targetDir
|
||||||
packetFilterInstalledByProxyWarden = (-not $packetFilterAlreadyInstalled)
|
packetFilterInstalledByProxyWarden = (-not $packetFilterAlreadyInstalled)
|
||||||
} | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath $markerPath -Encoding UTF8
|
} | ConvertTo-Json -Depth 4
|
||||||
|
[IO.File]::WriteAllText($markerPath, $markerJson, [Text.UTF8Encoding]::new($false))
|
||||||
|
|
||||||
Write-ProxyWardenProgress 'install' 'proxifyre' 'running' 90 'Устанавливаю и запускаю службу ProxiFyre.'
|
Write-ProxyWardenProgress 'install' 'proxifyre' 'running' 90 'Устанавливаю и запускаю службу ProxiFyre.'
|
||||||
Push-Location $targetDir
|
Push-Location $targetDir
|
||||||
@@ -483,6 +486,38 @@ pub fn install_proxifyre_script_for_target(
|
|||||||
script
|
script
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn configure_proxifyre_firewall_script(executable_path: &Path) -> String {
|
||||||
|
let executable_path = escape_powershell_single(&executable_path.display().to_string());
|
||||||
|
format!(
|
||||||
|
r#"
|
||||||
|
$exePath = '{executable_path}'
|
||||||
|
if (-not (Test-Path -LiteralPath $exePath -PathType Leaf)) {{
|
||||||
|
throw "ProxiFyre.exe не найден по подтвержденному пути: $exePath"
|
||||||
|
}}
|
||||||
|
|
||||||
|
$ruleSpecs = @(
|
||||||
|
@{{ Name = '{PROXIFYRE_FIREWALL_INBOUND_RULE}'; DisplayName = 'ProxyWarden: ProxiFyre (входящие)'; Direction = 'Inbound' }},
|
||||||
|
@{{ Name = '{PROXIFYRE_FIREWALL_OUTBOUND_RULE}'; DisplayName = 'ProxyWarden: ProxiFyre (исходящие)'; Direction = 'Outbound' }}
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach ($rule in $ruleSpecs) {{
|
||||||
|
Get-NetFirewallRule -Name $rule.Name -ErrorAction SilentlyContinue |
|
||||||
|
Remove-NetFirewallRule -ErrorAction Stop
|
||||||
|
New-NetFirewallRule `
|
||||||
|
-Name $rule.Name `
|
||||||
|
-DisplayName $rule.DisplayName `
|
||||||
|
-Group 'ProxyWarden' `
|
||||||
|
-Program $exePath `
|
||||||
|
-Direction $rule.Direction `
|
||||||
|
-Action Allow `
|
||||||
|
-Profile Any `
|
||||||
|
-Enabled True `
|
||||||
|
-ErrorAction Stop | Out-Null
|
||||||
|
}}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn uninstall_proxifyre_script(
|
pub fn uninstall_proxifyre_script(
|
||||||
detected: Option<&DetectedProxyfier>,
|
detected: Option<&DetectedProxyfier>,
|
||||||
ownership: &ManagedProxiFyreOwnership,
|
ownership: &ManagedProxiFyreOwnership,
|
||||||
@@ -619,6 +654,11 @@ pub fn uninstall_proxifyre_script(
|
|||||||
sc.exe delete $service.Name | Out-Null
|
sc.exe delete $service.Name | Out-Null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($firewallRuleName in @('ProxyWarden.ProxiFyre.Inbound', 'ProxyWarden.ProxiFyre.Outbound')) {
|
||||||
|
Get-NetFirewallRule -Name $firewallRuleName -ErrorAction SilentlyContinue |
|
||||||
|
Remove-NetFirewallRule -ErrorAction Stop
|
||||||
|
}
|
||||||
|
|
||||||
if (-not [string]::IsNullOrWhiteSpace($installDir) -and (Test-Path -LiteralPath $installDir)) {
|
if (-not [string]::IsNullOrWhiteSpace($installDir) -and (Test-Path -LiteralPath $installDir)) {
|
||||||
Remove-Item -LiteralPath $installDir -Recurse -Force
|
Remove-Item -LiteralPath $installDir -Recurse -Force
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "ProxyWarden",
|
"productName": "ProxyWarden",
|
||||||
"version": "1.0.3",
|
"version": "1.1.0",
|
||||||
"identifier": "ru.dokops.proxywarden.windows",
|
"identifier": "ru.dokops.proxywarden.windows",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "npm run dev",
|
"beforeDevCommand": "npm run dev",
|
||||||
|
|||||||
@@ -287,6 +287,11 @@ fn proxifyre_install_script_uses_resilient_download_helpers() {
|
|||||||
));
|
));
|
||||||
assert!(script.contains("github.com/wiresock/ndisapi/releases/download"));
|
assert!(script.contains("github.com/wiresock/ndisapi/releases/download"));
|
||||||
assert!(script.contains("github.com/wiresock/proxifyre/releases/download"));
|
assert!(script.contains("github.com/wiresock/proxifyre/releases/download"));
|
||||||
|
assert!(script.contains(
|
||||||
|
"[IO.File]::WriteAllText($markerPath, $markerJson, [Text.UTF8Encoding]::new($false))"
|
||||||
|
));
|
||||||
|
assert!(!script
|
||||||
|
.contains("ConvertTo-Json -Depth 4 | Set-Content -LiteralPath $markerPath -Encoding UTF8"));
|
||||||
let packet_filter_step = script
|
let packet_filter_step = script
|
||||||
.find("Write-ProxyWardenProgress 'install' 'packet-filter'")
|
.find("Write-ProxyWardenProgress 'install' 'packet-filter'")
|
||||||
.expect("packet filter install step should be present");
|
.expect("packet filter install step should be present");
|
||||||
@@ -342,6 +347,52 @@ fn proxifyre_install_script_prefers_bundled_assets_before_downloads() {
|
|||||||
cleanup(&root);
|
cleanup(&root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn proxifyre_firewall_script_scopes_rules_to_managed_executable() {
|
||||||
|
let executable =
|
||||||
|
PathBuf::from(r"C:\Program Files\Proxy'Warden\components\ProxiFyre\ProxiFyre.exe");
|
||||||
|
let script = commands::configure_proxifyre_firewall_script(&executable);
|
||||||
|
|
||||||
|
assert!(script.contains(
|
||||||
|
"$exePath = 'C:\\Program Files\\Proxy''Warden\\components\\ProxiFyre\\ProxiFyre.exe'"
|
||||||
|
));
|
||||||
|
assert!(script.contains("ProxyWarden.ProxiFyre.Inbound"));
|
||||||
|
assert!(script.contains("ProxyWarden.ProxiFyre.Outbound"));
|
||||||
|
assert!(script.contains("-Program $exePath"));
|
||||||
|
assert!(script.contains("Get-NetFirewallRule -Name $rule.Name"));
|
||||||
|
assert!(!script.contains("Get-NetFirewallRule -DisplayName"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn proxifyre_firewall_script_parses_as_powershell() {
|
||||||
|
let root = test_root("proxifyre-firewall-script");
|
||||||
|
fs::create_dir_all(&root).expect("test root should be created");
|
||||||
|
let script = commands::configure_proxifyre_firewall_script(
|
||||||
|
&root.join("ProxiFyre").join("ProxiFyre.exe"),
|
||||||
|
);
|
||||||
|
let script_path = root.join("firewall.ps1");
|
||||||
|
fs::write(&script_path, script).expect("script should be written");
|
||||||
|
|
||||||
|
let escaped_path = script_path.display().to_string().replace('\'', "''");
|
||||||
|
let parser = format!(
|
||||||
|
"$tokens = $null; $errors = $null; [System.Management.Automation.Language.Parser]::ParseFile('{escaped_path}', [ref]$tokens, [ref]$errors) | Out-Null; if ($errors.Count -gt 0) {{ $errors | ForEach-Object {{ $_.Message }}; exit 1 }}"
|
||||||
|
);
|
||||||
|
let output = ProcessCommand::new("powershell")
|
||||||
|
.args(["-NoProfile", "-NonInteractive", "-Command", &parser])
|
||||||
|
.output()
|
||||||
|
.expect("powershell parser should run");
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
output.status.success(),
|
||||||
|
"firewall script should parse\nstdout:\n{}\nstderr:\n{}",
|
||||||
|
String::from_utf8_lossy(&output.stdout),
|
||||||
|
String::from_utf8_lossy(&output.stderr),
|
||||||
|
);
|
||||||
|
|
||||||
|
cleanup(&root);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn proxifyre_uninstall_script_parses_as_powershell() {
|
fn proxifyre_uninstall_script_parses_as_powershell() {
|
||||||
@@ -410,6 +461,8 @@ fn proxifyre_uninstall_script_removes_packet_filter_after_proxifyre() {
|
|||||||
assert!(script.contains("Start-Process -FilePath 'msiexec.exe'"));
|
assert!(script.contains("Start-Process -FilePath 'msiexec.exe'"));
|
||||||
assert!(script.contains("ArgumentList @('/x', $productCode, '/qn', '/norestart'"));
|
assert!(script.contains("ArgumentList @('/x', $productCode, '/qn', '/norestart'"));
|
||||||
assert!(script.contains("Uninstall-MsiProgram $packetFilter 'Windows Packet Filter'"));
|
assert!(script.contains("Uninstall-MsiProgram $packetFilter 'Windows Packet Filter'"));
|
||||||
|
assert!(script.contains("ProxyWarden.ProxiFyre.Inbound"));
|
||||||
|
assert!(script.contains("ProxyWarden.ProxiFyre.Outbound"));
|
||||||
let proxifyre_step = script
|
let proxifyre_step = script
|
||||||
.find("Write-ProxyWardenProgress 'uninstall' 'proxifyre'")
|
.find("Write-ProxyWardenProgress 'uninstall' 'proxifyre'")
|
||||||
.expect("proxifyre uninstall step should be present");
|
.expect("proxifyre uninstall step should be present");
|
||||||
@@ -419,6 +472,16 @@ fn proxifyre_uninstall_script_removes_packet_filter_after_proxifyre() {
|
|||||||
assert!(proxifyre_step < packet_filter_step);
|
assert!(proxifyre_step < packet_filter_step);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn proxywarden_uninstall_hook_removes_only_managed_firewall_rules() {
|
||||||
|
let script = include_str!("../bundled/cleanup/uninstall-managed-components.ps1");
|
||||||
|
|
||||||
|
assert!(script.contains("ProxyWarden.ProxiFyre.Inbound"));
|
||||||
|
assert!(script.contains("ProxyWarden.ProxiFyre.Outbound"));
|
||||||
|
assert!(script.contains("Get-NetFirewallRule -Name $name"));
|
||||||
|
assert!(!script.contains("Get-NetFirewallRule -DisplayName"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn proxifyre_uninstall_script_leaves_shared_packet_filter_installed() {
|
fn proxifyre_uninstall_script_leaves_shared_packet_filter_installed() {
|
||||||
let detected = DetectedProxyfier {
|
let detected = DetectedProxyfier {
|
||||||
|
|||||||
@@ -18,6 +18,35 @@ fn accepts_matching_managed_install_and_returns_packet_filter_ownership() {
|
|||||||
assert!(ownership.remove_packet_filter);
|
assert!(ownership.remove_packet_filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn accepts_windows_powershell_utf8_bom_marker() {
|
||||||
|
let fixture = ManagedInstallFixture::new("utf8-bom");
|
||||||
|
let marker = json!({
|
||||||
|
"manager": "ProxyWarden",
|
||||||
|
"component": "proxifyre",
|
||||||
|
"serviceName": "ProxiFyreService",
|
||||||
|
"installRoot": fixture.install_dir,
|
||||||
|
"packetFilterInstalledByProxyWarden": false
|
||||||
|
});
|
||||||
|
let mut marker_bytes = vec![0xEF, 0xBB, 0xBF];
|
||||||
|
marker_bytes.extend(serde_json::to_vec_pretty(&marker).expect("marker should serialize"));
|
||||||
|
fs::write(
|
||||||
|
fixture.install_dir.join("proxywarden-component.json"),
|
||||||
|
marker_bytes,
|
||||||
|
)
|
||||||
|
.expect("BOM marker should be written");
|
||||||
|
|
||||||
|
let ownership = verify_managed_proxifyre_install(
|
||||||
|
&fixture.install_dir,
|
||||||
|
&fixture.executable_path,
|
||||||
|
&fixture.install_dir,
|
||||||
|
)
|
||||||
|
.expect("Windows PowerShell BOM marker should prove ownership");
|
||||||
|
|
||||||
|
assert_eq!(ownership.service_name, "ProxiFyreService");
|
||||||
|
assert!(!ownership.remove_packet_filter);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rejects_install_outside_expected_managed_directory() {
|
fn rejects_install_outside_expected_managed_directory() {
|
||||||
let fixture = ManagedInstallFixture::new("unexpected-root");
|
let fixture = ManagedInstallFixture::new("unexpected-root");
|
||||||
|
|||||||
@@ -270,6 +270,10 @@ export function installProxiFyre(): Promise<ComponentStatus> {
|
|||||||
return invoke<ComponentStatus>("install_proxifyre");
|
return invoke<ComponentStatus>("install_proxifyre");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function configureProxiFyreFirewallRules(): Promise<void> {
|
||||||
|
return invoke<void>("configure_proxifyre_firewall_rules");
|
||||||
|
}
|
||||||
|
|
||||||
export function uninstallProxiFyre(): Promise<ComponentStatus> {
|
export function uninstallProxiFyre(): Promise<ComponentStatus> {
|
||||||
return invoke<ComponentStatus>("uninstall_proxifyre");
|
return invoke<ComponentStatus>("uninstall_proxifyre");
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-1
@@ -18,6 +18,7 @@ import {
|
|||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import {
|
import {
|
||||||
applyConfiguration,
|
applyConfiguration,
|
||||||
|
configureProxiFyreFirewallRules,
|
||||||
fetchSingBoxSubscription,
|
fetchSingBoxSubscription,
|
||||||
forgetSingBoxSubscription,
|
forgetSingBoxSubscription,
|
||||||
generateSingBoxConfig,
|
generateSingBoxConfig,
|
||||||
@@ -591,7 +592,7 @@ export function App() {
|
|||||||
|
|
||||||
async function installProxiFyrePackage() {
|
async function installProxiFyrePackage() {
|
||||||
const confirmed = window.confirm(
|
const confirmed = window.confirm(
|
||||||
"Установить ProxiFyre? ProxyWarden запросит права администратора, скачает ProxiFyre, Windows Packet Filter и при необходимости Visual C++ Runtime, затем создаст и запустит Windows-службу.",
|
"Установить ProxiFyre? ProxyWarden запросит права администратора, установит ProxiFyre, Windows Packet Filter и при необходимости Visual C++ Runtime, затем создаст и запустит Windows-службу. После установки приложение отдельно предложит добавить правила Windows Firewall.",
|
||||||
);
|
);
|
||||||
if (!confirmed) return;
|
if (!confirmed) return;
|
||||||
|
|
||||||
@@ -621,6 +622,34 @@ export function App() {
|
|||||||
title: "ProxiFyre установлен",
|
title: "ProxiFyre установлен",
|
||||||
text: proxyfierDetails(component, false),
|
text: proxyfierDetails(component, false),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const firewallConfirmed = window.confirm(
|
||||||
|
"Добавить разрешающие правила Windows Firewall для установленного ProxiFyre.exe? Будут созданы отдельные входящее и исходящее правила только для подтвержденного пути программы. Windows снова запросит права администратора.",
|
||||||
|
);
|
||||||
|
if (firewallConfirmed) {
|
||||||
|
try {
|
||||||
|
await configureProxiFyreFirewallRules();
|
||||||
|
const firewallProgress = await getProxiFyreSetupProgress();
|
||||||
|
setSetupProgress(firewallProgress);
|
||||||
|
showNotice({
|
||||||
|
kind: "success",
|
||||||
|
title: "Windows Firewall настроен",
|
||||||
|
text: "Входящее и исходящее правила добавлены для установленного ProxiFyre.exe.",
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
showNotice({
|
||||||
|
kind: "error",
|
||||||
|
title: "Windows Firewall не настроен",
|
||||||
|
text: `ProxiFyre установлен, но правила не добавлены. ${errorMessage(error)}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
showNotice({
|
||||||
|
kind: "info",
|
||||||
|
title: "Правила Firewall пропущены",
|
||||||
|
text: "ProxiFyre установлен без правил Windows Firewall. Их можно будет добавить повторной установкой компонента.",
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
void getProxiFyreSetupProgress()
|
void getProxiFyreSetupProgress()
|
||||||
.then(setSetupProgress)
|
.then(setSetupProgress)
|
||||||
|
|||||||
Reference in New Issue
Block a user