From e77a3e26ee838c262bd7bae3a445e606706d0d6b Mon Sep 17 00:00:00 2001 From: Francesco Cogno Date: Sun, 11 Oct 2020 16:21:51 +0200 Subject: [PATCH] Support for # key values in comment (friendly_name at the moment) (#44) * Draft support for friendly_name * updated version and readme --- Cargo.toml | 2 +- README.md | 23 +++++---- src/wireguard_config.rs | 107 ++++++++++++++++++++++++++++++++++------ 3 files changed, 105 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 06b9e71..9a457e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "prometheus_wireguard_exporter" -version = "3.3.2" +version = "3.4.0" authors = ["Francesco Cogno "] description = "Prometheus WireGuard Exporter" edition = "2018" diff --git a/README.md b/README.md index 222d131..d07e97a 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,11 @@ [![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.3.2) -[![tag](https://img.shields.io/github/tag/mindflavor/prometheus_wireguard_exporter.svg)](https://github.com/MindFlavor/prometheus_wireguard_exporter/tree/3.3.2) +[![release](https://img.shields.io/github/release/MindFlavor/prometheus_wireguard_exporter.svg)](https://github.com/MindFlavor/prometheus_wireguard_exporter/tree/3.4.0) +[![tag](https://img.shields.io/github/tag/mindflavor/prometheus_wireguard_exporter.svg)](https://github.com/MindFlavor/prometheus_wireguard_exporter/tree/3.4.0) [![Rust build](https://github.com/mindflavor/prometheus_wireguard_exporter/workflows/Rust/badge.svg)](https://github.com/mindflavor/prometheus_wireguard_exporter/actions?query=workflow%3ARust) -[![commitssince](https://img.shields.io/github/commits-since/mindflavor/prometheus_wireguard_exporter/3.3.2.svg)](https://img.shields.io/github/commits-since/mindflavor/prometheus_wireguard_exporter/3.3.2.svg) +[![commitssince](https://img.shields.io/github/commits-since/mindflavor/prometheus_wireguard_exporter/3.4.0.svg)](https://img.shields.io/github/commits-since/mindflavor/prometheus_wireguard_exporter/3.4.0.svg) ## Intro @@ -18,7 +18,7 @@ A Prometheus exporter for [WireGuard](https://www.wireguard.com), written in Rus ## Changelog -* From release [3.3.2](https://github.com/MindFlavor/prometheus_wireguard_exporter/releases/tag/3.3.2) the exporter parses the peer file in a case-insensitive manner. +* **BREAKING** From release [3.4.0](https://github.com/MindFlavor/prometheus_wireguard_exporter/releases/tag/3.4.0) the exporter requires you to specify the friendly names in a specific format (only if you want to use them of course). This allows you to use arbitrary comments in the file while keeping the friendly name functionality. Thank you [Miloš Bunčić](https://github.com/psyhomb) for this. This also paves the way for future metadata. In order to migrate you can use this sed command: `sed -i 's/#/# friendly_name=/' peers.conf`. Please make sure to do a backup before using it! * From release [3.3.1](https://github.com/MindFlavor/prometheus_wireguard_exporter/releases/tag/3.3.1) the exporter accepts multiple interfaces in the command line options. Just pass the `-i` parameter multiple times. Note the not specifying the interface is equivalent to specifying every one of them (the exporter will pass the `all` parameter to `wg show` command). * **BREAKING** Starting from release [3.3.0](https://github.com/MindFlavor/prometheus_wireguard_exporter/releases/tag/3.3.0) the exporter allows you to specify a different interface from the file name. Previously if you specified the file name (the `-n` flag) the program would infer the interface name from the file name. Now the two items are decoupled: you need to specify the file name (with `-n`) and the interface name (with `-i`) separately. Thank you [Vincent Debergue](https://github.com/vdebergue) for helping with this (see issue [#22](https://github.com/MindFlavor/prometheus_wireguard_exporter/issues/22)). Upgrading from [3.2.4](https://github.com/MindFlavor/prometheus_wireguard_exporter/releases/tag/3.2.4): Please note that the `-n` flag no longer infer automatically the interface name from the file name. We now have the `-i` parameter for that. In order to keep the previous behaviour (if you use the `-n` flag) please add the `-i` flag to the command line arguments as well. For example, if you had `prometheus_wireguard_exporter -n /etc/wireguard/wg0.conf` you must specify `prometheus_wireguard_exporter -n /etc/wireguard/wg0.conf -i wg0` to keep the same behaviour. * 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 issue [#8](https://github.com/MindFlavor/prometheus_wireguard_exporter/issues/8)). @@ -136,31 +136,34 @@ wireguard_latest_handshake_seconds{interface="wg0",public_key="928vO9Lf4+Mo84cWu wireguard_latest_handshake_seconds{interface="wg0",public_key="wTjv6hS6fKfNK+SzOLo7O6BQjEb6AD1TN9GjwZ08IwA=",allowed_ips="10.70.0.5/32",friendly_name="folioarch"} 0 ``` -In order for this to work, you need to add comments to your wireguard configuration file (below the `[Peer]` definition). The comment will be interpreted as `friendly_name` and added to the entry exported to Prometheus. Note that this is not a standard but, since it's a comment, will not interfere with WireGuard in any way. For example this is how you edit your WireGuard configuration file: +In order for this to work, you need to add the `friendly_name` key value to the comments preceding a peer a specific metadata (in your wireguard configuration file). See below the `[Peer]` definition for an example. +The tag is called `friendly_name` and it will be added to the entry exported to Prometheus. Note that this is not a standard but, since it's a comment, will not interfere with WireGuard in any way. For example this is how you edit your WireGuard configuration file: -``` +```toml [Peer] PublicKey = lqYcojJMsIZXMUw1heAFbQHBoKjCEaeo7M1WXDh/KWc= AllowedIPs = 10.70.0.40/32 [Peer] +# Custom comment PublicKey = 928vO9Lf4+Mo84cWu4k1oRyzf0AR7FTGoPKHGoTMSHk= AllowedIPs = 10.70.0.80/32 ``` -``` +```toml [Peer] -# frcognowin10 +# friendly_name = frcognowin10 PublicKey = lqYcojJMsIZXMUw1heAFbQHBoKjCEaeo7M1WXDh/KWc= AllowedIPs = 10.70.0.40/32 [Peer] -# OnePlus 5T +# friendly_name = OnePlus 5T +# Custom comment PublicKey = 928vO9Lf4+Mo84cWu4k1oRyzf0AR7FTGoPKHGoTMSHk= AllowedIPs = 10.70.0.80/32 ``` -As you can see, all you need to do is to add the friendly name as comment (and enable the flag since this feature is opt-in). +As you can see, all you need to do is to add the friendly name in the comments preceding a peer (and enable the flag since this feature is opt-in). This is a sample of the label split mode: diff --git a/src/wireguard_config.rs b/src/wireguard_config.rs index 4ed63b1..97014eb 100644 --- a/src/wireguard_config.rs +++ b/src/wireguard_config.rs @@ -23,6 +23,21 @@ fn after_char(s: &str, c_split: char) -> &str { s } +fn from_pound_line_to_key_value(line: &str) -> Option<(&str, &str)> { + // since the pound sign is 1 byte the below slice will work + let line = &line[1..]; + let equals_pos = line.find('='); + if let Some(equals_pos) = equals_pos { + // we should trim the key + let key = &line[..equals_pos].trim(); + // we should trim the value as well? this can be debated + let value = &line[equals_pos + 1..].trim(); + Some((key, value)) + } else { + None + } +} + impl<'a> TryFrom<&[&'a str]> for PeerEntry<'a> { type Error = PeerEntryParseError; @@ -40,9 +55,16 @@ impl<'a> TryFrom<&[&'a str]> for PeerEntry<'a> { public_key = after_char(line, '=').trim(); } else if line_lowercase.starts_with("allowedips") { allowed_ips = after_char(line, '=').trim(); - } else if line.starts_with('#') { - // since the pound sign is 1 byte the below slice will work - name = Some(line[1..].trim()); + } else if line.trim().starts_with('#') { + if let Some((key, value)) = from_pound_line_to_key_value(line) { + // if it's a supported key, let' map it + match key { + "friendly_name" => { + name = Some(value); + } + _ => {} + } + } } } @@ -129,17 +151,22 @@ PrivateKey = my_super_secret_private_key # PostDown = iptables -t nat -D POSTROUTING -s 10.70.0.0/24 -o enp7s0 -j MASQUERADE [Peer] -# OnePlus 6T +# This is a comment +# friendly_name=OnePlus 6T +# This is a comment +# This is a comment +# This is a comment +# This is a comment PublicKey = 2S7mA0vEMethCNQrJpJKE81/JmhgtB+tHHLYQhgM6kk= AllowedIPs = 10.70.0.2/32 [Peer] -# varch.local (laptop) +# friendly_name=varch.local (laptop) PublicKey = qnoxQoQI8KKMupLnSSureORV0wMmH7JryZNsmGVISzU= AllowedIPs = 10.70.0.3/32 [Peer] -# cantarch +# friendly_name=cantarch PublicKey = L2UoJZN7RmEKsMmqaJgKG0m1S2Zs2wd2ptAf+kb3008= AllowedIPs = 10.70.0.4/32 @@ -149,12 +176,14 @@ PublicKey = MdVOIPKt9K2MPj/sO2NlWQbOnFJ6L/qX80mmhQwsUlA= AllowedIPs = 10.70.0.50/32 [Peer] -# frcognowin10 +# This is a comment +# friendly_name = frcognowin10 +# This is something PublicKey = lqYcojJMsIZXMUw1heAFbQHBoKjCEaeo7M1WXDh/KWc= AllowedIPs = 10.70.0.40/32 [Peer] -# OnePlus 5T +#friendly_name = OnePlus 5T PublicKey = 928vO9Lf4+Mo84cWu4k1oRyzf0AR7FTGoPKHGoTMSHk= AllowedIPs = 10.70.0.80/32 "; @@ -166,16 +195,16 @@ PrivateKey = my_super_secret_private_key # PostDown = iptables -t nat -D POSTROUTING -s 10.70.0.0/24 -o enp7s0 -j MASQUERADE [Peer] -# OnePlus 6T +# friendly_name = OnePlus 6T PublicKey = 2S7mA0vEMethCNQrJpJKE81/JmhgtB+tHHLYQhgM6kk= AllowedIPs = 10.70.0.2/32 [Peer] -# varch.local (laptop) +# friendly_name = varch.local (laptop) AllowedIPs = 10.70.0.3/32 [Peer] -# cantarch +#friendly_name= cantarch PublicKey = L2UoJZN7RmEKsMmqaJgKG0m1S2Zs2wd2ptAf+kb3008= AllowedIPs = 10.70.0.4/32 "; @@ -187,29 +216,75 @@ PrivateKey = my_super_secret_private_key # PostDown = iptables -t nat -D POSTROUTING -s 10.70.0.0/24 -o enp7s0 -j MASQUERADE [Peer] -# OnePlus 6T +# friendly_name=OnePlus 6T PublicKey = 2S7mA0vEMethCNQrJpJKE81/JmhgtB+tHHLYQhgM6kk= AllowedIPs = 10.70.0.2/32 [Peer] -# varch.local (laptop) +# friendly_name=varch.local (laptop) AllowedIPs = 10.70.0.3/32 PublicKey = 6S7mA0vEMethCNQrJpJKE81/JmhgtB+tHHLYQhgM6kk= [Peer] -# cantarch +# friendly_name=cantarch PublicKey = L2UoJZN7RmEKsMmqaJgKG0m1S2Zs2wd2ptAf+kb3008= "; + #[test] + fn test_from_pound_line_to_key_value() { + let a = from_pound_line_to_key_value("# ignore"); + assert_eq!(None, a); + + let a = from_pound_line_to_key_value("# soooo much space "); + assert_eq!(None, a); + + let a = from_pound_line_to_key_value( + "# test = This can be tricky ", + ); + let a = a.expect("this should have been Some!"); + assert_eq!(a.0, "test"); + assert_eq!(a.1, "This can be tricky"); + + let a = from_pound_line_to_key_value("# nasty ="); + let a = a.expect("this should have been Some!"); + assert_eq!(a.0, "nasty"); + assert_eq!(a.1, ""); + + let a = from_pound_line_to_key_value("# nasty 2 = "); + let a = a.expect("this should have been Some!"); + assert_eq!(a.0, "nasty 2"); + assert_eq!(a.1, ""); + } + #[test] fn test_parse_ok() { let a: PeerEntryHashMap = peer_entry_hashmap_try_from(TEXT).unwrap(); println!("{:?}", a); } + #[test] + fn test_parse_friendly_name() { + let a: PeerEntryHashMap = peer_entry_hashmap_try_from(TEXT).unwrap(); + let entry = a.get("2S7mA0vEMethCNQrJpJKE81/JmhgtB+tHHLYQhgM6kk="); + let entry = entry.expect("this should have been Some!"); + assert_eq!(Some("OnePlus 6T"), entry.name); + + let entry = a.get("lqYcojJMsIZXMUw1heAFbQHBoKjCEaeo7M1WXDh/KWc="); + let entry = entry.expect("this should have been Some!"); + assert_eq!(Some("frcognowin10"), entry.name); + + let entry = a.get("928vO9Lf4+Mo84cWu4k1oRyzf0AR7FTGoPKHGoTMSHk="); + let entry = entry.expect("this should have been Some!"); + assert_eq!(Some("OnePlus 5T"), entry.name); + + let entry = a.get("MdVOIPKt9K2MPj/sO2NlWQbOnFJ6L/qX80mmhQwsUlA="); + let entry = entry.expect("this should have been Some!"); + assert_eq!(None, entry.name); + } + #[test] #[should_panic( - expected = "PublicKeyNotFound { lines: [\"# varch.local (laptop)\", \"AllowedIPs = 10.70.0.3/32\"] }" + expected = "PublicKeyNotFound { lines: [\"# friendly_name = varch.local (laptop)\", \"AllowedIPs = 10.70.0.3/32\"] }" )] fn test_parse_no_public_key() { let _: PeerEntryHashMap = peer_entry_hashmap_try_from(TEXT_NOPK).unwrap(); @@ -217,7 +292,7 @@ PublicKey = L2UoJZN7RmEKsMmqaJgKG0m1S2Zs2wd2ptAf+kb3008= #[test] #[should_panic( - expected = "AllowedIPsEntryNotFound { lines: [\"# cantarch\", \"PublicKey = L2UoJZN7RmEKsMmqaJgKG0m1S2Zs2wd2ptAf+kb3008=\"] }" + expected = "AllowedIPsEntryNotFound { lines: [\"# friendly_name=cantarch\", \"PublicKey = L2UoJZN7RmEKsMmqaJgKG0m1S2Zs2wd2ptAf+kb3008=\"] }" )] fn test_parse_no_allowed_ips() { let _: PeerEntryHashMap = peer_entry_hashmap_try_from(TEXT_AIP).unwrap();