mirror of
https://github.com/usestrix/firewall.git
synced 2026-11-09 13:31:32 -10:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10fdd0f272 |
No files matched your search
@@ -3,6 +3,7 @@ name = "strix-firewall"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
authors = ["Strix"]
|
||||||
description = "Screens package downloads for known malware before they reach your machine"
|
description = "Screens package downloads for known malware before they reach your machine"
|
||||||
repository = "https://github.com/usestrix/firewall"
|
repository = "https://github.com/usestrix/firewall"
|
||||||
homepage = "https://github.com/usestrix/firewall"
|
homepage = "https://github.com/usestrix/firewall"
|
||||||
@@ -18,6 +19,12 @@ x86_64-apple-darwin = ["firewall"]
|
|||||||
aarch64-unknown-linux-gnu = ["firewall"]
|
aarch64-unknown-linux-gnu = ["firewall"]
|
||||||
x86_64-unknown-linux-gnu = ["firewall"]
|
x86_64-unknown-linux-gnu = ["firewall"]
|
||||||
|
|
||||||
|
[package.metadata.wix]
|
||||||
|
upgrade-guid = "D944C2F6-BF55-4B21-A60E-1BBDB1A66293"
|
||||||
|
path-guid = "033B1050-23D0-4F5A-A0D3-F4757B4B67E0"
|
||||||
|
license = false
|
||||||
|
eula = false
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "firewall"
|
name = "firewall"
|
||||||
# The macOS extension links a staticlib, built on demand:
|
# The macOS extension links a staticlib, built on demand:
|
||||||
|
|||||||
@@ -2,14 +2,25 @@
|
|||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn main() {
|
fn main() {
|
||||||
if let Err(e) = firewall::os::windows::run_service() {
|
// Run by the MSI as it removes the firewall.
|
||||||
tracing::error!("could not run the service: {e}");
|
if std::env::args().nth(1).as_deref() == Some("uninstall") {
|
||||||
firewall::telemetry::error("service");
|
firewall::init_logging("info");
|
||||||
firewall::telemetry::flush();
|
if let Err(e) = firewall::os::windows::uninstall() {
|
||||||
std::process::exit(1)
|
fail("uninstall", &format!("could not uninstall: {e}"))
|
||||||
|
}
|
||||||
|
} else if let Err(e) = firewall::os::windows::run_service() {
|
||||||
|
fail("service", &format!("could not run the service: {e}"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn fail(kind: &'static str, message: &str) -> ! {
|
||||||
|
tracing::error!("{message}");
|
||||||
|
firewall::telemetry::error(kind);
|
||||||
|
firewall::telemetry::flush();
|
||||||
|
std::process::exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
/// The service is Windows-only.
|
/// The service is Windows-only.
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
fn main() {}
|
fn main() {}
|
||||||
@@ -7,9 +7,11 @@ use schannel::cert_context::CertContext;
|
|||||||
use schannel::cert_store::{CertAdd, CertStore};
|
use schannel::cert_store::{CertAdd, CertStore};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
use windows::Win32::Security::Authorization::{GetNamedSecurityInfoW, SE_FILE_OBJECT};
|
use windows::Win32::Security::Authorization::{GetNamedSecurityInfoW, SE_FILE_OBJECT};
|
||||||
use windows::Win32::Security::*;
|
use windows::Win32::Security::*;
|
||||||
|
use windows_registry::Type;
|
||||||
|
|
||||||
/// SYSTEM and admins write, users only read, so no one else can plant a CA for node.
|
/// SYSTEM and admins write, users only read, so no one else can plant a CA for node.
|
||||||
const DATA_DIR_ACL: &str = concat!(
|
const DATA_DIR_ACL: &str = concat!(
|
||||||
@@ -20,6 +22,9 @@ const DATA_DIR_ACL: &str = concat!(
|
|||||||
"(A;OICI;FRFX;;;BU)", // allow Users read and execute only
|
"(A;OICI;FRFX;;;BU)", // allow Users read and execute only
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// 1970 in FILETIME, which counts 100 ns from 1601.
|
||||||
|
const FILETIME_UNIX_EPOCH: u128 = 116_444_736_000_000_000;
|
||||||
|
|
||||||
/// Trusted machine-wide so restarts keep it; replaced if the hosts change.
|
/// Trusted machine-wide so restarts keep it; replaced if the hosts change.
|
||||||
pub fn load_or_create_ca(
|
pub fn load_or_create_ca(
|
||||||
hosts: &[&str],
|
hosts: &[&str],
|
||||||
@@ -34,6 +39,16 @@ pub fn load_or_create_ca(
|
|||||||
LOCAL_MACHINE
|
LOCAL_MACHINE
|
||||||
.create(REGISTRY_KEY)?
|
.create(REGISTRY_KEY)?
|
||||||
.set_string("ca", ca.to_blob(hosts))?;
|
.set_string("ca", ca.to_blob(hosts))?;
|
||||||
|
// Windows discards CRLs it cached before now, in case an old CA's is cached at our URL.
|
||||||
|
let now = SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos() / 100
|
||||||
|
+ FILETIME_UNIX_EPOCH;
|
||||||
|
let chain = r"SOFTWARE\Microsoft\Cryptography\OID\EncodingType 0\CertDllCreateCertificateChainEngine\Config";
|
||||||
|
let resync = (now as u64).to_le_bytes();
|
||||||
|
LOCAL_MACHINE.create(chain)?.set_bytes(
|
||||||
|
"ChainCacheResyncFiletime",
|
||||||
|
Type::Bytes,
|
||||||
|
&resync,
|
||||||
|
)?;
|
||||||
ca
|
ca
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,9 +4,11 @@
|
|||||||
mod ca;
|
mod ca;
|
||||||
mod service;
|
mod service;
|
||||||
mod tpm;
|
mod tpm;
|
||||||
|
mod uninstall;
|
||||||
|
|
||||||
pub use service::run as run_service;
|
pub use service::run as run_service;
|
||||||
pub use tpm::CaKey;
|
pub use tpm::CaKey;
|
||||||
|
pub use uninstall::uninstall;
|
||||||
|
|
||||||
use rama::error::BoxError;
|
use rama::error::BoxError;
|
||||||
use std::net::Ipv4Addr;
|
use std::net::Ipv4Addr;
|
||||||
|
|||||||
@@ -27,6 +27,13 @@ impl CaKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn delete_machine_key() -> Result<(), BoxError> {
|
||||||
|
if let Some(key) = open_key(KEY_NAME, NCRYPT_MACHINE_KEY_FLAG)? {
|
||||||
|
key.delete_from_tpm()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// A per-user key under a random name, which needs no elevation; `delete_from_tpm` it after.
|
/// A per-user key under a random name, which needs no elevation; `delete_from_tpm` it after.
|
||||||
pub fn create_test_key() -> WinResult<Self> {
|
pub fn create_test_key() -> WinResult<Self> {
|
||||||
let name = format!("Strix Firewall test {:016x}", rand::random::<u64>());
|
let name = format!("Strix Firewall test {:016x}", rand::random::<u64>());
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
use super::ca::remove_our_cas_except;
|
||||||
|
use super::tpm::CaKey;
|
||||||
|
use super::*;
|
||||||
|
use std::io::ErrorKind;
|
||||||
|
use std::path::Path;
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
|
/// Run by the MSI after it deletes the service, which would restore the hosts entries. Does
|
||||||
|
/// nothing for what is already gone.
|
||||||
|
pub fn uninstall() -> Result<(), BoxError> {
|
||||||
|
write_hosts_entries(&[])?;
|
||||||
|
remove_our_cas_except(None)?;
|
||||||
|
none_if_missing(LOCAL_MACHINE.remove_tree(REGISTRY_KEY))?;
|
||||||
|
CaKey::delete_machine_key()?;
|
||||||
|
let entries = match std::fs::read_dir(data_dir()) {
|
||||||
|
Err(error) if error.kind() == ErrorKind::NotFound => Vec::new(),
|
||||||
|
entries => entries?.collect::<Result<Vec<_>, _>>()?,
|
||||||
|
};
|
||||||
|
for path in entries.into_iter().map(|entry| entry.path()) {
|
||||||
|
// The admin's config stays for a reinstall, as on macOS.
|
||||||
|
if path != config_file() {
|
||||||
|
remove_path(&path)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fails while the config is in it.
|
||||||
|
let _ = std::fs::remove_dir(data_dir());
|
||||||
|
info!("removed the CA, its key and the data folder");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A file or a folder, if it is there.
|
||||||
|
fn remove_path(path: &Path) -> std::io::Result<()> {
|
||||||
|
let removed = match path.is_dir() {
|
||||||
|
true => std::fs::remove_dir_all(path),
|
||||||
|
false => std::fs::remove_file(path),
|
||||||
|
};
|
||||||
|
match removed {
|
||||||
|
Err(error) if error.kind() == ErrorKind::NotFound => Ok(()),
|
||||||
|
removed => removed,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
<?xml version='1.0' encoding='windows-1252'?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2017 Christopher R. Field.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<?if $(sys.BUILDARCH) = x64 or $(sys.BUILDARCH) = arm64 ?>
|
||||||
|
<?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
|
||||||
|
<?else ?>
|
||||||
|
<?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
|
||||||
|
<?endif ?>
|
||||||
|
|
||||||
|
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
|
||||||
|
|
||||||
|
<Product
|
||||||
|
Id='*'
|
||||||
|
Name='strix-firewall'
|
||||||
|
UpgradeCode='D944C2F6-BF55-4B21-A60E-1BBDB1A66293'
|
||||||
|
Manufacturer='Strix'
|
||||||
|
Language='1033'
|
||||||
|
Codepage='1252'
|
||||||
|
Version='$(var.Version)'>
|
||||||
|
|
||||||
|
<Package Id='*'
|
||||||
|
Keywords='Installer'
|
||||||
|
Description='Screens package downloads for known malware before they reach your machine'
|
||||||
|
Manufacturer='Strix'
|
||||||
|
InstallerVersion='500'
|
||||||
|
Languages='1033'
|
||||||
|
Compressed='yes'
|
||||||
|
InstallScope='perMachine'
|
||||||
|
SummaryCodepage='1252'
|
||||||
|
/>
|
||||||
|
|
||||||
|
<MajorUpgrade
|
||||||
|
Schedule='afterInstallInitialize'
|
||||||
|
DowngradeErrorMessage='A newer version of [ProductName] is already installed. Setup will now exit.'/>
|
||||||
|
|
||||||
|
<Media Id='1' Cabinet='media1.cab' EmbedCab='yes' DiskPrompt='CD-ROM #1'/>
|
||||||
|
<Property Id='DiskPrompt' Value='strix-firewall Installation'/>
|
||||||
|
|
||||||
|
<Directory Id='TARGETDIR' Name='SourceDir'>
|
||||||
|
<Directory Id='$(var.PlatformProgramFilesFolder)' Name='PFiles'>
|
||||||
|
<Directory Id='APPLICATIONFOLDER' Name='Strix Firewall'>
|
||||||
|
<Component Id='Path' Guid='033B1050-23D0-4F5A-A0D3-F4757B4B67E0' KeyPath='yes'>
|
||||||
|
<Environment
|
||||||
|
Id='PATH'
|
||||||
|
Name='PATH'
|
||||||
|
Value='[APPLICATIONFOLDER]'
|
||||||
|
Permanent='no'
|
||||||
|
Part='last'
|
||||||
|
Action='set'
|
||||||
|
System='yes'/>
|
||||||
|
</Component>
|
||||||
|
<Component Id='binary0' Guid='*'>
|
||||||
|
<File
|
||||||
|
Id='exe0'
|
||||||
|
Name='firewall.exe'
|
||||||
|
DiskId='1'
|
||||||
|
Source='$(var.CargoTargetBinDir)\firewall.exe'
|
||||||
|
KeyPath='yes'/>
|
||||||
|
</Component>
|
||||||
|
<Component Id='binary1' Guid='*'>
|
||||||
|
<File
|
||||||
|
Id='exe1'
|
||||||
|
Name='firewall-service.exe'
|
||||||
|
DiskId='1'
|
||||||
|
Source='$(var.CargoTargetBinDir)\firewall-service.exe'
|
||||||
|
KeyPath='yes'/>
|
||||||
|
<ServiceInstall
|
||||||
|
Name='StrixFirewall'
|
||||||
|
DisplayName='Strix Firewall'
|
||||||
|
Description='Screens package downloads for known malware'
|
||||||
|
Type='ownProcess'
|
||||||
|
Start='auto'
|
||||||
|
ErrorControl='normal'
|
||||||
|
Vital='yes'>
|
||||||
|
<!-- The default, except that interactive users (IU) may also start (RP), stop (WP) and pause (DT) it, as the tray does. -->
|
||||||
|
<PermissionEx Sddl='D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWRPWPDTLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)'/>
|
||||||
|
</ServiceInstall>
|
||||||
|
<!-- Waits, so a service that cannot start fails the install. -->
|
||||||
|
<ServiceControl Id='StrixFirewall' Name='StrixFirewall' Start='install' Stop='both' Remove='uninstall' Wait='yes'/>
|
||||||
|
<!-- For clients that read no trust store. SSL_CERT_FILE would replace OpenSSL's bundle. -->
|
||||||
|
<Environment Id='NODE_EXTRA_CA_CERTS' Name='NODE_EXTRA_CA_CERTS' Value='[CommonAppDataFolder]Strix Firewall\ca.pem' Action='set' System='yes'/>
|
||||||
|
<Environment Id='UV_SYSTEM_CERTS' Name='UV_SYSTEM_CERTS' Value='1' Action='set' System='yes'/>
|
||||||
|
</Component>
|
||||||
|
</Directory>
|
||||||
|
</Directory>
|
||||||
|
</Directory>
|
||||||
|
|
||||||
|
<Feature
|
||||||
|
Id='Binaries'
|
||||||
|
Title='Application'
|
||||||
|
Description='Installs the firewall and its service.'
|
||||||
|
Level='1'
|
||||||
|
ConfigurableDirectory='APPLICATIONFOLDER'
|
||||||
|
AllowAdvertise='no'
|
||||||
|
Display='expand'
|
||||||
|
Absent='disallow'>
|
||||||
|
<ComponentRef Id='binary0'/>
|
||||||
|
<ComponentRef Id='binary1'/>
|
||||||
|
|
||||||
|
<Feature
|
||||||
|
Id='Environment'
|
||||||
|
Title='PATH Environment Variable'
|
||||||
|
Description='Add the install location of the [ProductName] executable to the PATH system environment variable. This allows the [ProductName] executable to be called from any location.'
|
||||||
|
Level='1'
|
||||||
|
Absent='allow'>
|
||||||
|
<ComponentRef Id='Path'/>
|
||||||
|
</Feature>
|
||||||
|
</Feature>
|
||||||
|
|
||||||
|
<!-- Removing the app must not leave the hosts file pointing at a service that is gone. -->
|
||||||
|
<CustomAction
|
||||||
|
Id='UninstallFirewall'
|
||||||
|
FileKey='exe1'
|
||||||
|
ExeCommand='uninstall'
|
||||||
|
Execute='deferred'
|
||||||
|
Impersonate='no'
|
||||||
|
Return='check'/>
|
||||||
|
<!--
|
||||||
|
Registry hosts lead nowhere while the service is down, so bring it back, also when it fails to
|
||||||
|
start. Through sc.exe, as MsiServiceConfigFailureActions cannot restart a service.
|
||||||
|
-->
|
||||||
|
<CustomAction
|
||||||
|
Id='SetFailureActions'
|
||||||
|
Directory='APPLICATIONFOLDER'
|
||||||
|
ExeCommand='"[System64Folder]sc.exe" failure StrixFirewall reset= 3600 actions= restart/5000/restart/5000/restart/5000'
|
||||||
|
Execute='deferred'
|
||||||
|
Impersonate='no'
|
||||||
|
Return='check'/>
|
||||||
|
<CustomAction
|
||||||
|
Id='SetFailureFlag'
|
||||||
|
Directory='APPLICATIONFOLDER'
|
||||||
|
ExeCommand='"[System64Folder]sc.exe" failureflag StrixFirewall 1'
|
||||||
|
Execute='deferred'
|
||||||
|
Impersonate='no'
|
||||||
|
Return='check'/>
|
||||||
|
<InstallExecuteSequence>
|
||||||
|
<!-- An upgrade removes the old version too, but should keep screening. -->
|
||||||
|
<Custom Action='UninstallFirewall' After='DeleteServices'>REMOVE~="ALL" AND NOT UPGRADINGPRODUCTCODE</Custom>
|
||||||
|
<Custom Action='SetFailureActions' After='InstallServices'>NOT REMOVE~="ALL"</Custom>
|
||||||
|
<Custom Action='SetFailureFlag' After='SetFailureActions'>NOT REMOVE~="ALL"</Custom>
|
||||||
|
</InstallExecuteSequence>
|
||||||
|
|
||||||
|
<SetProperty Id='ARPINSTALLLOCATION' Value='[APPLICATIONFOLDER]' After='CostFinalize'/>
|
||||||
|
<Property Id='ARPHELPLINK' Value='https://github.com/usestrix/firewall'/>
|
||||||
|
|
||||||
|
<UI>
|
||||||
|
<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>
|
||||||
|
</UI>
|
||||||
|
</Product>
|
||||||
|
|
||||||
|
</Wix>
|
||||||
+3
-1
@@ -4,7 +4,9 @@ members = ["cargo:core"]
|
|||||||
[dist]
|
[dist]
|
||||||
cargo-dist-version = "0.32.0"
|
cargo-dist-version = "0.32.0"
|
||||||
ci = "github"
|
ci = "github"
|
||||||
installers = ["shell", "powershell", "homebrew"]
|
installers = ["shell", "powershell", "homebrew", "msi"]
|
||||||
|
# core/wix/main.wxs is edited to install the service.
|
||||||
|
allow-dirty = ["msi"]
|
||||||
tap = "usestrix/homebrew-tap"
|
tap = "usestrix/homebrew-tap"
|
||||||
publish-jobs = ["homebrew", "./publish-crates"]
|
publish-jobs = ["homebrew", "./publish-crates"]
|
||||||
targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"]
|
targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"]
|
||||||
Reference in new issue
Block a user