Compare commits

..

8 Commits

Author SHA1 Message Date
dswd ea9e3cd5e1
Create FUNDING.yml 2019-12-06 16:43:10 +01:00
Dennis Schwerdel 6c85e749d8 Only run coveralls on stable 2019-12-06 13:08:45 +01:00
Dennis Schwerdel bc994a959d Fixed tests 2019-12-06 10:23:50 +01:00
Dennis Schwerdel 5e7752b097 Added service restrictions to systemd 2019-12-06 10:21:07 +01:00
Dennis Schwerdel 55358b3561 Set keepalive to 120 secs when NAT is detected 2019-12-06 10:20:24 +01:00
Dennis Schwerdel cd09311059 Fixed problems on stats file when dropping perms 2019-12-06 09:55:24 +01:00
Dennis Schwerdel 04e2892c8e Also drop privileges in foreground mode 2019-12-06 08:54:27 +01:00
Dennis Schwerdel 7307b25405 Adding keepalive parameter to manpage 2019-12-06 08:48:36 +01:00
11 changed files with 132 additions and 38 deletions

12
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,12 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: dswd
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: ['https://paypal.me/dswd73']

View File

@ -27,7 +27,8 @@ script:
- |
cargo build && cargo test
after_success:
- cargo coveralls
- |
[ $TRAVIS_RUST_VERSION = stable ] && cargo coveralls
env:
global:
- TRAVIS_CARGO_NIGHTLY_FEATURE=""

View File

