Skip to content

Commit

Permalink
Introduce --allow command-line argument to allow traffic to CIDRs (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
edigaryev authored Mar 11, 2024
1 parent 0a92c29 commit a92f4e0
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 15 deletions.
25 changes: 23 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,5 @@ num_enum = "0.5.7"
sentry = { version = "0.29.1", features = ["debug-images"] }
sentry-anyhow = { version = "0.29.1", features = ["backtrace"] }
nix = "0.26.2"
prefix-trie = "0.3.0"
ipnet = "2.9.0"
18 changes: 13 additions & 5 deletions lib/host.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use clap::ArgEnum;
use anyhow::{anyhow, Context, Result};
use clap::ArgEnum;
use std::net::Ipv4Addr;
use std::os::unix::io::{AsRawFd, RawFd};
use std::os::unix::net::UnixDatagram;
Expand Down Expand Up @@ -46,15 +46,23 @@ impl Host {
.context("failed to initialize vmnet interface")?;

// Retrieve first IP (gateway) used for this interface
let Some(Parameter::StartAddress(gateway_ip)) = interface.parameters().get(ParameterKind::StartAddress) else {
return Err(anyhow!("failed to retrieve vmnet's interface start address"));
let Some(Parameter::StartAddress(gateway_ip)) =
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)
.context("failed to parse vmnet's interface start address")?;

// Retrieve max packet size for this interface
let Some(Parameter::MaxPacketSize(max_packet_size)) = interface.parameters().get(ParameterKind::MaxPacketSize) else {
return Err(anyhow!("failed to retrieve vmnet's interface max packet size"));
let Some(Parameter::MaxPacketSize(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
Expand Down
11 changes: 10 additions & 1 deletion lib/proxy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ use crate::host::NetType;
use crate::poller::Poller;
use crate::vm::VM;
use anyhow::Result;
use ipnet::Ipv4Net;
use mac_address::MacAddress;
use prefix_trie::PrefixSet;
use smoltcp::wire::EthernetFrame;
use std::io::ErrorKind;
use std::os::unix::io::{AsRawFd, RawFd};
Expand All @@ -19,11 +21,17 @@ pub struct Proxy {
poller: Poller,
vm_mac_address: smoltcp::wire::EthernetAddress,
dhcp_snooper: DhcpSnooper,
allow: PrefixSet<Ipv4Net>,
enobufs_encountered: bool,
}

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 host = Host::new(vm_net_type)?;
let poller = Poller::new(vm.as_raw_fd(), host.as_raw_fd())?;
Expand All @@ -34,6 +42,7 @@ impl Proxy {
poller,
vm_mac_address: smoltcp::wire::EthernetAddress(vm_mac_address.bytes()),
dhcp_snooper: Default::default(),
allow,
enobufs_encountered: false,
})
}
Expand Down
16 changes: 12 additions & 4 deletions lib/proxy/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::proxy::udp_packet_helper::UdpPacketHelper;
use crate::proxy::Proxy;
use anyhow::Context;
use anyhow::Result;
use ipnet::Ipv4Net;
use smoltcp::wire::{
ArpPacket, EthernetFrame, EthernetProtocol, IpProtocol, Ipv4Packet, UdpPacket,
};
Expand Down Expand Up @@ -58,15 +59,22 @@ impl Proxy {
}

fn allowed_from_vm_ipv4(&self, ipv4_pkt: Ipv4Packet<&[u8]>) -> Option<()> {
// Once we've learned the VM's IP from the DHCP snooping,
// allow all global traffic for that VM's IP
// Have we learned the VM's IP from the DHCP snooping?
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();
// If so, allow all global traffic
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 {
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
Expand Down
21 changes: 18 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use anyhow::{anyhow, Context};
use clap::Parser;
use ipnet::Ipv4Net;
use nix::sys::signal::{signal, SigHandler, Signal};
use prefix_trie::PrefixSet;
use privdrop::PrivDrop;
use softnet::NetType;
use softnet::proxy::Proxy;
use softnet::NetType;
use std::borrow::Cow;
use std::env;

use std::os::raw::c_int;
use std::os::unix::io::RawFd;
use std::os::unix::process::CommandExt;
Expand Down Expand Up @@ -45,6 +48,13 @@ struct Args {
#[clap(long, help = "group name to drop privileges to")]
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)]
sudo_escalation_probing: bool,

Expand Down Expand Up @@ -144,8 +154,13 @@ 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, args.vm_net_type)
.context("failed to initialize proxy")?;
let mut proxy = Proxy::new(
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
// and group which have had invoked us
Expand Down

0 comments on commit a92f4e0

Please sign in to comment.