Monitor Wireguard clients

These are my notes on enabling openHAB to display and use WireGuard client information.

Prerequisites

All setup assumes a Debian/Ubuntu based operating system on the devices.

  • Wireguard - mine is running on a separate device within my network.
  • wireguard-tools, installed on the same device as Wireguard
  • mosquitto-clients, installed on the same device as Wireguard
  • Mosquitto MQTT Broker, with openHAB connected
  • JSONPath Transformation Service installed in openHAB

Wireguard device

I installed and setup Wireguard using PIVPN. This tutorial assumes Wireguard is already setup, and youā€™ve succesfully connected clients.

On the same device as Wireguard, ensure wireguard-tools is installed. With this installed, you should be able to run

wg-json

to show Wireguard status in JSON form.

In my installation that command was not recognised - I have to run

/usr/share/doc/wireguard-tools/examples/json/wg-json

The output might be something similar to:

{
   "wg1": {
      "privateKey": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=",
      "publicKey": "/TOE4TKtAqVsePRVR+5AA43HkAK5DSntkOCO7nYq5xU=",
      "listenPort": 51821,
      "peers": {
         "fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=": {
            "endpoint": "172.19.0.8:51822",
            "latestHandshake": 1617235493,
            "transferRx": 3481633,
            "transferTx": 33460136,
            "allowedIps": [
               "10.0.0.2/24"
            ]
         },
         "jUd41n3XYa3yXBzyBvWqlLhYgRef5RiBD7jwo70U+Rw=": {
            "endpoint": "172.19.0.7:51823",
            "latestHandshake": 1609974495,
            "transferRx": 1403752,
            "transferTx": 19462368,
            "allowedIps": [
               "10.0.0.3/24"
            ]
         }
      }
   }
}

To send this to our Mosquitto MQTT Broker we can pipe the above command to mosquitto_pub, as below:

/usr/share/doc/wireguard-tools/examples/json/wg-json | mosquitto_pub --host 192.168.1.151:1883 --stdin-file --topic wireguard/dump
  • I run Mosquitto on yet another device, so have to specify the host address, otherwise it defaults to localhost and searches for a broker on your Wireguard device.
  • --stdin-file ensures mosquitto_pub ingests the full contents of the wg-json output, and sends it as a single message.
  • I am dumping to the MQTT topic wireguard/dump

I have added this command into Crontab, to send this data every minute.

*/1 * * * * /usr/share/doc/wireguard-tools/examples/json/wg-json | mosquitto_pub --host 192.168.1.151 --stdin-file --topic wireguard/dump

openHAB

I have a connection to my Mosquitto MQTT Broker as described in this tutorial. Importantly, my Broker Thing is called MosquittoMqttBroker.

Thing

things file

// Wireguard Client1
Thing mqtt:topic:wireguardClient1 "Wireguard Client1" (mqtt:broker:MosquittoMqttBroker) {
	Channels:
		Type string : endpoint "endpoint" [
			stateTopic="wireguard/dump",
			transformationPattern="JSONPATH:$.wg0.peers.fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=.endpoint"		
		]
		Type string : latestHandshake "latestHandshake" [
			stateTopic="wireguard/dump",
			transformationPattern="JSONPATH:$.wg0.peers.fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=.latestHandshake"		
		]
		Type datetime : latestHandshake2 "latestHandshake2" [
			stateTopic="wireguard/dump",
			transformationPattern="JSONPATH:$.wg0.peers.fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=.latestHandshakeāˆ©JS:epoch2datetime.js"		
		]
		Type string : transferRx "transferRx" [
			stateTopic="wireguard/dump",
			transformationPattern="JSONPATH:$.wg0.peers.fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=.transferRx"		
		]
		Type string : transferTx "transferTx" [
			stateTopic="wireguard/dump",
			transformationPattern="JSONPATH:$.wg0.peers.fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=.transferTx"		
		]
}