@ -2,6 +2,15 @@
This project follows [semantic versioning](http://semver.org).
### Unreleased
- [added] Added service restrictions to systemd
- [changed] Also drop privileges in foreground mode
- [changed] Set builders to Ubuntu 16.04 and CentOS 7
- [changed] Set keepalive to 120 secs when NAT is detected
- [fixed] Added parameter keepalive to manpage
- [fixed] Fixed problems on stats file when dropping permissions
### v1.1.0 (2019-12-04)
- [added] Exchange peer timeout and adapt keepalive accordingly

24
Cargo.lock generated
View File

@ -230,6 +230,18 @@ dependencies = [
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "nix"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "nom"
version = "4.2.3"
@ -254,6 +266,15 @@ name = "ppv-lite86"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "privdrop"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "proc-macro2"
version = "1.0.6"
@ -551,6 +572,7 @@ dependencies = [
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
"privdrop 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)",
@ -728,10 +750,12 @@ dependencies = [
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88"
"checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce"
"checksum nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229"
"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
"checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
"checksum privdrop 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "432c2e1a6d9d56e3c14710a9b807ade349c48ccd5647bcda9a175f40f81ca5a9"
"checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27"
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"

View File

@ -28,6 +28,7 @@ igd = "0.9"
siphasher = "0.3"
daemonize = "0.4"
ring = "0.16"
privdrop = "0.3"
[build-dependencies]
cc = "^1"

View File

@ -2,12 +2,22 @@
Description=VpnCloud network '%I'
After=network-online.target
Wants=network-online.target
Documentation=man:vpncloud(1)
[Service]
Type=forking
ExecStart=/usr/bin/vpncloud --config /etc/vpncloud/%i.net --daemon --log-file /var/log/vpncloud-%i.log --stats-file /var/log/vpncloud-%i.stats --pid-file /run/vpncloud-%i.pid
Type=simple
ExecStart=/usr/bin/vpncloud --config /etc/vpncloud/%i.net --log-file /var/log/vpncloud-%i.log --stats-file /var/log/vpncloud-%i.stats
WorkingDirectory=/etc/vpncloud
PIDFile=/run/vpncloud-%i.pid
RestartSec=5s
Restart=on-failure
LimitNPROC=10
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=strict
ReadWritePaths=/var/log/vpncloud-%i.log /var/log/vpncloud-%i.stats
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT
DeviceAllow=/dev/null rw
DeviceAllow=/dev/net/tun rw
[Install]
WantedBy=multi-user.target

View File

@ -6,12 +6,11 @@ use std::{
cmp::min,
collections::HashMap,
fmt,
fs::{self, File, Permissions},
fs::File,
hash::BuildHasherDefault,
io::{self, Write},
marker::PhantomData,
net::{SocketAddr, ToSocketAddrs},
os::unix::fs::PermissionsExt
net::{SocketAddr, ToSocketAddrs}
};
use fnv::FnvHasher;
@ -230,6 +229,7 @@ pub struct GenericCloud<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSou
peer_timeout_publish: u16,
update_freq: u16,
buffer_out: [u8; 64 * 1024],
stats_file: Option<File>,
next_housekeep: Time,
next_stats_out: Time,
next_beacon: Time,
@ -244,7 +244,7 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
#[allow(clippy::too_many_arguments)]
pub fn new(
config: &Config, device: D, table: T, learning: bool, broadcast: bool, addresses: Vec<Range>, crypto: Crypto,
port_forwarding: Option<PortForwarding>
port_forwarding: Option<PortForwarding>, stats_file: Option<File>
) -> Self
{
let socket4 = match S::listen_v4("0.0.0.0", config.port) {
@ -256,11 +256,11 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
Err(err) => fail!("Failed to open ipv6 address ::{}: {}", config.port, err)
};
let now = TS::now();
let peer_timeout_publish = if socket4.detect_nat() {
info!("Private IP detected, setting published peer timeout to 300s");
300
let update_freq = if socket4.detect_nat() && config.get_keepalive() > 120 {
info!("Private IP detected, setting keepalive interval to 120s");
120
} else {
config.peer_timeout as u16
config.get_keepalive() as u16
};
let mut res = GenericCloud {
magic: config.get_magic(),
@ -271,13 +271,14 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
broadcast,
reconnect_peers: Vec::new(),
own_addresses: Vec::new(),
peer_timeout_publish,
peer_timeout_publish: config.peer_timeout as u16,
table,
socket4,
socket6,
device,
next_peerlist: now,
update_freq: config.get_keepalive() as u16,
update_freq,
stats_file,
buffer_out: [0; 64 * 1024],
next_housekeep: now,
next_stats_out: now + STATS_INTERVAL,
@ -480,8 +481,8 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
let mut msg = Message::Peers(peers);
self.broadcast_msg(&mut msg)?;
// Reschedule for next update
self.update_freq = min(self.config.get_keepalive() as u16, self.peers.min_peer_timeout());
self.next_peerlist = now + Time::from(self.update_freq);
let interval = min(self.update_freq as u16, self.peers.min_peer_timeout());
self.next_peerlist = now + Time::from(interval);
}
// Connect to those reconnect_peers that are due
for entry in self.reconnect_peers.clone() {
@ -526,7 +527,7 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
// Write out the statistics
self.write_out_stats().map_err(|err| Error::File("Failed to write stats file", err))?;
self.next_stats_out = now + STATS_INTERVAL;
self.traffic.period(Some(60));
self.traffic.period(Some(5));
}
if let Some(peers) = self.beacon_serializer.get_cmd_results() {
debug!("Loaded beacon with peers: {:?}", peers);
@ -586,18 +587,16 @@ impl<D: Device, P: Protocol, T: Table, S: Socket, TS: TimeSource> GenericCloud<D
/// Calculates, resets and writes out the statistics to a file
fn write_out_stats(&mut self) -> Result<(), io::Error> {
if self.config.stats_file.is_none() {
return Ok(())
}
if let Some(ref mut f) = self.stats_file {
debug!("Writing out stats");
let mut f = File::create(self.config.stats_file.as_ref().unwrap())?;
self.peers.write_out(&mut f)?;
writeln!(&mut f)?;
self.table.write_out(&mut f)?;
writeln!(&mut f)?;
self.traffic.write_out(&mut f)?;
writeln!(&mut f)?;
fs::set_permissions(self.config.stats_file.as_ref().unwrap(), Permissions::from_mode(0o644))?;
f.set_len(0)?;
self.peers.write_out(f)?;
writeln!(f)?;
self.table.write_out(f)?;
writeln!(f)?;
self.traffic.write_out(f)?;
writeln!(f)?;
}
Ok(())
}

View File

@ -33,9 +33,10 @@ pub mod udpmessage;
use docopt::Docopt;
use std::{
fs::File,
fs::{self, File, Permissions},
io::{self, Write},
net::UdpSocket,
os::unix::fs::PermissionsExt,
path::Path,
process::Command,
str::FromStr,
@ -166,7 +167,7 @@ impl<P: Protocol> AnyCloud<P> {
#[allow(unknown_lints, clippy::too_many_arguments)]
fn new(
config: &Config, device: TunTapDevice, table: AnyTable, learning: bool, broadcast: bool, addresses: Vec<Range>,
crypto: Crypto, port_forwarding: Option<PortForwarding>
crypto: Crypto, port_forwarding: Option<PortForwarding>, stats_file: Option<File>
) -> Self
{
match table {
@ -178,7 +179,15 @@ impl<P: Protocol> AnyCloud<P> {
UdpSocket,
SystemTimeSource
>::new(
config, device, t, learning, broadcast, addresses, crypto, port_forwarding
config,
device,
t,
learning,
broadcast,
addresses,
crypto,
port_forwarding,
stats_file
))
}
AnyTable::Routing(t) => {
@ -190,7 +199,8 @@ impl<P: Protocol> AnyCloud<P> {
broadcast,
addresses,
crypto,
port_forwarding
port_forwarding,
stats_file
))
}
}
@ -256,7 +266,19 @@ fn run<P: Protocol>(config: Config) {
None => Crypto::None
};
let port_forwarding = if config.port_forwarding { PortForwarding::new(config.port) } else { None };
let mut cloud = AnyCloud::<P>::new(&config, device, table, learning, broadcasting, ranges, crypto, port_forwarding);
let stats_file = match config.stats_file {
None => None,
Some(ref name) => {
let file = try_fail!(File::create(name), "Failed to create stats file: {}");
try_fail!(
fs::set_permissions(name, Permissions::from_mode(0o644)),
"Failed to set permissions on stats file: {}"
);
Some(file)
}
};
let mut cloud =
AnyCloud::<P>::new(&config, device, table, learning, broadcasting, ranges, crypto, port_forwarding, stats_file);
if let Some(script) = config.ifup {
run_script(&script, cloud.ifname());
}
@ -277,6 +299,16 @@ fn run<P: Protocol>(config: Config) {
daemonize = daemonize.pid_file(pid_file).chown_pid_file(true);
}
try_fail!(daemonize.start(), "Failed to daemonize: {}");
} else if config.user.is_some() || config.group.is_some() {
info!("Dropping privileges");
let mut pd = privdrop::PrivDrop::default();
if let Some(user) = config.user {
pd = pd.user(user);
}
if let Some(group) = config.group {
pd = pd.group(group);
}
try_fail!(pd.apply(), "Failed to drop privileges: {}");
}
cloud.run();
if let Some(script) = config.ifdown {

View File

@ -80,7 +80,7 @@ fn create_tap_node(nat: bool) -> TapTestNode {
fn create_tap_node_with_config(nat: bool, mut config: Config) -> TapTestNode {
MockSocket::set_nat(nat);
config.port = NEXT_PORT.with(|p| p.fetch_add(1, Ordering::Relaxed)) as u16;
TestNode::new(&config, MockDevice::new(), SwitchTable::new(1800, 10), true, true, vec![], Crypto::None, None)
TestNode::new(&config, MockDevice::new(), SwitchTable::new(1800, 10), true, true, vec![], Crypto::None, None, None)
}
#[allow(dead_code)]
@ -94,6 +94,7 @@ fn create_tun_node(nat: bool, addresses: Vec<Range>) -> TunTestNode {
false,
addresses,
Crypto::None,
None,
None
)
}

View File

@ -31,8 +31,8 @@ Options:
interface.
--pid-file <file> Store the process id in this file when
daemonizing.
--user <user> Run as other user when daemonizing.
--group <group> Run as other group when daemonizing.
--user <user> Run as other user.
--group <group> Run as other group.
--log-file <file> Print logs also to this file.
--stats-file <file> Print statistics to this file.
--no-port-forwarding Disable automatic port forward.

View File

@ -80,6 +80,12 @@ vpncloud(1) -- Peer-to-peer VPN
Peer timeout in seconds. The peers will exchange information periodically
and drop peers that are silent for this period of time. [default: `1800`]
* `--keepalive <secs>`:
Interval of peer exchange messages in seconds. The peers will exchange
information periodically to keep connections alive. This setting overrides
how often this will happen. [default: `peer-timeout/2-60`]
* `--dst-timeout <secs>`:
Switch table entry timeout in seconds. This parameter is only used in switch
@ -140,8 +146,7 @@ vpncloud(1) -- Peer-to-peer VPN
* `--group <group>`:
Change the user and/or group of the process once all the setup has been
done and before spawning the background process. This option is only used
when running in background.
done.
* `--log-file <file>`: