Compare commits

...
Author SHA1 Message Date
fedor a540299219 fixed build 2024-03-07 07:25:01 -05:00
fedor 16864f38f0 Allow traffic to private networks 2024-03-07 05:57:53 -05:00
Sergei Parshev 0a92c290be Added a way to enable host-only networking through tart using --net-host (#32)
* Added a way to enable host-only networking through tart using SOFTNET_NET_TYPE=host

* Removed env variable and moved to Enum instead of str

* Fixed defaults & restricted publicity of host

* Fixed usage of NetType
2024-03-01 11:25:19 -05:00
Fedor Korotkov f5a1b1cdbd Goreleaser Fix (#30)
* Goreleaser Bug

Theoretically this config should work but it doesn't

* Specify `goamd64`
2024-01-24 19:09:59 +04:00
Fedor Korotkov 817dbb6e32 Fixed x86_64 Homebrew update (#29)
Seems we need to build separate archives too and pass it to `brew`.
2024-01-24 07:30:36 -05:00
Fedor Korotkov 5f3b371e93 Build for x86_64 (#28) 2024-01-24 15:23:04 +04:00
Nikolay Edigaryev cd5f1d2f4f Fix unaligned read by switching from unmaintained users crate (#26)
* Fix unaligned read by switching from unmaintained users crate

* Bump proc-macro2 to to fix "unknown feature `proc_macro_span_shrink`"
2023-09-12 06:59:02 -04:00
Fedor Korotkov a775a92772 Add link to the blog post (#25) 2023-05-05 03:19:40 +04:00
Nikolay Edigaryev f38d65f98f README.md: Tart now uses --net-softnet 2023-03-28 08:21:19 +04:00
11 changed files with 69 additions and 40 deletions
+7 -5
View File
@@ -4,17 +4,18 @@ env:
task:
name: Build
macos_instance:
image: ghcr.io/cirruslabs/macos-monterey-xcode:14
image: ghcr.io/cirruslabs/macos-sonoma-xcode:latest
install_rust_script:
- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
- rustup target add x86_64-apple-darwin
build_script:
- cargo build
- cargo build --target aarch64-apple-darwin --target x86_64-apple-darwin
task:
name: Release
only_if: $CIRRUS_TAG != ''
macos_instance:
image: ghcr.io/cirruslabs/macos-monterey-xcode:14
image: ghcr.io/cirruslabs/macos-sonoma-xcode:latest
env:
GITHUB_TOKEN: ENCRYPTED[!98ace8259c6024da912c14d5a3c5c6aac186890a8d4819fad78f3e0c41a4e0cd3a2537dd6e91493952fb056fa434be7c!]
GORELEASER_KEY: ENCRYPTED[!9b80b6ef684ceaf40edd4c7af93014ee156c8aba7e6e5795f41c482729887b5c31f36b651491d790f1f668670888d9fd!]
@@ -23,13 +24,14 @@ task:
SENTRY_AUTH_TOKEN: ENCRYPTED[!c16a5cf7da5f856b4bc2f21fe8cb7aa2a6c981f851c094ed4d3025fd02ea59a58a86cee8b193a69a1fc20fa217e56ac3!]
install_rust_script:
- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
- rustup target add x86_64-apple-darwin
install_script:
- brew install go goreleaser/tap/goreleaser-pro getsentry/tools/sentry-cli
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
upload_sentry_debug_files_script:
- cd target/release-with-debug/
- cd target/aarch64-apple-darwin/release-with-debug/
# Generate and upload symbols
- dsymutil softnet
- sentry-cli debug-files upload -o $SENTRY_ORG -p $SENTRY_PROJECT softnet.dSYM/
+2
View File
@@ -1 +1,3 @@
/.idea
/dist
/target
+7 -9
View File
@@ -1,29 +1,27 @@
project_name: softnet
builds:
- builder: prebuilt
- id: softnet
builder: prebuilt
goamd64: [v1]
goos:
- darwin
goarch:
- arm64
- amd64
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:
- id: binary
format: binary
name_template: "{{ .ProjectName }}"
- id: regular
name_template: "{{ .ProjectName }}"
name_template: "{{ .ProjectName }}-{{ .Arch }}"
release:
prerelease: auto
brews:
- name: softnet
ids:
- regular
tap:
repository:
owner: cirruslabs
name: homebrew-cli
caveats: See the Github repository for more information
Generated
+13 -13
View File
@@ -970,9 +970,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.51"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
dependencies = [
"unicode-ident",
]
@@ -1351,7 +1351,7 @@ dependencies = [
"sentry-anyhow",
"smoltcp",
"system-configuration",
"users",
"uzers",
"vmnet",
]
@@ -1634,16 +1634,6 @@ dependencies = [
"serde",
]
[[package]]
name = "users"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032"
dependencies = [
"libc",
"log",
]
[[package]]
name = "uuid"
version = "1.3.0"
@@ -1654,6 +1644,16 @@ dependencies = [
"serde",
]
[[package]]
name = "uzers"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76d283dc7e8c901e79e32d077866eaf599156cbf427fffa8289aecc52c5c3f63"
dependencies = [
"libc",
"log",
]
[[package]]
name = "vcpkg"
version = "0.2.15"
+1 -1
View File
@@ -22,7 +22,7 @@ mac_address = "1.1.3"
privdrop = "0.5.2"
anyhow = { version = "1.0.66", features = ["backtrace"] }
ip_network = "0.4.1"
users = "0.11.0"
uzers = "0.11.3"
system-configuration = "0.5.0"
num_enum = "0.5.7"
sentry = { version = "0.29.1", features = ["debug-images"] }
+3 -2
View File
@@ -1,13 +1,14 @@
# 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.
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
Softnet solves two problems:
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
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
@@ -30,4 +31,4 @@ For proper functioning, Softnet binary requires two things:
## 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`.
+19 -3
View File
@@ -1,3 +1,4 @@
use clap::ArgEnum;
use anyhow::{anyhow, Context, Result};
use std::net::Ipv4Addr;
use std::os::unix::io::{AsRawFd, RawFd};
@@ -8,6 +9,18 @@ use vmnet::mode::Mode;
use vmnet::parameters::{Parameter, ParameterKind};
use vmnet::{Events, Options};
#[derive(ArgEnum, 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 {
interface: vmnet::Interface,
new_packets_rx: UnixDatagram,
@@ -18,10 +31,13 @@ pub struct Host {
}
impl Host {
pub fn new() -> Result<Host> {
// Initialize a vmnet.framework NAT interface with isolation enabled
pub fn new(vm_net_type: NetType) -> Result<Host> {
// Initialize a vmnet.framework NAT or Host interface with isolation enabled
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 {
enable_isolation: Some(true),
..Default::default()
+1
View File
@@ -1,5 +1,6 @@
mod dhcp_snooper;
mod host;
pub use host::NetType;
mod poller;
pub mod proxy;
mod vm;
+3 -2
View File
@@ -4,6 +4,7 @@ mod vm;
use crate::dhcp_snooper::DhcpSnooper;
use crate::host::Host;
use crate::host::NetType;
use crate::poller::Poller;
use crate::vm::VM;
use anyhow::Result;
@@ -22,9 +23,9 @@ pub struct Proxy {
}
impl Proxy {
pub fn new(vm_fd: RawFd, vm_mac_address: MacAddress) -> Result<Proxy> {
pub fn new(vm_fd: RawFd, vm_mac_address: MacAddress, vm_net_type: NetType) -> Result<Proxy> {
let vm = VM::new(vm_fd)?;
let host = Host::new()?;
let host = Host::new(vm_net_type)?;
let poller = Poller::new(vm.as_raw_fd(), host.as_raw_fd())?;
Ok(Proxy {
+7 -3
View File
@@ -61,10 +61,14 @@ impl Proxy {
// Once we've 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() {
let dst_is_global =
ip_network::IpNetwork::from(Ipv4Addr::from(ipv4_pkt.dst_addr().0)).is_global();
let ip_net = &ip_network::IpNetwork::from(Ipv4Addr::from(ipv4_pkt.dst_addr().0));
let dst_is_global_or_private =
match &ip_net {
ip_network::IpNetwork::V4(ip_net) => ip_net.is_global() || ip_net.is_private(),
ip_network::IpNetwork::V6(ip_net) => ip_net.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_or_private {
return Some(());
}
}
+6 -2
View File
@@ -2,6 +2,7 @@ use anyhow::{anyhow, Context};
use clap::Parser;
use nix::sys::signal::{signal, SigHandler, Signal};
use privdrop::PrivDrop;
use softnet::NetType;
use softnet::proxy::Proxy;
use std::borrow::Cow;
use std::env;
@@ -15,7 +16,7 @@ use system_configuration::core_foundation::number::CFNumber;
use system_configuration::core_foundation::string::CFString;
use system_configuration::preferences::SCPreferences;
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)]
struct Args {
@@ -28,6 +29,9 @@ struct Args {
#[clap(long, help = "MAC address to enforce for the VM")]
vm_mac_address: mac_address::MacAddress,
#[clap(long, arg_enum, help = "type of network to use for the VM", default_value_t=NetType::Nat)]
vm_net_type: NetType,
#[clap(
long,
help = "set bootpd(8) lease time to this value (in seconds) before starting the VM",
@@ -140,7 +144,7 @@ fn try_main() -> anyhow::Result<()> {
set_bootpd_lease_time(args.bootpd_lease_time);
// 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(args.vm_fd as RawFd, args.vm_mac_address, args.vm_net_type)
.context("failed to initialize proxy")?;
// Drop effective privileges to the user