feat: Windows stack (daemon, installer, GUI)
Components: - ostp-daemon: Windows Service with Named Pipe IPC - ostp-installer: Setup wizard with admin privileges - ostp-gui: Tauri dark theme UI (450x600) Features: - Background service management (OspabGuard) - IPC commands: CONNECT/DISCONNECT/STATUS - Firewall rules auto-configuration - Wintun driver placeholder (download from wintun.net) - Real-time stats display (upload/download/ping) Note: Requires wintun.dll download for full functionality
This commit is contained in:
32
ostp-installer/Cargo.toml
Normal file
32
ostp-installer/Cargo.toml
Normal file
@@ -0,0 +1,32 @@
|
||||
[package]
|
||||
name = "ostp-installer"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
description = "OSTP Windows Installer - Setup Wizard"
|
||||
|
||||
[[bin]]
|
||||
name = "ostp-installer"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
tracing.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
|
||||
# Windows-specific
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3", features = [
|
||||
"winuser", "winsvc", "winbase", "processthreadsapi", "handleapi", "errhandlingapi",
|
||||
"securitybaseapi", "winnt"
|
||||
] }
|
||||
windows = { version = "0.58", features = [
|
||||
"Win32_System_Services",
|
||||
"Win32_Security",
|
||||
"Win32_Foundation",
|
||||
"Win32_Storage_FileSystem",
|
||||
"Win32_System_Registry",
|
||||
] }
|
||||
|
||||
# GUI for wizard
|
||||
native-windows-gui = "1.0"
|
||||
native-windows-derive = "1.0"
|
||||
12
ostp-installer/assets/README-wintun.md
Normal file
12
ostp-installer/assets/README-wintun.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Wintun DLL Placeholder
|
||||
|
||||
**NOTE**: This is a placeholder file. You need to download the actual `wintun.dll` from:
|
||||
https://www.wintun.net/
|
||||
|
||||
**Instructions:**
|
||||
1. Download Wintun from https://www.wintun.net/
|
||||
2. Extract the archive
|
||||
3. Copy `wintun/bin/amd64/wintun.dll` to this directory
|
||||
4. The installer will embed this DLL at compile time using `include_bytes!`
|
||||
|
||||
**License**: Wintun is dual-licensed under GPLv2 and a commercial license.
|
||||
3
ostp-installer/assets/wintun.dll
Normal file
3
ostp-installer/assets/wintun.dll
Normal file
@@ -0,0 +1,3 @@
|
||||
// Placeholder wintun.dll
|
||||
// Download actual file from https://www.wintun.net/
|
||||
// This file is here to allow compilation without the actual DLL
|
||||
17
ostp-installer/ostp-installer.manifest
Normal file
17
ostp-installer/ostp-installer.manifest
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity
|
||||
version="1.0.0.0"
|
||||
processorArchitecture="amd64"
|
||||
name="OspabInstaller"
|
||||
type="win32"
|
||||
/>
|
||||
<description>OSTP Installer</description>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
</assembly>
|
||||
63
ostp-installer/src/firewall.rs
Normal file
63
ostp-installer/src/firewall.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
//! Windows Firewall configuration
|
||||
|
||||
use anyhow::Result;
|
||||
use std::process::Command;
|
||||
|
||||
pub fn add_firewall_rules() -> Result<()> {
|
||||
let daemon_path = crate::get_install_path().join("ostp-daemon.exe");
|
||||
let gui_path = crate::get_install_path().join("ostp-gui.exe");
|
||||
|
||||
// Add inbound rule for daemon
|
||||
add_rule(
|
||||
"OSTP Daemon Inbound",
|
||||
&daemon_path.to_string_lossy(),
|
||||
"in",
|
||||
)?;
|
||||
|
||||
// Add outbound rule for daemon
|
||||
add_rule(
|
||||
"OSTP Daemon Outbound",
|
||||
&daemon_path.to_string_lossy(),
|
||||
"out",
|
||||
)?;
|
||||
|
||||
// Add inbound rule for GUI
|
||||
add_rule(
|
||||
"OSTP GUI Inbound",
|
||||
&gui_path.to_string_lossy(),
|
||||
"in",
|
||||
)?;
|
||||
|
||||
// Add outbound rule for GUI
|
||||
add_rule(
|
||||
"OSTP GUI Outbound",
|
||||
&gui_path.to_string_lossy(),
|
||||
"out",
|
||||
)?;
|
||||
|
||||
tracing::info!("Firewall rules added successfully");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_rule(name: &str, program: &str, direction: &str) -> Result<()> {
|
||||
let output = Command::new("netsh")
|
||||
.args([
|
||||
"advfirewall",
|
||||
"firewall",
|
||||
"add",
|
||||
"rule",
|
||||
&format!("name={}", name),
|
||||
&format!("dir={}", direction),
|
||||
"action=allow",
|
||||
&format!("program={}", program),
|
||||
"enable=yes",
|
||||
])
|
||||
.output()?;
|
||||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
anyhow::bail!("Failed to add firewall rule '{}': {}", name, stderr);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
88
ostp-installer/src/main.rs
Normal file
88
ostp-installer/src/main.rs
Normal file
@@ -0,0 +1,88 @@
|
||||
//! OSTP Windows Installer - Setup Wizard
|
||||
//!
|
||||
//! Installs:
|
||||
//! - Wintun driver
|
||||
//! - OspabGuard Windows Service
|
||||
//! - Firewall rules
|
||||
//! - GUI shortcuts
|
||||
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use std::path::PathBuf;
|
||||
|
||||
mod wizard;
|
||||
mod wintun;
|
||||
mod service;
|
||||
mod firewall;
|
||||
|
||||
// Placeholder - download actual wintun.dll from https://www.wintun.net/
|
||||
// const WINTUN_DLL: &[u8] = include_bytes!("../../assets/wintun.dll");
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Check for admin privileges
|
||||
if !is_elevated() {
|
||||
anyhow::bail!("Installer must be run as Administrator");
|
||||
}
|
||||
|
||||
// Initialize logging
|
||||
tracing_subscriber::fmt()
|
||||
.with_max_level(tracing::Level::INFO)
|
||||
.init();
|
||||
|
||||
tracing::info!("OSTP Installer starting");
|
||||
|
||||
// Show wizard UI
|
||||
wizard::run_wizard()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn is_elevated() -> bool {
|
||||
use winapi::um::securitybaseapi::*;
|
||||
use winapi::um::processthreadsapi::*;
|
||||
use winapi::um::winnt::*;
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
|
||||
unsafe {
|
||||
let mut handle: HANDLE = ptr::null_mut();
|
||||
if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut handle) == 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut elevation: TOKEN_ELEVATION = mem::zeroed();
|
||||
let mut size = mem::size_of::<TOKEN_ELEVATION>() as u32;
|
||||
|
||||
if GetTokenInformation(
|
||||
handle,
|
||||
TokenElevation,
|
||||
&mut elevation as *mut _ as *mut _,
|
||||
size,
|
||||
&mut size,
|
||||
) == 0
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
elevation.TokenIsElevated != 0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_install_path() -> PathBuf {
|
||||
PathBuf::from(r"C:\Program Files\Ospab\OSTP")
|
||||
}
|
||||
|
||||
pub fn install_wintun_dll() -> Result<()> {
|
||||
let install_path = get_install_path();
|
||||
std::fs::create_dir_all(&install_path)?;
|
||||
|
||||
// TODO: Copy wintun.dll when available
|
||||
// let wintun_path = install_path.join("wintun.dll");
|
||||
// std::fs::write(&wintun_path, WINTUN_DLL)
|
||||
// .context("Failed to write wintun.dll")?;
|
||||
|
||||
tracing::info!("Wintun DLL installation placeholder");
|
||||
Ok(())
|
||||
}
|
||||
80
ostp-installer/src/service.rs
Normal file
80
ostp-installer/src/service.rs
Normal file
@@ -0,0 +1,80 @@
|
||||
//! Windows Service registration
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use std::ffi::OsString;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use windows::Win32::System::Services::*;
|
||||
use windows::Win32::Foundation::*;
|
||||
use windows::core::PCWSTR;
|
||||
|
||||
pub fn install_service() -> Result<()> {
|
||||
let service_name = "OspabGuard";
|
||||
let display_name = "Ospab OSTP VPN Service";
|
||||
let description = "Manages secure OSTP VPN tunnel connections";
|
||||
|
||||
let daemon_path = crate::get_install_path().join("ostp-daemon.exe");
|
||||
|
||||
unsafe {
|
||||
// Open Service Control Manager
|
||||
let scm = OpenSCManagerW(
|
||||
PCWSTR::null(),
|
||||
PCWSTR::null(),
|
||||
SC_MANAGER_CREATE_SERVICE,
|
||||
)?;
|
||||
|
||||
if scm.is_invalid() {
|
||||
anyhow::bail!("Failed to open Service Control Manager");
|
||||
}
|
||||
|
||||
// Create service
|
||||
let service_name_wide = to_wide_string(service_name);
|
||||
let display_name_wide = to_wide_string(display_name);
|
||||
let binary_path_wide = to_wide_string(&daemon_path.to_string_lossy());
|
||||
|
||||
let service = CreateServiceW(
|
||||
scm,
|
||||
PCWSTR(service_name_wide.as_ptr()),
|
||||
PCWSTR(display_name_wide.as_ptr()),
|
||||
SERVICE_ALL_ACCESS,
|
||||
SERVICE_WIN32_OWN_PROCESS,
|
||||
SERVICE_AUTO_START,
|
||||
SERVICE_ERROR_NORMAL,
|
||||
PCWSTR(binary_path_wide.as_ptr()),
|
||||
PCWSTR::null(),
|
||||
None,
|
||||
PCWSTR::null(),
|
||||
PCWSTR::null(),
|
||||
PCWSTR::null(),
|
||||
)?;
|
||||
|
||||
if service.is_invalid() {
|
||||
CloseServiceHandle(scm)?;
|
||||
anyhow::bail!("Failed to create service");
|
||||
}
|
||||
|
||||
// Set description
|
||||
let description_wide = to_wide_string(description);
|
||||
let mut service_desc = SERVICE_DESCRIPTIONW {
|
||||
lpDescription: PWSTR(description_wide.as_ptr() as *mut _),
|
||||
};
|
||||
|
||||
ChangeServiceConfig2W(
|
||||
service,
|
||||
SERVICE_CONFIG_DESCRIPTION,
|
||||
Some(&service_desc as *const _ as *const _),
|
||||
)?;
|
||||
|
||||
CloseServiceHandle(service)?;
|
||||
CloseServiceHandle(scm)?;
|
||||
}
|
||||
|
||||
tracing::info!("Service '{}' installed successfully", service_name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn to_wide_string(s: &str) -> Vec<u16> {
|
||||
OsString::from(s)
|
||||
.encode_wide()
|
||||
.chain(std::iter::once(0))
|
||||
.collect()
|
||||
}
|
||||
10
ostp-installer/src/wintun.rs
Normal file
10
ostp-installer/src/wintun.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
//! Wintun driver installation
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
pub fn install_wintun_driver() -> Result<()> {
|
||||
// Wintun doesn't require manual driver installation
|
||||
// The DLL handles driver installation automatically on first use
|
||||
tracing::info!("Wintun DLL will auto-install driver on first use");
|
||||
Ok(())
|
||||
}
|
||||
100
ostp-installer/src/wizard.rs
Normal file
100
ostp-installer/src/wizard.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
//! Wizard UI for installation process
|
||||
|
||||
use anyhow::Result;
|
||||
use native_windows_gui as nwg;
|
||||
use native_windows_derive as nwd;
|
||||
|
||||
use crate::{install_wintun_dll, service, firewall};
|
||||
|
||||
#[derive(Default, nwd::NwgUi)]
|
||||
pub struct InstallerWizard {
|
||||
#[nwg_control(size: (600, 400), position: (300, 300), title: "OSTP Installer", flags: "WINDOW|VISIBLE")]
|
||||
#[nwg_events(OnWindowClose: [InstallerWizard::exit])]
|
||||
window: nwg::Window,
|
||||
|
||||
#[nwg_layout(parent: window, spacing: 1)]
|
||||
grid: nwg::GridLayout,
|
||||
|
||||
#[nwg_control(text: "Welcome to OSTP Installer", font: Some(&data.title_font))]
|
||||
#[nwg_layout_item(layout: grid, row: 0, col: 0, col_span: 2)]
|
||||
title: nwg::Label,
|
||||
|
||||
#[nwg_control(text: "This wizard will install OSTP VPN on your system.\n\nClick Next to continue.")]
|
||||
#[nwg_layout_item(layout: grid, row: 1, col: 0, col_span: 2, row_span: 4)]
|
||||
description: nwg::Label,
|
||||
|
||||
#[nwg_control(text: "Next")]
|
||||
#[nwg_layout_item(layout: grid, row: 5, col: 1)]
|
||||
#[nwg_events(OnButtonClick: [InstallerWizard::next])]
|
||||
next_button: nwg::Button,
|
||||
|
||||
#[nwg_control(text: "Cancel")]
|
||||
#[nwg_layout_item(layout: grid, row: 5, col: 0)]
|
||||
#[nwg_events(OnButtonClick: [InstallerWizard::exit])]
|
||||
cancel_button: nwg::Button,
|
||||
|
||||
#[nwg_resource(family: "Segoe UI", size: 18, weight: 700)]
|
||||
title_font: nwg::Font,
|
||||
|
||||
step: std::cell::RefCell<usize>,
|
||||
}
|
||||
|
||||
impl InstallerWizard {
|
||||
fn next(&self) {
|
||||
let mut step = self.step.borrow_mut();
|
||||
*step += 1;
|
||||
|
||||
match *step {
|
||||
1 => self.install_step(),
|
||||
2 => self.finish_step(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn install_step(&self) {
|
||||
self.title.set_text("Installing...");
|
||||
self.description.set_text("Please wait while OSTP is being installed.");
|
||||
self.next_button.set_enabled(false);
|
||||
|
||||
// Perform installation
|
||||
std::thread::spawn(move || {
|
||||
if let Err(e) = perform_installation() {
|
||||
eprintln!("Installation error: {}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn finish_step(&self) {
|
||||
self.title.set_text("Installation Complete");
|
||||
self.description.set_text("OSTP has been successfully installed.\n\nClick Finish to exit.");
|
||||
self.next_button.set_text("Finish");
|
||||
}
|
||||
|
||||
fn exit(&self) {
|
||||
nwg::stop_thread_dispatch();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_wizard() -> Result<()> {
|
||||
nwg::init()?;
|
||||
nwg::Font::set_global_family("Segoe UI")?;
|
||||
|
||||
let _app = InstallerWizard::default();
|
||||
nwg::dispatch_thread_events();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn perform_installation() -> Result<()> {
|
||||
tracing::info!("Step 1: Installing Wintun DLL");
|
||||
install_wintun_dll()?;
|
||||
|
||||
tracing::info!("Step 2: Registering Windows Service");
|
||||
service::install_service()?;
|
||||
|
||||
tracing::info!("Step 3: Configuring Firewall");
|
||||
firewall::add_firewall_rules()?;
|
||||
|
||||
tracing::info!("Installation complete");
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user