// Wireguard Client2
Thing mqtt:topic:wireguardClient2 "Wireguard Client2" (mqtt:broker:MosquittoMqttBroker) {
	Channels:
		Type string : endpoint "endpoint" [
			stateTopic="wireguard/dump",
			transformationPattern="JSONPATH:$.wg0.peers.jUd41n3XYa3yXBzyBvWqlLhYgRef5RiBD7jwo70U+Rw=.endpoint"		
		]
		Type string : latestHandshake "latestHandshake" [
			stateTopic="wireguard/dump",
			transformationPattern="JSONPATH:$.wg0.peers.jUd41n3XYa3yXBzyBvWqlLhYgRef5RiBD7jwo70U+Rw=.latestHandshake"		
		]
		Type datetime : latestHandshake2 "latestHandshake2" [
			stateTopic="wireguard/dump",
			transformationPattern="JSONPATH:$.wg0.peers.jUd41n3XYa3yXBzyBvWqlLhYgRef5RiBD7jwo70U+Rw=.latestHandshakeāˆ©JS:epoch2datetime.js"		
		]
		Type string : transferRx "transferRx" [
			stateTopic="wireguard/dump",
			transformationPattern="JSONPATH:$.wg0.peers.jUd41n3XYa3yXBzyBvWqlLhYgRef5RiBD7jwo70U+Rw=.transferRx"		
		]
		Type string : transferTx "transferTx" [
			stateTopic="wireguard/dump",
			transformationPattern="JSONPATH:$.wg0.peers.jUd41n3XYa3yXBzyBvWqlLhYgRef5RiBD7jwo70U+Rw=.transferTx"		
		]
}

MainUI YAML

Client 1
UID: mqtt:topic:wireguardClient1
label: Wireguard Client1
thingTypeUID: mqtt:topic
configuration: {}
bridgeUID: mqtt:broker:MosquittoMqttBroker
channels:
  - id: endpoint
    channelTypeUID: mqtt:string
    label: endpoint
    description: null
    configuration:
      retained: false
      postCommand: false
      formatBeforePublish: "%s"
      stateTopic: wireguard/dump
      transformationPattern: JSONPATH:$.wg0.peers.fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=.endpoint
  - id: latestHandshake
    channelTypeUID: mqtt:string
    label: latestHandshake
    description: null
    configuration:
      retained: false
      postCommand: false
      formatBeforePublish: "%s"
      stateTopic: wireguard/dump
      transformationPattern: JSONPATH:$.wg0.peers.fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=.latestHandshake
  - id: latestHandshake2
    channelTypeUID: mqtt:datetime
    label: latestHandshake2
    description: null
    configuration:
      retained: false
      postCommand: false
      formatBeforePublish: "%s"
      stateTopic: wireguard/dump
      transformationPattern: JSONPATH:$.wg0.peers.fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=.latestHandshakeāˆ©JS:epoch2datetime.js
  - id: transferRx
    channelTypeUID: mqtt:string
    label: transferRx
    description: null
    configuration:
      retained: false
      postCommand: false
      formatBeforePublish: "%s"
      stateTopic: wireguard/dump
      transformationPattern: JSONPATH:$.wg0.peers.fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=.transferRx
  - id: transferTx
    channelTypeUID: mqtt:string
    label: transferTx
    description: null
    configuration:
      retained: false
      postCommand: false
      formatBeforePublish: "%s"
      stateTopic: wireguard/dump
      transformationPattern: JSONPATH:$.wg0.peers.fE/wdxzl0klVp/IR8UcaoGUMjqaWi3jAd7KzHKFS6Ds=.transferTx
Client 2
UID: mqtt:topic:wireguardClient2
label: Wireguard Client2
thingTypeUID: mqtt:topic
configuration: {}
bridgeUID: mqtt:broker:MosquittoMqttBroker
channels:
  - id: endpoint
    channelTypeUID: mqtt:string
    label: endpoint
    description: null
    configuration:
      retained: false
      postCommand: false
      formatBeforePublish: "%s"
      stateTopic: wireguard/dump
      transformationPattern: JSONPATH:$.wg0.peers.jUd41n3XYa3yXBzyBvWqlLhYgRef5RiBD7jwo70U+Rw=.endpoint
  - id: latestHandshake
    channelTypeUID: mqtt:string
    label: latestHandshake
    description: null
    configuration:
      retained: false
      postCommand: false
      formatBeforePublish: "%s"
      stateTopic: wireguard/dump
      transformationPattern: JSONPATH:$.wg0.peers.jUd41n3XYa3yXBzyBvWqlLhYgRef5RiBD7jwo70U+Rw=.latestHandshake
  - id: latestHandshake2
    channelTypeUID: mqtt:datetime
    label: latestHandshake2
    description: null
    configuration:
      retained: false
      postCommand: false
      formatBeforePublish: "%s"
      stateTopic: wireguard/dump
      transformationPattern: JSONPATH:$.wg0.peers.jUd41n3XYa3yXBzyBvWqlLhYgRef5RiBD7jwo70U+Rw=.latestHandshakeāˆ©JS:epoch2datetime.js
  - id: transferRx
    channelTypeUID: mqtt:string
    label: transferRx
    description: null
    configuration:
      retained: false
      postCommand: false
      formatBeforePublish: "%s"
      stateTopic: wireguard/dump
      transformationPattern: JSONPATH:$.wg0.peers.jUd41n3XYa3yXBzyBvWqlLhYgRef5RiBD7jwo70U+Rw=.transferRx
  - id: transferTx
    channelTypeUID: mqtt:string
    label: transferTx
    description: null
    configuration:
      retained: false
      postCommand: false
      formatBeforePublish: "%s"
      stateTopic: wireguard/dump
      transformationPattern: JSONPATH:$.wg0.peers.jUd41n3XYa3yXBzyBvWqlLhYgRef5RiBD7jwo70U+Rw=.transferTx

