feat: add setcap step to Setup tab for port 443 binding
Adds Step 2 in the Setup tab: grants cap_net_bind_service to the bridge binary via pkexec/sudo so it can bind port 443 directly. On success, automatically updates bridge_listen_addr to 0.0.0.0:443 — no iptables rules required. Step re-detects cap state on each frame so it stays accurate after a rebuild clears the capability. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+65
-1
@@ -34,6 +34,7 @@ pub struct LauncherApp {
|
|||||||
|
|
||||||
// Setup state
|
// Setup state
|
||||||
hook_deployed: bool,
|
hook_deployed: bool,
|
||||||
|
bridge_has_cap: bool,
|
||||||
cert_path: Option<std::path::PathBuf>,
|
cert_path: Option<std::path::PathBuf>,
|
||||||
setup_message: Option<(bool, String)>, // (success, text)
|
setup_message: Option<(bool, String)>, // (success, text)
|
||||||
}
|
}
|
||||||
@@ -56,6 +57,8 @@ impl LauncherApp {
|
|||||||
let cert_path = setup::find_bridge_cert(&config.bridge_captures_dir);
|
let cert_path = setup::find_bridge_cert(&config.bridge_captures_dir);
|
||||||
let hook_deployed =
|
let hook_deployed =
|
||||||
setup::hook_dll_deployed(std::path::Path::new(&config.fifa_game_dir));
|
setup::hook_dll_deployed(std::path::Path::new(&config.fifa_game_dir));
|
||||||
|
let bridge_has_cap =
|
||||||
|
setup::bridge_has_cap443(std::path::Path::new(&config.bridge_binary));
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
config,
|
config,
|
||||||
@@ -68,6 +71,7 @@ impl LauncherApp {
|
|||||||
log_tab: 0,
|
log_tab: 0,
|
||||||
log_follow: true,
|
log_follow: true,
|
||||||
hook_deployed,
|
hook_deployed,
|
||||||
|
bridge_has_cap,
|
||||||
cert_path,
|
cert_path,
|
||||||
setup_message: None,
|
setup_message: None,
|
||||||
}
|
}
|
||||||
@@ -379,10 +383,70 @@ impl LauncherApp {
|
|||||||
|
|
||||||
ui.add_space(12.0);
|
ui.add_space(12.0);
|
||||||
|
|
||||||
|
// ── Port 443 capability ───────────────────────────────────────────────
|
||||||
|
ui.group(|ui| {
|
||||||
|
ui.set_min_width(ui.available_width());
|
||||||
|
ui.strong("Step 2 — Grant port 443 capability");
|
||||||
|
ui.add_space(4.0);
|
||||||
|
ui.label("Allows the bridge to bind port 443 directly — the same port FIFA 23 \
|
||||||
|
uses for HTTPS — without running as root. No iptables rules needed.");
|
||||||
|
ui.add_space(6.0);
|
||||||
|
|
||||||
|
// Clone to avoid holding a borrow on config while we mutate it below.
|
||||||
|
let binary_path = std::path::PathBuf::from(&self.config.bridge_binary);
|
||||||
|
self.bridge_has_cap = setup::bridge_has_cap443(&binary_path);
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
if self.bridge_has_cap {
|
||||||
|
ui.colored_label(Color32::from_rgb(80, 200, 120), "✔ cap_net_bind_service granted");
|
||||||
|
} else {
|
||||||
|
ui.colored_label(Color32::from_rgb(220, 60, 60), "✘ Not set");
|
||||||
|
ui.add_space(8.0);
|
||||||
|
let binary_exists = binary_path.exists();
|
||||||
|
if ui
|
||||||
|
.add_enabled(binary_exists, egui::Button::new("Grant (requires sudo)"))
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
match setup::setcap_bridge_443(&binary_path) {
|
||||||
|
Ok(()) => {
|
||||||
|
self.bridge_has_cap = true;
|
||||||
|
// Switch listen addr to 443 automatically
|
||||||
|
self.config.bridge_listen_addr = "0.0.0.0:443".into();
|
||||||
|
self.config.save();
|
||||||
|
self.setup_message = Some((
|
||||||
|
true,
|
||||||
|
"cap_net_bind_service granted. Bridge listen addr set to 0.0.0.0:443.".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Err(e) => self.setup_message = Some((false, format!("setcap failed: {e}"))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !binary_exists {
|
||||||
|
ui.add_space(4.0);
|
||||||
|
ui.colored_label(
|
||||||
|
Color32::from_rgb(220, 150, 0),
|
||||||
|
"⚠ Bridge binary not found — build it first.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.add_space(4.0);
|
||||||
|
ui.label(
|
||||||
|
egui::RichText::new(
|
||||||
|
"Re-run this step any time the bridge binary is rebuilt (setcap is cleared on recompile).",
|
||||||
|
)
|
||||||
|
.weak()
|
||||||
|
.small(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.add_space(12.0);
|
||||||
|
|
||||||
// ── Cert install ──────────────────────────────────────────────────────
|
// ── Cert install ──────────────────────────────────────────────────────
|
||||||
ui.group(|ui| {
|
ui.group(|ui| {
|
||||||
ui.set_min_width(ui.available_width());
|
ui.set_min_width(ui.available_width());
|
||||||
ui.strong("Step 2 — Install TLS certificate");
|
ui.strong("Step 3 — Install TLS certificate");
|
||||||
ui.add_space(4.0);
|
ui.add_space(4.0);
|
||||||
ui.label("Installs the bridge's self-signed cert into the Wine/Proton cert store \
|
ui.label("Installs the bridge's self-signed cert into the Wine/Proton cert store \
|
||||||
so the game accepts HTTPS connections to the bridge.");
|
so the game accepts HTTPS connections to the bridge.");
|
||||||
|
|||||||
@@ -1,5 +1,26 @@
|
|||||||
use std::{path::{Path, PathBuf}, process::Command};
|
use std::{path::{Path, PathBuf}, process::Command};
|
||||||
|
|
||||||
|
// ── Port 443 capability ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/// Check whether the bridge binary already has cap_net_bind_service set.
|
||||||
|
pub fn bridge_has_cap443(binary: &Path) -> bool {
|
||||||
|
std::process::Command::new("getcap")
|
||||||
|
.arg(binary)
|
||||||
|
.output()
|
||||||
|
.map(|o| String::from_utf8_lossy(&o.stdout).contains("cap_net_bind_service"))
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Grant cap_net_bind_service to the bridge binary so it can bind port 443
|
||||||
|
/// without running as root. Uses pkexec (or sudo as fallback).
|
||||||
|
pub fn setcap_bridge_443(binary: &Path) -> anyhow::Result<()> {
|
||||||
|
let script = format!(
|
||||||
|
"setcap cap_net_bind_service=+ep '{}'",
|
||||||
|
binary.to_string_lossy()
|
||||||
|
);
|
||||||
|
run_elevated(&script)
|
||||||
|
}
|
||||||
|
|
||||||
// ── Cert installation ─────────────────────────────────────────────────────────
|
// ── Cert installation ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// Find the bridge cert in the given captures dir.
|
/// Find the bridge cert in the given captures dir.
|
||||||
|
|||||||
Reference in New Issue
Block a user