mirror of https://github.com/dswd/vpncloud.git
Daemonizing and proper systemd support
This commit is contained in:
parent
4497ddaf13
commit
88f5272023
|
@ -7,6 +7,10 @@ This project follows [semantic versioning](http://semver.org).
|
|||
- [added] Support for automatic port forwarding via UPnP
|
||||
- [added] Added `-s` shorthand for `--subnet`
|
||||
- [added] Support for YAML config file via `--config`
|
||||
- [added] Support for running in the background
|
||||
- [added] Support for dropping permissions
|
||||
- [added] Support for writing a pid file
|
||||
- [added] Support for writing logs to logfile
|
||||
- [changed] Not overriding recently learnt addresses in switch mode
|
||||
- [changed] Caching resolved addresses to increase performance
|
||||
- [changed] Configurable magic header is now used instead of Network-ID (**incompatible**)
|
||||
|
|
|
@ -4,6 +4,7 @@ version = "0.7.0"
|
|||
dependencies = [
|
||||
"aligned_alloc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"daemonize 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"docopt 0.6.86 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -11,11 +12,10 @@ dependencies = [
|
|||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"net2 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nix 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"signal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"signal 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"siphasher 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"yaml-rust 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -63,6 +63,14 @@ dependencies = [
|
|||
"url 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "daemonize"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "docopt"
|
||||
version = "0.6.86"
|
||||
|
@ -272,7 +280,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "signal"
|
||||
version = "0.2.0"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -422,6 +430,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
|
||||
"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
|
||||
"checksum cookie 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0e3d6405328b6edb412158b3b7710e2634e23f3614b9bb1c412df7952489a626"
|
||||
"checksum daemonize 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0239832c1b4ca406d5ec73728cf4c7336d25cf85dd32db9e047e9e706ee0e935"
|
||||
"checksum docopt 0.6.86 (registry+https://github.com/rust-lang/crates.io-index)" = "4a7ef30445607f6fc8720f0a0a2c7442284b629cf0d049286860fae23e71c4d9"
|
||||
"checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344"
|
||||
"checksum gcc 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "553f11439bdefe755bf366b264820f1da70f3aaf3924e594b886beb9c831bcf5"
|
||||
|
@ -448,7 +457,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)" = "bff9fc1c79f2dec76b253273d07682e94a978bd8f132ded071188122b2af9818"
|
||||
"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
|
||||
"checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
|
||||
"checksum signal 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "beb615e58999635b6063277cf520f2d88824955c1056cf4f166b0f55b218512d"
|
||||
"checksum signal 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "904a4bba60e8e7a53b7a7eec8f59084a9ceafe3df5aa9d24846a83a5e351aa34"
|
||||
"checksum siphasher 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b1c3c58c9ac43c530919fe6bd8ef11ae2612f64c2bf8eab9346f5b71ce0617f2"
|
||||
"checksum solicit 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "172382bac9424588d7840732b250faeeef88942e37b6e35317dce98cafdd75b2"
|
||||
"checksum strsim 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "67f84c44fbb2f91db7fef94554e6b2ac05909c9c0b0bc23bb98d3a1aebfe7f7c"
|
||||
|
|
|
@ -15,8 +15,7 @@ time = "0.1"
|
|||
docopt = "0.6"
|
||||
rustc-serialize = "0.3"
|
||||
log = "0.3"
|
||||
signal = "0.2"
|
||||
nix = "0.6"
|
||||
signal = "0.3"
|
||||
libc = "0.2"
|
||||
aligned_alloc = "0.1"
|
||||
rand = "0.3"
|
||||
|
@ -26,6 +25,7 @@ bitflags = "0.7"
|
|||
yaml-rust = "0.3"
|
||||
igd = "0.5"
|
||||
siphasher = "0.1"
|
||||
daemonize = "0.2"
|
||||
|
||||
[build-dependencies]
|
||||
gcc = "0.3"
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
vpncloud/debian/files
|
||||
vpncloud/debian/vpncloud
|
||||
vpncloud/debian/debhelper*
|
||||
vpncloud/vpncloud
|
||||
vpncloud-nocrypto/debian/vpncloud*
|
||||
vpncloud-nocrypto/debian/files
|
||||
vpncloud-nocrypto/vpncloud
|
||||
*.deb
|
||||
*.build
|
||||
*.changes
|
||||
|
|
|
@ -11,3 +11,5 @@ install:
|
|||
install -d $(DESTDIR)/usr/bin
|
||||
install -m 755 vpncloud $(DESTDIR)/usr/bin/vpncloud
|
||||
install -m 755 vpncloud-control $(DESTDIR)/usr/bin/vpncloud-control
|
||||
install -d $(DESTDIR)/lib/systemd/system
|
||||
install -m 644 vpncloud@.service $(DESTDIR)/lib/systemd/system/vpncloud@.service
|
||||
|
|
|
@ -1 +1 @@
|
|||
7
|
||||
9
|
||||
|
|
|
@ -2,7 +2,7 @@ Source: vpncloud
|
|||
Section: misc
|
||||
Priority: extra
|
||||
Maintainer: Dennis Schwerdel <schwerdel@informatik.uni-kl.de>
|
||||
Build-Depends: debhelper (>= 7), ruby-ronn
|
||||
Build-Depends: debhelper (>= 9), ruby-ronn
|
||||
Standards-Version: 3.8.3
|
||||
|
||||
Package: vpncloud
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
[Unit]
|
||||
Description=VpnCloud networks
|
||||
Description=VpnCloud
|
||||
Before=systemd-user-sessions.service
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
ExecStart=/usr/bin/vpncloud-control start
|
||||
ExecStop=/usr/bin/vpncloud-control stop
|
||||
RemainAfterExit=yes
|
||||
ExecStartPre=/bin/echo "Please use instantiated services (vpncloud@NAME) instead."
|
||||
ExecStart=/bin/false
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
WantedBy=
|
||||
|
|
|
@ -38,7 +38,7 @@ start() {
|
|||
# 2 if daemon could not be started
|
||||
for net in $NETWORKS; do
|
||||
[ -f "$NETCONFIGS/$net.net" ] || continue
|
||||
start-stop-daemon --start --pidfile /run/$NAME-$net.pid --make-pidfile --name $NAME --background --startas /bin/sh -- -c "exec $DAEMON --config $NETCONFIGS/$net.net >/var/log/vpncloud-$net.log 2>&1"
|
||||
start-stop-daemon --start --pidfile /run/$NAME-$net.pid --name $NAME -- "$DAEMON --daemon --config $NETCONFIGS/$net.net --pid-file /run/$NAME-$net.pid"
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use std::time::Instant;
|
|||
use std::cmp::{min, max};
|
||||
|
||||
use fnv::FnvHasher;
|
||||
use nix::sys::signal::{SIGTERM, SIGQUIT, SIGINT};
|
||||
use libc::{SIGTERM, SIGQUIT, SIGINT};
|
||||
use signal::trap::Trap;
|
||||
use rand::{random, sample, thread_rng};
|
||||
use net2::UdpBuilder;
|
||||
|
|
|
@ -25,6 +25,10 @@ pub struct Config {
|
|||
pub dst_timeout: Duration,
|
||||
pub subnets: Vec<String>,
|
||||
pub port_forwarding: bool,
|
||||
pub daemonize: bool,
|
||||
pub pid_file: Option<String>,
|
||||
pub user: Option<String>,
|
||||
pub group: Option<String>
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
|
@ -37,7 +41,11 @@ impl Default for Config {
|
|||
port: 3210, peers: vec![], peer_timeout: 1800,
|
||||
mode: Mode::Normal, dst_timeout: 300,
|
||||
subnets: vec![],
|
||||
port_forwarding: true
|
||||
port_forwarding: true,
|
||||
daemonize: false,
|
||||
pid_file: None,
|
||||
user: None,
|
||||
group: None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -86,6 +94,15 @@ impl Config {
|
|||
if let Some(val) = file.port_forwarding {
|
||||
self.port_forwarding = val;
|
||||
}
|
||||
if let Some(val) = file.pid_file {
|
||||
self.pid_file = Some(val);
|
||||
}
|
||||
if let Some(val) = file.user {
|
||||
self.user = Some(val);
|
||||
}
|
||||
if let Some(val) = file.group {
|
||||
self.group = Some(val);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn merge_args(&mut self, mut args: Args) {
|
||||
|
@ -131,6 +148,18 @@ impl Config {
|
|||
if args.flag_no_port_forwarding {
|
||||
self.port_forwarding = false;
|
||||
}
|
||||
if args.flag_daemon {
|
||||
self.daemonize = true;
|
||||
}
|
||||
if let Some(val) = args.flag_pid_file {
|
||||
self.pid_file = Some(val);
|
||||
}
|
||||
if let Some(val) = args.flag_user {
|
||||
self.user = Some(val);
|
||||
}
|
||||
if let Some(val) = args.flag_group {
|
||||
self.group = Some(val);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_magic(&self) -> HeaderMagic {
|
||||
|
@ -169,5 +198,8 @@ pub struct ConfigFile {
|
|||
pub mode: Option<Mode>,
|
||||
pub dst_timeout: Option<Duration>,
|
||||
pub subnets: Option<Vec<String>>,
|
||||
pub port_forwarding: Option<bool>
|
||||
pub port_forwarding: Option<bool>,
|
||||
pub pid_file: Option<String>,
|
||||
pub user: Option<String>,
|
||||
pub group: Option<String>,
|
||||
}
|
||||
|
|
57
src/main.rs
57
src/main.rs
|
@ -10,7 +10,6 @@ extern crate time;
|
|||
extern crate docopt;
|
||||
extern crate rustc_serialize;
|
||||
extern crate signal;
|
||||
extern crate nix;
|
||||
extern crate libc;
|
||||
extern crate aligned_alloc;
|
||||
extern crate rand;
|
||||
|
@ -19,6 +18,7 @@ extern crate net2;
|
|||
extern crate yaml_rust;
|
||||
extern crate igd;
|
||||
extern crate siphasher;
|
||||
extern crate daemonize;
|
||||
#[cfg(feature = "bench")] extern crate test;
|
||||
|
||||
#[macro_use] pub mod util;
|
||||
|
@ -38,8 +38,12 @@ pub mod port_forwarding;
|
|||
|
||||
use docopt::Docopt;
|
||||
|
||||
use std::sync::Mutex;
|
||||
use std::str::FromStr;
|
||||
use std::process::Command;
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
use std::io::{self, Write};
|
||||
|
||||
use device::{Device, Type};
|
||||
use ethernet::SwitchTable;
|
||||
|
@ -77,13 +81,31 @@ pub struct Args {
|
|||
flag_ifup: Option<String>,
|
||||
flag_ifdown: Option<String>,
|
||||
flag_version: bool,
|
||||
flag_no_port_forwarding: bool
|
||||
flag_no_port_forwarding: bool,
|
||||
flag_daemon: bool,
|
||||
flag_pid_file: Option<String>,
|
||||
flag_user: Option<String>,
|
||||
flag_group: Option<String>,
|
||||
flag_log_file: Option<String>
|
||||
}
|
||||
|
||||
|
||||
struct SimpleLogger;
|
||||
struct DualLogger {
|
||||
file: Mutex<Option<File>>
|
||||
}
|
||||
|
||||
impl log::Log for SimpleLogger {
|
||||
impl DualLogger {
|
||||
pub fn new<P: AsRef<Path>>(path: Option<P>) -> Result<Self, io::Error> {
|
||||
if let Some(path) = path {
|
||||
let file = try!(File::create(path));
|
||||
Ok(DualLogger{file: Mutex::new(Some(file))})
|
||||
} else {
|
||||
Ok(DualLogger{file: Mutex::new(None)})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl log::Log for DualLogger {
|
||||
#[inline]
|
||||
fn enabled(&self, _metadata: &log::LogMetadata) -> bool {
|
||||
true
|
||||
|
@ -92,11 +114,12 @@ impl log::Log for SimpleLogger {
|
|||
#[inline]
|
||||
fn log(&self, record: &log::LogRecord) {
|
||||
if self.enabled(record.metadata()) {
|
||||
println!("{} - {} - {}",
|
||||
time::strftime("%F %T", &time::now()).expect("Failed to format timestamp"),
|
||||
record.level(),
|
||||
record.args()
|
||||
);
|
||||
println!("{} - {}", record.level(), record.args());
|
||||
let mut file = self.file.lock().expect("Lock poisoned");
|
||||
if let &mut Some(ref mut file) = &mut file as &mut Option<File> {
|
||||
let time = time::strftime("%F %T", &time::now()).expect("Failed to format timestamp");
|
||||
writeln!(file, "{} - {} - {}", time, record.level(), record.args()).expect("Failed to write to logfile");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -153,6 +176,20 @@ fn run<T: Protocol> (config: Config) {
|
|||
try_fail!(cloud.connect(&addr as &str), "Failed to send message to {}: {}", &addr);
|
||||
cloud.add_reconnect_peer(addr);
|
||||
}
|
||||
if config.daemonize {
|
||||
info!("Running process as daemon");
|
||||
let mut daemonize = daemonize::Daemonize::new();
|
||||
if let Some(user) = config.user {
|
||||
daemonize = daemonize.user(&user as &str);
|
||||
}
|
||||
if let Some(group) = config.group {
|
||||
daemonize = daemonize.group(&group as &str);
|
||||
}
|
||||
if let Some(pid_file) = config.pid_file {
|
||||
daemonize = daemonize.pid_file(pid_file).chown_pid_file(true);
|
||||
}
|
||||
try_fail!(daemonize.start(), "Failed to daemonize: {}");
|
||||
}
|
||||
cloud.run();
|
||||
if let Some(script) = config.ifdown {
|
||||
run_script(script, cloud.ifname());
|
||||
|
@ -180,7 +217,7 @@ fn main() {
|
|||
} else {
|
||||
max_log_level.set(log::LogLevelFilter::Info);
|
||||
}
|
||||
Box::new(SimpleLogger)
|
||||
Box::new(try_fail!(DualLogger::new(args.flag_log_file.as_ref()), "Failed to open logfile: {}"))
|
||||
}).unwrap();
|
||||
let mut config = Config::default();
|
||||
if let Some(ref file) = args.flag_config {
|
||||
|
|
|
@ -23,7 +23,13 @@ Options:
|
|||
--ifup <command> A command to setup the network interface.
|
||||
--ifdown <command> A command to bring down the network
|
||||
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.
|
||||
--log-file <file> Print logs also to this file.
|
||||
--no-port-forwarding Disable automatic port forward.
|
||||
--daemon Run the process in the background.
|
||||
-v, --verbose Print debug information.
|
||||
-q, --quiet Only print errors and warnings.
|
||||
-h, --help Display the help.
|
||||
|
|
40
vpncloud.md
40
vpncloud.md
|
@ -88,6 +88,8 @@ vpncloud(1) -- Peer-to-peer VPN
|
|||
parameter to `sh -c`) when the device has been created to configure it.
|
||||
The name of the allocated device will be available via the environment
|
||||
variable `IFNAME`.
|
||||
Please note that this command is executed with the full permissions of the
|
||||
caller.
|
||||
|
||||
* `--ifdown <command>`:
|
||||
|
||||
|
@ -95,6 +97,36 @@ vpncloud(1) -- Peer-to-peer VPN
|
|||
parameter to `sh -c`) to remove any configuration from the device.
|
||||
The name of the allocated device will be available via the environment
|
||||
variable `IFNAME`.
|
||||
Please note that this command is executed with the (limited) permissions of
|
||||
the user and group given as `--user` and `--group`.
|
||||
|
||||
* `--pid-file <file>`:
|
||||
|
||||
Store the process id in this file when running in the background. If set,
|
||||
the given file will be created containing the process id of the new
|
||||
background process. This option is only used when running in background.
|
||||
|
||||
* `--user <user>`:
|
||||
* `--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.
|
||||
|
||||
* `--log-file <file>`:
|
||||
|
||||
If set, print logs also to the given file. The file will be created and
|
||||
truncated if is exists.
|
||||
|
||||
* `--daemon`:
|
||||
|
||||
Spawn a background process instead of running the process in the foreground.
|
||||
If this flag is set, the process will first carry out all the
|
||||
initialization, then drop permissions if `--user` or `--group` is used and
|
||||
then spawn a background process and write its process id to a file if
|
||||
`--pid-file` is set. Then, the main process will exit and the background
|
||||
process continues to provide the VPN. At the time, when the main process
|
||||
exits, the interface exists and is properly configured to be used.
|
||||
|
||||
* `--no-port-forwarding`:
|
||||
|
||||
|
@ -245,6 +277,9 @@ detailed descriptions of the options.
|
|||
* `dst_timeout`: Switch table entry timeout in seconds. Same as `--dst-timeout`
|
||||
* `subnets`: A list of local subnets to use. See `--subnet`
|
||||
* `port_forwarding`: Whether to activate port forwardig. See `--no-port-forwarding`
|
||||
* `user`: The name of a user to run the background process under. See `--user`
|
||||
* `group`: The name of a group to run the background process under. See `--group`
|
||||
* `pid_file`: The path of the pid file to create. See `--pid-file`
|
||||
|
||||
|
||||
### Example
|
||||
|
@ -263,6 +298,9 @@ mode: normal
|
|||
subnets:
|
||||
- 10.0.1.0/24
|
||||
port_forwarding: true
|
||||
user: nobody
|
||||
group: nogroup
|
||||
pid_file: /run/vpncloud.pid
|
||||
|
||||
|
||||
## NETWORK PROTOCOL
|
||||
|
@ -297,7 +335,7 @@ Every packet sent over UDP contains the following header (in order):
|
|||
`libsodium::crypto_aead_aes256gcm` method, using the 8 byte header
|
||||
as additional data.
|
||||
|
||||
* 2 `reserved bytes` that are currently unused
|
||||
* 2 `reserved bytes` that are currently unused and set to 0
|
||||
|
||||
* 1 byte for the `message type`
|
||||
|
||||
|
|
Loading…
Reference in New Issue