6 Commits

Author SHA1 Message Date
shimunn
5f997ca191 supply more info
Some checks are pending
continuous-integration/drone/push Build was killed
2019-01-19 16:44:05 +01:00
shimunn
32a86b45ab more reliable events 2019-01-19 15:43:55 +01:00
Drone CI
215cb7ec8f proper hash
Some checks are pending
continuous-integration/drone/push Build is failing
2019-01-18 19:03:27 +01:00
Drone CI
5526084882 use debug build
Some checks are pending
continuous-integration/drone/push Build is failing
2019-01-18 18:54:00 +01:00
Drone CI
a96c557b45 target alpine
Some checks are pending
continuous-integration/drone/push Build is failing
2019-01-18 10:37:16 +01:00
Drone CI
fa757372cb verify working binary
Some checks are pending
continuous-integration/drone/push Build is failing
2019-01-18 10:27:42 +01:00
4 changed files with 179 additions and 63 deletions

View File

@@ -4,11 +4,19 @@ WORKDIR /build
COPY wg-event-gen/Cargo.* /build/
RUN mkdir -p src && echo "fn main() {}" > src/main.rs && cargo build --release
RUN rustup target add x86_64-unknown-linux-musl
RUN mkdir -p src && echo "fn main() {}" > src/main.rs && cargo build --release --target x86_64-unknown-linux-musl
COPY wg-event-gen/ /build
RUN cargo build --release
RUN cargo build --target x86_64-unknown-linux-musl
FROM frolvlad/alpine-glibc AS test
COPY --from=eventbuild /build/target/x86_64-unknown-linux-musl/debug/wg-event-gen /usr/bin/
RUN echo "2df798799c5049324174c4df0189695d -" > test.md5 && wg-event-gen | md5sum -c test.md5
FROM golang AS build
@@ -28,7 +36,7 @@ ENV WG_I_PREFER_BUGGY_USERSPACE_TO_POLISHED_KMOD=1
COPY --from=build /go/bin/wireguard /usr/bin/wireguard-go
COPY --from=eventbuild /build/target/release/wg-event-gen /usr/bin/
COPY --from=eventbuild /build/target/x86_64-unknown-linux-musl/debug/wg-event-gen /usr/bin/
COPY init.sh /init.sh

View File

