Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

148 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Softnet

Softnet is a software networking for Tart which provides better network isolation and alleviates DHCP shortage on production systems.

It is essentially a userspace packet filter which restricts the VM networking and prevents a class of security issues, such as ARP spoofing. By default, the VM will only be able to:

  • send traffic from its own MAC-address
  • send traffic from the IP-address assigned to it by the DHCP
  • send traffic to globally routable IPv4 addresses
  • send traffic to gateway IP of the vmnet bridge (this would normally be "bridge100" interface)
  • receive any incoming traffic

In addition, Softnet tunes macOS built-in DHCP server to decrease its lease time from the default 86,400 seconds (one day) to 600 seconds (10 minutes). This is especially important when you use Tart to clone and run a lot of ephemeral VMs over a period of one day.

Please check out this blog post for backstory.

Working model

Softnet solves two problems:

  1. VM network isolation
  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

And assumes that:

  1. Tart gives it's VMs unique MAC-addresses
  2. macOS built-in DHCP-server won't re-use the IP-addresses from it's pool until their lease expire

...otherwise it's possible for two VMs to receive an identical IP-address from the macOS built-in DHCP-server (even in the presence of Softnet's packet filtering) and thus bypass the protections offered by Softnet.

Stateful flow authorization

Stateful in/out rules use a bounded authorization cache that records the direction and exact transport tuple of policy-approved flows, allowing matching return traffic without treating it as a new flow and thus requiring a separate policy entry. It is not a complete TCP connection tracker: endpoint transport stacks remain responsible for validating sequence numbers, receive windows, resets, and application-level traffic.

This cache deliberately favors security, bounded resource use, and a simple implementation over availability. Only policy-authorized initiator traffic renews an entry; return traffic does not. Softnet does not maintain fairness quotas, eviction heuristics, or complete TCP lifecycle state.

A packet admitted by a stateful rule is denied when Softnet cannot represent its flow, including when the cache is full. High flow churn, long idle connections, or ambiguous retransmissions may therefore interrupt networking and require the affected VM to reconnect.

For TCP, a bare TCP SYN on an existing tuple is deliberately returned to policy because Softnet cannot distinguish a retransmission from tuple reuse without tracking TCP sequence state. If authorized, it may replace the tuple's previous cache lifetime; this can reduce availability but cannot grant traffic that policy did not permit.

For ICMP, stateful flow authorization supports only echo requests and replies.

Installing

For proper functioning, Softnet binary requires two things:

  • a SUID-bit to be set on the binary or a passwordless sudo to be configured, which effectively gives the binary root privileges
    • these privileges are needed to create vmnet.framework interface and perform DHCP-related system tweaks
    • the privileges will be dropped automatically to that of the calling user (or those represented by the --user and --group command-line arguments) once all of the initialization is completed
  • the binary to be available in PATH
    • so that the Tart will be able to find it

Running

Softnet is started and managed automatically by Tart if --net-softnet flag is provided when calling tart run.

Dynamic network policy

Softnet can update the running VM's IPv4 policy without restarting the VM. Pass a connected Unix stream socket as --control-fd to enable a newline-delimited JSON-RPC 2.0 control channel. The socket is duplex and must be separate from --vm-fd, which carries VM packets.

The supported methods are softnet.policy.get and softnet.policy.set. A complete policy update looks like this (each request and response occupies one line):

{"jsonrpc":"2.0","id":"42","method":"softnet.policy.set","params":{"allow":["@host","10.0.0.0/8"],"block":["0.0.0.0/0"]}}
{"jsonrpc":"2.0","id":"42","result":{"allow":["10.0.0.0/8","@host"],"block":["0.0.0.0/0"],"ruleCount":3}}

Every request must include a non-null string (at most 256 bytes) or non-negative integer id; notifications are rejected so policy changes always have an acknowledgment. Policy updates are atomic: all rules are parsed and a new prefix map is built before the active policy changes. Longest-prefix matching and block precedence for identical rules are preserved. Rules are normalized and deduplicated. A policy update may contain at most 4096 combined allow/block rules, and a request frame may not exceed 1 MiB.

When the normalized allow or block policy changes, Softnet clears the flow table so the new policy applies to established flows immediately. This may interrupt active connections. Repeating the same normalized policy is a no-op and preserves the flow table.

Use block=["0.0.0.0/0"] with specific allow rules for a default-deny egress policy. Closing the control socket leaves the last accepted policy active.

Releases

Packages

Used by

Contributors

Languages