Skip to content

Commit

Permalink
option for host access on local network
Browse files Browse the repository at this point in the history
  • Loading branch information
arch committed Apr 5, 2022
1 parent a664d5c commit 5cb767e
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 13 deletions.
8 changes: 8 additions & 0 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@ pub struct ExecCommand {
#[clap(long = "dns", short = 'd')]
pub dns: Option<Vec<IpAddr>>,

/// List of /etc/hosts entries for the network namespace (e.g. "10.0.1.10 webdav.server01.lan","10.0.1.10 vaultwarden.server01.lan"). For an local host you should also specifiy the open-hosts command
#[clap(long = "hosts", use_value_delimiter = true)]
pub hosts_entries: Option<Vec<String>>,

/// List of host ip's to open on network namespace (comma separated)
#[clap(long = "open-hosts", use_value_delimiter = true)]
pub open_hosts: Option<Vec<IpAddr>>,

/// Disable killswitch
#[clap(long = "no-killswitch")]
pub no_killswitch: bool,
Expand Down
51 changes: 47 additions & 4 deletions src/dns_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@ use log::{debug, warn};
use serde::{Deserialize, Serialize};
use std::io::Write;
use std::net::IpAddr;
use std::io::BufRead;
use regex::{Regex,Captures};

#[derive(Serialize, Deserialize, Debug)]
pub struct DnsConfig {
ns_name: String,
}

impl DnsConfig {
pub fn new(ns_name: String, servers: &[IpAddr], suffixes: &[&str]) -> anyhow::Result<Self> {
pub fn new(ns_name: String, servers: &[IpAddr], suffixes: &[&str], hosts_entries: Option<&Vec<String>>) -> anyhow::Result<Self> {
std::fs::create_dir_all(format!("/etc/netns/{}", ns_name))
.with_context(|| format!("Failed to create directory: /etc/netns/{}", ns_name))?;

let mut f = std::fs::File::create(format!("/etc/netns/{}/resolv.conf", ns_name))
let mut resolv = std::fs::File::create(format!("/etc/netns/{}/resolv.conf", ns_name))
.with_context(|| {
format!(
"Failed to open resolv.conf: /etc/netns/{}/resolv.conf",
Expand All @@ -34,7 +36,7 @@ impl DnsConfig {

let suffix = suffixes.join(" ");
if !suffix.is_empty() {
writeln!(f, "search {}", suffix).with_context(|| {
writeln!(resolv, "search {}", suffix).with_context(|| {
format!(
"Failed to overwrite resolv.conf: /etc/netns/{}/resolv.conf",
ns_name
Expand All @@ -43,14 +45,55 @@ impl DnsConfig {
}

for dns in servers {
writeln!(f, "nameserver {}", dns).with_context(|| {
writeln!(resolv, "nameserver {}", dns).with_context(|| {
format!(
"Failed to overwrite resolv.conf: /etc/netns/{}/resolv.conf",
ns_name
)
})?;
}

if let Some(my_hosts_entries) = hosts_entries {
let mut hosts = std::fs::File::create(format!("/etc/netns/{}/hosts", ns_name))
.with_context(|| {
format!(
"Failed to open hosts: /etc/netns/{}/hosts",
ns_name
)
})?;

for hosts_enty in my_hosts_entries {
writeln!(hosts, "{}", hosts_enty).with_context(|| {
format!(
"Failed to overwrite hosts: /etc/netns/{}/hosts",
ns_name
)
})?;
}
}

let nsswitch_src = std::fs::File::open("/etc/nsswitch.conf")
.with_context(|| {
"Failed to open nsswitch.conf: /etc/nsswitch.conf"
})?;

let mut nsswitch = std::fs::File::create(format!("/etc/netns/{}/nsswitch.conf", ns_name))
.with_context(|| {
format!(
"Failed to open nsswitch.conf: /etc/netns/{}/nsswitch.conf",
ns_name
)
})?;

for line in std::io::BufReader::new(nsswitch_src).lines() {
writeln!(nsswitch, "{}", Regex::new(r"^hosts:.*$").unwrap().replace(&line?, |_caps: &Captures| { "hosts: files mymachines myhostname dns" })).with_context(|| {
format!(
"Failed to overwrite nsswitch.conf: /etc/netns/{}/nsswitch.conf",
ns_name
)
})?;
}

Ok(Self { ns_name })
}
}
Expand Down
10 changes: 6 additions & 4 deletions src/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ pub fn exec(command: ExecCommand) -> anyhow::Result<()> {
let target_subnet = get_target_subnet()?;
ns.add_loopback()?;
ns.add_veth_pair()?;
ns.add_routing(target_subnet)?;
ns.add_routing(target_subnet, command.open_hosts)?;
ns.add_host_masquerade(target_subnet, interface.clone(), firewall)?;
ns.add_firewall_exception(
interface,
Expand Down Expand Up @@ -278,7 +278,7 @@ pub fn exec(command: ExecCommand) -> anyhow::Result<()> {
.unwrap_or_else(|| vec![IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8))]);

// TODO: DNS suffixes?
ns.dns_config(&dns, &[])?;
ns.dns_config(&dns, &[], command.hosts_entries.as_ref())?;
// Check if using Shadowsocks
if let Some((ss_host, ss_lport)) =
uses_shadowsocks(config_file.as_ref().expect("No config file provided"))?
Expand Down Expand Up @@ -329,7 +329,7 @@ pub fn exec(command: ExecCommand) -> anyhow::Result<()> {
let old_dns = ns.dns_config.take();
std::mem::forget(old_dns);
// TODO: DNS suffixes?
ns.dns_config(&[newdns], &[])?;
ns.dns_config(&[newdns], &[], command.hosts_entries.as_ref())?;
}
}
}
Expand All @@ -342,12 +342,13 @@ pub fn exec(command: ExecCommand) -> anyhow::Result<()> {
firewall,
command.disable_ipv6,
base_dns.as_ref(),
command.hosts_entries.as_ref(),
)?;
}
Protocol::OpenConnect => {
let dns = base_dns.unwrap_or_else(|| vec![IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8))]);
// TODO: DNS suffixes?
ns.dns_config(&dns, &[])?;
ns.dns_config(&dns, &[], command.hosts_entries.as_ref())?;
ns.run_openconnect(
config_file,
command.open_ports.as_ref(),
Expand All @@ -362,6 +363,7 @@ pub fn exec(command: ExecCommand) -> anyhow::Result<()> {
config_file.expect("No OpenFortiVPN config file provided"),
command.open_ports.as_ref(),
command.forward_ports.as_ref(),
command.hosts_entries.as_ref(),
firewall,
)?;
}
Expand Down
26 changes: 23 additions & 3 deletions src/netns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ impl NetworkNamespace {
Ok(())
}

pub fn add_routing(&mut self, target_subnet: u8) -> anyhow::Result<()> {
pub fn add_routing(&mut self, target_subnet: u8, hosts: Option<Vec<IpAddr>>) -> anyhow::Result<()> {
// TODO: Handle case where IP address taken in better way i.e. don't just change subnet
let veth_dest = &self
.veth_pair
Expand Down Expand Up @@ -200,6 +200,22 @@ impl NetworkNamespace {
])
.with_context(|| format!("Failed to assign static IP to veth source: {}", veth_source))?;

if let Some(my_hosts) = hosts {
for host in my_hosts {
self.exec(&[
"ip",
"route",
"add",
&format!("{}", host),
"via",
&ip_nosub,
"dev",
veth_source,
])
.with_context(|| format!("Failed to assign hosts route to veth source: {}", veth_source))?;
}
}

info!(
"IP address of namespace as seen from host: {}",
veth_source_ip_nosub
Expand All @@ -212,8 +228,8 @@ impl NetworkNamespace {
Ok(())
}

pub fn dns_config(&mut self, server: &[IpAddr], suffixes: &[&str]) -> anyhow::Result<()> {
self.dns_config = Some(DnsConfig::new(self.name.clone(), server, suffixes)?);
pub fn dns_config(&mut self, server: &[IpAddr], suffixes: &[&str], hosts_entries: Option<&Vec<String>>) -> anyhow::Result<()> {
self.dns_config = Some(DnsConfig::new(self.name.clone(), server, suffixes, hosts_entries)?);
Ok(())
}

Expand Down Expand Up @@ -267,13 +283,15 @@ impl NetworkNamespace {
config_file: PathBuf,
open_ports: Option<&Vec<u16>>,
forward_ports: Option<&Vec<u16>>,
hosts_entries: Option<&Vec<String>>,
firewall: Firewall,
) -> anyhow::Result<()> {
self.openfortivpn = Some(OpenFortiVpn::run(
self,
config_file,
open_ports,
forward_ports,
hosts_entries,
firewall,
)?);
Ok(())
Expand Down Expand Up @@ -308,6 +326,7 @@ impl NetworkNamespace {
firewall: Firewall,
disable_ipv6: bool,
dns: Option<&Vec<IpAddr>>,
hosts_entries: Option<&Vec<String>>,
) -> anyhow::Result<()> {
self.wireguard = Some(Wireguard::run(
self,
Expand All @@ -318,6 +337,7 @@ impl NetworkNamespace {
firewall,
disable_ipv6,
dns,
hosts_entries,
)?);
Ok(())
}
Expand Down
3 changes: 2 additions & 1 deletion src/openfortivpn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ impl OpenFortiVpn {
config_file: PathBuf,
open_ports: Option<&Vec<u16>>,
forward_ports: Option<&Vec<u16>>,
hosts_entries: Option<&Vec<String>>,
firewall: Firewall,
) -> anyhow::Result<Self> {
if let Err(x) = which::which("openfortivpn") {
Expand Down Expand Up @@ -94,7 +95,7 @@ impl OpenFortiVpn {
let dns_ip: Vec<IpAddr> = (dns.0).into_iter().map(IpAddr::from).collect();
// TODO: Avoid this meaningless collect
let suffixes: Vec<&str> = (dns.1).iter().map(|x| x.as_str()).collect();
netns.dns_config(dns_ip.as_slice(), suffixes.as_slice())?;
netns.dns_config(dns_ip.as_slice(), suffixes.as_slice(), hosts_entries)?;
// Allow input to and output from open ports (for port forwarding in tunnel)
if let Some(opens) = open_ports {
super::util::open_ports(netns, opens.as_slice(), firewall)?;
Expand Down
3 changes: 2 additions & 1 deletion src/wireguard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ impl Wireguard {
firewall: Firewall,
disable_ipv6: bool,
dns: Option<&Vec<IpAddr>>,
hosts_entries: Option<&Vec<String>>,
) -> anyhow::Result<Self> {
if let Err(x) = which::which("wg") {
error!("wg binary not found. Is wireguard-tools installed and on PATH?");
Expand Down Expand Up @@ -138,7 +139,7 @@ impl Wireguard {
vec![IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8))]
});
// TODO: DNS suffixes?
namespace.dns_config(&dns, &[])?;
namespace.dns_config(&dns, &[], hosts_entries)?;
let fwmark = "51820";
namespace.exec(&["wg", "set", &if_name, "fwmark", fwmark])?;

Expand Down

0 comments on commit 5cb767e

Please sign in to comment.