mirror of
https://github.com/cirruslabs/softnet.git
synced 2026-10-21 10:45:23 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8519fa2f86 | ||
|
|
f3acef87a7 | ||
|
|
a35e5ae92a | ||
|
|
1f5aeb29f3 | ||
|
|
9a62801cd6 | ||
|
|
694f2e138a | ||
|
|
c2ff5761cb | ||
|
|
762868a8eb | ||
|
|
95a3358f59 | ||
|
|
ebc7cf8973 | ||
|
|
86f082ac77 | ||
|
|
b3df49889a | ||
|
|
82be9577b3 | ||
|
|
04c6019437 | ||
|
|
7374ceb239 | ||
|
|
cc7cc0e740 | ||
|
|
e53beeeb79 | ||
|
|
05cba5d771 | ||
|
|
cf97e3878d | ||
|
|
7f5293dd5e | ||
|
|
9a2e59b844 | ||
|
|
603c8b4889 | ||
|
|
24641d5325 | ||
|
|
ed64c139cf | ||
|
|
8359992a08 | ||
|
|
147c051b0e | ||
|
|
6456ed7228 | ||
|
|
56808c591f | ||
|
|
eba21ed33e | ||
|
|
867679446e | ||
|
|
4a13c5922b | ||
|
|
a92f4e0c99 | ||
|
|
0a92c290be | ||
|
|
f5a1b1cdbd | ||
|
|
817dbb6e32 | ||
|
|
5f3b371e93 | ||
|
|
cd5f1d2f4f | ||
|
|
a775a92772 | ||
|
|
f38d65f98f | ||
|
|
4ba480ff4f |
+7
-5
@@ -4,17 +4,18 @@ env:
|
|||||||
task:
|
task:
|
||||||
name: Build
|
name: Build
|
||||||
macos_instance:
|
macos_instance:
|
||||||
image: ghcr.io/cirruslabs/macos-monterey-xcode:14
|
image: ghcr.io/cirruslabs/macos-sonoma-xcode:latest
|
||||||
install_rust_script:
|
install_rust_script:
|
||||||
- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||||
|
- rustup target add x86_64-apple-darwin
|
||||||
build_script:
|
build_script:
|
||||||
- cargo build
|
- cargo build --target aarch64-apple-darwin --target x86_64-apple-darwin
|
||||||
|
|
||||||
task:
|
task:
|
||||||
name: Release
|
name: Release
|
||||||
only_if: $CIRRUS_TAG != ''
|
only_if: $CIRRUS_TAG != ''
|
||||||
macos_instance:
|
macos_instance:
|
||||||
image: ghcr.io/cirruslabs/macos-monterey-xcode:14
|
image: ghcr.io/cirruslabs/macos-sonoma-xcode:latest
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ENCRYPTED[!98ace8259c6024da912c14d5a3c5c6aac186890a8d4819fad78f3e0c41a4e0cd3a2537dd6e91493952fb056fa434be7c!]
|
GITHUB_TOKEN: ENCRYPTED[!98ace8259c6024da912c14d5a3c5c6aac186890a8d4819fad78f3e0c41a4e0cd3a2537dd6e91493952fb056fa434be7c!]
|
||||||
GORELEASER_KEY: ENCRYPTED[!9b80b6ef684ceaf40edd4c7af93014ee156c8aba7e6e5795f41c482729887b5c31f36b651491d790f1f668670888d9fd!]
|
GORELEASER_KEY: ENCRYPTED[!9b80b6ef684ceaf40edd4c7af93014ee156c8aba7e6e5795f41c482729887b5c31f36b651491d790f1f668670888d9fd!]
|
||||||
@@ -23,13 +24,14 @@ task:
|
|||||||
SENTRY_AUTH_TOKEN: ENCRYPTED[!c16a5cf7da5f856b4bc2f21fe8cb7aa2a6c981f851c094ed4d3025fd02ea59a58a86cee8b193a69a1fc20fa217e56ac3!]
|
SENTRY_AUTH_TOKEN: ENCRYPTED[!c16a5cf7da5f856b4bc2f21fe8cb7aa2a6c981f851c094ed4d3025fd02ea59a58a86cee8b193a69a1fc20fa217e56ac3!]
|
||||||
install_rust_script:
|
install_rust_script:
|
||||||
- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||||
|
- rustup target add x86_64-apple-darwin
|
||||||
install_script:
|
install_script:
|
||||||
- brew install go goreleaser/tap/goreleaser-pro getsentry/tools/sentry-cli
|
- brew install go goreleaser/tap/goreleaser-pro getsentry/tools/sentry-cli
|
||||||
build_script:
|
build_script:
|
||||||
- cargo build --profile release-with-debug
|
- cargo build --target aarch64-apple-darwin --target x86_64-apple-darwin --profile release-with-debug
|
||||||
release_script: goreleaser
|
release_script: goreleaser
|
||||||
upload_sentry_debug_files_script:
|
upload_sentry_debug_files_script:
|
||||||
- cd target/release-with-debug/
|
- cd target/aarch64-apple-darwin/release-with-debug/
|
||||||
# Generate and upload symbols
|
# Generate and upload symbols
|
||||||
- dsymutil softnet
|
- dsymutil softnet
|
||||||
- sentry-cli debug-files upload -o $SENTRY_ORG -p $SENTRY_PROJECT softnet.dSYM/
|
- sentry-cli debug-files upload -o $SENTRY_ORG -p $SENTRY_PROJECT softnet.dSYM/
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "cargo"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
@@ -1 +1,3 @@
|
|||||||
|
/.idea
|
||||||
|
/dist
|
||||||
/target
|
/target
|
||||||
|
|||||||
+7
-9
@@ -1,29 +1,27 @@
|
|||||||
project_name: softnet
|
project_name: softnet
|
||||||
|
|
||||||
builds:
|
builds:
|
||||||
- builder: prebuilt
|
- id: softnet
|
||||||
|
builder: prebuilt
|
||||||
|
goamd64: [v1]
|
||||||
goos:
|
goos:
|
||||||
- darwin
|
- darwin
|
||||||
goarch:
|
goarch:
|
||||||
- arm64
|
- arm64
|
||||||
|
- amd64
|
||||||
prebuilt:
|
prebuilt:
|
||||||
path: "target/release-with-debug/softnet"
|
path: 'target/{{- if eq .Arch "arm64" }}aarch64{{- else }}x86_64{{ end }}-apple-darwin/release-with-debug/softnet'
|
||||||
|
|
||||||
archives:
|
archives:
|
||||||
- id: binary
|
|
||||||
format: binary
|
|
||||||
name_template: "{{ .ProjectName }}"
|
|
||||||
- id: regular
|
- id: regular
|
||||||
name_template: "{{ .ProjectName }}"
|
name_template: "{{ .ProjectName }}-{{ .Arch }}"
|
||||||
|
|
||||||
release:
|
release:
|
||||||
prerelease: auto
|
prerelease: auto
|
||||||
|
|
||||||
brews:
|
brews:
|
||||||
- name: softnet
|
- name: softnet
|
||||||
ids:
|
repository:
|
||||||
- regular
|
|
||||||
tap:
|
|
||||||
owner: cirruslabs
|
owner: cirruslabs
|
||||||
name: homebrew-cli
|
name: homebrew-cli
|
||||||
caveats: See the Github repository for more information
|
caveats: See the Github repository for more information
|
||||||
|
|||||||
Generated
+1268
-581
File diff suppressed because it is too large
Load Diff
+20
-16
@@ -12,19 +12,23 @@ inherits = "release"
|
|||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
smoltcp = "0.8.1"
|
smoltcp = "0"
|
||||||
libc = "0.2.126"
|
libc = "0"
|
||||||
polling = { git = "https://github.com/smol-rs/polling.git" }
|
polling = "3"
|
||||||
dhcproto = "0.7.0"
|
dhcproto = { git = "https://github.com/bluecatengineering/dhcproto.git", branch = "master" }
|
||||||
vmnet = "0.1.1"
|
vmnet = "0"
|
||||||
clap = { version = "3.1.18", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
mac_address = "1.1.3"
|
mac_address = "1"
|
||||||
privdrop = "0.5.2"
|
privdrop = "0"
|
||||||
anyhow = { version = "1.0.66", features = ["backtrace"] }
|
anyhow = { version = "1", features = ["backtrace"] }
|
||||||
ip_network = "0.4.1"
|
ip_network = "0"
|
||||||
users = "0.11.0"
|
uzers = "0"
|
||||||
system-configuration = "0.5.0"
|
system-configuration = "0"
|
||||||
num_enum = "0.5.7"
|
num_enum = "0"
|
||||||
sentry = { version = "0.29.1", features = ["debug-images"] }
|
sentry = { version = "0", features = ["debug-images"] }
|
||||||
sentry-anyhow = { version = "0.29.1", features = ["backtrace"] }
|
sentry-anyhow = { version = "0", features = ["backtrace"] }
|
||||||
nix = "0.26.2"
|
nix = { version = "0", features = ["signal"] }
|
||||||
|
prefix-trie = "0"
|
||||||
|
ipnet = "2"
|
||||||
|
oslog = "0.2.0"
|
||||||
|
log = "0.4.22"
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
# Softnet
|
# Softnet
|
||||||
|
|
||||||
Softnet is a software networking for [Tart](https://github.com/cirruslabs/tart) which provides better network isolation and alleviates DHCP shortage on production systems.
|
Softnet is a software networking for [Tart](https://github.com/cirruslabs/tart) which provides better network isolation and alleviates DHCP shortage on production systems.
|
||||||
|
Please check out [this blog post](https://cirrus-ci.org/blog/2022/07/07/isolating-network-between-tarts-macos-virtual-machines/) for backstory.
|
||||||
|
|
||||||
## Working model
|
## Working model
|
||||||
|
|
||||||
Softnet solves two problems:
|
Softnet solves two problems:
|
||||||
|
|
||||||
1. VM network isolation
|
1. VM network isolation
|
||||||
* [`VZNATNetworkDeviceAttachment`](https://developer.apple.com/documentation/virtualization/vznatnetworkdeviceattachment) (the default networking in Tart) enables [vmnet's bridge isolation](https://developer.apple.com/documentation/vmnet/vmnet_enable_isolation_key) by default and prevents cross-VM traffic, however it's still possible for any VM to spoof the host's ARP-table and capture other VMs traffic, for example
|
* [`VZNATNetworkDeviceAttachment`](https://developer.apple.com/documentation/virtualization/vznatnetworkdeviceattachment) (the default networking in Tart) enables [vmnet's bridge isolation](https://developer.apple.com/documentation/vmnet/vmnet_enable_isolation_key) by default and prevents cross-VM traffic, however it's still possible for any VM to spoof the host's ARP-table and capture other VMs traffic by using tools that enable conducting the [ARP spoofing attacks](https://en.wikipedia.org/wiki/ARP_spoofing) (e.g. [arpspoof](https://www.monkey.org/~dugsong/dsniff/), [arpoison](http://www.arpoison.net/) and so on)
|
||||||
2. DHCP exhaustion
|
2. DHCP exhaustion
|
||||||
* macOS built-in DHCP-server allocates a `/24` subnet with 86400 seconds lease time by default, which only allows for ~253 VMs a day (or 1 VM every ~6 minutes) to be spawned without causing a denial-of-service, which is pretty limiting for CI services like Cirrus CI
|
* macOS built-in DHCP-server allocates a `/24` subnet with 86400 seconds lease time by default, which only allows for ~253 VMs a day (or 1 VM every ~6 minutes) to be spawned without causing a denial-of-service, which is pretty limiting for CI services like Cirrus CI
|
||||||
|
|
||||||
And assumes that:
|
And assumes that:
|
||||||
|
|
||||||
@@ -30,4 +31,4 @@ For proper functioning, Softnet binary requires two things:
|
|||||||
|
|
||||||
## Running
|
## Running
|
||||||
|
|
||||||
Softnet is started and managed automatically by Tart if `--with-softnet` flag is present when calling `tart run`.
|
Softnet is started and managed automatically by Tart if `--net-softnet` flag is provided when calling `tart run`.
|
||||||
|
|||||||
+13
-5
@@ -26,14 +26,14 @@ impl DhcpSnooper {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let dns_ips = match message.opts().get(OptionCode::DomainNameServer) {
|
let dns_ips = match message.opts().get(OptionCode::DomainNameServer) {
|
||||||
Some(DhcpOption::DomainNameServer(dns_ips)) => HashSet::from_iter(
|
Some(DhcpOption::DomainNameServer(dns_ips)) => {
|
||||||
dns_ips.iter().map(|dns_ip| Ipv4Address(dns_ip.octets())),
|
HashSet::from_iter(dns_ips.iter().cloned())
|
||||||
),
|
}
|
||||||
_ => HashSet::new(),
|
_ => HashSet::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.vm_lease = Some(Lease::new(
|
self.vm_lease = Some(Lease::new(
|
||||||
message.yiaddr().into(),
|
message.yiaddr(),
|
||||||
Duration::from_secs(*lease_time as u64),
|
Duration::from_secs(*lease_time as u64),
|
||||||
dns_ips,
|
dns_ips,
|
||||||
))
|
))
|
||||||
@@ -73,7 +73,15 @@ impl Lease {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn address(&self) -> Ipv4Address {
|
||||||
|
self.address
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn valid(&self) -> bool {
|
||||||
|
Instant::now() < self.valid_until
|
||||||
|
}
|
||||||
|
|
||||||
pub fn valid_ip_source(&self, address: Ipv4Address) -> bool {
|
pub fn valid_ip_source(&self, address: Ipv4Address) -> bool {
|
||||||
self.address == address && Instant::now() < self.valid_until
|
self.address == address && self.valid()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+67
-10
@@ -1,4 +1,6 @@
|
|||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
use clap::ValueEnum;
|
||||||
|
use log::info;
|
||||||
use std::net::Ipv4Addr;
|
use std::net::Ipv4Addr;
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
use std::os::unix::net::UnixDatagram;
|
use std::os::unix::net::UnixDatagram;
|
||||||
@@ -6,8 +8,21 @@ use std::str::FromStr;
|
|||||||
use std::sync::mpsc::{sync_channel, SyncSender};
|
use std::sync::mpsc::{sync_channel, SyncSender};
|
||||||
use vmnet::mode::Mode;
|
use vmnet::mode::Mode;
|
||||||
use vmnet::parameters::{Parameter, ParameterKind};
|
use vmnet::parameters::{Parameter, ParameterKind};
|
||||||
|
use vmnet::port_forwarding::{AddressFamily, Protocol};
|
||||||
use vmnet::{Events, Options};
|
use vmnet::{Events, Options};
|
||||||
|
|
||||||
|
#[derive(ValueEnum, Clone, Debug)]
|
||||||
|
pub enum NetType {
|
||||||
|
/// Shared network
|
||||||
|
///
|
||||||
|
/// Uses NAT-translation to give guests access to the global network
|
||||||
|
Nat,
|
||||||
|
/// Host network
|
||||||
|
///
|
||||||
|
/// Guests will be able to talk only to the host without access to global network
|
||||||
|
Host,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Host {
|
pub struct Host {
|
||||||
interface: vmnet::Interface,
|
interface: vmnet::Interface,
|
||||||
new_packets_rx: UnixDatagram,
|
new_packets_rx: UnixDatagram,
|
||||||
@@ -18,27 +33,38 @@ pub struct Host {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Host {
|
impl Host {
|
||||||
pub fn new() -> Result<Host> {
|
pub fn new(vm_net_type: NetType, enable_isolation: bool) -> Result<Host> {
|
||||||
// Initialize a vmnet.framework NAT interface with isolation enabled
|
// Initialize a vmnet.framework NAT or Host interface with isolation enabled
|
||||||
let mut interface = vmnet::Interface::new(
|
let mut interface = vmnet::Interface::new(
|
||||||
Mode::Shared(Default::default()),
|
match vm_net_type {
|
||||||
|
NetType::Nat => Mode::Shared(Default::default()),
|
||||||
|
NetType::Host => Mode::Host(Default::default()),
|
||||||
|
},
|
||||||
Options {
|
Options {
|
||||||
enable_isolation: Some(true),
|
enable_isolation: Some(enable_isolation),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.context("failed to initialize vmnet interface")?;
|
.context("failed to initialize vmnet interface")?;
|
||||||
|
|
||||||
// Retrieve first IP (gateway) used for this interface
|
// Retrieve first IP (gateway) used for this interface
|
||||||
let Some(Parameter::StartAddress(gateway_ip)) = interface.parameters().get(ParameterKind::StartAddress) else {
|
let Some(Parameter::StartAddress(gateway_ip)) =
|
||||||
return Err(anyhow!("failed to retrieve vmnet's interface start address"));
|
interface.parameters().get(ParameterKind::StartAddress)
|
||||||
|
else {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"failed to retrieve vmnet's interface start address"
|
||||||
|
));
|
||||||
};
|
};
|
||||||
let gateway_ip = Ipv4Addr::from_str(&gateway_ip)
|
let gateway_ip = Ipv4Addr::from_str(&gateway_ip)
|
||||||
.context("failed to parse vmnet's interface start address")?;
|
.context("failed to parse vmnet's interface start address")?;
|
||||||
|
|
||||||
// Retrieve max packet size for this interface
|
// Retrieve max packet size for this interface
|
||||||
let Some(Parameter::MaxPacketSize(max_packet_size)) = interface.parameters().get(ParameterKind::MaxPacketSize) else {
|
let Some(Parameter::MaxPacketSize(max_packet_size)) =
|
||||||
return Err(anyhow!("failed to retrieve vmnet's interface max packet size"));
|
interface.parameters().get(ParameterKind::MaxPacketSize)
|
||||||
|
else {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"failed to retrieve vmnet's interface max packet size"
|
||||||
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set up a socketpair() to emulate polling of the vmnet interface
|
// Set up a socketpair() to emulate polling of the vmnet interface
|
||||||
@@ -50,7 +76,9 @@ impl Host {
|
|||||||
interface
|
interface
|
||||||
.set_event_callback(Events::PACKETS_AVAILABLE, move |_mask, _params| {
|
.set_event_callback(Events::PACKETS_AVAILABLE, move |_mask, _params| {
|
||||||
// Send a dummy datagram to make the other end of socketpair() readable
|
// Send a dummy datagram to make the other end of socketpair() readable
|
||||||
new_packets_tx.send(&[0; 1]).unwrap();
|
// and ignore the error as this merely a signalling channel to wake up
|
||||||
|
// the poller
|
||||||
|
new_packets_tx.send(&[0; 1]).ok();
|
||||||
|
|
||||||
// Wait for the permission to continue to avoid
|
// Wait for the permission to continue to avoid
|
||||||
// wasting CPU cycles or in case of termination,
|
// wasting CPU cycles or in case of termination,
|
||||||
@@ -66,7 +94,7 @@ impl Host {
|
|||||||
interface,
|
interface,
|
||||||
new_packets_rx,
|
new_packets_rx,
|
||||||
callback_can_continue_tx,
|
callback_can_continue_tx,
|
||||||
gateway_ip: gateway_ip.into(),
|
gateway_ip,
|
||||||
max_packet_size,
|
max_packet_size,
|
||||||
finalized: false,
|
finalized: false,
|
||||||
})
|
})
|
||||||
@@ -74,6 +102,35 @@ impl Host {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Host {
|
impl Host {
|
||||||
|
pub fn port_forwarding_add_rule(
|
||||||
|
&mut self,
|
||||||
|
external_port: u16,
|
||||||
|
internal_addr: Ipv4Addr,
|
||||||
|
internal_port: u16,
|
||||||
|
) -> Result<()> {
|
||||||
|
let details = format!("external_port={external_port}, internal_addr={internal_addr}, internal_port={internal_port}");
|
||||||
|
|
||||||
|
self.interface
|
||||||
|
.port_forwarding_rule_add(
|
||||||
|
AddressFamily::Ipv4,
|
||||||
|
Protocol::Tcp,
|
||||||
|
external_port,
|
||||||
|
internal_addr.into(),
|
||||||
|
internal_port,
|
||||||
|
)
|
||||||
|
.map(|_| info!("added port forwarding rule {details}"))
|
||||||
|
.map_err(|err| anyhow!("failed to add port forwarding rule {details}: {err}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn port_forwarding_remove_rule(&mut self, external_port: u16) -> Result<()> {
|
||||||
|
let details = format!("external_port={external_port}");
|
||||||
|
|
||||||
|
self.interface
|
||||||
|
.port_forwarding_rule_remove(AddressFamily::Ipv4, Protocol::Tcp, external_port)
|
||||||
|
.map(|_| info!("removed port forwarding rule {details}"))
|
||||||
|
.map_err(|err| anyhow!("failed to remove port forwarding rule {details}: {err}"))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn read(&mut self, buf: &mut [u8]) -> vmnet::Result<usize> {
|
pub fn read(&mut self, buf: &mut [u8]) -> vmnet::Result<usize> {
|
||||||
// Dequeue dummy datagram from the socket (if any)
|
// Dequeue dummy datagram from the socket (if any)
|
||||||
// to free up buffer space and reduce false-positives
|
// to free up buffer space and reduce false-positives
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
mod dhcp_snooper;
|
mod dhcp_snooper;
|
||||||
mod host;
|
mod host;
|
||||||
|
pub use host::NetType;
|
||||||
mod poller;
|
mod poller;
|
||||||
pub mod proxy;
|
pub mod proxy;
|
||||||
mod vm;
|
mod vm;
|
||||||
|
|||||||
+18
-16
@@ -2,14 +2,15 @@ use anyhow::Result;
|
|||||||
use num_enum::IntoPrimitive;
|
use num_enum::IntoPrimitive;
|
||||||
use polling::os::kqueue::PollerKqueueExt;
|
use polling::os::kqueue::PollerKqueueExt;
|
||||||
use polling::PollMode;
|
use polling::PollMode;
|
||||||
|
use std::os::fd::{AsRawFd, BorrowedFd};
|
||||||
use std::os::unix::io::RawFd;
|
use std::os::unix::io::RawFd;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
pub struct Poller {
|
pub struct Poller<'poller> {
|
||||||
poller: polling::Poller,
|
poller: polling::Poller,
|
||||||
events: Vec<polling::Event>,
|
events: polling::Events,
|
||||||
vm_fd: RawFd,
|
vm_fd: BorrowedFd<'poller>,
|
||||||
host_fd: RawFd,
|
host_fd: BorrowedFd<'poller>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(IntoPrimitive)]
|
#[derive(IntoPrimitive)]
|
||||||
@@ -20,22 +21,25 @@ enum EventKey {
|
|||||||
Interrupt,
|
Interrupt,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Poller {
|
impl Poller<'_> {
|
||||||
pub fn new(vm_fd: RawFd, host_fd: RawFd) -> Result<Poller> {
|
pub fn new<'poller>(vm_fd: RawFd, host_fd: RawFd) -> Result<Poller<'poller>> {
|
||||||
let poller = polling::Poller::new()?;
|
let poller = polling::Poller::new()?;
|
||||||
|
|
||||||
Ok(Poller {
|
Ok(Poller {
|
||||||
poller,
|
poller,
|
||||||
events: Vec::new(),
|
events: polling::Events::new(),
|
||||||
vm_fd,
|
vm_fd: unsafe { BorrowedFd::borrow_raw(vm_fd) },
|
||||||
host_fd,
|
host_fd: unsafe { BorrowedFd::borrow_raw(host_fd) },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn arm(&self) -> Result<()> {
|
pub fn arm(&self) -> Result<()> {
|
||||||
self.poller.add(self.vm_fd as RawFd, self.vm_interest())?;
|
unsafe {
|
||||||
self.poller
|
self.poller
|
||||||
.add(self.host_fd as RawFd, self.host_interest())?;
|
.add(self.vm_fd.as_raw_fd(), self.vm_interest())?;
|
||||||
|
self.poller
|
||||||
|
.add(self.host_fd.as_raw_fd(), self.host_interest())?;
|
||||||
|
}
|
||||||
|
|
||||||
let interrupt_signal = polling::os::kqueue::Signal(libc::SIGINT);
|
let interrupt_signal = polling::os::kqueue::Signal(libc::SIGINT);
|
||||||
self.poller
|
self.poller
|
||||||
@@ -52,10 +56,8 @@ impl Poller {
|
|||||||
pub fn rearm(&mut self) -> Result<()> {
|
pub fn rearm(&mut self) -> Result<()> {
|
||||||
self.events.clear();
|
self.events.clear();
|
||||||
|
|
||||||
self.poller
|
self.poller.modify(self.vm_fd, self.vm_interest())?;
|
||||||
.modify(self.vm_fd as RawFd, self.vm_interest())?;
|
self.poller.modify(self.host_fd, self.host_interest())?;
|
||||||
self.poller
|
|
||||||
.modify(self.host_fd as RawFd, self.host_interest())?;
|
|
||||||
|
|
||||||
let interrupt_signal = polling::os::kqueue::Signal(libc::SIGINT);
|
let interrupt_signal = polling::os::kqueue::Signal(libc::SIGINT);
|
||||||
self.poller.modify_filter(
|
self.poller.modify_filter(
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
use anyhow::{anyhow, Context, Error};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct ExposedPort {
|
||||||
|
pub external_port: u16,
|
||||||
|
pub internal_port: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for ExposedPort {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let splits: Vec<&str> = s.split(':').collect();
|
||||||
|
|
||||||
|
match splits.len() {
|
||||||
|
2 => Ok(ExposedPort {
|
||||||
|
external_port: splits[0]
|
||||||
|
.parse()
|
||||||
|
.context(format!("invalid external port {:?}", splits[0]))?,
|
||||||
|
internal_port: splits[1]
|
||||||
|
.parse()
|
||||||
|
.context(format!("invalid internal port {:?}", splits[1]))?,
|
||||||
|
}),
|
||||||
|
_ => Err(anyhow!(
|
||||||
|
"invalid exposed port specification {:?}, the format should be EXTERNAL:INTERNAL",
|
||||||
|
s
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::proxy::exposed_port::ExposedPort;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exposed_port() {
|
||||||
|
assert_eq!(
|
||||||
|
ExposedPort {
|
||||||
|
external_port: 2222,
|
||||||
|
internal_port: 22
|
||||||
|
},
|
||||||
|
"2222:22".parse().unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -3,7 +3,7 @@ use crate::proxy::Proxy;
|
|||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use smoltcp::wire::{EthernetFrame, EthernetProtocol, Ipv4Packet, UdpPacket};
|
use smoltcp::wire::{EthernetFrame, EthernetProtocol, Ipv4Packet, UdpPacket};
|
||||||
|
|
||||||
impl Proxy {
|
impl Proxy<'_> {
|
||||||
pub(crate) fn process_frame_from_host(&mut self, frame: &EthernetFrame<&[u8]>) -> Result<()> {
|
pub(crate) fn process_frame_from_host(&mut self, frame: &EthernetFrame<&[u8]>) -> Result<()> {
|
||||||
if self.allowed_from_host(frame).is_none() {
|
if self.allowed_from_host(frame).is_none() {
|
||||||
// Block packet by not forwarding it to the VM
|
// Block packet by not forwarding it to the VM
|
||||||
@@ -58,7 +58,7 @@ impl Proxy {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ipv4_pkt.protocol() != smoltcp::wire::IpProtocol::Udp {
|
if ipv4_pkt.next_header() != smoltcp::wire::IpProtocol::Udp {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+28
-5
@@ -1,30 +1,45 @@
|
|||||||
|
mod exposed_port;
|
||||||
mod host;
|
mod host;
|
||||||
|
mod port_forwarder;
|
||||||
mod udp_packet_helper;
|
mod udp_packet_helper;
|
||||||
mod vm;
|
mod vm;
|
||||||
|
|
||||||
use crate::dhcp_snooper::DhcpSnooper;
|
use crate::dhcp_snooper::DhcpSnooper;
|
||||||
use crate::host::Host;
|
use crate::host::Host;
|
||||||
|
use crate::host::NetType;
|
||||||
use crate::poller::Poller;
|
use crate::poller::Poller;
|
||||||
use crate::vm::VM;
|
use crate::vm::VM;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
pub use exposed_port::ExposedPort;
|
||||||
|
use ipnet::Ipv4Net;
|
||||||
use mac_address::MacAddress;
|
use mac_address::MacAddress;
|
||||||
|
use port_forwarder::PortForwarder;
|
||||||
|
use prefix_trie::{Prefix, PrefixSet};
|
||||||
use smoltcp::wire::EthernetFrame;
|
use smoltcp::wire::EthernetFrame;
|
||||||
use std::io::ErrorKind;
|
use std::io::ErrorKind;
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
|
|
||||||
pub struct Proxy {
|
pub struct Proxy<'proxy> {
|
||||||
vm: VM,
|
vm: VM,
|
||||||
host: Host,
|
host: Host,
|
||||||
poller: Poller,
|
poller: Poller<'proxy>,
|
||||||
vm_mac_address: smoltcp::wire::EthernetAddress,
|
vm_mac_address: smoltcp::wire::EthernetAddress,
|
||||||
dhcp_snooper: DhcpSnooper,
|
dhcp_snooper: DhcpSnooper,
|
||||||
|
allow: PrefixSet<Ipv4Net>,
|
||||||
enobufs_encountered: bool,
|
enobufs_encountered: bool,
|
||||||
|
port_forwarder: PortForwarder,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Proxy {
|
impl Proxy<'_> {
|
||||||
pub fn new(vm_fd: RawFd, vm_mac_address: MacAddress) -> Result<Proxy> {
|
pub fn new<'proxy>(
|
||||||
|
vm_fd: RawFd,
|
||||||
|
vm_mac_address: MacAddress,
|
||||||
|
vm_net_type: NetType,
|
||||||
|
allow: PrefixSet<Ipv4Net>,
|
||||||
|
exposed_ports: Vec<ExposedPort>,
|
||||||
|
) -> Result<Proxy<'proxy>> {
|
||||||
let vm = VM::new(vm_fd)?;
|
let vm = VM::new(vm_fd)?;
|
||||||
let host = Host::new()?;
|
let host = Host::new(vm_net_type, !allow.contains(&Ipv4Net::zero()))?;
|
||||||
let poller = Poller::new(vm.as_raw_fd(), host.as_raw_fd())?;
|
let poller = Poller::new(vm.as_raw_fd(), host.as_raw_fd())?;
|
||||||
|
|
||||||
Ok(Proxy {
|
Ok(Proxy {
|
||||||
@@ -33,7 +48,9 @@ impl Proxy {
|
|||||||
poller,
|
poller,
|
||||||
vm_mac_address: smoltcp::wire::EthernetAddress(vm_mac_address.bytes()),
|
vm_mac_address: smoltcp::wire::EthernetAddress(vm_mac_address.bytes()),
|
||||||
dhcp_snooper: Default::default(),
|
dhcp_snooper: Default::default(),
|
||||||
|
allow,
|
||||||
enobufs_encountered: false,
|
enobufs_encountered: false,
|
||||||
|
port_forwarder: PortForwarder::new(exposed_ports),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,6 +75,12 @@ impl Proxy {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Timeout
|
||||||
|
if !vm_readable && !host_readable && !interrupt {
|
||||||
|
self.port_forwarder
|
||||||
|
.tick(&mut self.host, self.dhcp_snooper.lease());
|
||||||
|
}
|
||||||
|
|
||||||
self.poller.rearm()?;
|
self.poller.rearm()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
use crate::dhcp_snooper::Lease;
|
||||||
|
use crate::host::Host;
|
||||||
|
use crate::proxy::exposed_port::ExposedPort;
|
||||||
|
use anyhow::Result;
|
||||||
|
use log::error;
|
||||||
|
use std::net::Ipv4Addr;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct PortForwarder {
|
||||||
|
port_forwardings: Vec<PortForwarding>,
|
||||||
|
failed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
struct PortForwarding {
|
||||||
|
exposed_port: ExposedPort,
|
||||||
|
forwarding_to_addr: Option<Ipv4Addr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PortForwarder {
|
||||||
|
pub fn new(exposed_ports: Vec<ExposedPort>) -> PortForwarder {
|
||||||
|
let port_forwardings = exposed_ports
|
||||||
|
.into_iter()
|
||||||
|
.map(|exposed_port| PortForwarding {
|
||||||
|
exposed_port,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
PortForwarder {
|
||||||
|
port_forwardings,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tick(&mut self, host: &mut Host, lease: &Option<Lease>) {
|
||||||
|
if self.failed {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(err) = self.tick_inner(host, lease) {
|
||||||
|
error!("port-forwarding failed: {}", err);
|
||||||
|
|
||||||
|
self.failed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tick_inner(&mut self, host: &mut Host, lease: &Option<Lease>) -> Result<()> {
|
||||||
|
if let Some(lease) = lease {
|
||||||
|
// Lease exists, but is not valid, remove all port forwardings
|
||||||
|
if !lease.valid() {
|
||||||
|
self.remove_all_port_forwardings(host)?;
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lease exists and is valid, install/re-install port forwardings
|
||||||
|
for port_forwarding in &mut self.port_forwardings {
|
||||||
|
if let Some(installed_addr) = port_forwarding.forwarding_to_addr {
|
||||||
|
// Port forwarding already installed, perhaps it's outdated?
|
||||||
|
if installed_addr == lease.address() {
|
||||||
|
// Nope, the port forwarding is up to date
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove port forwarding since the lease address had changed
|
||||||
|
host.port_forwarding_remove_rule(port_forwarding.exposed_port.external_port)?;
|
||||||
|
port_forwarding.forwarding_to_addr = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install new port forwarding
|
||||||
|
host.port_forwarding_add_rule(
|
||||||
|
port_forwarding.exposed_port.external_port,
|
||||||
|
lease.address(),
|
||||||
|
port_forwarding.exposed_port.internal_port,
|
||||||
|
)?;
|
||||||
|
port_forwarding.forwarding_to_addr = Some(lease.address());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Lease does not exist, remove all port forwardings
|
||||||
|
self.remove_all_port_forwardings(host)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_all_port_forwardings(&mut self, host: &mut Host) -> Result<()> {
|
||||||
|
for port_forwarding in &mut self.port_forwardings {
|
||||||
|
if port_forwarding.forwarding_to_addr.is_none() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
host.port_forwarding_remove_rule(port_forwarding.exposed_port.external_port)?;
|
||||||
|
port_forwarding.forwarding_to_addr = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
-7
@@ -2,12 +2,13 @@ use crate::proxy::udp_packet_helper::UdpPacketHelper;
|
|||||||
use crate::proxy::Proxy;
|
use crate::proxy::Proxy;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use ipnet::Ipv4Net;
|
||||||
use smoltcp::wire::{
|
use smoltcp::wire::{
|
||||||
ArpPacket, EthernetFrame, EthernetProtocol, IpProtocol, Ipv4Packet, UdpPacket,
|
ArpPacket, EthernetFrame, EthernetProtocol, IpProtocol, Ipv4Packet, UdpPacket,
|
||||||
};
|
};
|
||||||
use std::net::Ipv4Addr;
|
use std::net::Ipv4Addr;
|
||||||
|
|
||||||
impl Proxy {
|
impl Proxy<'_> {
|
||||||
pub(crate) fn process_frame_from_vm(&mut self, frame: EthernetFrame<&[u8]>) -> Result<()> {
|
pub(crate) fn process_frame_from_vm(&mut self, frame: EthernetFrame<&[u8]>) -> Result<()> {
|
||||||
if self.allowed_from_vm(&frame).is_none() {
|
if self.allowed_from_vm(&frame).is_none() {
|
||||||
// Block packet by not forwarding it to the host
|
// Block packet by not forwarding it to the host
|
||||||
@@ -47,7 +48,7 @@ impl Proxy {
|
|||||||
let source_protocol_addr = Ipv4Addr::from(source_protocol_addr);
|
let source_protocol_addr = Ipv4Addr::from(source_protocol_addr);
|
||||||
|
|
||||||
if let Some(lease) = self.dhcp_snooper.lease() {
|
if let Some(lease) = self.dhcp_snooper.lease() {
|
||||||
if lease.valid_ip_source(source_protocol_addr.into()) {
|
if lease.valid_ip_source(source_protocol_addr) {
|
||||||
return Some(());
|
return Some(());
|
||||||
}
|
}
|
||||||
} else if source_protocol_addr.is_unspecified() {
|
} else if source_protocol_addr.is_unspecified() {
|
||||||
@@ -58,15 +59,26 @@ impl Proxy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn allowed_from_vm_ipv4(&self, ipv4_pkt: Ipv4Packet<&[u8]>) -> Option<()> {
|
fn allowed_from_vm_ipv4(&self, ipv4_pkt: Ipv4Packet<&[u8]>) -> Option<()> {
|
||||||
// Once we've learned the VM's IP from the DHCP snooping,
|
// Have we learned the VM's IP from the DHCP snooping?
|
||||||
// allow all global traffic for that VM's IP
|
|
||||||
if let Some(lease) = &self.dhcp_snooper.lease() {
|
if let Some(lease) = &self.dhcp_snooper.lease() {
|
||||||
let dst_is_global =
|
// If so, allow all global traffic
|
||||||
ip_network::IpNetwork::from(Ipv4Addr::from(ipv4_pkt.dst_addr().0)).is_global();
|
let dst_addr = ipv4_pkt.dst_addr();
|
||||||
|
let dst_is_global = ip_network::IpNetwork::from(dst_addr).is_global();
|
||||||
|
|
||||||
if lease.valid_ip_source(ipv4_pkt.src_addr()) && dst_is_global {
|
if lease.valid_ip_source(ipv4_pkt.src_addr()) && dst_is_global {
|
||||||
return Some(());
|
return Some(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Also allow all traffic to the user-specified CIDRs
|
||||||
|
let dst_net = Ipv4Net::from(dst_addr);
|
||||||
|
|
||||||
|
// Use get_lpm() instead of get_spm() to work around prefix-trie
|
||||||
|
// not handling prefixes like 0.0.0.0/0 correctly[1]
|
||||||
|
//
|
||||||
|
// [1]: https://github.com/tiborschneider/prefix-trie/issues/8
|
||||||
|
if self.allow.get_lpm(&dst_net).is_some() {
|
||||||
|
return Some(());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow communication with host
|
// Allow communication with host
|
||||||
@@ -74,7 +86,7 @@ impl Proxy {
|
|||||||
return Some(());
|
return Some(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if ipv4_pkt.protocol() == IpProtocol::Udp {
|
if ipv4_pkt.next_header() == IpProtocol::Udp {
|
||||||
let udp_pkt = UdpPacket::new_checked(ipv4_pkt.payload()).ok()?;
|
let udp_pkt = UdpPacket::new_checked(ipv4_pkt.payload()).ok()?;
|
||||||
|
|
||||||
// Allow DNS communication with the DNS-servers provided by DHCP
|
// Allow DNS communication with the DNS-servers provided by DHCP
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
nightly
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "nightly"
|
||||||
+41
-3
@@ -1,8 +1,14 @@
|
|||||||
use anyhow::{anyhow, Context};
|
use anyhow::{anyhow, Context};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use ipnet::Ipv4Net;
|
||||||
|
use log::LevelFilter;
|
||||||
use nix::sys::signal::{signal, SigHandler, Signal};
|
use nix::sys::signal::{signal, SigHandler, Signal};
|
||||||
|
use oslog::OsLogger;
|
||||||
|
use prefix_trie::PrefixSet;
|
||||||
use privdrop::PrivDrop;
|
use privdrop::PrivDrop;
|
||||||
|
use softnet::proxy::ExposedPort;
|
||||||
use softnet::proxy::Proxy;
|
use softnet::proxy::Proxy;
|
||||||
|
use softnet::NetType;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
@@ -15,7 +21,7 @@ use system_configuration::core_foundation::number::CFNumber;
|
|||||||
use system_configuration::core_foundation::string::CFString;
|
use system_configuration::core_foundation::string::CFString;
|
||||||
use system_configuration::preferences::SCPreferences;
|
use system_configuration::preferences::SCPreferences;
|
||||||
use system_configuration::sys::preferences::{SCPreferencesCommitChanges, SCPreferencesSetValue};
|
use system_configuration::sys::preferences::{SCPreferencesCommitChanges, SCPreferencesSetValue};
|
||||||
use users::{get_current_groupname, get_current_username, get_effective_uid};
|
use uzers::{get_current_groupname, get_current_username, get_effective_uid};
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
struct Args {
|
struct Args {
|
||||||
@@ -28,6 +34,9 @@ struct Args {
|
|||||||
#[clap(long, help = "MAC address to enforce for the VM")]
|
#[clap(long, help = "MAC address to enforce for the VM")]
|
||||||
vm_mac_address: mac_address::MacAddress,
|
vm_mac_address: mac_address::MacAddress,
|
||||||
|
|
||||||
|
#[clap(long, value_enum, help = "type of network to use for the VM", default_value_t=NetType::Nat)]
|
||||||
|
vm_net_type: NetType,
|
||||||
|
|
||||||
#[clap(
|
#[clap(
|
||||||
long,
|
long,
|
||||||
help = "set bootpd(8) lease time to this value (in seconds) before starting the VM",
|
help = "set bootpd(8) lease time to this value (in seconds) before starting the VM",
|
||||||
@@ -41,6 +50,24 @@ struct Args {
|
|||||||
#[clap(long, help = "group name to drop privileges to")]
|
#[clap(long, help = "group name to drop privileges to")]
|
||||||
group: Option<String>,
|
group: Option<String>,
|
||||||
|
|
||||||
|
#[clap(
|
||||||
|
long,
|
||||||
|
help = "comma-separated list of CIDRs to allow the traffic to (e.g. --allow=192.168.0.0/24)",
|
||||||
|
value_name = "comma-separated CIDRs",
|
||||||
|
use_value_delimiter = true,
|
||||||
|
action = clap::ArgAction::Set
|
||||||
|
)]
|
||||||
|
allow: Vec<Ipv4Net>,
|
||||||
|
|
||||||
|
#[clap(
|
||||||
|
long,
|
||||||
|
help = "comma-separated list of TCP ports to expose (e.g. --expose 2222:22,8080:80)",
|
||||||
|
value_name = "comma-separated port specifications",
|
||||||
|
use_value_delimiter = true,
|
||||||
|
action = clap::ArgAction::Set
|
||||||
|
)]
|
||||||
|
expose: Vec<ExposedPort>,
|
||||||
|
|
||||||
#[clap(long, hide = true)]
|
#[clap(long, hide = true)]
|
||||||
sudo_escalation_probing: bool,
|
sudo_escalation_probing: bool,
|
||||||
|
|
||||||
@@ -85,6 +112,11 @@ fn main() -> ExitCode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn try_main() -> anyhow::Result<()> {
|
fn try_main() -> anyhow::Result<()> {
|
||||||
|
// Initialize logger
|
||||||
|
OsLogger::new("org.cirruslabs.softnet")
|
||||||
|
.level_filter(LevelFilter::Info)
|
||||||
|
.init()?;
|
||||||
|
|
||||||
// The default signal(3)[1] action for SIGINT is to interrupt program,
|
// The default signal(3)[1] action for SIGINT is to interrupt program,
|
||||||
// but we want to handle SIGINT ourselves, so we ignore it. The kqueue(2)'s[2]
|
// but we want to handle SIGINT ourselves, so we ignore it. The kqueue(2)'s[2]
|
||||||
// EVFILT_SIGNAL will receive it anyways, because it has lower precedence.
|
// EVFILT_SIGNAL will receive it anyways, because it has lower precedence.
|
||||||
@@ -140,8 +172,14 @@ fn try_main() -> anyhow::Result<()> {
|
|||||||
set_bootpd_lease_time(args.bootpd_lease_time);
|
set_bootpd_lease_time(args.bootpd_lease_time);
|
||||||
|
|
||||||
// Initialize the proxy while still having the root privileges
|
// Initialize the proxy while still having the root privileges
|
||||||
let mut proxy = Proxy::new(args.vm_fd as RawFd, args.vm_mac_address)
|
let mut proxy = Proxy::new(
|
||||||
.context("failed to initialize proxy")?;
|
args.vm_fd as RawFd,
|
||||||
|
args.vm_mac_address,
|
||||||
|
args.vm_net_type,
|
||||||
|
PrefixSet::from_iter(args.allow),
|
||||||
|
args.expose,
|
||||||
|
)
|
||||||
|
.context("failed to initialize proxy")?;
|
||||||
|
|
||||||
// Drop effective privileges to the user
|
// Drop effective privileges to the user
|
||||||
// and group which have had invoked us
|
// and group which have had invoked us
|
||||||
|
|||||||
Reference in New Issue
Block a user