Transforms

epoch2datetime.js

(function(epoch) {
	return new Date(epoch*1000).toISOString();
})(input)

Items

Items file

String strWireguardClient1Endpoint {channel="mqtt:topic:wireguardClient1:endpoint"}
String strWireguardClient1LatestHandshake {channel="mqtt:topic:wireguardClient1:latestHandshake"}
DateTime dtWireguardClient1LatestHandshake2 {channel="mqtt:topic:wireguardClient1:latestHandshake2",stateDescription=""[pattern="%1$td/%1$tm/%1$tY %1$tH:%1$tM"]}
String strWireguardClient1EndpointTransferRx {channel="mqtt:topic:wireguardClient1:transferRx"}
String strWireguardClient1EndpointTransferTx {channel="mqtt:topic:wireguardClient1:transferTx"}

String strWireguardClient2Endpoint {channel="mqtt:topic:wireguardClient2:endpoint"}
String strWireguardClient2LatestHandshake {channel="mqtt:topic:wireguardClient2:latestHandshake"}
DateTime dtWireguardClient2LatestHandshake2 {channel="mqtt:topic:wireguardClient2:latestHandshake2",stateDescription=""[pattern="%1$td/%1$tm/%1$tY %1$tH:%1$tM"]}
String strWireguardClient2EndpointTransferRx {channel="mqtt:topic:wireguardClient2:transferRx"}
String strWireguardClient2EndpointTransferTx {channel="mqtt:topic:wireguardClient2:transferTx"}

The dtWireguardClient1LatestHandshake2 and dtWireguardClient2LatestHandshake2 Items will be formatted nicely so you can see when the last time that the client performed a handshake was. dtWireguardClient1LatestHandshake and dtWireguardClient2LatestHandshake contain the same information, but in seconds since the epoch - this may be more useful in rules.

1 Like

Thanks for writing this down.

FYI and that of others, if you are in a similar situation you should also consider using Tailscale.
Itā€™s actually Wireguard under the hood, too, plus it provides the framework so you donā€™t have to create it yourself. The service is free for private use.
Remember with n locations you have O(nĀ²) tunnels, this quickly becomes a nightmare to manage.
Tailscale is part of openHABian, too.

Tailscale is super cool technology. As far as I understand it relies on 3rd party identity providers (Google, Microsoft, GitHub) for login, and in some cases may relay traffic over 3rd party servers. This is perfect for most, but I wanted to try and keep my reliance on 3rd party systems to a minimum.

Cool option for openhabian though - good work!

This is great tutorial. I do use wireguard yet I was unaware of this json tool. Thanks to you I will now have a way to gather client statistics.

This tutorial outlines how to integrate with Wireguard which is a VPN tool. I believe that OP intention was to show how to integrate wireguard server monitoring capabilities within openhab and not to advertise specific VPN provider.
It is great that Tailscale is free, however this is yet another managed VPN provider who, depending on actual configuration, gains access to your network traffic.

I wouldnā€™t think so, as I said under the hood itā€™s wireguard thus encrypted end-2-end in the first place.
Sure as with any service you could be tricked into misconfiguring the system but I donā€™t consider this to be very probable given even the code is Open Source, and if they tried their business would be dead right the day someone notices.

Clearly Iā€™m not interested in pushing or advertising anything, but want to make you aware of options.
I just discovered the stuff myself when I was working on a wireguard based implementation for openHABian and to me, it was convincing. YMMV.

Remember that for the majority of openHAB users, the real challenge is to configure wireguard to match your network, including routing, firewalling ā€¦ and in particular with 3+ locations.

FWIW:
I also completed the option to install wireguard from within openHABian so you can also use that.