mirror of
https://github.com/usestrix/firewall.git
synced 2026-11-09 05:19:35 -10:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
219765e4ff | ||
|
|
8b0d734f02 | ||
|
|
10fdd0f272 |
No files matched your search
+1
-1
@@ -118,7 +118,7 @@ schannel = "0.1.29"
|
||||
tokio = { version = "1.53.1", features = ["net"] }
|
||||
tracing-appender = "0.2.5"
|
||||
tray-icon = { version = "0.26.1", default-features = false }
|
||||
windows = { version = "0.62", features = ["Win32_Foundation", "Win32_Security", "Win32_Security_Authorization", "Win32_Security_Cryptography", "Win32_System_Com", "Win32_System_Console", "Win32_System_Environment", "Win32_System_JobObjects", "Win32_System_Threading", "Win32_UI_Shell", "Win32_UI_WindowsAndMessaging"] }
|
||||
windows = { version = "0.62", features = ["Win32_Foundation", "Win32_Security", "Win32_Security_Authorization", "Win32_Security_Cryptography", "Win32_System_Com", "Win32_System_Environment", "Win32_System_Threading", "Win32_UI_Shell", "Win32_UI_WindowsAndMessaging"] }
|
||||
windows-registry = "0.6.1"
|
||||
windows-service = "0.8.0"
|
||||
|
||||
|
||||
@@ -89,16 +89,12 @@ async fn handle(req: Request, firewall: &Firewall) -> Result<Response, Infallibl
|
||||
}
|
||||
|
||||
info!(method = %req.method(), %url, "forwarding");
|
||||
// Only an HTTPS registry was downgraded to reach us, so only it is upgraded back and rewritten.
|
||||
let tls = firewall.is_https_url(&url);
|
||||
let index = tls && firewall.is_index_url(&url);
|
||||
let index = firewall.is_index_url(&url);
|
||||
let (mut parts, body) = req.into_parts();
|
||||
if tls {
|
||||
let Ok(uri) = url.parse() else {
|
||||
return Ok(text(400, "firewall: expected an absolute URL\n"));
|
||||
};
|
||||
parts.uri = uri;
|
||||
}
|
||||
let Ok(uri) = url.parse() else {
|
||||
return Ok(text(400, "firewall: expected an absolute URL\n"));
|
||||
};
|
||||
parts.uri = uri;
|
||||
|
||||
let client = DecompressionLayer::new().into_layer(EasyHttpWebClient::default());
|
||||
let response = match client.serve(Request::from_parts(parts, body)).await {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
pub mod forward;
|
||||
pub mod managers;
|
||||
#[cfg(windows)]
|
||||
mod windows;
|
||||
|
||||
pub use forward::spawn;
|
||||
pub use managers::*;
|
||||
@@ -36,8 +34,7 @@ impl Invocation {
|
||||
|
||||
pub fn run(port: u16, invocation: &Invocation, dir: Option<&Path>) -> std::io::Result<i32> {
|
||||
let proxy = crate::local_url(port, "");
|
||||
let program = resolve(&invocation.program)?;
|
||||
let mut command = Command::new(&program);
|
||||
let mut command = Command::new(resolve(&invocation.program)?);
|
||||
command.args(invocation.arguments(&proxy));
|
||||
if let Some(dir) = dir {
|
||||
command.current_dir(dir);
|
||||
@@ -45,12 +42,7 @@ pub fn run(port: u16, invocation: &Invocation, dir: Option<&Path>) -> std::io::R
|
||||
for (key, value) in managers::variables(&proxy) {
|
||||
command.env(key, value);
|
||||
}
|
||||
#[cfg(windows)]
|
||||
windows::prepare();
|
||||
let status = command.status();
|
||||
#[cfg(windows)]
|
||||
let status = status.map_err(|error| windows::explain(error, &program));
|
||||
Ok(status?.code().unwrap_or(1))
|
||||
Ok(command.status()?.code().unwrap_or(1))
|
||||
}
|
||||
|
||||
// Command::new only finds .exe on Windows; npm, pnpm and gem are .cmd shims found via PATHEXT.
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::path::Path;
|
||||
use std::sync::Once;
|
||||
use tracing::debug;
|
||||
use windows::core::{BOOL, PCWSTR};
|
||||
use windows::Win32::System::Console::{SetConsoleCtrlHandler, CTRL_BREAK_EVENT, CTRL_C_EVENT};
|
||||
use windows::Win32::System::JobObjects::{
|
||||
AssignProcessToJobObject, CreateJobObjectW, JobObjectExtendedLimitInformation,
|
||||
SetInformationJobObject, JOBOBJECT_EXTENDED_LIMIT_INFORMATION,
|
||||
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE,
|
||||
};
|
||||
use windows::Win32::System::Threading::GetCurrentProcess;
|
||||
|
||||
/// Called before spawning, so the manager behaves as it would run from the shell directly.
|
||||
/// Once per process: a caller may run several managers.
|
||||
pub fn prepare() {
|
||||
static PREPARED: Once = Once::new();
|
||||
PREPARED.call_once(|| {
|
||||
// The console sends Ctrl+C to the manager too. Exiting first would hand the prompt back
|
||||
// while a .cmd shim still asks "Terminate batch job (Y/N)?", so wait for its exit instead.
|
||||
// A handler, unlike ignoring the signal outright, is not inherited by the manager.
|
||||
let _ = unsafe { SetConsoleCtrlHandler(Some(ignore_interrupt), true) };
|
||||
if let Err(error) = kill_children_on_exit() {
|
||||
debug!(%error, "the manager may outlive the firewall");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
unsafe extern "system" fn ignore_interrupt(event: u32) -> BOOL {
|
||||
(event == CTRL_C_EVENT || event == CTRL_BREAK_EVENT).into()
|
||||
}
|
||||
|
||||
/// Joins a job that kills what is left in it once the firewall exits, even if killed itself.
|
||||
/// The manager inherits the job when spawned, as do its own children.
|
||||
fn kill_children_on_exit() -> windows::core::Result<()> {
|
||||
let mut limits = JOBOBJECT_EXTENDED_LIMIT_INFORMATION::default();
|
||||
limits.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
|
||||
// Never closed: the handle closing as the firewall exits is what kills the rest.
|
||||
let job = unsafe { CreateJobObjectW(None, PCWSTR::null())? };
|
||||
unsafe {
|
||||
SetInformationJobObject(
|
||||
job,
|
||||
JobObjectExtendedLimitInformation,
|
||||
(&raw const limits).cast(),
|
||||
size_of_val(&limits) as u32,
|
||||
)?
|
||||
};
|
||||
unsafe { AssignProcessToJobObject(job, GetCurrentProcess()) }
|
||||
}
|
||||
|
||||
/// Rust refuses to pass a line break to a batch file, where it would cut the command short.
|
||||
pub fn explain(error: Error, program: &Path) -> Error {
|
||||
let batch = program
|
||||
.extension()
|
||||
.is_some_and(|ext| ext.eq_ignore_ascii_case("cmd") || ext.eq_ignore_ascii_case("bat"));
|
||||
if !batch || error.kind() != ErrorKind::InvalidInput {
|
||||
return error;
|
||||
}
|
||||
let name = program.file_name().unwrap_or(program.as_os_str()).to_string_lossy();
|
||||
Error::new(
|
||||
ErrorKind::InvalidInput,
|
||||
format!("{name} is a batch file, which cannot be passed an argument with a line break"),
|
||||
)
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
use crate::policies::Package;
|
||||
use serde::Serialize;
|
||||
use std::sync::Mutex;
|
||||
|
||||
/// Bounded, so blocks pile up harmlessly while nothing collects them.
|
||||
const KEPT: usize = 50;
|
||||
|
||||
static RECENT: Mutex<Vec<BlockedPackage>> = Mutex::new(Vec::new());
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct BlockedPackage {
|
||||
pub eco: &'static str,
|
||||
pub name: String,
|
||||
pub version: Option<String>,
|
||||
pub filter: &'static str,
|
||||
pub reason: String,
|
||||
}
|
||||
|
||||
pub fn record(eco: &'static str, pkg: &Package, filter: &'static str, reason: &str) {
|
||||
let mut recent = RECENT.lock().unwrap_or_else(|e| e.into_inner());
|
||||
if recent.len() == KEPT {
|
||||
recent.remove(0);
|
||||
}
|
||||
recent.push(BlockedPackage {
|
||||
eco,
|
||||
name: pkg.name.clone(),
|
||||
version: pkg.version.clone(),
|
||||
filter,
|
||||
reason: reason.to_owned(),
|
||||
});
|
||||
}
|
||||
|
||||
/// Hands each block over once.
|
||||
pub fn take() -> Vec<BlockedPackage> {
|
||||
std::mem::take(&mut *RECENT.lock().unwrap_or_else(|e| e.into_inner()))
|
||||
}
|
||||
@@ -48,7 +48,7 @@ impl Default for FirewallConfig {
|
||||
FirewallConfig {
|
||||
port: DEFAULT_PORT,
|
||||
fail_open: true,
|
||||
min_age_hours: 24,
|
||||
min_age_hours: 0,
|
||||
denylist: DenylistSource::default(),
|
||||
allow: HashMap::new(),
|
||||
telemetry: true,
|
||||
|
||||
@@ -47,11 +47,6 @@ impl Firewall {
|
||||
self.config.hosts()
|
||||
}
|
||||
|
||||
pub fn is_https_url(&self, url: &str) -> bool {
|
||||
let host = host_of(url);
|
||||
self.config.registries.iter().any(|r| r.upstream.starts_with("https://") && r.host() == host)
|
||||
}
|
||||
|
||||
pub fn is_index_url(&self, url: &str) -> bool {
|
||||
self.config.registries.iter().any(|r| r.index && r.host() == host_of(url))
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
pub mod aliasing;
|
||||
pub mod blocks;
|
||||
pub mod config;
|
||||
pub mod firewall;
|
||||
pub mod http;
|
||||
|
||||
@@ -5,7 +5,7 @@ mod uninstall;
|
||||
pub use uninstall::uninstall;
|
||||
|
||||
use crate::config::FirewallConfig;
|
||||
use crate::{blocks, mitm, telemetry};
|
||||
use crate::{mitm, telemetry};
|
||||
use crate::Firewall;
|
||||
use rama::bytes::Bytes;
|
||||
use rama::error::BoxError;
|
||||
@@ -58,7 +58,6 @@ impl TransparentProxyHandler for Handler {
|
||||
let message = String::from_utf8_lossy(&message).into_owned();
|
||||
Some(Bytes::from(match message.as_str() {
|
||||
"ca" => self.ca.pem.clone(),
|
||||
"blocks" => serde_json::to_string(&blocks::take()).expect("blocks serialize"),
|
||||
other => format!("error: unknown message {other}"),
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
use super::*;
|
||||
use muda::{Menu, MenuEvent, MenuItem, PredefinedMenuItem};
|
||||
use std::io::Cursor;
|
||||
use std::time::Duration;
|
||||
use tray_icon::{Icon, MouseButtonState, TrayIcon, TrayIconBuilder, TrayIconEvent};
|
||||
use windows::core::{w, PCWSTR};
|
||||
use windows::Win32::Foundation::*;
|
||||
@@ -15,7 +14,6 @@ use windows_service::service::{Service, ServiceAccess, ServiceState};
|
||||
use windows_service::service_manager::{ServiceManager, ServiceManagerAccess};
|
||||
|
||||
const MENU_PAUSE: &str = "pause";
|
||||
const MENU_RESTART: &str = "restart";
|
||||
const MENU_LOG: &str = "log";
|
||||
const MENU_QUIT: &str = "quit";
|
||||
const LOGO: &[u8] = include_bytes!("tray.png");
|
||||
@@ -33,7 +31,6 @@ pub fn run() -> Result<(), BoxError> {
|
||||
&status,
|
||||
&PredefinedMenuItem::separator(),
|
||||
&pause,
|
||||
&MenuItem::with_id(MENU_RESTART, "Restart", true, None),
|
||||
&MenuItem::with_id(MENU_LOG, "Open logs", true, None),
|
||||
&PredefinedMenuItem::separator(),
|
||||
&MenuItem::with_id(MENU_QUIT, "Quit", true, None),
|
||||
@@ -126,11 +123,6 @@ impl Tray {
|
||||
|| Ok(open_service()?.resume().map(drop)?)
|
||||
}
|
||||
MENU_PAUSE => || Ok(open_service()?.start::<&str>(&[])?),
|
||||
MENU_RESTART => {
|
||||
// Off the UI thread, since stopping takes a while.
|
||||
std::thread::spawn(|| restart().unwrap_or_else(show_error));
|
||||
return;
|
||||
}
|
||||
MENU_LOG => {
|
||||
let log = HSTRING::from(data_dir().as_path());
|
||||
let none = PCWSTR::null();
|
||||
@@ -139,15 +131,13 @@ impl Tray {
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
action().unwrap_or_else(show_error);
|
||||
if let Err(error) = action() {
|
||||
let text = HSTRING::from(error.to_string());
|
||||
unsafe { MessageBoxW(None, &text, w!("Strix Firewall"), MB_ICONERROR) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn show_error(error: BoxError) {
|
||||
let text = HSTRING::from(error.to_string());
|
||||
unsafe { MessageBoxW(None, &text, w!("Strix Firewall"), MB_ICONERROR) };
|
||||
}
|
||||
|
||||
/// `None` when the service is not installed.
|
||||
fn service_state() -> Option<ServiceState> {
|
||||
let manager =
|
||||
@@ -176,28 +166,10 @@ fn status_text(state: Option<ServiceState>) -> &'static str {
|
||||
|
||||
fn open_service() -> Result<Service, BoxError> {
|
||||
let manager = ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CONNECT)?;
|
||||
let access = ServiceAccess::QUERY_STATUS
|
||||
| ServiceAccess::START
|
||||
| ServiceAccess::STOP
|
||||
| ServiceAccess::PAUSE_CONTINUE;
|
||||
let access = ServiceAccess::START | ServiceAccess::PAUSE_CONTINUE;
|
||||
Ok(manager.open_service(SERVICE_NAME, access)?)
|
||||
}
|
||||
|
||||
/// Starting again re-reads the config.
|
||||
fn restart() -> Result<(), BoxError> {
|
||||
let service = open_service()?;
|
||||
if service.query_status()?.current_state != ServiceState::Stopped {
|
||||
service.stop()?;
|
||||
}
|
||||
for _ in 0..60 {
|
||||
if service.query_status()?.current_state == ServiceState::Stopped {
|
||||
return Ok(service.start::<&str>(&[])?);
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(500));
|
||||
}
|
||||
Err("The service did not stop.".into())
|
||||
}
|
||||
|
||||
/// The alpha channel of the RGBA logo, and its width.
|
||||
fn logo_mask() -> Result<(Vec<u8>, u32), BoxError> {
|
||||
let mut reader = png::Decoder::new(Cursor::new(LOGO)).read_info()?;
|
||||
|
||||
@@ -69,8 +69,7 @@ pub async fn screen(url: &str, firewall: &Firewall) -> Option<String> {
|
||||
};
|
||||
info!(eco = registry.eco, pkg = %pkg.label(), %url, "screening");
|
||||
match consult(firewall, registry, &pkg).await {
|
||||
Some((filter, Verdict::Deny(reason))) => {
|
||||
crate::blocks::record(registry.eco, &pkg, filter, &reason);
|
||||
Some((_, Verdict::Deny(reason))) => {
|
||||
Some(format!("{} {reason}", pkg.label()))
|
||||
}
|
||||
Some((filter, Verdict::Unknown(e))) if !firewall.config.fail_open => {
|
||||
|
||||
+1
-22
@@ -1,9 +1,7 @@
|
||||
use firewall::aliasing::forward::downgrade;
|
||||
use firewall::http::https_url;
|
||||
use firewall::{aliasing, Firewall, FirewallConfig};
|
||||
use firewall::Firewall;
|
||||
use rama::http::{Body, Request};
|
||||
use std::io::{Read, Write};
|
||||
use std::net::{TcpListener, TcpStream};
|
||||
|
||||
fn request(target: &str, host: Option<&str>) -> Request {
|
||||
let mut builder = Request::builder().method("GET").uri(target);
|
||||
@@ -37,25 +35,6 @@ fn requests_always_go_out_over_tls() {
|
||||
assert_eq!(https_url(&request("/six", None)), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_plain_http_upstream_is_not_upgraded() {
|
||||
let upstream = TcpListener::bind("127.0.0.1:0").expect("bind");
|
||||
let addr = upstream.local_addr().expect("address");
|
||||
std::thread::spawn(move || {
|
||||
let (mut stream, _) = upstream.accept().expect("accept");
|
||||
let _ = stream.read(&mut [0; 4096]);
|
||||
let _ = stream.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nok");
|
||||
});
|
||||
|
||||
let port = aliasing::spawn(0, FirewallConfig::default()).expect("spawn");
|
||||
let mut proxy = TcpStream::connect(("127.0.0.1", port)).expect("connect");
|
||||
let request = format!("GET http://{addr}/x HTTP/1.1\r\nHost: {addr}\r\nConnection: close\r\n\r\n");
|
||||
proxy.write_all(request.as_bytes()).expect("write");
|
||||
let mut response = String::new();
|
||||
proxy.read_to_string(&mut response).expect("read");
|
||||
assert!(response.starts_with("HTTP/1.1 200") && response.ends_with("ok"), "{response}");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn downgrade_touches_known_hosts_in_text_only() {
|
||||
let text = b"https://pypi.org/x https://example.com/y".to_vec();
|
||||
|
||||
@@ -162,12 +162,6 @@
|
||||
Execute='deferred'
|
||||
Impersonate='no'
|
||||
Return='check'/>
|
||||
<!-- From the exit dialog, which runs as the signed-in user rather than elevated. -->
|
||||
<CustomAction
|
||||
Id='StartTray'
|
||||
Directory='APPLICATIONFOLDER'
|
||||
ExeCommand='"[#exe2]"'
|
||||
Return='asyncNoWait'/>
|
||||
<!-- Before its file is replaced or removed. Trays WM_CLOSE does not reach, as in other sessions, are terminated. -->
|
||||
<util:CloseApplication Id='CloseTray' Target='firewall-tray.exe' CloseMessage='yes' RebootPrompt='no' TerminateProcess='0'/>
|
||||
<InstallExecuteSequence>
|
||||
@@ -186,7 +180,6 @@
|
||||
<UIRef Id='WixUI_FeatureTree'/>
|
||||
<Publish Dialog='WelcomeDlg' Control='Next' Event='NewDialog' Value='CustomizeDlg' Order='99'>1</Publish>
|
||||
<Publish Dialog='CustomizeDlg' Control='Back' Event='NewDialog' Value='WelcomeDlg' Order='99'>1</Publish>
|
||||
<Publish Dialog='ExitDialog' Control='Finish' Event='DoAction' Value='StartTray' Order='1'>NOT Installed</Publish>
|
||||
</UI>
|
||||
</Product>
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import AppKit
|
||||
|
||||
struct BlockedPackage: Codable, Hashable, Identifiable {
|
||||
let eco: String
|
||||
let name: String
|
||||
let version: String?
|
||||
let filter: String
|
||||
let reason: String
|
||||
|
||||
var id: String { "\(eco)/\(name)@\(version ?? "")" }
|
||||
var label: String { version.map { "\(name) \($0)" } ?? name }
|
||||
/// The denylist and OSV only flag packages known to be malicious; the age check only holds back new ones.
|
||||
var malicious: Bool { filter != "age" }
|
||||
}
|
||||
|
||||
@MainActor
|
||||
enum BlockAlert {
|
||||
static func show(_ package: BlockedPackage) {
|
||||
let alert = NSAlert()
|
||||
alert.addButton(withTitle: "OK")
|
||||
if package.malicious {
|
||||
alert.alertStyle = .critical
|
||||
alert.messageText = "Blocked a malicious package"
|
||||
alert.informativeText = "\(package.label) (\(package.eco)) \(package.reason). Do not install it."
|
||||
} else {
|
||||
alert.messageText = "Held back a new package"
|
||||
alert.informativeText = "\(package.label) (\(package.eco)) \(package.reason)."
|
||||
}
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
alert.runModal()
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,6 @@ final class FirewallModel: NSObject, ObservableObject {
|
||||
@Published private(set) var completed = false
|
||||
@Published private(set) var removed = false
|
||||
@Published private(set) var screening = false
|
||||
@Published private(set) var blocked: [BlockedPackage] = []
|
||||
|
||||
private let logger = Logger(subsystem: FirewallIDs.appBundleID, category: "installer")
|
||||
private var pendingRequest: (request: OSSystemExtensionRequest, continuation: CheckedContinuation<Void, Error>)?
|
||||
@@ -73,7 +72,6 @@ final class FirewallModel: NSObject, ObservableObject {
|
||||
await refresh()
|
||||
if CommandLine.arguments.contains("--install") { await install() }
|
||||
}
|
||||
Task { await watchBlocks() }
|
||||
}
|
||||
|
||||
// MARK: - Flows
|
||||
@@ -201,21 +199,6 @@ final class FirewallModel: NSObject, ObservableObject {
|
||||
if let url = URL(string: pane) { NSWorkspace.shared.open(url) }
|
||||
}
|
||||
|
||||
/// The extension cannot message the app, so the app asks it for blocks.
|
||||
private func watchBlocks() async {
|
||||
while true {
|
||||
if screening, let reply = try? await providerMessage("blocks"),
|
||||
let packages = try? JSONDecoder().decode([BlockedPackage].self, from: Data(reply.utf8)) {
|
||||
for package in packages where !blocked.contains(package) {
|
||||
blocked.append(package)
|
||||
BlockAlert.show(package)
|
||||
}
|
||||
blocked = Array(blocked.suffix(10))
|
||||
}
|
||||
try? await Task.sleep(nanoseconds: 2_000_000_000)
|
||||
}
|
||||
}
|
||||
|
||||
private func stage(_ step: ReferenceWritableKeyPath<FirewallModel, StepStatus>,
|
||||
running: String,
|
||||
work: @MainActor () async throws -> String) async -> Bool {
|
||||
|
||||
@@ -22,20 +22,5 @@ struct MenuBarView: View {
|
||||
Task { await model.restart() }
|
||||
}
|
||||
.disabled(model.busy)
|
||||
|
||||
if !model.blocked.isEmpty {
|
||||
Divider()
|
||||
ForEach(model.blocked) { package in
|
||||
if !package.malicious {
|
||||
Menu("Held back \(package.label) (\(package.eco))") {
|
||||
Text(package.reason)
|
||||
}
|
||||
} else {
|
||||
Menu("Blocked \(package.label) (\(package.eco))") {
|
||||
Text("Malicious: \(package.reason)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,6 @@ build() {
|
||||
echo "==> Building"
|
||||
xcodegen generate >/dev/null
|
||||
xcodebuild -project Firewall.xcodeproj -scheme StrixFirewall -configuration $CONFIGURATION \
|
||||
-destination generic/platform=macOS \
|
||||
-derivedDataPath build CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO \
|
||||
CODE_SIGN_IDENTITY="" CODE_SIGN_ENTITLEMENTS="" build >/dev/null
|
||||
}
|
||||
|
||||
+3
-11
@@ -10,7 +10,7 @@ settings:
|
||||
"CARGO_PROFILE[config=Debug]": debug
|
||||
"LIBRARY_SEARCH_PATHS[arch=arm64]": $(SRCROOT)/../core/target/aarch64-apple-darwin/$(CARGO_PROFILE)
|
||||
"LIBRARY_SEARCH_PATHS[arch=x86_64]": $(SRCROOT)/../core/target/x86_64-apple-darwin/$(CARGO_PROFILE)
|
||||
ARCHS: arm64 x86_64
|
||||
ARCHS: arm64
|
||||
"ONLY_ACTIVE_ARCH[config=Debug]": "YES"
|
||||
SWIFT_VERSION: "5.0"
|
||||
DEVELOPMENT_TEAM: ${TEAM}
|
||||
@@ -127,13 +127,5 @@ targets:
|
||||
unset CC CXX CFLAGS CXXFLAGS
|
||||
export SDKROOT="$(xcrun --sdk macosx --show-sdk-path)"
|
||||
cd "$SRCROOT/../core"
|
||||
bins=
|
||||
for arch in $ARCHS; do
|
||||
case $arch in
|
||||
arm64|arm64e) triple=aarch64-apple-darwin ;;
|
||||
x86_64) triple=x86_64-apple-darwin ;;
|
||||
esac
|
||||
cargo build --bin firewall-uninstall --target $triple $([ "$CONFIGURATION" = Debug ] || echo --release)
|
||||
bins="$bins target/$triple/$CARGO_PROFILE/firewall-uninstall"
|
||||
done
|
||||
lipo -create $bins -output "$TARGET_BUILD_DIR/$EXECUTABLE_FOLDER_PATH/firewall-uninstall"
|
||||
cargo build --bin firewall-uninstall --target aarch64-apple-darwin $([ "$CONFIGURATION" = Debug ] || echo --release)
|
||||
cp "target/aarch64-apple-darwin/$CARGO_PROFILE/firewall-uninstall" "$TARGET_BUILD_DIR/$EXECUTABLE_FOLDER_PATH/"
|
||||
Reference in new issue
Block a user