Skip to content

Instantly share code, notes, and snippets.

@zh-if
Forked from brandon1024/README.md
Created October 24, 2024 10:32
Show Gist options
  • Save zh-if/768a63544f48b0caabc89860133a3abb to your computer and use it in GitHub Desktop.
Save zh-if/768a63544f48b0caabc89860133a3abb to your computer and use it in GitHub Desktop.

Revisions

  1. @brandon1024 brandon1024 revised this gist Mar 24, 2023. 2 changed files with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -59,6 +59,6 @@ pQNmjK+YF7/OLTYCon/rUf707gD29SHuKvhxM6f93Eg= Friend

    ## Building a Grafana Dashboard

    Once set up, create some visualization for the health and statistics of your Wireguard network in Grafana. You can import the dashboard shown below from the JSON model `grafana-dashboard.json`.
    Once set up, create some visualization for the health and statistics of your Wireguard network in Grafana. You can import the dashboard shown below from the JSON model `wg-grafana-dashboard.json`.

    ![image](https://user-images.githubusercontent.com/22732449/227527289-4ee4d326-6ba4-47b4-800e-1915ce018b2c.png)
    File renamed without changes.
  2. @brandon1024 brandon1024 renamed this gist Mar 24, 2023. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  3. @brandon1024 brandon1024 renamed this gist Mar 24, 2023. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  4. @brandon1024 brandon1024 renamed this gist Mar 24, 2023. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  5. @brandon1024 brandon1024 revised this gist Mar 24, 2023. 2 changed files with 952 additions and 1 deletion.
    8 changes: 7 additions & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -55,4 +55,10 @@ Zo0Z5MClSQZsWlG3hS9RgoE6kHQHWYhGJ3i9DuB1yV4= Home Server
    fvEBlU5mZGelXse9copyYt/c75H9XfQeVMFGVItJu1Q= Phone
    wDArzrW4UnZ6Zfp7/zHvGNH0wx71yhEqOXTu6Jgfbgc= Personal Laptop
    pQNmjK+YF7/OLTYCon/rUf707gD29SHuKvhxM6f93Eg= Friend
    ```
    ```

    ## Building a Grafana Dashboard

    Once set up, create some visualization for the health and statistics of your Wireguard network in Grafana. You can import the dashboard shown below from the JSON model `grafana-dashboard.json`.

    ![image](https://user-images.githubusercontent.com/22732449/227527289-4ee4d326-6ba4-47b4-800e-1915ce018b2c.png)
    945 changes: 945 additions & 0 deletions grafana-dashboard.json
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,945 @@
    {
    "annotations": {
    "list": [
    {
    "builtIn": 1,
    "datasource": {
    "type": "grafana",
    "uid": "-- Grafana --"
    },
    "enable": true,
    "hide": true,
    "iconColor": "rgba(0, 211, 255, 1)",
    "name": "Annotations & Alerts",
    "target": {
    "limit": 100,
    "matchAny": false,
    "tags": [],
    "type": "dashboard"
    },
    "type": "dashboard"
    }
    ]
    },
    "editable": true,
    "fiscalYearStartMonth": 0,
    "graphTooltip": 0,
    "id": 5,
    "links": [],
    "liveNow": false,
    "panels": [
    {
    "datasource": {
    "type": "prometheus",
    "uid": "s8uwo_x4k"
    },
    "fieldConfig": {
    "defaults": {
    "color": {
    "mode": "fixed"
    },
    "displayName": "Peers",
    "mappings": [],
    "noValue": "None",
    "thresholds": {
    "mode": "absolute",
    "steps": [
    {
    "color": "green",
    "value": null
    },
    {
    "color": "red",
    "value": 80
    }
    ]
    }
    },
    "overrides": []
    },
    "gridPos": {
    "h": 4,
    "w": 2,
    "x": 0,
    "y": 0
    },
    "id": 10,
    "options": {
    "colorMode": "value",
    "graphMode": "none",
    "justifyMode": "auto",
    "orientation": "auto",
    "reduceOptions": {
    "calcs": [
    "lastNotNull"
    ],
    "fields": "",
    "values": false
    },
    "textMode": "value_and_name"
    },
    "pluginVersion": "9.4.3",
    "targets": [
    {
    "datasource": {
    "type": "prometheus",
    "uid": "s8uwo_x4k"
    },
    "editorMode": "code",
    "expr": "count(node_network_wireguard_peer_handshake{job=\"$job\",instance=\"$node\",device=\"$interface\"})",
    "legendFormat": "__auto",
    "range": true,
    "refId": "A"
    }
    ],
    "transparent": true,
    "type": "stat"
    },
    {
    "datasource": {
    "type": "prometheus",
    "uid": "s8uwo_x4k"
    },
    "fieldConfig": {
    "defaults": {
    "color": {
    "mode": "thresholds"
    },
    "displayName": "Active Peers",
    "mappings": [],
    "min": 0,
    "noValue": "None",
    "thresholds": {
    "mode": "absolute",
    "steps": [
    {
    "color": "green",
    "value": null
    },
    {
    "color": "red",
    "value": 80
    }
    ]
    }
    },
    "overrides": []
    },
    "gridPos": {
    "h": 4,
    "w": 6,
    "x": 2,
    "y": 0
    },
    "id": 11,
    "options": {
    "colorMode": "value",
    "graphMode": "area",
    "justifyMode": "auto",
    "orientation": "auto",
    "reduceOptions": {
    "calcs": [
    "lastNotNull"
    ],
    "fields": "",
    "values": false
    },
    "textMode": "value_and_name"
    },
    "pluginVersion": "9.4.3",
    "targets": [
    {
    "datasource": {
    "type": "prometheus",
    "uid": "s8uwo_x4k"
    },
    "editorMode": "code",
    "expr": "count((time() - node_network_wireguard_peer_handshake{job=\"$job\",instance=\"$node\",device=\"$interface\"}) <= 180)",
    "legendFormat": "__auto",
    "range": true,
    "refId": "A"
    }
    ],
    "transparent": true,
    "type": "stat"
    },
    {
    "datasource": {
    "type": "prometheus",
    "uid": "s8uwo_x4k"
    },
    "fieldConfig": {
    "defaults": {
    "color": {
    "mode": "thresholds"
    },
    "custom": {
    "align": "auto",
    "cellOptions": {
    "type": "color-text"
    },
    "filterable": false,
    "inspect": false
    },
    "mappings": [],
    "thresholds": {
    "mode": "absolute",
    "steps": [
    {
    "color": "green",
    "value": null
    },
    {
    "color": "red",
    "value": 180
    }
    ]
    },
    "unit": "dtdurations"
    },
    "overrides": [
    {
    "matcher": {
    "id": "byName",
    "options": "Name"
    },
    "properties": [
    {
    "id": "custom.cellOptions",
    "value": {
    "type": "auto"
    }
    }
    ]
    },
    {
    "matcher": {
    "id": "byName",
    "options": "Public Key"
    },
    "properties": [
    {
    "id": "custom.cellOptions",
    "value": {
    "type": "auto"
    }
    },
    {
    "id": "custom.width",
    "value": 258
    }
    ]
    },
    {
    "matcher": {
    "id": "byName",
    "options": "Allowed IPs"
    },
    "properties": [
    {
    "id": "custom.cellOptions",
    "value": {
    "type": "auto"
    }
    },
    {
    "id": "custom.width",
    "value": 142
    }
    ]
    },
    {
    "matcher": {
    "id": "byName",
    "options": "Persistent Keepalive"
    },
    "properties": [
    {
    "id": "custom.cellOptions",
    "value": {
    "type": "auto"
    }
    },
    {
    "id": "custom.width",
    "value": 161
    }
    ]
    }
    ]
    },
    "gridPos": {
    "h": 8,
    "w": 16,
    "x": 8,
    "y": 0
    },
    "id": 4,
    "options": {
    "footer": {
    "countRows": false,
    "enablePagination": true,
    "fields": [],
    "reducer": [
    "sum"
    ],
    "show": false
    },
    "showHeader": true,
    "sortBy": []
    },
    "pluginVersion": "9.4.3",
    "targets": [
    {
    "datasource": {
    "type": "prometheus",
    "uid": "s8uwo_x4k"
    },
    "editorMode": "code",
    "exemplar": false,
    "expr": "time() - node_network_wireguard_peer_handshake{job=\"$job\",instance=\"$node\",device=\"$interface\"}",
    "format": "table",
    "instant": true,
    "legendFormat": "__auto",
    "range": false,
    "refId": "A"
    }
    ],
    "title": "Active Peers",
    "transformations": [
    {
    "id": "filterFieldsByName",
    "options": {
    "include": {
    "names": [
    "endpoint",
    "name",
    "persistent_keepalive",
    "public_key",
    "Value"
    ]
    }
    }
    },
    {
    "id": "organize",
    "options": {
    "excludeByName": {},
    "indexByName": {
    "Value": 5,
    "device": 0,
    "endpoint": 3,
    "name": 1,
    "persistent_keepalive": 4,
    "public_key": 2
    },
    "renameByName": {
    "Value": "Handshake",
    "device": "Interface",
    "endpoint": "Allowed IPs",
    "name": "Name",
    "persistent_keepalive": "Persistent Keepalive",
    "public_key": "Public Key"
    }
    }
    },
    {
    "id": "sortBy",
    "options": {
    "fields": {},
    "sort": [
    {
    "field": "Handshake"
    }
    ]
    }
    }
    ],
    "transparent": true,
    "type": "table"
    },
    {
    "datasource": {
    "type": "prometheus",
    "uid": "s8uwo_x4k"
    },
    "fieldConfig": {
    "defaults": {
    "color": {
    "mode": "thresholds"
    },
    "displayName": "",
    "mappings": [],
    "noValue": "None",
    "thresholds": {
    "mode": "absolute",
    "steps": [
    {
    "color": "green",
    "value": null
    }
    ]
    },
    "unit": "decbytes"
    },
    "overrides": []
    },
    "gridPos": {
    "h": 4,
    "w": 4,
    "x": 0,
    "y": 4
    },
    "id": 12,
    "options": {
    "colorMode": "value",
    "graphMode": "area",
    "justifyMode": "auto",
    "orientation": "auto",
    "reduceOptions": {
    "calcs": [
    "lastNotNull"
    ],
    "fields": "",
    "values": false
    },
    "textMode": "value_and_name"
    },
    "pluginVersion": "9.4.3",
    "targets": [
    {
    "datasource": {
    "type": "prometheus",
    "uid": "s8uwo_x4k"
    },
    "editorMode": "code",
    "expr": "sum(node_network_wireguard_peer_tx{job=\"$job\",instance=\"$node\",device=\"$interface\"})",
    "legendFormat": "__auto",
    "range": true,
    "refId": "A"
    }
    ],
    "transparent": true,
    "type": "stat"
    },
    {
    "datasource": {
    "type": "prometheus",
    "uid": "s8uwo_x4k"
    },
    "fieldConfig": {
    "defaults": {
    "color": {
    "mode": "thresholds"
    },
    "displayName": "",
    "mappings": [],
    "noValue": "None",
    "thresholds": {
    "mode": "absolute",
    "steps": [
    {
    "color": "green",
    "value": null
    }
    ]
    },
    "unit": "decbytes"
    },
    "overrides": []
    },
    "gridPos": {
    "h": 4,
    "w": 4,
    "x": 4,
    "y": 4
    },
    "id": 13,
    "options": {
    "colorMode": "value",
    "graphMode": "area",
    "justifyMode": "auto",
    "orientation": "auto",
    "reduceOptions": {
    "calcs": [
    "lastNotNull"
    ],
    "fields": "",
    "values": false
    },
    "textMode": "value_and_name"
    },
    "pluginVersion": "9.4.3",
    "targets": [
    {
    "datasource": {
    "type": "prometheus",
    "uid": "s8uwo_x4k"
    },
    "editorMode": "code",
    "expr": "sum(node_network_wireguard_peer_rx{job=\"$job\",instance=\"$node\",device=\"$interface\"})",
    "legendFormat": "__auto",
    "range": true,
    "refId": "A"
    }
    ],
    "transparent": true,
    "type": "stat"
    },
    {
    "datasource": {
    "type": "prometheus",
    "uid": "s8uwo_x4k"
    },
    "fieldConfig": {
    "defaults": {
    "color": {
    "mode": "palette-classic"
    },
    "custom": {
    "axisCenteredZero": false,
    "axisColorMode": "text",
    "axisLabel": "",
    "axisPlacement": "auto",
    "barAlignment": 0,
    "drawStyle": "line",
    "fillOpacity": 0,
    "gradientMode": "none",
    "hideFrom": {
    "legend": false,
    "tooltip": false,
    "viz": false
    },
    "lineInterpolation": "linear",
    "lineWidth": 1,
    "pointSize": 5,
    "scaleDistribution": {
    "type": "linear"
    },
    "showPoints": "auto",
    "spanNulls": false,
    "stacking": {
    "group": "A",
    "mode": "none"
    },
    "thresholdsStyle": {
    "mode": "off"
    }
    },
    "mappings": [],
    "thresholds": {
    "mode": "absolute",
    "steps": [
    {
    "color": "green",
    "value": null
    },
    {
    "color": "red",
    "value": 80
    }
    ]
    },
    "unit": "bytes"
    },
    "overrides": [
    {
    "matcher": {
    "id": "byFrameRefID",
    "options": "B"
    },
    "properties": [
    {
    "id": "custom.transform",
    "value": "negative-Y"
    }
    ]
    }
    ]
    },
    "gridPos": {
    "h": 11,
    "w": 24,
    "x": 0,
    "y": 8
    },
    "id": 6,
    "options": {
    "legend": {
    "calcs": [],
    "displayMode": "list",
    "placement": "bottom",
    "showLegend": true
    },
    "tooltip": {
    "mode": "single",
    "sort": "none"
    }
    },
    "targets": [
    {
    "datasource": {
    "type": "prometheus",
    "uid": "s8uwo_x4k"
    },
    "editorMode": "code",
    "exemplar": false,
    "expr": "label_replace(sum(irate(node_network_wireguard_peer_rx{job=\"$job\",instance=\"$node\",device=\"$interface\"}[$__rate_interval])), \"device\", \"$interface\", \"device\", \"\")",
    "format": "time_series",
    "instant": false,
    "legendFormat": "recv {{device}}",
    "range": true,
    "refId": "A"
    },
    {
    "datasource": {
    "type": "prometheus",
    "uid": "s8uwo_x4k"
    },
    "editorMode": "code",
    "exemplar": false,
    "expr": "label_replace(sum(irate(node_network_wireguard_peer_tx{job=\"$job\",instance=\"$node\",device=\"$interface\"}[$__rate_interval])), \"device\", \"$interface\", \"device\", \"\")",
    "format": "time_series",
    "hide": false,
    "instant": false,
    "legendFormat": "trans {{device}} ",
    "range": true,
    "refId": "B"
    }
    ],
    "title": "Wireguard Traffic",
    "transformations": [],
    "type": "timeseries"
    },
    {
    "datasource": {
    "type": "prometheus",
    "uid": "s8uwo_x4k"
    },
    "fieldConfig": {
    "defaults": {
    "color": {
    "mode": "palette-classic"
    },
    "custom": {
    "axisCenteredZero": false,
    "axisColorMode": "text",
    "axisLabel": "",
    "axisPlacement": "auto",
    "barAlignment": 0,
    "drawStyle": "line",
    "fillOpacity": 0,
    "gradientMode": "none",
    "hideFrom": {
    "legend": false,
    "tooltip": false,
    "viz": false
    },
    "lineInterpolation": "linear",
    "lineWidth": 1,
    "pointSize": 5,
    "scaleDistribution": {
    "type": "linear"
    },
    "showPoints": "auto",
    "spanNulls": false,
    "stacking": {
    "group": "A",
    "mode": "none"
    },
    "thresholdsStyle": {
    "mode": "off"
    }
    },
    "mappings": [],
    "thresholds": {
    "mode": "absolute",
    "steps": [
    {
    "color": "green",
    "value": null
    },
    {
    "color": "red",
    "value": 80
    }
    ]
    },
    "unit": "bytes"
    },
    "overrides": []
    },
    "gridPos": {
    "h": 13,
    "w": 12,
    "x": 0,
    "y": 19
    },
    "id": 7,
    "options": {
    "legend": {
    "calcs": [
    "lastNotNull"
    ],
    "displayMode": "table",
    "placement": "bottom",
    "showLegend": true,
    "sortBy": "Last *",
    "sortDesc": true
    },
    "tooltip": {
    "mode": "single",
    "sort": "none"
    }
    },
    "targets": [
    {
    "datasource": {
    "type": "prometheus",
    "uid": "s8uwo_x4k"
    },
    "editorMode": "code",
    "exemplar": false,
    "expr": "node_network_wireguard_peer_rx{job=\"$job\",instance=\"$node\",device=\"$interface\"}",
    "format": "time_series",
    "instant": false,
    "legendFormat": "__auto",
    "range": true,
    "refId": "A"
    }
    ],
    "title": "Peer Rx",
    "transformations": [
    {
    "id": "labelsToFields",
    "options": {
    "keepLabels": [
    "endpoint",
    "name",
    "persistent_keepalive",
    "public_key"
    ],
    "mode": "columns",
    "valueLabel": "name"
    }
    }
    ],
    "type": "timeseries"
    },
    {
    "datasource": {
    "type": "prometheus",
    "uid": "s8uwo_x4k"
    },
    "fieldConfig": {
    "defaults": {
    "color": {
    "mode": "palette-classic"
    },
    "custom": {
    "axisCenteredZero": false,
    "axisColorMode": "text",
    "axisLabel": "",
    "axisPlacement": "auto",
    "barAlignment": 0,
    "drawStyle": "line",
    "fillOpacity": 0,
    "gradientMode": "none",
    "hideFrom": {
    "legend": false,
    "tooltip": false,
    "viz": false
    },
    "lineInterpolation": "linear",
    "lineWidth": 1,
    "pointSize": 5,
    "scaleDistribution": {
    "type": "linear"
    },
    "showPoints": "auto",
    "spanNulls": false,
    "stacking": {
    "group": "A",
    "mode": "none"
    },
    "thresholdsStyle": {
    "mode": "off"
    }
    },
    "mappings": [],
    "thresholds": {
    "mode": "absolute",
    "steps": [
    {
    "color": "green",
    "value": null
    },
    {
    "color": "red",
    "value": 80
    }
    ]
    },
    "unit": "bytes"
    },
    "overrides": []
    },
    "gridPos": {
    "h": 13,
    "w": 12,
    "x": 12,
    "y": 19
    },
    "id": 8,
    "options": {
    "legend": {
    "calcs": [
    "lastNotNull"
    ],
    "displayMode": "table",
    "placement": "bottom",
    "showLegend": true,
    "sortBy": "Last *",
    "sortDesc": true
    },
    "tooltip": {
    "mode": "single",
    "sort": "none"
    }
    },
    "targets": [
    {
    "datasource": {
    "type": "prometheus",
    "uid": "s8uwo_x4k"
    },
    "editorMode": "code",
    "exemplar": false,
    "expr": "node_network_wireguard_peer_tx{job=\"$job\",instance=\"$node\",device=\"$interface\"}",
    "format": "time_series",
    "instant": false,
    "legendFormat": "__auto",
    "range": true,
    "refId": "A"
    }
    ],
    "title": "Peer Tx",
    "transformations": [
    {
    "id": "labelsToFields",
    "options": {
    "keepLabels": [
    "endpoint",
    "name",
    "persistent_keepalive",
    "public_key"
    ],
    "mode": "columns",
    "valueLabel": "name"
    }
    }
    ],
    "type": "timeseries"
    }
    ],
    "refresh": "10s",
    "revision": 1,
    "schemaVersion": 38,
    "style": "dark",
    "tags": [],
    "templating": {
    "list": [
    {
    "current": {
    "selected": false,
    "text": "node",
    "value": "node"
    },
    "datasource": {
    "type": "prometheus",
    "uid": "s8uwo_x4k"
    },
    "definition": "label_values(node_uname_info, job)",
    "hide": 0,
    "includeAll": false,
    "label": "Job",
    "multi": false,
    "name": "job",
    "options": [],
    "query": {
    "query": "label_values(node_uname_info, job)",
    "refId": "StandardVariableQuery"
    },
    "refresh": 1,
    "regex": "",
    "skipUrlSync": false,
    "sort": 0,
    "type": "query"
    },
    {
    "current": {
    "selected": false,
    "text": "n1.opti.lan:9100",
    "value": "n1.opti.lan:9100"
    },
    "datasource": {
    "type": "prometheus",
    "uid": "s8uwo_x4k"
    },
    "definition": "label_values(node_uname_info{job=\"$job\"}, instance)",
    "hide": 0,
    "includeAll": false,
    "label": "Host",
    "multi": false,
    "name": "node",
    "options": [],
    "query": {
    "query": "label_values(node_uname_info{job=\"$job\"}, instance)",
    "refId": "StandardVariableQuery"
    },
    "refresh": 1,
    "regex": "",
    "skipUrlSync": false,
    "sort": 0,
    "type": "query"
    },
    {
    "current": {
    "selected": false,
    "text": "wg0",
    "value": "wg0"
    },
    "datasource": {
    "type": "prometheus",
    "uid": "s8uwo_x4k"
    },
    "definition": "label_values(node_network_wireguard_interface{job=\"$job\",instance=\"$node\"}, device)",
    "hide": 0,
    "includeAll": false,
    "label": "Interface",
    "multi": false,
    "name": "interface",
    "options": [],
    "query": {
    "query": "label_values(node_network_wireguard_interface{job=\"$job\",instance=\"$node\"}, device)",
    "refId": "StandardVariableQuery"
    },
    "refresh": 1,
    "regex": "",
    "skipUrlSync": false,
    "sort": 0,
    "type": "query"
    }
    ]
    },
    "time": {
    "from": "now-1h",
    "to": "now"
    },
    "timepicker": {},
    "timezone": "",
    "title": "Wireguard Network",
    "uid": "DnjWvia4z",
    "version": 34,
    "weekStart": ""
    }
  6. @brandon1024 brandon1024 revised this gist Mar 24, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -5,7 +5,7 @@ This file can be read by the [node_exporter textfile collector](https://github.c

    You might be asking, why does this exist? Why not [MindFlavor/prometheus_wireguard_exporter](https://github.com/MindFlavor/prometheus_wireguard_exporter)? The reality is that a full-fledged webserver written in Rust to expose wireguard metrics is a bit overkill. I've accomplished the same thing in ~100 lines of (well documented) Awk. It's dead simple, and a no brainer if you're already using node-exporter.

    For those conscious about security, this is bit easier to deal with too. The script itself does't run any wireguard commands; it doesn't even need to be run as root. It accepts a wg dump from stdin, and that's it. The metrics exposition is completely isolated from the wireguard configuration, unlike [MindFlavor/prometheus_wireguard_exporter](https://github.com/MindFlavor/prometheus_wireguard_exporter). No need to deal with docker containers attached to the host network, which is annoying when you're running mostly rootless containers.
    For those conscious about security, you'll be happy to know the script itself does't run any wireguard commands; it doesn't even need to be run as root. It accepts a wg dump from stdin, and that's it. The metrics exposition is completely isolated from the wireguard configuration, unlike [MindFlavor/prometheus_wireguard_exporter](https://github.com/MindFlavor/prometheus_wireguard_exporter). No need to deal with docker containers attached to the host network, which is annoying when you're running mostly rootless containers.

    ## Simplest Usage

  7. @brandon1024 brandon1024 revised this gist Mar 8, 2023. 5 changed files with 88 additions and 87 deletions.
    34 changes: 10 additions & 24 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -3,11 +3,7 @@
    Here's a simple set of scripts that allow you to export wireguard tunnel statistics to a file in Prometheus text format.
    This file can be read by the [node_exporter textfile collector](https://github.com/prometheus/node_exporter#textfile-collector), for example.

    The script also supports writing individual statistics to a set of files in a particular directory. This can be
    handy if you're looking to pick apart wireguard tunnel statistics but don't fancy parsing `wg show all dump` yourself.
    More on this later.

    You might be asking, why does this exist? Why not [MindFlavor/prometheus_wireguard_exporter](https://github.com/MindFlavor/prometheus_wireguard_exporter)? The reality is that a full-fledged webserver written in Rust to expose wireguard metrics is a bit overkill. I've accomplished the same thing in less than 100 lines of Awk. It's dead simple.
    You might be asking, why does this exist? Why not [MindFlavor/prometheus_wireguard_exporter](https://github.com/MindFlavor/prometheus_wireguard_exporter)? The reality is that a full-fledged webserver written in Rust to expose wireguard metrics is a bit overkill. I've accomplished the same thing in ~100 lines of (well documented) Awk. It's dead simple, and a no brainer if you're already using node-exporter.

    For those conscious about security, this is bit easier to deal with too. The script itself does't run any wireguard commands; it doesn't even need to be run as root. It accepts a wg dump from stdin, and that's it. The metrics exposition is completely isolated from the wireguard configuration, unlike [MindFlavor/prometheus_wireguard_exporter](https://github.com/MindFlavor/prometheus_wireguard_exporter). No need to deal with docker containers attached to the host network, which is annoying when you're running mostly rootless containers.

    @@ -48,25 +44,15 @@ If you want to use this script to expose wireguard metrics to Prometheus:
    # systemctl start prometheus-collect-wg.timer
    ```

    ## Reading Wireguard Tunnel Statistics
    ## Mapping Public Keys to Human Friendly Names

    If you set the `WG_DUMP_PATH` environment variable, `wg-dump.awk` will write each metric to a file with a
    deterministic name.
    Public keys aren't all that useful for us mortals. Mapping these keys to human-friendly names is easy (and no it does not involve [vanity keys](https://github.com/warner/wireguard-vanity-address)). Simply create a key map file and provide the path to the file as an environment variable.

    ```
    # export WG_DUMP_PATH=/var/run/wireguard/
    # mkdir -p $WG_DUMP_PATH
    # wg show all dump | wg-dump.awk
    # ls $WG_DUMP_PATH
    wg0.interface.fwmark wg0.peer.38a040a78e8b723c43ada8b55d13542d.allowed_ips wg0.peer.5029b1a7ffe9ffcc7f783743106e3041.persistent_keepalive wg0.peer.91e923da80f9eb48773436be45184cd4.transfer_rx
    wg0.interface.listen_port wg0.peer.38a040a78e8b723c43ada8b55d13542d.endpoint wg0.peer.5029b1a7ffe9ffcc7f783743106e3041.pre_shared_key wg0.peer.91e923da80f9eb48773436be45184cd4.transfer_tx
    wg0.interface.public_key wg0.peer.38a040a78e8b723c43ada8b55d13542d.latest_handshake wg0.peer.5029b1a7ffe9ffcc7f783743106e3041.public_key wg0.peer.b072fc5cda6ed8033001db938c707071.allowed_ips
    wg0.peer.0ffebdcc608dbe8be125fbf2f31a3317.allowed_ips wg0.peer.38a040a78e8b723c43ada8b55d13542d.persistent_keepalive wg0.peer.5029b1a7ffe9ffcc7f783743106e3041.transfer_rx wg0.peer.b072fc5cda6ed8033001db938c707071.endpoint
    wg0.peer.0ffebdcc608dbe8be125fbf2f31a3317.endpoint wg0.peer.38a040a78e8b723c43ada8b55d13542d.pre_shared_key wg0.peer.5029b1a7ffe9ffcc7f783743106e3041.transfer_tx wg0.peer.b072fc5cda6ed8033001db938c707071.latest_handshake
    wg0.peer.0ffebdcc608dbe8be125fbf2f31a3317.latest_handshake wg0.peer.38a040a78e8b723c43ada8b55d13542d.public_key wg0.peer.64bb269ee450cd04d0971deb8eb0edff.allowed_ips wg0.peer.b072fc5cda6ed8033001db938c707071.persistent_keepalive
    ...
    ```
    The key map file is a tab-separated file where each line in the file maps a public key to it's human-friendly name.

    In the example above, `wg0` is the interface name. Files prefixed with `wg0.interface` are statistics for the
    interface itself (listen port, public key, etc.). Files prefixed with `wg0.peer` are statistics for each peer.
    The hash (e.g. `0ffebdcc608dbe8be125fbf2f31a3317`) is the MD5 hash of the peer's public key.
    ```
    Zo0Z5MClSQZsWlG3hS9RgoE6kHQHWYhGJ3i9DuB1yV4= Home Server
    fvEBlU5mZGelXse9copyYt/c75H9XfQeVMFGVItJu1Q= Phone
    wDArzrW4UnZ6Zfp7/zHvGNH0wx71yhEqOXTu6Jgfbgc= Personal Laptop
    pQNmjK+YF7/OLTYCon/rUf707gD29SHuKvhxM6f93Eg= Friend
    ```
    5 changes: 4 additions & 1 deletion prometheus-collect-wg.service
    Original file line number Diff line number Diff line change
    @@ -3,7 +3,10 @@ Description=Export Wireguard metrics to a file for consumption by node exporter

    [Service]
    Type=oneshot
    ExecStart=%h/wg-dump-collector.sh
    Environment="WG_KEY_ID_MAP_PATH=/etc/wireguard/peer-key-ids.conf"
    Environment="PROMETHEUS_TEXTFILE_DIR=/var/run/prometheus"
    ExecStartPre=/usr/bin/mkdir -p $PROMETHEUS_TEXTFILE_DIR
    ExecStart=wg-dump-collector.sh

    [Install]
    WantedBy=default.target
    2 changes: 1 addition & 1 deletion prometheus-collect-wg.timer
    Original file line number Diff line number Diff line change
    @@ -6,4 +6,4 @@ OnBootSec=30
    OnUnitActiveSec=15

    [Install]
    WantedBy=timers.target
    WantedBy=timers.target
    14 changes: 6 additions & 8 deletions wg-dump-collector.sh
    Original file line number Diff line number Diff line change
    @@ -1,12 +1,10 @@
    #!/bin/sh
    #
    # wg-dump-collector.sh - write prometheus metrics to a textfile directory
    #
    # This simple script writes the output from the awk script to the textfile
    # collector directory, so that it can be picked up by the node exporter.
    # This script is inteded to be run by a cron or systemd timer.

    set -e

    wg show all dump | /root/wg-dump.awk >/var/run/prometheus/wireguard.prom.$$
    mv /var/run/prometheus/wireguard.prom.$$ /var/run/prometheus/wireguard.prom
    TMP=$$

    wg show all dump | wg-dump.awk >$PROMETHEUS_TEXTFILE_DIR/wireguard.prom.$TMP
    mv $PROMETHEUS_TEXTFILE_DIR/wireguard.prom.$TMP $PROMETHEUS_TEXTFILE_DIR/wireguard.prom

    rm -f $TMP
    120 changes: 67 additions & 53 deletions wg-dump.awk
    Original file line number Diff line number Diff line change
    @@ -1,94 +1,108 @@
    #!/usr/bin/awk -f
    #
    # wg-dump - pick apart wireguard tunnel information
    # wg-dump - pick apart wireguard tunnel information and print in prometheus
    # line format
    #
    # Accepts input from `wg show all dump` and transform into metrics in prometheus format.
    # If the WG_DUMP_PATH is set, write metrics to disk at that path.
    # Accepts input from `wg show all dump` and transforms into metrics in
    # prometheus line format to stdout. Human-friendly names can be assigned to peers
    # by providing a key map file.
    #
    # # Key Map File Format & Configuration
    #
    # To configure a key map file, set the WG_KEY_ID_MAP_PATH environment variable with
    # the path to the file. The file must be tab-separated, the first field being
    # the public key and the second field being an arbitrary name for the key.
    #
    # Usage:
    # $ wg show all dump | wg-dump
    # $ WG_DUMP_PATH=/var/run/wireguard/ wg show all dump | wg-dump
    # $ wg show all dump | wg-dump.awk
    # $ WG_KEY_ID_MAP_PATH=/etc/wireguard/peer-key-ids.conf wg show all dump | wg-dump.awk

    # Read wireguard interface information.
    # read wireguard interface information
    BEGIN {
    out_dir = ENVIRON["WG_DUMP_PATH"]
    wg_key_id_map_file = ENVIRON["WG_KEY_ID_MAP_PATH"]

    getline
    # if a key id map is configured, use it to provide human-friendly names for keys
    if (wg_key_id_map_file) {
    FS = "\t"
    while ((getline < wg_key_id_map_file) > 0) {
    wg_key_id_map[$1] = $2
    }

    device_name = $1
    public_key = $3
    listen_port = $4
    fwmark = $5
    close(wg_key_id_map_file)
    }

    wireguard_iface["node_network_wireguard_interface{device=\"" device_name "\",public_key=\"" public_key "\",port=\"" listen_port "\",fwmark=\"" fwmark "\"}"] = "1.0"
    FS = " "
    ERR = 0
    }

    if ( length(out_dir) != 0 ) {
    print public_key > out_dir device_name ".interface.public_key"
    print listen_port > out_dir device_name ".interface.listen_port"
    print fwmark > out_dir device_name ".interface.fwmark"
    # Lookup the human-friendly name for a key, returning the original key
    # if no name exists.
    function public_key_to_name(key) {
    if (key in wg_key_id_map) {
    return wg_key_id_map[key]
    }

    return key
    }

    function compute_md5( data ) {
    compute_hash = "echo '" data "' | md5sum"
    compute_hash | getline
    return $1
    # Create metrics for an interface.
    function interface(dev, pubkey, port, fwmark) {
    name = public_key_to_name(pubkey)
    labels = "device=\"" dev "\",public_key=\"" pubkey "\",port=\"" port "\",fwmark=\"" fwmark "\",name=\"" name "\""

    wireguard_iface["node_network_wireguard_interface{" labels "}"] = "1.0"
    }

    # Read wireguard peer information.
    # Register metrics for a peer.
    function peer(dev, pubkey, pre_shared_key, endpoint, allowed_ips, last_handshake, transfer_rx, transfer_tx, persistent_keepalive) {
    name = public_key_to_name(pubkey)
    labels = "device=\"" dev "\",public_key=\"" pubkey "\",endpoint=\"" allowed_ips "\",persistent_keepalive=\"" persistent_keepalive "\",name=\"" name "\""

    wireguard_peer_handshake["node_network_wireguard_peer_handshake{" labels "}"] = last_handshake
    wireguard_peer_tx["node_network_wireguard_peer_tx{" labels "}"] = transfer_tx
    wireguard_peer_rx["node_network_wireguard_peer_rx{" labels "}"] = transfer_rx
    }

    # read wireguard peer information
    {
    device_name = $1
    public_key = $2
    pre_shared_key = $3
    endpoint = $4
    allowed_ips = $5
    latest_handshake = $6
    transfer_rx = $7
    transfer_tx = $8
    persistent_keepalive = $9

    hash = compute_md5(public_key)

    if ( length(out_dir) != 0 ) {
    print public_key > out_dir device_name ".peer." hash ".public_key"
    print pre_shared_key > out_dir device_name ".peer." hash ".pre_shared_key"
    print endpoint > out_dir device_name ".peer." hash ".endpoint"
    print allowed_ips > out_dir device_name ".peer." hash ".allowed_ips"
    print latest_handshake > out_dir device_name ".peer." hash ".latest_handshake"
    print transfer_rx > out_dir device_name ".peer." hash ".transfer_rx"
    print transfer_tx > out_dir device_name ".peer." hash ".transfer_tx"
    print persistent_keepalive > out_dir device_name ".peer." hash ".persistent_keepalive"
    if (NF == 5) {
    # update metrics for interface
    interface($1, $3, $4, $5)
    } else if (NF == 9) {
    # update metrics for peer
    peer($1, $2, $3, $4, $5, $6, $7, $8, $9)
    } else {
    print "panic: unexpected number of fields in input; expected 5 or 9, was " NF
    ERR = 1
    exit
    }

    wireguard_peer_handshake["node_network_wireguard_peer_handshake{device=\"" device_name "\",public_key=\"" public_key "\",endpoint=\"" allowed_ips "\",persistent_keepalive=\"" persistent_keepalive "\"}"] = latest_handshake
    wireguard_peer_tx["node_network_wireguard_peer_tx{device=\"" device_name "\",public_key=\"" public_key "\",endpoint=\"" allowed_ips "\",persistent_keepalive=\"" persistent_keepalive "\"}"] = transfer_tx
    wireguard_peer_rx["node_network_wireguard_peer_rx{device=\"" device_name "\",public_key=\"" public_key "\",endpoint=\"" allowed_ips "\",persistent_keepalive=\"" persistent_keepalive "\"}"] = transfer_rx
    }

    # write Prometheus-style metrics to stdout
    END {
    # Write Prometheus-style metrics to stdout.
    if (ERR) exit 1

    print "# HELP node_network_wireguard_interface Wireguard network interface information."
    print "# TYPE node_network_wireguard_interface gauge"
    for ( metric in wireguard_iface ) {
    for (metric in wireguard_iface) {
    print metric " " wireguard_iface[metric]
    }

    print "# HELP node_network_wireguard_peer_handshake Latest handshake for a particular Wireguard peer."
    print "# TYPE node_network_wireguard_peer_handshake gauge"
    for ( metric in wireguard_peer_handshake ) {
    for (metric in wireguard_peer_handshake) {
    print metric " " wireguard_peer_handshake[metric]
    }

    print "# HELP node_network_wireguard_peer_tx Transmit statistics for a particular Wireguard peer."
    print "# TYPE node_network_wireguard_peer_tx counter"
    for ( metric in wireguard_peer_tx ) {
    for (metric in wireguard_peer_tx) {
    print metric " " wireguard_peer_tx[metric]
    }

    print "# HELP node_network_wireguard_peer_rx Transmit statistics for a particular Wireguard peer."
    print "# HELP node_network_wireguard_peer_rx Receive statistics for a particular Wireguard peer."
    print "# TYPE node_network_wireguard_peer_rx counter"
    for ( metric in wireguard_peer_rx ) {
    for (metric in wireguard_peer_rx) {
    print metric " " wireguard_peer_rx[metric]
    }
    }
  8. @brandon1024 brandon1024 revised this gist Feb 26, 2023. 1 changed file with 4 additions and 0 deletions.
    4 changes: 4 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -7,6 +7,10 @@ The script also supports writing individual statistics to a set of files in a pa
    handy if you're looking to pick apart wireguard tunnel statistics but don't fancy parsing `wg show all dump` yourself.
    More on this later.

    You might be asking, why does this exist? Why not [MindFlavor/prometheus_wireguard_exporter](https://github.com/MindFlavor/prometheus_wireguard_exporter)? The reality is that a full-fledged webserver written in Rust to expose wireguard metrics is a bit overkill. I've accomplished the same thing in less than 100 lines of Awk. It's dead simple.

    For those conscious about security, this is bit easier to deal with too. The script itself does't run any wireguard commands; it doesn't even need to be run as root. It accepts a wg dump from stdin, and that's it. The metrics exposition is completely isolated from the wireguard configuration, unlike [MindFlavor/prometheus_wireguard_exporter](https://github.com/MindFlavor/prometheus_wireguard_exporter). No need to deal with docker containers attached to the host network, which is annoying when you're running mostly rootless containers.

    ## Simplest Usage

    If you want to dump prometheus metrics to stdout, try this:
  9. @brandon1024 brandon1024 created this gist Feb 26, 2023.
    68 changes: 68 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,68 @@
    # A Simple Solution for Wireguard Interface and Peer Metrics Exposition to Prometheus

    Here's a simple set of scripts that allow you to export wireguard tunnel statistics to a file in Prometheus text format.
    This file can be read by the [node_exporter textfile collector](https://github.com/prometheus/node_exporter#textfile-collector), for example.

    The script also supports writing individual statistics to a set of files in a particular directory. This can be
    handy if you're looking to pick apart wireguard tunnel statistics but don't fancy parsing `wg show all dump` yourself.
    More on this later.

    ## Simplest Usage

    If you want to dump prometheus metrics to stdout, try this:

    ```
    # wg show all dump | wg-dump.awk
    ```

    You'll get something like this:

    ```
    # HELP node_network_wireguard_interface Wireguard network interface information.
    # TYPE node_network_wireguard_interface gauge
    node_network_wireguard_interface{device="wg0",public_key="wDArzrW4UnZ6Zfp7/zHvGNH0wx71yhEqOXTu6Jgfbgc=",port="51820",fwmark="off"} 1.0
    # HELP node_network_wireguard_peer_handshake Latest handshake for a particular Wireguard peer.
    # TYPE node_network_wireguard_peer_handshake gauge
    node_network_wireguard_peer_handshake{device="wg0",public_key="Hav2lvmaicPSly4I25oEHcv8o4ycFNIzriADheeSFjY=",endpoint="192.168.3.8/32",persistent_keepalive="off"} 1677452335
    node_network_wireguard_peer_handshake{device="wg0",public_key="ypLMnc92ZUDNMSuEUtW0Nh5VWFooxXXWYcaZx8zLU2c=",endpoint="192.168.3.14/32",persistent_keepalive="off"} 1677208600
    node_network_wireguard_peer_handshake{device="wg0",public_key="JAJ264l63wILq02WmcWDwFuLAUPhI8XTweRc/Wgxw2M=",endpoint="192.168.3.5/32",persistent_keepalive="off"} 1677452428
    ...
    ```

    ## Exposing Metrics to Prometheus

    If you want to use this script to expose wireguard metrics to Prometheus:

    - enable the prometheus textfile collector (e.g. `--collector.textfile.directory=/var/run/prometheus`)
    - create a systemd timer or cron that runs the script on a interval

    ```
    # cp prometheus-collect-wg.timer /etc/systemd/system/
    # cp prometheus-collect-wg.service /etc/systemd/system/
    # systemctl enable prometheus-collect-wg.timer
    # systemctl enable prometheus-collect-wg.service
    # systemctl start prometheus-collect-wg.timer
    ```

    ## Reading Wireguard Tunnel Statistics

    If you set the `WG_DUMP_PATH` environment variable, `wg-dump.awk` will write each metric to a file with a
    deterministic name.

    ```
    # export WG_DUMP_PATH=/var/run/wireguard/
    # mkdir -p $WG_DUMP_PATH
    # wg show all dump | wg-dump.awk
    # ls $WG_DUMP_PATH
    wg0.interface.fwmark wg0.peer.38a040a78e8b723c43ada8b55d13542d.allowed_ips wg0.peer.5029b1a7ffe9ffcc7f783743106e3041.persistent_keepalive wg0.peer.91e923da80f9eb48773436be45184cd4.transfer_rx
    wg0.interface.listen_port wg0.peer.38a040a78e8b723c43ada8b55d13542d.endpoint wg0.peer.5029b1a7ffe9ffcc7f783743106e3041.pre_shared_key wg0.peer.91e923da80f9eb48773436be45184cd4.transfer_tx
    wg0.interface.public_key wg0.peer.38a040a78e8b723c43ada8b55d13542d.latest_handshake wg0.peer.5029b1a7ffe9ffcc7f783743106e3041.public_key wg0.peer.b072fc5cda6ed8033001db938c707071.allowed_ips
    wg0.peer.0ffebdcc608dbe8be125fbf2f31a3317.allowed_ips wg0.peer.38a040a78e8b723c43ada8b55d13542d.persistent_keepalive wg0.peer.5029b1a7ffe9ffcc7f783743106e3041.transfer_rx wg0.peer.b072fc5cda6ed8033001db938c707071.endpoint
    wg0.peer.0ffebdcc608dbe8be125fbf2f31a3317.endpoint wg0.peer.38a040a78e8b723c43ada8b55d13542d.pre_shared_key wg0.peer.5029b1a7ffe9ffcc7f783743106e3041.transfer_tx wg0.peer.b072fc5cda6ed8033001db938c707071.latest_handshake
    wg0.peer.0ffebdcc608dbe8be125fbf2f31a3317.latest_handshake wg0.peer.38a040a78e8b723c43ada8b55d13542d.public_key wg0.peer.64bb269ee450cd04d0971deb8eb0edff.allowed_ips wg0.peer.b072fc5cda6ed8033001db938c707071.persistent_keepalive
    ...
    ```

    In the example above, `wg0` is the interface name. Files prefixed with `wg0.interface` are statistics for the
    interface itself (listen port, public key, etc.). Files prefixed with `wg0.peer` are statistics for each peer.
    The hash (e.g. `0ffebdcc608dbe8be125fbf2f31a3317`) is the MD5 hash of the peer's public key.
    9 changes: 9 additions & 0 deletions prometheus-collect-wg.service
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,9 @@
    [Unit]
    Description=Export Wireguard metrics to a file for consumption by node exporter textfile collector.

    [Service]
    Type=oneshot
    ExecStart=%h/wg-dump-collector.sh

    [Install]
    WantedBy=default.target
    9 changes: 9 additions & 0 deletions prometheus-collect-wg.timer
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,9 @@
    [Unit]
    Description=Export Wireguard metrics to a file for consumption by node exporter textfile collector.

    [Timer]
    OnBootSec=30
    OnUnitActiveSec=15

    [Install]
    WantedBy=timers.target
    12 changes: 12 additions & 0 deletions wg-dump-collector.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,12 @@
    #!/bin/sh
    #
    # wg-dump-collector.sh - write prometheus metrics to a textfile directory
    #
    # This simple script writes the output from the awk script to the textfile
    # collector directory, so that it can be picked up by the node exporter.
    # This script is inteded to be run by a cron or systemd timer.

    set -e

    wg show all dump | /root/wg-dump.awk >/var/run/prometheus/wireguard.prom.$$
    mv /var/run/prometheus/wireguard.prom.$$ /var/run/prometheus/wireguard.prom
    94 changes: 94 additions & 0 deletions wg-dump.awk
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,94 @@
    #!/usr/bin/awk -f
    #
    # wg-dump - pick apart wireguard tunnel information
    #
    # Accepts input from `wg show all dump` and transform into metrics in prometheus format.
    # If the WG_DUMP_PATH is set, write metrics to disk at that path.
    #
    # Usage:
    # $ wg show all dump | wg-dump
    # $ WG_DUMP_PATH=/var/run/wireguard/ wg show all dump | wg-dump

    # Read wireguard interface information.
    BEGIN {
    out_dir = ENVIRON["WG_DUMP_PATH"]

    getline

    device_name = $1
    public_key = $3
    listen_port = $4
    fwmark = $5

    wireguard_iface["node_network_wireguard_interface{device=\"" device_name "\",public_key=\"" public_key "\",port=\"" listen_port "\",fwmark=\"" fwmark "\"}"] = "1.0"

    if ( length(out_dir) != 0 ) {
    print public_key > out_dir device_name ".interface.public_key"
    print listen_port > out_dir device_name ".interface.listen_port"
    print fwmark > out_dir device_name ".interface.fwmark"
    }
    }

    function compute_md5( data ) {
    compute_hash = "echo '" data "' | md5sum"
    compute_hash | getline
    return $1
    }

    # Read wireguard peer information.
    {
    device_name = $1
    public_key = $2
    pre_shared_key = $3
    endpoint = $4
    allowed_ips = $5
    latest_handshake = $6
    transfer_rx = $7
    transfer_tx = $8
    persistent_keepalive = $9

    hash = compute_md5(public_key)

    if ( length(out_dir) != 0 ) {
    print public_key > out_dir device_name ".peer." hash ".public_key"
    print pre_shared_key > out_dir device_name ".peer." hash ".pre_shared_key"
    print endpoint > out_dir device_name ".peer." hash ".endpoint"
    print allowed_ips > out_dir device_name ".peer." hash ".allowed_ips"
    print latest_handshake > out_dir device_name ".peer." hash ".latest_handshake"
    print transfer_rx > out_dir device_name ".peer." hash ".transfer_rx"
    print transfer_tx > out_dir device_name ".peer." hash ".transfer_tx"
    print persistent_keepalive > out_dir device_name ".peer." hash ".persistent_keepalive"
    }

    wireguard_peer_handshake["node_network_wireguard_peer_handshake{device=\"" device_name "\",public_key=\"" public_key "\",endpoint=\"" allowed_ips "\",persistent_keepalive=\"" persistent_keepalive "\"}"] = latest_handshake
    wireguard_peer_tx["node_network_wireguard_peer_tx{device=\"" device_name "\",public_key=\"" public_key "\",endpoint=\"" allowed_ips "\",persistent_keepalive=\"" persistent_keepalive "\"}"] = transfer_tx
    wireguard_peer_rx["node_network_wireguard_peer_rx{device=\"" device_name "\",public_key=\"" public_key "\",endpoint=\"" allowed_ips "\",persistent_keepalive=\"" persistent_keepalive "\"}"] = transfer_rx
    }

    END {
    # Write Prometheus-style metrics to stdout.

    print "# HELP node_network_wireguard_interface Wireguard network interface information."
    print "# TYPE node_network_wireguard_interface gauge"
    for ( metric in wireguard_iface ) {
    print metric " " wireguard_iface[metric]
    }

    print "# HELP node_network_wireguard_peer_handshake Latest handshake for a particular Wireguard peer."
    print "# TYPE node_network_wireguard_peer_handshake gauge"
    for ( metric in wireguard_peer_handshake ) {
    print metric " " wireguard_peer_handshake[metric]
    }

    print "# HELP node_network_wireguard_peer_tx Transmit statistics for a particular Wireguard peer."
    print "# TYPE node_network_wireguard_peer_tx counter"
    for ( metric in wireguard_peer_tx ) {
    print metric " " wireguard_peer_tx[metric]
    }

    print "# HELP node_network_wireguard_peer_rx Transmit statistics for a particular Wireguard peer."
    print "# TYPE node_network_wireguard_peer_rx counter"
    for ( metric in wireguard_peer_rx ) {
    print metric " " wireguard_peer_rx[metric]
    }
    }