wg show <interface> if specified in the command line (#17)

Using `interface` instead of `all` if specified
This commit is contained in:
Francesco Cogno 2019-10-13 19:42:44 +02:00 committed by GitHub
parent 42ae2f5927
commit e04a3f833b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 107 additions and 36 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "prometheus_wireguard_exporter" name = "prometheus_wireguard_exporter"
version = "3.1.1" version = "3.2.0"
authors = ["Francesco Cogno <francesco.cogno@outlook.com>"] authors = ["Francesco Cogno <francesco.cogno@outlook.com>"]
description = "Prometheus WireGuard Exporter" description = "Prometheus WireGuard Exporter"
edition = "2018" edition = "2018"

View File

@ -4,15 +4,15 @@
[![Crate](https://img.shields.io/crates/v/prometheus_wireguard_exporter.svg)](https://crates.io/crates/prometheus_wireguard_exporter) [![cratedown](https://img.shields.io/crates/d/prometheus_wireguard_exporter.svg)](https://crates.io/crates/prometheus_wireguard_exporter) [![cratelastdown](https://img.shields.io/crates/dv/prometheus_wireguard_exporter.svg)](https://crates.io/crates/prometheus_wireguard_exporter) [![Crate](https://img.shields.io/crates/v/prometheus_wireguard_exporter.svg)](https://crates.io/crates/prometheus_wireguard_exporter) [![cratedown](https://img.shields.io/crates/d/prometheus_wireguard_exporter.svg)](https://crates.io/crates/prometheus_wireguard_exporter) [![cratelastdown](https://img.shields.io/crates/dv/prometheus_wireguard_exporter.svg)](https://crates.io/crates/prometheus_wireguard_exporter)
[![release](https://img.shields.io/github/release/MindFlavor/prometheus_wireguard_exporter.svg)](https://github.com/MindFlavor/prometheus_wireguard_exporter/tree/3.1.1) [![release](https://img.shields.io/github/release/MindFlavor/prometheus_wireguard_exporter.svg)](https://github.com/MindFlavor/prometheus_wireguard_exporter/tree/3.2.0)
[![tag](https://img.shields.io/github/tag/mindflavor/prometheus_wireguard_exporter.svg)](https://github.com/MindFlavor/prometheus_wireguard_exporter/tree/3.1.1) [![tag](https://img.shields.io/github/tag/mindflavor/prometheus_wireguard_exporter.svg)](https://github.com/MindFlavor/prometheus_wireguard_exporter/tree/3.2.0)
[![Build Status](https://travis-ci.org/MindFlavor/prometheus_wireguard_exporter.svg?branch=master)](https://travis-ci.org/MindFlavor/prometheus_wireguard_exporter) [![Build Status](https://travis-ci.org/MindFlavor/prometheus_wireguard_exporter.svg?branch=master)](https://travis-ci.org/MindFlavor/prometheus_wireguard_exporter)
[![commitssince](https://img.shields.io/github/commits-since/mindflavor/prometheus_wireguard_exporter/3.1.1.svg)](https://img.shields.io/github/commits-since/mindflavor/prometheus_wireguard_exporter/3.1.1.svg) [![commitssince](https://img.shields.io/github/commits-since/mindflavor/prometheus_wireguard_exporter/3.2.0.svg)](https://img.shields.io/github/commits-since/mindflavor/prometheus_wireguard_exporter/3.2.0.svg)
## Intro ## Intro
A Prometheus exporter for [WireGuard](https://www.wireguard.com), written in Rust. This tool exports the `wg show all dump` results in a format that [Prometheus](https://prometheus.io/) can understand. The exporter is very light on your server resources, both in terms of memory and CPU usage. A Prometheus exporter for [WireGuard](https://www.wireguard.com), written in Rust. This tool exports the `wg show all dump` (or `wg show <interface> dump` if you specify a config file) results in a format that [Prometheus](https://prometheus.io/) can understand. The exporter is very light on your server resources, both in terms of memory and CPU usage.
Starting from release [2.0.2](https://github.com/MindFlavor/prometheus_wireguard_exporter/releases/tag/2.0.2) this exporter supports IPv6 addressess too (thanks to [Maximilian Bosch](https://github.com/Ma27)'s PR [#5](https://github.com/MindFlavor/prometheus_wireguard_exporter/pull/5)). Starting from release [2.0.2](https://github.com/MindFlavor/prometheus_wireguard_exporter/releases/tag/2.0.2) this exporter supports IPv6 addressess too (thanks to [Maximilian Bosch](https://github.com/Ma27)'s PR [#5](https://github.com/MindFlavor/prometheus_wireguard_exporter/pull/5)).
From release [3.0.0](https://github.com/MindFlavor/prometheus_wireguard_exporter/releases/tag/3.0.0) the exporter allows two label modes: one is to dump every allowed ip in a single label (called `allowed_ips`) along with their subnets. The second one is to create a pair of labels for each allowed ip/subnet pair (called `allowed_ip_0`/`allowed_subnet_0`, `allowed_ip_1`/`allowed_subnet_1` and so on for every allowed ip). The default if the single label mode but you can enable the second mode by specifying the `-s` switch at startup. Thank you [Toon Schoenmakers](https://github.com/schoentoon) for this solution (see [https://github.com/MindFlavor/prometheus_wireguard_exporter/issues/8](https://github.com/MindFlavor/prometheus_wireguard_exporter/issues/8)). From release [3.0.0](https://github.com/MindFlavor/prometheus_wireguard_exporter/releases/tag/3.0.0) the exporter allows two label modes: one is to dump every allowed ip in a single label (called `allowed_ips`) along with their subnets. The second one is to create a pair of labels for each allowed ip/subnet pair (called `allowed_ip_0`/`allowed_subnet_0`, `allowed_ip_1`/`allowed_subnet_1` and so on for every allowed ip). The default if the single label mode but you can enable the second mode by specifying the `-s` switch at startup. Thank you [Toon Schoenmakers](https://github.com/schoentoon) for this solution (see [https://github.com/MindFlavor/prometheus_wireguard_exporter/issues/8](https://github.com/MindFlavor/prometheus_wireguard_exporter/issues/8)).

View File

@ -5,7 +5,7 @@ use clap;
use clap::{crate_authors, crate_name, crate_version, Arg}; use clap::{crate_authors, crate_name, crate_version, Arg};
use futures::future::{done, ok, Either, Future}; use futures::future::{done, ok, Either, Future};
use hyper::{Body, Request, Response}; use hyper::{Body, Request, Response};
use log::{info, trace}; use log::{debug, info, trace};
use std::env; use std::env;
mod options; mod options;
use options::Options; use options::Options;
@ -25,14 +25,13 @@ use std::sync::Arc;
fn wg_with_text( fn wg_with_text(
wg_config_str: &str, wg_config_str: &str,
wg_output: ::std::process::Output, wg_output_str: &str,
options: Arc<Options>, options: Arc<Options>,
) -> Result<Response<Body>, ExporterError> { ) -> Result<Response<Body>, ExporterError> {
let pehm = peer_entry_hashmap_try_from(wg_config_str)?; let pehm = peer_entry_hashmap_try_from(wg_config_str)?;
trace!("pehm == {:?}", pehm); trace!("pehm == {:?}", pehm);
let wg_output_string = String::from_utf8(wg_output.stdout)?; let wg = WireGuard::try_from(wg_output_str)?;
let wg = WireGuard::try_from(&wg_output_string as &str)?;
Ok(Response::new(Body::from(wg.render_with_names( Ok(Response::new(Body::from(wg.render_with_names(
Some(&pehm), Some(&pehm),
options.separate_allowed_ips, options.separate_allowed_ips,
@ -45,45 +44,75 @@ fn perform_request(
options: &Arc<Options>, options: &Arc<Options>,
) -> impl Future<Item = Response<Body>, Error = failure::Error> { ) -> impl Future<Item = Response<Body>, Error = failure::Error> {
trace!("perform_request"); trace!("perform_request");
// this is needed to satisfy the borrow checker // this is needed to satisfy the borrow checker
let options = options.clone(); let options = options.clone();
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);
done( done(
Command::new("wg") Command::new("wg")
.arg("show") .arg("show")
.arg("all") .arg(&interface_str)
.arg("dump") .arg("dump")
.output(), .output(),
) )
.from_err() .from_err()
.and_then(move |output| { .and_then(move |output| {
if let Some(extract_names_config_file) = &options.extract_names_config_file { done(String::from_utf8(output.stdout))
Either::A( .from_err()
done(::std::fs::read_to_string(extract_names_config_file)) .and_then(move |output_str| {
.from_err() trace!("wg show output == {}", output_str);
.and_then(|wg_config_string| {
wg_with_text(&wg_config_string as &str, output, options) // 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
} else { // column less in the second case). We solve this prepending the interface name in every
Either::B(done(String::from_utf8(output.stdout)).from_err().and_then( // line so the output of the second case will be equal to the first case.
move |output_str| { let output_str = if interface_str != "all" {
trace!("{}", output_str); debug!("injecting {} to the wg show output", interface_str);
done(WireGuard::try_from(&output_str as &str)) let mut result = String::new();
.from_err() for s in output_str.lines() {
.and_then(move |wg| { result.push_str(&format!("{}\t{}\n", interface_str, s));
ok(Response::new(Body::from(wg.render_with_names( }
None, result
options.separate_allowed_ips, } else {
options.export_remote_ip_and_port, output_str
)))) };
})
}, if let Some(extract_names_config_file) = &options.extract_names_config_file {
)) Either::A(
} done(::std::fs::read_to_string(extract_names_config_file))
.from_err()
.and_then(move |wg_config_string| {
wg_with_text(&wg_config_string as &str, &output_str, options)
}),
)
} else {
Either::B({
trace!("{}", output_str);
done(WireGuard::try_from(&output_str as &str))
.from_err()
.and_then(move |wg| {
ok(Response::new(Body::from(wg.render_with_names(
None,
options.separate_allowed_ips,
options.export_remote_ip_and_port,
))))
})
})
}
})
.from_err()
}) })
.from_err()
} }
fn main() { fn main() {

View File

@ -24,4 +24,46 @@ impl Options {
} }
} }
} }
pub fn get_interface(&self) -> Option<&str> {
if let Some(config_file) = &self.extract_names_config_file {
let path = std::path::Path::new(config_file);
if let Some(file_stem) = path.file_stem() {
file_stem.to_str()
} else {
None
}
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_interface_some() {
let options = Options {
verbose: true,
separate_allowed_ips: false,
extract_names_config_file: Some("/etc/wireguard/wg0.conf".to_owned()),
export_remote_ip_and_port: true,
};
assert_eq!(options.get_interface(), Some("wg0"));
}
#[test]
fn test_interface_none() {
let options = Options {
verbose: true,
separate_allowed_ips: false,
extract_names_config_file: None,
export_remote_ip_and_port: true,
};
assert_eq!(options.get_interface(), None);
}
} }