A Prometheus exporter for WireGuard, 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 can understand. The exporter is very light on your server resources, both in terms of memory and CPU usage. It's also built for Docker for the following CPU architectures: amd64
, 386
, arm64
, armv7
and armv6
.
- From release 3.6.4 the exporter optionally calculates the delta, in seconds, since the last handshake. The metric is
wireguard_latest_handshake_delay_seconds
. Thanks to mmahacek for the idea. - From release 3.6.3 the exporter automatically parses the systemd-networkd's peer syntax too (
[WireGuardPeer]
rather than[Peer]
). Thanks to mbonino for the PR (see MindFlavor#92). - From release 3.6.1 the exporter correctly escapes the double quotes in
friendly_name
. Thanks to Steven Wood for finding the bug in #82. - BREAKING From version
3.6.0
the exporter takes fallback configuration values from the environment variables. Thanks to j_r0dd for the idea. This changes how the exporter evaluates the command line parameters: make sure to consult the documentation on how to convert your command line to the new format. Basically every switch (for example verbose-v
) not expect values, eithertrue
orfalse
. This is necessary because there is no way to discriminate between an empty environment variable and one that has not been set. - From release 3.5.1 the exporter supports multiple peer files. Thanks to Tobias Krischer for the idea.
- From release 3.5.0 the exporter supports the
friendly_json
tag. Entries prepended with thefriendly_json
tag will output all the entries in the specificed json as Prometheus attributes. Thanks to DrProxyProSupport for the idea. - From release 3.4.1 the exporter supports prepending
sudo
to thewg
command. This allows to run the exporter as a non root user (although sudoer without password). Thanks to Jonas Seydel for the idea. - BREAKING From release 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ć 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 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 theall
parameter towg show
command). - BREAKING Starting from release 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 for helping with this (see issue #22). Upgrading from 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 hadprometheus_wireguard_exporter -n /etc/wireguard/wg0.conf
you must specifyprometheus_wireguard_exporter -n /etc/wireguard/wg0.conf -i wg0
to keep the same behaviour. - From release 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 (calledallowed_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 for this solution (see issue #8). - Starting from release 2.0.2 this exporter supports IPv6 addresses too (thanks to Maximilian Bosch's PR #5).
Coming soon, subcribe to #59
-
You need Docker installed
-
You need WireGuard installed in your host kernel
-
You need some Wireguard interfaces running
-
Download and run the container with:
docker run -d --net=host --cap-add=NET_ADMIN --name wgexporter mindflavor/prometheus-wireguard-exporter
⚠️ If you encounter time issues on your 32 bit operating system, check this -
Check it's up by visiting http://localhost:9586/metrics
You can then update the image with
docker pull mindflavor/prometheus_wireguard_exporter
Or use a tagged image such as :3.5.1
.
If your host has an amd64
or 686
CPU, you can also build the Docker image from source (you need git
) with:
docker build -t mindflavor/prometheus_wireguard_exporter https://github.com/MindFlavor/prometheus_wireguard_exporter.git#master
-
You need Rust installed
-
You need WireGuard installed on your host
-
You need
wg
accessible in your path. The tool will callwg show <interface(s)>|all dump
and of course will fail if thewg
executable is not found. -
You need some Wireguard interfaces running
-
You need
git
installed -
Clone the repository with
git clone https://github.com/MindFlavor/prometheus_wireguard_exporter.git cd prometheus_wireguard_exporter
-
Compile the program with
cargo install --path .
💁 If you encounter errors, please try updating your rust installation with
rustup update
. The code should compile with any relatively recent, 2018-compliant rustc version. As a frame of reference, the last release was built using the Rust Docker image usingrustc 1.53.0 (53cb7b09b 2021-06-17)
. -
Run the program
./prometheus_wireguard_exporter
-
Check it's up by visiting http://localhost:9586/metrics
Start the binary with -h
to get the complete syntax. The parameters are below.
❗Important ❗: since 3.6.0, every parameter requires a value. In other words, even the -v
(verbose) parameter requires true
or false
after it. Passing a parameter without value (for example -v
) is the same of not passing the parameter at all: the default value will be used instead (in the case of the verbose option, it means false
).
For example, if you want to enable the verbose mode and enable the prepend sudo option you would use the following command line:
prometheus_wireguard_exporter -a true -v true <...>
Parameter | Env | Mandatory | Valid values | Default | Accepts multiple occurrences? | Description |
---|---|---|---|---|---|---|
-v |
PROMETHEUS_WIREGUARD_EXPORTER_VERBOSE_ENABLED |
No | true or false |
false |
No | Enable verbose mode. |
-a |
PROMETHEUS_WIREGUARD_EXPORTER_PREPEND_SUDO_ENABLED |
No | true or false |
false |
No | Prepends sudo to wg commands. |
-l |
PROMETHEUS_WIREGUARD_EXPORTER_ADDRESS |
No | Any valid IP address | 0.0.0.0 |
No | Specify the service address. This is the address your Prometheus instance should point to. |
-p |
PROMETHEUS_WIREGUARD_EXPORTER_PORT |
No | Any valid port number | 9586 |
No | Specify the service port. This is the port your Prometheus instance should point to. |
-n |
PROMETHEUS_WIREGUARD_EXPORTER_CONFIG_FILE_NAMES |
No | Path to the wireguard configuration file | Yes | This flag adds the friendly_name attribute or the friendly_json attributes to the exported entries. See Friendly tags for more details. Multiple files are allowed (they will be merged as a single file in memory so avoid duplicates). | |
-s |
PROMETHEUS_WIREGUARD_EXPORTER_SEPARATE_ALLOWED_IPS_ENABLED |
No | true or false |
false |
No | Enable the allowed ip + subnet split mode for the labels. |
-r |
PROMETHEUS_WIREGUARD_EXPORTER_EXPORT_REMOTE_IP_AND_PORT_ENABLED |
No | true or false |
false |
No | Exports peer's remote ip and port as labels (if available). |
-i |
PROMETHEUS_WIREGUARD_EXPORTER_INTERFACES |
No | Your interface name(s) | all |
Yes | Specifies the interface(s) passed to the wg show <interface> dump parameter. Multiple parameters are allowed. |
-d |
EXPORT_LATEST_HANDSHAKE_DELAY |
No | true or false |
false |
No | Adds the wireguard_latest_handshake_delay_seconds metric that automatically calculates the seconds passed since the last handshake. |
Keep in mind that command line values take precedence over environment variables.
Once started, the tool will listen on the specified port (or the default one, 9586, if not specified) and return a Prometheus valid response at the url /metrics
. So to check if the tool is working properly simply browse the http://localhost:9586/metrics
(or whichever port you choose).
Starting from version 3.5 you can instruct the exporter to append a friendly name or a friendly_json to the exported entries. This can make the output more understandable than using the public keys. For example this is the standard output:
# HELP wireguard_sent_bytes_total Bytes sent to the peer
# TYPE wireguard_sent_bytes_total counter
wireguard_sent_bytes_total{interface="wg0",public_key="2S7mA0vEMethCNQrJpJKE81/JmhgtB+tHHLYQhgM6kk=",allowed_ips="10.70.0.2/32,10.70.0.66/32"} 3208804
wireguard_sent_bytes_total{interface="wg0",public_key="qnoxQoQI8KKMupLnSSureORV0wMmH7JryZNsmGVISzU=",allowed_ips="10.70.0.3/32"} 0
wireguard_sent_bytes_total{interface="wg0",public_key="L2UoJZN7RmEKsMmqaJgKG0m1S2Zs2wd2ptAf+kb3008=",allowed_ips="10.70.0.4/32"} 0
wireguard_sent_bytes_total{interface="wg0",public_key="MdVOIPKt9K2MPj/sO2NlWQbOnFJ6L/qX80mmhQwsUlA=",allowed_ips="10.70.0.50/32"} 0
wireguard_sent_bytes_total{interface="wg0",public_key="lqYcojJMsIZXMUw1heAFbQHBoKjCEaeo7M1WXDh/KWc=",allowed_ips="10.70.0.40/32"} 0
wireguard_sent_bytes_total{interface="wg0",public_key="928vO9Lf4+Mo84cWu4k1oRyzf0AR7FTGoPKHGoTMSHk=",allowed_ips="10.70.0.80/32"} 0
wireguard_sent_bytes_total{interface="wg0",public_key="wTjv6hS6fKfNK+SzOLo7O6BQjEb6AD1TN9GjwZ08IwA=",allowed_ips="10.70.0.5/32"} 0
# HELP wireguard_received_bytes_total Bytes received from the peer
# TYPE wireguard_received_bytes_total counter
wireguard_received_bytes_total{interface="wg0",public_key="2S7mA0vEMethCNQrJpJKE81/JmhgtB+tHHLYQhgM6kk=",allowed_ips="10.70.0.2/32,10.70.0.66/32"} 71420072
wireguard_received_bytes_total{interface="wg0",public_key="qnoxQoQI8KKMupLnSSureORV0wMmH7JryZNsmGVISzU=",allowed_ips="10.70.0.3/32"} 0
wireguard_received_bytes_total{interface="wg0",public_key="L2UoJZN7RmEKsMmqaJgKG0m1S2Zs2wd2ptAf+kb3008=",allowed_ips="10.70.0.4/32"} 0
wireguard_received_bytes_total{interface="wg0",public_key="MdVOIPKt9K2MPj/sO2NlWQbOnFJ6L/qX80mmhQwsUlA=",allowed_ips="10.70.0.50/32"} 0
wireguard_received_bytes_total{interface="wg0",public_key="lqYcojJMsIZXMUw1heAFbQHBoKjCEaeo7M1WXDh/KWc=",allowed_ips="10.70.0.40/32"} 0
wireguard_received_bytes_total{interface="wg0",public_key="928vO9Lf4+Mo84cWu4k1oRyzf0AR7FTGoPKHGoTMSHk=",allowed_ips="10.70.0.80/32"} 0
wireguard_received_bytes_total{interface="wg0",public_key="wTjv6hS6fKfNK+SzOLo7O6BQjEb6AD1TN9GjwZ08IwA=",allowed_ips="10.70.0.5/32"} 0
# HELP wireguard_latest_handshake_seconds Seconds from the last handshake
# TYPE wireguard_latest_handshake_seconds gauge
wireguard_latest_handshake_seconds{interface="wg0",public_key="2S7mA0vEMethCNQrJpJKE81/JmhgtB+tHHLYQhgM6kk=",allowed_ips="10.70.0.2/32,10.70.0.66/32"} 1562834127
wireguard_latest_handshake_seconds{interface="wg0",public_key="qnoxQoQI8KKMupLnSSureORV0wMmH7JryZNsmGVISzU=",allowed_ips="10.70.0.3/32"} 0
wireguard_latest_handshake_seconds{interface="wg0",public_key="L2UoJZN7RmEKsMmqaJgKG0m1S2Zs2wd2ptAf+kb3008=",allowed_ips="10.70.0.4/32"} 0
wireguard_latest_handshake_seconds{interface="wg0",public_key="MdVOIPKt9K2MPj/sO2NlWQbOnFJ6L/qX80mmhQwsUlA=",allowed_ips="10.70.0.50/32"} 0
wireguard_latest_handshake_seconds{interface="wg0",public_key="lqYcojJMsIZXMUw1heAFbQHBoKjCEaeo7M1WXDh/KWc=",allowed_ips="10.70.0.40/32"} 0
wireguard_latest_handshake_seconds{interface="wg0",public_key="928vO9Lf4+Mo84cWu4k1oRyzf0AR7FTGoPKHGoTMSHk=",allowed_ips="10.70.0.80/32"} 0
wireguard_latest_handshake_seconds{interface="wg0",public_key="wTjv6hS6fKfNK+SzOLo7O6BQjEb6AD1TN9GjwZ08IwA=",allowed_ips="10.70.0.5/32"} 0
And this is the one augmented with friendly names:
# HELP wireguard_sent_bytes_total Bytes sent to the peer
# TYPE wireguard_sent_bytes_total counter
wireguard_sent_bytes_total{interface="wg0",public_key="2S7mA0vEMethCNQrJpJKE81/JmhgtB+tHHLYQhgM6kk=",allowed_ips="10.70.0.2/32,10.70.0.66/32",friendly_name="OnePlus 6T"} 3208804
wireguard_sent_bytes_total{interface="wg0",public_key="qnoxQoQI8KKMupLnSSureORV0wMmH7JryZNsmGVISzU=",allowed_ips="10.70.0.3/32",friendly_name="varch.local (laptop)"} 0
wireguard_sent_bytes_total{interface="wg0",public_key="L2UoJZN7RmEKsMmqaJgKG0m1S2Zs2wd2ptAf+kb3008=",allowed_ips="10.70.0.4/32",friendly_name="cantarch"} 0
wireguard_sent_bytes_total{interface="wg0",public_key="MdVOIPKt9K2MPj/sO2NlWQbOnFJ6L/qX80mmhQwsUlA=",allowed_ips="10.70.0.50/32",friendly_name="frcognoarch"} 0
wireguard_sent_bytes_total{interface="wg0",public_key="lqYcojJMsIZXMUw1heAFbQHBoKjCEaeo7M1WXDh/KWc=",allowed_ips="10.70.0.40/32",friendly_name="frcognowin10"} 0
wireguard_sent_bytes_total{interface="wg0",public_key="928vO9Lf4+Mo84cWu4k1oRyzf0AR7FTGoPKHGoTMSHk=",allowed_ips="10.70.0.80/32",friendly_name="OnePlus 5T"} 0
wireguard_sent_bytes_total{interface="wg0",public_key="wTjv6hS6fKfNK+SzOLo7O6BQjEb6AD1TN9GjwZ08IwA=",allowed_ips="10.70.0.5/32",friendly_name="folioarch"} 0
# HELP wireguard_received_bytes_total Bytes received from the peer
# TYPE wireguard_received_bytes_total counter
wireguard_received_bytes_total{interface="wg0",public_key="2S7mA0vEMethCNQrJpJKE81/JmhgtB+tHHLYQhgM6kk=",allowed_ips="10.70.0.2/32,10.70.0.66/32",friendly_name="OnePlus 6T"} 71420072
wireguard_received_bytes_total{interface="wg0",public_key="qnoxQoQI8KKMupLnSSureORV0wMmH7JryZNsmGVISzU=",allowed_ips="10.70.0.3/32",friendly_name="varch.local (laptop)"} 0
wireguard_received_bytes_total{interface="wg0",public_key="L2UoJZN7RmEKsMmqaJgKG0m1S2Zs2wd2ptAf+kb3008=",allowed_ips="10.70.0.4/32",friendly_name="cantarch"} 0
wireguard_received_bytes_total{interface="wg0",public_key="MdVOIPKt9K2MPj/sO2NlWQbOnFJ6L/qX80mmhQwsUlA=",allowed_ips="10.70.0.50/32",friendly_name="frcognoarch"} 0
wireguard_received_bytes_total{interface="wg0",public_key="lqYcojJMsIZXMUw1heAFbQHBoKjCEaeo7M1WXDh/KWc=",allowed_ips="10.70.0.40/32",friendly_name="frcognowin10"} 0
wireguard_received_bytes_total{interface="wg0",public_key="928vO9Lf4+Mo84cWu4k1oRyzf0AR7FTGoPKHGoTMSHk=",allowed_ips="10.70.0.80/32",friendly_name="OnePlus 5T"} 0
wireguard_received_bytes_total{interface="wg0",public_key="wTjv6hS6fKfNK+SzOLo7O6BQjEb6AD1TN9GjwZ08IwA=",allowed_ips="10.70.0.5/32",friendly_name="folioarch"} 0
# HELP wireguard_latest_handshake_seconds Seconds from the last handshake
# TYPE wireguard_latest_handshake_seconds gauge
wireguard_latest_handshake_seconds{interface="wg0",public_key="2S7mA0vEMethCNQrJpJKE81/JmhgtB+tHHLYQhgM6kk=",allowed_ips="10.70.0.2/32,10.70.0.66/32",friendly_name="OnePlus 6T"} 1562834127
wireguard_latest_handshake_seconds{interface="wg0",public_key="qnoxQoQI8KKMupLnSSureORV0wMmH7JryZNsmGVISzU=",allowed_ips="10.70.0.3/32",friendly_name="varch.local (laptop)"} 0
wireguard_latest_handshake_seconds{interface="wg0",public_key="L2UoJZN7RmEKsMmqaJgKG0m1S2Zs2wd2ptAf+kb3008=",allowed_ips="10.70.0.4/32",friendly_name="cantarch"} 0
wireguard_latest_handshake_seconds{interface="wg0",public_key="MdVOIPKt9K2MPj/sO2NlWQbOnFJ6L/qX80mmhQwsUlA=",allowed_ips="10.70.0.50/32",friendly_name="frcognoarch"} 0
wireguard_latest_handshake_seconds{interface="wg0",public_key="lqYcojJMsIZXMUw1heAFbQHBoKjCEaeo7M1WXDh/KWc=",allowed_ips="10.70.0.40/32",friendly_name="frcognowin10"} 0
wireguard_latest_handshake_seconds{interface="wg0",public_key="928vO9Lf4+Mo84cWu4k1oRyzf0AR7FTGoPKHGoTMSHk=",allowed_ips="10.70.0.80/32",friendly_name="OnePlus 5T"} 0
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 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.
From version 3.5.0 you can optionally specify a friendly_json
tag followed by a flat json (that is, a json with only top level, simple entries). If a friendly_json
tag will be found every entry will be used as attribute in the exported Prometheus instance. No compliance check will be done. Also, numbers will be converted to strings (as it's expected for a Prometheus attribute).
For example this is how you edit your WireGuard configuration file:
[Peer]
PublicKey = lqYcojJMsIZXMUw1heAFbQHBoKjCEaeo7M1WXDh/KWc=
AllowedIPs = 10.70.0.40/32
[Peer]
# Custom comment
PublicKey = 928vO9Lf4+Mo84cWu4k1oRyzf0AR7FTGoPKHGoTMSHk=
AllowedIPs = 10.70.0.80/32
[Peer]
# friendly_name = frcognowin10
PublicKey = lqYcojJMsIZXMUw1heAFbQHBoKjCEaeo7M1WXDh/KWc=
AllowedIPs = 10.70.0.40/32
[Peer]
# 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 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:
# HELP wireguard_sent_bytes_total Bytes sent to the peer
# TYPE wireguard_sent_bytes_total counter
wireguard_sent_bytes_total{interface="wg0",public_key="2S7mA0vEMethCNQrJpJKE81/JmhgtB+tHHLYQhgM6kk=",allowed_ip_0="10.70.0.2",allowed_subnet_0="32",allowed_ip_1="10.70.0.66",allowed_subnet_1="32",friendly_name="OnePlus 6T"} 3208804
wireguard_sent_bytes_total{interface="wg0",public_key="qnoxQoQI8KKMupLnSSureORV0wMmH7JryZNsmGVISzU=",allowed_ip_0="10.70.0.3",allowed_subnet_0="32",friendly_name="varch.local (laptop)"} 0
wireguard_sent_bytes_total{interface="wg0",public_key="L2UoJZN7RmEKsMmqaJgKG0m1S2Zs2wd2ptAf+kb3008=",allowed_ip_0="10.70.0.4",allowed_subnet_0="32",friendly_name="cantarch"} 0
wireguard_sent_bytes_total{interface="wg0",public_key="MdVOIPKt9K2MPj/sO2NlWQbOnFJ6L/qX80mmhQwsUlA=",allowed_ip_0="10.70.0.50",allowed_subnet_0="32",friendly_name="frcognoarch"} 0
wireguard_sent_bytes_total{interface="wg0",public_key="lqYcojJMsIZXMUw1heAFbQHBoKjCEaeo7M1WXDh/KWc=",allowed_ip_0="10.70.0.40",allowed_subnet_0="32",friendly_name="frcognowin10"} 0
wireguard_sent_bytes_total{interface="wg0",public_key="928vO9Lf4+Mo84cWu4k1oRyzf0AR7FTGoPKHGoTMSHk=",allowed_ip_0="10.70.0.80",allowed_subnet_0="32",friendly_name="OnePlus 5T"} 0
wireguard_sent_bytes_total{interface="wg0",public_key="wTjv6hS6fKfNK+SzOLo7O6BQjEb6AD1TN9GjwZ08IwA=",allowed_ip_0="10.70.0.5",allowed_subnet_0="32",friendly_name="folioarch"} 0
# HELP wireguard_received_bytes_total Bytes received from the peer
# TYPE wireguard_received_bytes_total counter
wireguard_received_bytes_total{interface="wg0",public_key="2S7mA0vEMethCNQrJpJKE81/JmhgtB+tHHLYQhgM6kk=",allowed_ip_0="10.70.0.2",allowed_subnet_0="32",allowed_ip_1="10.70.0.66",allowed_subnet_1="32",friendly_name="OnePlus 6T"} 71420072
wireguard_received_bytes_total{interface="wg0",public_key="qnoxQoQI8KKMupLnSSureORV0wMmH7JryZNsmGVISzU=",allowed_ip_0="10.70.0.3",allowed_subnet_0="32",friendly_name="varch.local (laptop)"} 0
wireguard_received_bytes_total{interface="wg0",public_key="L2UoJZN7RmEKsMmqaJgKG0m1S2Zs2wd2ptAf+kb3008=",allowed_ip_0="10.70.0.4",allowed_subnet_0="32",friendly_name="cantarch"} 0
wireguard_received_bytes_total{interface="wg0",public_key="MdVOIPKt9K2MPj/sO2NlWQbOnFJ6L/qX80mmhQwsUlA=",allowed_ip_0="10.70.0.50",allowed_subnet_0="32",friendly_name="frcognoarch"} 0
wireguard_received_bytes_total{interface="wg0",public_key="lqYcojJMsIZXMUw1heAFbQHBoKjCEaeo7M1WXDh/KWc=",allowed_ip_0="10.70.0.40",allowed_subnet_0="32",friendly_name="frcognowin10"} 0
wireguard_received_bytes_total{interface="wg0",public_key="928vO9Lf4+Mo84cWu4k1oRyzf0AR7FTGoPKHGoTMSHk=",allowed_ip_0="10.70.0.80",allowed_subnet_0="32",friendly_name="OnePlus 5T"} 0
wireguard_received_bytes_total{interface="wg0",public_key="wTjv6hS6fKfNK+SzOLo7O6BQjEb6AD1TN9GjwZ08IwA=",allowed_ip_0="10.70.0.5",allowed_subnet_0="32",friendly_name="folioarch"} 0
# HELP wireguard_latest_handshake_seconds Seconds from the last handshake
# TYPE wireguard_latest_handshake_seconds gauge
wireguard_latest_handshake_seconds{interface="wg0",public_key="2S7mA0vEMethCNQrJpJKE81/JmhgtB+tHHLYQhgM6kk=",allowed_ip_0="10.70.0.2",allowed_subnet_0="32",allowed_ip_1="10.70.0.66",allowed_subnet_1="32",friendly_name="OnePlus 6T"} 1562834127
wireguard_latest_handshake_seconds{interface="wg0",public_key="qnoxQoQI8KKMupLnSSureORV0wMmH7JryZNsmGVISzU=",allowed_ip_0="10.70.0.3",allowed_subnet_0="32",friendly_name="varch.local (laptop)"} 0
wireguard_latest_handshake_seconds{interface="wg0",public_key="L2UoJZN7RmEKsMmqaJgKG0m1S2Zs2wd2ptAf+kb3008=",allowed_ip_0="10.70.0.4",allowed_subnet_0="32",friendly_name="cantarch"} 0
wireguard_latest_handshake_seconds{interface="wg0",public_key="MdVOIPKt9K2MPj/sO2NlWQbOnFJ6L/qX80mmhQwsUlA=",allowed_ip_0="10.70.0.50",allowed_subnet_0="32",friendly_name="frcognoarch"} 0
wireguard_latest_handshake_seconds{interface="wg0",public_key="lqYcojJMsIZXMUw1heAFbQHBoKjCEaeo7M1WXDh/KWc=",allowed_ip_0="10.70.0.40",allowed_subnet_0="32",friendly_name="frcognowin10"} 0
wireguard_latest_handshake_seconds{interface="wg0",public_key="928vO9Lf4+Mo84cWu4k1oRyzf0AR7FTGoPKHGoTMSHk=",allowed_ip_0="10.70.0.80",allowed_subnet_0="32",friendly_name="OnePlus 5T"} 0
wireguard_latest_handshake_seconds{interface="wg0",public_key="wTjv6hS6fKfNK+SzOLo7O6BQjEb6AD1TN9GjwZ08IwA=",allowed_ip_0="10.70.0.5",allowed_subnet_0="32",friendly_name="folioarch"} 0
Now add the exporter to the Prometheus exporters as usual. I recommend to start it as a service. It's necessary to run it as root or configure a sudo rule (if there is a non-root way to call wg show all dump
please let me know). My systemd service file is like this one:
[Unit]
Description=Prometheus WireGuard Exporter
Wants=network-online.target
After=network-online.target
[Service]
User=root
Group=root
Type=simple
ExecStart=/usr/local/bin/prometheus_wireguard_exporter -n /etc/wireguard/peers.conf -i wg0 -i wg1
[Install]
WantedBy=multi-user.target
Running it as normal user + hardening:
[Unit]
Description=Prometheus WireGuard Exporter
Wants=network-online.target
After=network-online.target
[Service]
User=wireguard_exporter
Group=wireguard_exporter
Type=simple
Restart=on-failure
EnvironmentFile=-/etc/conf.d/prometheus-wireguard-exporter
ExecStart=/usr/local/bin/prometheus-wireguard-exporter $WIREGUARD_EXPORTER_ARGS
PrivateTmp=yes
ProtectHome=yes
ProtectControlGroups=yes
UMask=077
RemoveIPC=yes
BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout
ProtectSystem=strict
ProtectProc=noaccess
[Install]
WantedBy=multi-user.target
Most of the other systemd hardening options won't work because they block sudo. With the above unit, you can use the following sudo rule:
wireguard_exporter ALL=(root) NOPASSWD: /usr/bin/wg
If you're interested in more hardening, you can analyze the unit with:
systemd-analyze security prometheus-wireguard-exporter.service
This example is for an installation on OPNsense.
Add service user: pw adduser wireguard_exporter -u 518 -g wheel -d /nonexistent -s /usr/sbin/nologin -c "Prometheus wireguard_exporter user"
Group wheel
is nessesary to read the wg*.conf
files.
Service: /usr/local/etc/rc.d/wireguard_exporter
#!/bin/sh
# PROVIDE: wireguard_exporter
# REQUIRE: LOGIN
# KEYWORD: shutdown
#
# Add the following lines to /etc/rc.conf.local or /etc/rc.conf
# to enable this service:
#
# wireguard_exporter_enable (bool): Set to NO by default.
# Set it to YES to enable wireguard_exporter.
# wireguard_exporter_user (string): Set user that wireguard_exporter will run under
# Default is "wireguard_exporter".
# wireguard_exporter_group (string): Set group that wireguard_exporter will run under
# Default is "wheel".
# wireguard_exporter_args (string): Set extra arguments to pass to wireguard_exporter
# Default is "".
# wireguard_exporter_listen_address (string):Set ip that wireguard_exporter will listen on
# Default is "0.0.0.0".
# wireguard_exporter_listen_port (integer): Set port that wireguard_exporter will listen on
# Default is "9586".
# node_exporter_configs (string): Set directory that wireguard_exporter will watch
# Default is "/usr/local/etc/wireguard/*.conf".
. /etc/rc.subr
name=wireguard_exporter
rcvar=wireguard_exporter_enable
load_rc_config $name
: ${wireguard_exporter_enable:="NO"}
: ${wireguard_exporter_user:="wireguard_exporter"}
: ${wireguard_exporter_group:="wheel"}
: ${wireguard_exporter_args:=""}
: ${wireguard_exporter_listen_address:="0.0.0.0"}
: ${wireguard_exporter_listen_port:="9586"}
: ${node_exporter_configs:="/usr/local/etc/wireguard/*.conf"}
pidfile=/var/run/wireguard_exporter.pid
command="/usr/sbin/daemon"
procname="/usr/local/bin/wireguard_exporter"
command_args="-f -p ${pidfile} -T ${name} \
/usr/bin/env ${procname} \
-l ${wireguard_exporter_listen_address} \
-p ${wireguard_exporter_listen_port} \
-n ${node_exporter_configs} \
${wireguard_exporter_args}"
start_precmd=wireguard_exporter_startprecmd
wireguard_exporter_startprecmd()
{
if [ ! -e ${pidfile} ]; then
install \
-o ${wireguard_exporter_user} \
-g ${wireguard_exporter_group} \
/dev/null ${pidfile};
fi
}
load_rc_config $name
run_rc_command "$1"
Service configuration /etc/rc.conf.d/wireguard_exporter
wireguard_exporter_args="-a true -r true -d true " # adjust to your liking
wireguard_exporter_listen_address="172.16.0.1" # listen address
wireguard_exporter_enable="YES" # enable startup on boot
With the above unit, you can use the following sudo rule:
wireguard_exporter ALL=(root) NOPASSWD: /usr/bin/wg # no sudo password required
Cmnd_Alias WGEXPORT = /usr/bin/wg # configuration alias for further use
Defaults!WGEXPORT !log_allowed # prevent logging of permitted executions
Testing
service wireguard_exporter stop
sudo su -m wireguard_exporter -c '/usr/local/bin/wireguard_exporter -l 0.0.0.0 -p 9586 -n /usr/local/etc/wireguard/*.conf -a true -r true -d true'
- Install Rust
- Install Rust Analyzer and set it up with your editor
- Install Clippy:
rustup clippy
You may want to install Docker as well to build and run the Docker image.
The following commands are available:
# Download dependencies
cargo fetch
# Build the program
cargo build
# Run tests
cargo test
# Run clippy to lint
cargo clippy
# Build the Docker image
docker build -t mindflavor/prometheus_wireguard_exporter .
This is more of a plug and play solution based on Docker and VSCode.