mirror of
https://github.com/cirruslabs/softnet.git
synced 2026-09-30 03:51:23 +02:00
Introduce --allow command-line argument to allow traffic to CIDRs (#34)
This commit is contained in:
Generated
+23
-2
@@ -603,9 +603,9 @@ checksum = "aa2f047c0a98b2f299aa5d6d7088443570faae494e9ae1305e48be000c9e0eb1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipnet"
|
name = "ipnet"
|
||||||
version = "2.7.1"
|
version = "2.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146"
|
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
@@ -773,6 +773,15 @@ dependencies = [
|
|||||||
"static_assertions",
|
"static_assertions",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num_cpus"
|
name = "num_cpus"
|
||||||
version = "1.15.0"
|
version = "1.15.0"
|
||||||
@@ -924,6 +933,16 @@ version = "0.2.17"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prefix-trie"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "04cb065e4407d69a5a5265221262cceeafff7f1aabc545d01ed955cce92ee78b"
|
||||||
|
dependencies = [
|
||||||
|
"ipnet",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "privdrop"
|
name = "privdrop"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
@@ -1341,11 +1360,13 @@ dependencies = [
|
|||||||
"clap",
|
"clap",
|
||||||
"dhcproto",
|
"dhcproto",
|
||||||
"ip_network",
|
"ip_network",
|
||||||
|
"ipnet",
|
||||||
"libc",
|
"libc",
|
||||||
"mac_address",
|
"mac_address",
|
||||||
"nix 0.26.2",
|
"nix 0.26.2",
|
||||||
"num_enum",
|
"num_enum",
|
||||||
"polling",
|
"polling",
|
||||||
|
"prefix-trie",
|
||||||
"privdrop",
|
"privdrop",
|
||||||
"sentry",
|
"sentry",
|
||||||
"sentry-anyhow",
|
"sentry-anyhow",
|
||||||
|
|||||||
@@ -28,3 +28,5 @@ num_enum = "0.5.7"
|
|||||||
sentry = { version = "0.29.1", features = ["debug-images"] }
|
sentry = { version = "0.29.1", features = ["debug-images"] }
|
||||||
sentry-anyhow = { version = "0.29.1", features = ["backtrace"] }
|
sentry-anyhow = { version = "0.29.1", features = ["backtrace"] }
|
||||||
nix = "0.26.2"
|
nix = "0.26.2"
|
||||||
|
prefix-trie = "0.3.0"
|
||||||
|
ipnet = "2.9.0"
|
||||||
|
|||||||
+13
-5
@@ -1,5 +1,5 @@
|
|||||||
use clap::ArgEnum;
|
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
use clap::ArgEnum;
|
||||||
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;
|
||||||
@@ -46,15 +46,23 @@ impl Host {
|
|||||||
.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
|
||||||
|
|||||||
+10
-1
@@ -8,7 +8,9 @@ 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;
|
||||||
|
use ipnet::Ipv4Net;
|
||||||
use mac_address::MacAddress;
|
use mac_address::MacAddress;
|
||||||
|
use prefix_trie::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};
|
||||||
@@ -19,11 +21,17 @@ pub struct Proxy {
|
|||||||
poller: Poller,
|
poller: Poller,
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Proxy {
|
impl Proxy {
|
||||||
pub fn new(vm_fd: RawFd, vm_mac_address: MacAddress, vm_net_type: NetType) -> Result<Proxy> {
|
pub fn new(
|
||||||
|
vm_fd: RawFd,
|
||||||
|
vm_mac_address: MacAddress,
|
||||||
|
vm_net_type: NetType,
|
||||||
|
allow: PrefixSet<Ipv4Net>,
|
||||||
|
) -> Result<Proxy> {
|
||||||
let vm = VM::new(vm_fd)?;
|
let vm = VM::new(vm_fd)?;
|
||||||
let host = Host::new(vm_net_type)?;
|
let host = Host::new(vm_net_type)?;
|
||||||
let poller = Poller::new(vm.as_raw_fd(), host.as_raw_fd())?;
|
let poller = Poller::new(vm.as_raw_fd(), host.as_raw_fd())?;
|
||||||
@@ -34,6 +42,7 @@ 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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-4
@@ -2,6 +2,7 @@ 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,
|
||||||
};
|
};
|
||||||
@@ -58,15 +59,22 @@ 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 = Ipv4Addr::from(ipv4_pkt.dst_addr().0);
|
||||||
|
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);
|
||||||
|
|
||||||
|
if self.allow.get_spm(&dst_net).is_some() {
|
||||||
|
return Some(());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow communication with host
|
// Allow communication with host
|
||||||
|
|||||||
+18
-3
@@ -1,11 +1,14 @@
|
|||||||
use anyhow::{anyhow, Context};
|
use anyhow::{anyhow, Context};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use ipnet::Ipv4Net;
|
||||||
use nix::sys::signal::{signal, SigHandler, Signal};
|
use nix::sys::signal::{signal, SigHandler, Signal};
|
||||||
|
use prefix_trie::PrefixSet;
|
||||||
use privdrop::PrivDrop;
|
use privdrop::PrivDrop;
|
||||||
use softnet::NetType;
|
|
||||||
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;
|
||||||
use std::os::unix::io::RawFd;
|
use std::os::unix::io::RawFd;
|
||||||
use std::os::unix::process::CommandExt;
|
use std::os::unix::process::CommandExt;
|
||||||
@@ -45,6 +48,13 @@ 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)",
|
||||||
|
use_value_delimiter = true
|
||||||
|
)]
|
||||||
|
allow: Vec<Ipv4Net>,
|
||||||
|
|
||||||
#[clap(long, hide = true)]
|
#[clap(long, hide = true)]
|
||||||
sudo_escalation_probing: bool,
|
sudo_escalation_probing: bool,
|
||||||
|
|
||||||
@@ -144,8 +154,13 @@ 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, args.vm_net_type)
|
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),
|
||||||
|
)
|
||||||
|
.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