2019-04-23 23:06:35 +02:00
|
|
|
extern crate serde_json;
|
|
|
|
#[macro_use]
|
|
|
|
extern crate failure;
|
|
|
|
use clap;
|
2019-06-09 20:09:06 +02:00
|
|
|
use clap::{crate_authors, crate_name, crate_version, Arg};
|
2019-12-01 19:42:47 +01:00
|
|
|
use hyper::{Body, Request};
|
2019-10-13 19:42:44 +02:00
|
|
|
use log::{debug, info, trace};
|
2019-04-23 23:06:35 +02:00
|
|
|
use std::env;
|
|
|
|
mod options;
|
|
|
|
use options::Options;
|
|
|
|
mod wireguard;
|
|
|
|
use std::convert::TryFrom;
|
|
|
|
use std::process::Command;
|
|
|
|
use std::string::String;
|
|
|
|
use wireguard::WireGuard;
|
2019-06-09 20:09:06 +02:00
|
|
|
mod exporter_error;
|
2019-05-15 16:55:07 +02:00
|
|
|
mod wireguard_config;
|
2019-05-20 10:09:02 +02:00
|
|
|
use wireguard_config::peer_entry_hashmap_try_from;
|
2019-06-09 20:09:06 +02:00
|
|
|
extern crate prometheus_exporter_base;
|
|
|
|
use prometheus_exporter_base::render_prometheus;
|
2019-09-26 23:44:48 +02:00
|
|
|
use std::net::Ipv4Addr;
|
2019-09-27 11:01:27 +02:00
|
|
|
use std::sync::Arc;
|
2019-04-23 23:06:35 +02:00
|
|
|
|
2019-05-20 09:45:20 +02:00
|
|
|
fn wg_with_text(
|
|
|
|
wg_config_str: &str,
|
2019-10-13 19:42:44 +02:00
|
|
|
wg_output_str: &str,
|
2019-07-11 15:31:25 +02:00
|
|
|
options: Arc<Options>,
|
2019-12-01 19:42:47 +01:00
|
|
|
) -> Result<String, failure::Error> {
|
2019-05-20 10:09:02 +02:00
|
|
|
let pehm = peer_entry_hashmap_try_from(wg_config_str)?;
|
|
|
|
trace!("pehm == {:?}", pehm);
|
2019-05-20 09:45:20 +02:00
|
|
|
|
2019-10-13 19:42:44 +02:00
|
|
|
let wg = WireGuard::try_from(wg_output_str)?;
|
2019-12-01 19:42:47 +01:00
|
|
|
Ok(wg.render_with_names(
|
2019-07-11 15:31:25 +02:00
|
|
|
Some(&pehm),
|
|
|
|
options.separate_allowed_ips,
|
2019-07-31 15:24:52 +02:00
|
|
|
options.export_remote_ip_and_port,
|
2019-12-01 19:42:47 +01:00
|
|
|
))
|
2019-05-20 09:45:20 +02:00
|
|
|
}
|
|
|
|
|
2019-12-01 19:42:47 +01:00
|
|
|
async fn perform_request(
|
2019-04-23 23:06:35 +02:00
|
|
|
_req: Request<Body>,
|
2019-12-01 19:42:47 +01:00
|
|
|
options: Arc<Options>,
|
|
|
|
) -> Result<String, failure::Error> {
|
2019-04-23 23:06:35 +02:00
|
|
|
trace!("perform_request");
|
2019-05-17 19:32:35 +02:00
|
|
|
// this is needed to satisfy the borrow checker
|
|
|
|
let options = options.clone();
|
2019-10-13 19:42:44 +02:00
|
|
|
debug!("options == {:?}", options);
|
|
|
|
|
|
|
|
//let interface = options.get_interface();
|
|
|
|
|
|
|
|
let interface_str = match options.get_interface() {
|
|
|
|
Some(interface_str) => interface_str,
|
|
|
|
None => "all",
|
|
|
|
}
|
|
|
|
.to_owned();
|
|
|
|
|
|
|
|
debug!("using inteface_str {}", interface_str);
|
2019-05-17 19:32:35 +02:00
|
|
|
|
2019-12-01 19:42:47 +01:00
|
|
|
let output = Command::new("wg")
|
|
|
|
.arg("show")
|
|
|
|
.arg(&interface_str)
|
|
|
|
.arg("dump")
|
|
|
|
.output()?;
|
|
|
|
let output_str = String::from_utf8(output.stdout)?;
|
|
|
|
trace!("wg show output == {}", output_str);
|
|
|
|
|
|
|
|
// the output of wg show is different if we use all or we specify an interface.
|
|
|
|
// In the first case the first column will be the interface name. In the second case
|
|
|
|
// the interface name will be omitted. We need to compensate for the skew somehow (one
|
|
|
|
// column less in the second case). We solve this prepending the interface name in every
|
|
|
|
// line so the output of the second case will be equal to the first case.
|
|
|
|
let output_str = if interface_str != "all" {
|
|
|
|
debug!("injecting {} to the wg show output", interface_str);
|
|
|
|
let mut result = String::new();
|
|
|
|
for s in output_str.lines() {
|
|
|
|
result.push_str(&format!("{}\t{}\n", interface_str, s));
|
|
|
|
}
|
|
|
|
result
|
|
|
|
} else {
|
|
|
|
output_str
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(extract_names_config_file) = &options.extract_names_config_file {
|
|
|
|
let wg_config_string = ::std::fs::read_to_string(extract_names_config_file)?;
|
|
|
|
wg_with_text(&wg_config_string as &str, &output_str, options)
|
|
|
|
} else {
|
|
|
|
let wg = WireGuard::try_from(&output_str as &str)?;
|
|
|
|
Ok(wg.render_with_names(
|
|
|
|
None,
|
|
|
|
options.separate_allowed_ips,
|
|
|
|
options.export_remote_ip_and_port,
|
|
|
|
))
|
|
|
|
}
|
2019-04-23 23:06:35 +02:00
|
|
|
}
|
|
|
|
|
2019-12-01 19:42:47 +01:00
|
|
|
#[tokio::main]
|
|
|
|
async fn main() {
|
2019-06-09 20:09:06 +02:00
|
|
|
let matches = clap::App::new(crate_name!())
|
|
|
|
.version(crate_version!())
|
|
|
|
.author(crate_authors!("\n"))
|
2019-09-26 23:44:48 +02:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("addr")
|
|
|
|
.short("l")
|
|
|
|
.help("exporter address")
|
2019-09-27 11:01:27 +02:00
|
|
|
.default_value("0.0.0.0")
|
2019-09-26 23:44:48 +02:00
|
|
|
.takes_value(true),
|
|
|
|
)
|
2019-04-23 23:06:35 +02:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("port")
|
|
|
|
.short("p")
|
2019-05-17 19:32:35 +02:00
|
|
|
.help("exporter port")
|
2019-05-20 11:23:28 +02:00
|
|
|
.default_value("9586")
|
2019-04-23 23:06:35 +02:00
|
|
|
.takes_value(true),
|
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("verbose")
|
|
|
|
.short("v")
|
|
|
|
.help("verbose logging")
|
|
|
|
.takes_value(false),
|
|
|
|
)
|
2019-05-17 19:32:35 +02:00
|
|
|
.arg(
|
2019-07-11 15:31:25 +02:00
|
|
|
Arg::with_name("separate_allowed_ips")
|
|
|
|
.short("s")
|
|
|
|
.help("separate allowed ips and ports")
|
|
|
|
.takes_value(false),
|
|
|
|
)
|
2019-07-31 15:24:52 +02:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("export_remote_ip_and_port")
|
|
|
|
.short("r")
|
|
|
|
.help("exports peer's remote ip and port as labels (if available)")
|
|
|
|
.takes_value(false),
|
|
|
|
)
|
2019-12-01 19:42:47 +01:00
|
|
|
.arg(
|
2019-05-17 19:32:35 +02:00
|
|
|
Arg::with_name("extract_names_config_file")
|
|
|
|
.short("n")
|
|
|
|
.help("If set, the exporter will look in the specified WireGuard config file for peer names (must be in [Peer] definition and be a comment)")
|
|
|
|
.takes_value(true))
|
2019-04-23 23:06:35 +02:00
|
|
|
.get_matches();
|
|
|
|
|
|
|
|
let options = Options::from_claps(&matches);
|
|
|
|
|
|
|
|
if options.verbose {
|
2019-06-09 20:09:06 +02:00
|
|
|
env::set_var(
|
|
|
|
"RUST_LOG",
|
|
|
|
format!("{}=trace,prometheus_exporter_base=trace", crate_name!()),
|
|
|
|
);
|
2019-04-23 23:06:35 +02:00
|
|
|
} else {
|
2019-06-09 20:09:06 +02:00
|
|
|
env::set_var(
|
|
|
|
"RUST_LOG",
|
|
|
|
format!("{}=info,prometheus_exporter_base=info", crate_name!()),
|
|
|
|
);
|
2019-04-23 23:06:35 +02:00
|
|
|
}
|
|
|
|
env_logger::init();
|
|
|
|
|
|
|
|
info!("using options: {:?}", options);
|
|
|
|
|
|
|
|
let bind = matches.value_of("port").unwrap();
|
|
|
|
let bind = u16::from_str_radix(&bind, 10).expect("port must be a valid number");
|
2019-09-27 11:01:27 +02:00
|
|
|
let ip = matches
|
|
|
|
.value_of("addr")
|
|
|
|
.unwrap()
|
|
|
|
.parse::<Ipv4Addr>()
|
|
|
|
.unwrap();
|
2019-09-26 23:44:48 +02:00
|
|
|
let addr = (ip, bind).into();
|
2019-04-23 23:06:35 +02:00
|
|
|
|
2019-09-26 23:44:48 +02:00
|
|
|
info!("starting exporter on http://{}/metrics", addr);
|
2019-04-23 23:06:35 +02:00
|
|
|
|
2019-12-01 19:42:47 +01:00
|
|
|
render_prometheus(addr, options, |request, options| {
|
|
|
|
Box::pin(perform_request(request, options))
|
|
|
|
})
|
|
|
|
.await;
|
2019-04-23 23:06:35 +02:00
|
|
|
}
|