@@ -16,6 +16,7 @@ pub(crate) fn gen_events(
prev: &HashMap<String, Peer>,
listeners: &Vec<Box<EventListener>>,
timeout: time::Duration,
poll_interval: time::Duration,
) {
let side_by_side = {
state
@@ -29,29 +30,30 @@ pub(crate) fn gen_events(
};
for (_id, (prev, cur)) in side_by_side {
match (prev, cur) {
(Some(prev), Some(cur)) if prev.endpoint != cur.endpoint => {
if let (Some(prev_addr), Some(_)) = (prev.endpoint, cur.endpoint) {
listeners.roaming(&cur, prev_addr);
}
}
(Some(prev), Some(cur)) => {
let timedout = |peer: &Peer| match peer.last_handshake_rel() {
Some(shake) if shake < timeout => false,
Some(shake) if shake > timeout && shake + poll_interval < timeout => true,
Some(_) => false,
_ => true,
};
//if _id == "HhRgEL2xsnEIqThSTUKLGaTXusorM1MFdjSSYvzBynY=" { dbg!((cur.last_handshake_rel(),timedout(&prev) , timedout(&cur))); }
if !timedout(&prev) && timedout(&cur) {
listeners.disconnected(&cur);
continue;
}
if timedout(&prev) && !timedout(&cur) {
listeners.connected(&cur);
}
if prev.endpoint != cur.endpoint {
if let (Some(prev_addr), Some(_)) = (prev.endpoint, cur.endpoint) {
listeners.roaming(&cur, prev_addr);
}
}
}
(None, Some(cur)) => (), //listeners.added(&cur),
(Some(prev), None) => (), //listeners.removed(&prev),
(None, Some(cur)) => listeners.added(&cur),
(Some(prev), None) => listeners.removed(&prev),
(None, Some(_cur)) => (),
(Some(_prev), None) => (),
fail => {
@@ -165,19 +167,38 @@ mod test {
let mut cur: HashMap<String, Peer> = HashMap::new();
cur.insert(peer_cur.public_key.clone(), peer_cur.clone());
let (listener, calls) = listeners();
gen_events(&cur, &prev, &listener, time::Duration::from_secs(3));
let interval = time::Duration::from_secs(3);
gen_events(
&cur,
&prev,
&listener,
time::Duration::from_secs(3),
interval,
);
assert_eq!(
vec![["add", &peer_cur.public_key].join(" ")],
calls.borrow().clone()
);
gen_events(&cur, &cur, &listener, time::Duration::from_secs(3));
gen_events(
&cur,
&cur,
&listener,
time::Duration::from_secs(3),
interval,
);
//Shouldn't gen any new events
assert!(calls.borrow().len() == 1);
let (listener, calls) = listeners();
gen_events(&prev, &cur, &listener, time::Duration::from_secs(10));
gen_events(
&prev,
&cur,
&listener,
time::Duration::from_secs(10),
interval,
);
assert_eq!(
vec![["rem", &peer.public_key].join(" ")],
calls.borrow().clone()
@@ -193,13 +214,19 @@ mod test {
prev.insert(peer_prev.public_key.clone(), peer_prev.clone());
gen_events(&prev, &cur, &listener, time::Duration::from_secs(10));
assert_eq!(
vec![["rom", &peer.public_key].join(" ")],
calls.borrow().clone()
gen_events(
&prev,
&cur,
&listener,
time::Duration::from_secs(10),
interval,
);
assert!(calls
.borrow()
.clone()
.contains(&["rom", &peer.public_key].join(" ")));
calls.borrow_mut().clear();
let mut peer_prev = peer.clone();
@@ -209,7 +236,13 @@ mod test {
cur.insert(peer_cur.public_key.clone(), peer_cur.clone());
prev.insert(peer_prev.public_key.clone(), peer_prev.clone());
gen_events(&cur, &prev, &listener, time::Duration::from_secs(10));
gen_events(
&cur,
&prev,
&listener,
time::Duration::from_secs(10),
interval,
);
assert_eq!(
vec![["con", &peer.public_key].join(" ")],
@@ -219,7 +252,13 @@ mod test {
calls.borrow_mut().clear();
//Other way around should be a disconnect
gen_events(&prev, &cur, &listener, time::Duration::from_secs(3));
gen_events(
&prev,
&cur,
&listener,
time::Duration::from_secs(3),
interval,
);
assert_eq!(
vec![["dis", &peer.public_key].join(" ")],

View File

@@ -22,7 +22,9 @@ pub trait EventListener {
impl EventListener for Vec<Box<EventListener>> {
fn added<'a>(&self, peer: &'a Peer) {
self.iter().for_each(|l| l.added(&peer));
if cfg!(feature = "addrem") || cfg!(test) {
self.iter().for_each(|l| l.added(&peer));
}
}
fn connected<'a>(&self, peer: &'a Peer) {
@@ -34,7 +36,9 @@ impl EventListener for Vec<Box<EventListener>> {
}
fn removed<'a>(&self, peer: &'a Peer) {
self.iter().for_each(|l| l.removed(&peer));
if cfg!(feature = "addrem") || cfg!(test) {
self.iter().for_each(|l| l.removed(&peer));
}
}
fn roaming<'a>(&self, peer: &'a Peer, previous_addr: SocketAddr) {
@@ -80,10 +84,43 @@ impl ScriptListener {
ScriptListener { script }
}
fn peer_props<'a>(&self,peer: &'a Peer) -> String {
format!(
"{id} {allowed_ips} {endpoint} {last_handshake} {persistent_keepalive} {traffic}",
id = peer.public_key,
allowed_ips = peer
.allowed_ips
.iter()
.map(|(addr, mask)| [addr.to_string(), mask.to_string()].join("/"))
.collect::<Vec<String>>()
.join(","),
endpoint = peer
.endpoint
.map(|e| e.to_string())
.unwrap_or("0".to_owned()),
last_handshake = peer
.last_handshake
.map(|s| s.as_secs() as i64)
.unwrap_or(-1),
persistent_keepalive = peer
.persistent_keepalive
.map(|k| k.as_secs() as i64)
.unwrap_or(-1),
traffic = {
let (rx, tx) = peer.traffic;
[rx.to_string(), tx.to_string()].join(",")
}
)
}
fn mkcmd<'a>(&self, args: Vec<&'a str>) -> Command {
let mut cmd = Command::new("/bin/sh");
cmd.arg("-c");
cmd.arg(format!("{} {}",(&self.script).to_str().unwrap(), args.join(" ")));
cmd.arg(format!(
"{} {}",
(&self.script).to_str().unwrap(),
args.join(" ")
));
cmd
}
@@ -97,27 +134,26 @@ impl ScriptListener {
impl EventListener for ScriptListener {
fn connected<'a>(&self, peer: &'a Peer) {
self.call_sub(vec!["connected", &peer.public_key]);
self.call_sub(vec!["connected", &self.peer_props(peer)]);
}
fn disconnected<'a>(&self, peer: &'a Peer) {
self.call_sub(vec!["disconnected", &peer.public_key]);
self.call_sub(vec!["disconnected", &self.peer_props(peer)]);
}
fn added<'a>(&self, peer: &'a Peer) {
self.call_sub(vec!["added", &peer.public_key]);
self.call_sub(vec!["added", &self.peer_props(peer)]);
}
fn removed<'a>(&self, peer: &'a Peer) {
self.call_sub(vec!["removed", &peer.public_key]);
self.call_sub(vec!["removed", &self.peer_props(peer)]);
}
fn roaming<'a>(&self, peer: &'a Peer, previous_addr: SocketAddr) {
self.call_sub(vec![
"roaming",
&peer.public_key,
&self.peer_props(peer),
&previous_addr.to_string(),
&peer.endpoint.unwrap().to_string(),
]);
}
}

View File

@@ -11,12 +11,12 @@ use std::env;
use std::fmt;
use std::io::prelude::*;
use std::io::{BufRead, BufReader, Error, ErrorKind, Result};
use std::net::SocketAddr;
use std::net::{IpAddr, SocketAddr};
use std::os::unix::net::UnixStream;
use std::path::PathBuf;
use time;
use std::thread;
use std::time::Duration;
use time;
pub type KV = (String, String);
@@ -30,8 +30,10 @@ enum State {
pub struct Peer {
public_key: String,
endpoint: Option<SocketAddr>,
allowed_ips: Vec<(IpAddr, u8)>,
last_handshake: Option<Duration>,
persistent_keepalive: Option<Duration>,
traffic: (u64, u64),
parsed: time::Timespec,
}
@@ -53,37 +55,58 @@ impl Peer {
.filter(|(key, _)| key == &"endpoint")
.map(|(_, value)| value.parse::<SocketAddr>().unwrap())
.next(),
allowed_ips: entries
.iter()
.filter(|(key, _)| key == &"allowed_ip")
.map(|(_, value)| {
let mut parts = value.split("/").into_iter();
match (
parts.next().and_then(|addr| addr.parse::<IpAddr>().ok()),
parts.next().and_then(|mask| mask.parse::<u8>().ok()),
) {
(Some(addr), Some(mask)) => Some((addr, mask)),
(Some(addr), None) if addr.is_ipv6() => Some((addr, 128)),
(Some(addr), None) => Some((addr, 32)),
_ => None,
}
})
.filter_map(|net| net)
.collect::<Vec<(IpAddr, u8)>>(),
last_handshake: entries
.iter()
.filter_map(|(key, value)| {
let value = || value.parse::<u64>().unwrap();
match key.as_ref() {
"last_handshake_time_sec" if value() != 0 => Some(Duration::new(value(), 0)),
"last_handshake_time_nsec" if value() != 0 => Some(Duration::from_nanos(value())),
_ => None
}
let value = || value.parse::<u64>().unwrap();
match key.as_ref() {
"last_handshake_time_sec" if value() != 0 => {
Some(Duration::new(value(), 0))
}
"last_handshake_time_nsec" if value() != 0 => {
Some(Duration::from_nanos(value()))
}
_ => None,
}
})
.fold(None, |acc, add| {
if let Some(dur) = acc {
Some(dur + add)
} else {
Some(add)
}
if let Some(dur) = acc {
Some(dur + add)
} else {
Some(add)
}
}),
persistent_keepalive: entries
.iter()
.filter(|(key, _)| key == &"persistent_keepalive")
.map(|(_, value)| Duration::from_secs(value.parse::<u64>().unwrap()))
.next(),
parsed: time::get_time(),
traffic: (0, 0),
parsed: time::get_time(),
})
}
pub fn last_handshake_rel(&self) -> Option<Duration> {
let time = self.parsed;
Some(Duration::new(time.sec as u64, time.nsec as u32) - self.last_handshake?)
let time = self.parsed;
Some(Duration::new(time.sec as u64, time.nsec as u32) - self.last_handshake?)
}
}
impl State {
@@ -222,13 +245,14 @@ fn main() {
.next()
.map(PathBuf::from)
.filter(|p| p.as_path().exists());
let interval = args
.next()
.map(|i| {
i.parse::<u64>()
.expect("[interval] has to be a positive int")
})
.unwrap_or(1000);
let interval = Duration::from_millis(
args.next()
.map(|i| {
i.parse::<u64>()
.expect("[interval] has to be a positive int")
})
.unwrap_or(1000),
);
let mut listeners: Vec<Box<EventListener>> = vec![Box::new(LogListener)];
let events: PathBuf = "/etc/wireguard/events.sh".into();
@@ -237,9 +261,18 @@ fn main() {
listeners.push(Box::new(ScriptListener::new(events)))
}
let timeout = env::vars().collect::<HashMap<String,String>>().get("WG_EVENT_GEN_TIMEOUT").map(|timeout| Duration::from_secs(timeout.parse::<u64>().expect(&format!("Can't parse {} as timeout", timeout)))).unwrap_or(Duration::from_secs(30));
let timeout = env::vars()
.collect::<HashMap<String, String>>()
.get("WG_EVENT_GEN_TIMEOUT")
.map(|timeout| {
Duration::from_secs(
timeout
.parse::<u64>()
.expect(&format!("Can't parse {} as timeout", timeout)),
)
})
.unwrap_or(Duration::from_secs(30));
if let Some(path) = path {
let sock = Socket { path };
let mut prev_state: Option<HashMap<String, Peer>> = None;
@@ -252,10 +285,10 @@ fn main() {
}
};
if let Some(prev_state) = prev_state {
gen::gen_events(&state, &prev_state, &listeners, timeout);
gen::gen_events(&state, &prev_state, &listeners, timeout, interval);
}
prev_state = Some(state);
thread::sleep(Duration::from_millis(interval));
thread::sleep(interval);
}
} else {
println!("<path> does not exist");