Monitor OpenVPN Connections with Prometheus and Grafana

|
Last Updated:
|
|

Welcome to our tutorial on how to monitor OpenVPN connections with Prometheus and Grafana. Grafana is a data visualization and monitoring tool and supports time series datastores such as Graphite, InfluxDB, Prometheus, Elasticsearch. Prometheus on the other hand is an open-source systems and service monitoring tool. It collects metrics from configured targets via HTTP calls at given intervals, evaluates rule expressions, displays the results, and can trigger alerts if some conditions are met. Grafana can be used to achieve better visualization of the metrics collected by the Prometheus. In this setup, however, we will learn how to collect OpenVPN connection metrics using Prometheus and visualize then on Grafana.

Monitor OpenVPN Connections with Prometheus and Grafana

Monitor OpenVPN Connections with Prometheus and Grafana

In this tutorial, we are using an Ubuntu 20.04 systems for demo labs. Feel free to use other OSes.

To monitor OpenVPN connections with Prometheus and Grafana, we will install and setup each component of the monitoring separately.

Install Prometheus on Ubuntu 20.04

We have covered the installation of Prometheus on Ubuntu 20.04 in our previous guide. Follow the link below to install and setup Prometheus on Ubuntu 20.04;

Install and Setup Prometheus on Ubuntu 20.04

You can as well use CentOS 8 instead.

Install and Configure Prometheus on CentOS 8 

Install Grafana on Ubuntu 20.04

Follow the link below to install Grafana on Ubuntu 20.04;

Installing Grafana on Ubuntu 20.04

Want to use CentOS instead? Follow the link below to setup Grafana on CentOS 8.

Install Latest Grafana on CentOS 8

Integrating Grafana with Prometheus

Once both Grafana and Prometheus are in place, you can then configure Grafana to fetch the metrics from Prometheus.

systemctl status grafana-server
● grafana-server.service - Grafana instance
     Loaded: loaded (/lib/systemd/system/grafana-server.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2020-09-21 17:01:07 UTC; 5min ago
       Docs: http://docs.grafana.org
   Main PID: 14610 (grafana-server)
      Tasks: 8 (limit: 2282)
     Memory: 18.0M
     CGroup: /system.slice/grafana-server.service
             └─14610 /usr/sbin/grafana-server --config=/etc/grafana/grafana.ini --pidfile=/var/run/grafana/grafana-server.pid --packaging=deb cfg:default.paths.logs=/var/l>

Sep 21 17:02:16 ubuntu20 grafana-server[14610]: t=2020-09-21T17:02:16+0000 lvl=info msg="Executing migration" logger=migrator id="create cache_data table"
...
systemctl status prometheus
● prometheus.service - Prometheus Time Series Collection and Processing Server
     Loaded: loaded (/etc/systemd/system/prometheus.service; enabled; vendor preset: enabled)
     Active: active (running) since Mon 2020-09-21 16:35:03 UTC; 32min ago
   Main PID: 2464 (prometheus)
      Tasks: 8 (limit: 2282)
     Memory: 27.6M
     CGroup: /system.slice/prometheus.service
             └─2464 /usr/local/bin/prometheus --config.file /etc/prometheus/prometheus.yml --storage.tsdb.path /var/lib/prometheus/ --web.console.templates=/etc/prometheus>

Sep 21 16:35:03 ubuntu20 prometheus[2464]: level=info ts=2020-09-21T16:35:03.205Z caller=head.go:644 component=tsdb msg="Replaying on-disk memory
...
ss -altnp | grep -E ":3000|:9090"
LISTEN    0         4096                     *:9090                   *:*        users:(("prometheus",pid=2464,fd=10))                                          
LISTEN    0         4096                     *:3000                   *:*        users:(("grafana-server",pid=14610,fd=8))

To integrate Grafana with Prometheus, you need to add Prometheus data source to Grafana.

This can be done by logging into Grafana web interface and navigating to Configuration (gear like icon) > Datasources > Add data source.

From the data source types, select Prometheus. This opens up Prometheus datasource configuration page.

Enter the Prometheus server URL. If you are running Grafana and Prometheus on the same server, use the address http://localhost:9090 otherwise, use the address http://<prometheus-server-IP>:9090.

Click Save & Test.

Monitor OpenVPN Connections with Prometheus and Grafana

Install OpenVPN Prometheus Node Exporter on OpenVPN Server

Next, you need to install OpenVPN node exporter on the OpenVPN Server.

We have already covered how to install and configure OpenVPN and OpenVPN clients;

Install and Setup OpenVPN Server on Ubuntu 20.04

Install and Configure OpenVPN Client on CentOS 8/Ubuntu 18.04

Setup OpenVPN Server on CentOS 8

In this setup, our OpenVPN server is running on a CentOS 8 system. Hence, we need to install the OpenVPN node exporter on CentOS 8 OpenVPN server. OpenVPN Exporter for Prometheus is an open source project hosted on Github and written by Kumina. All credit goes back to the creator of this exporter.

You can run the exporter as docker container or a standalone executable binary.

In this setup, we run it as standalone executable binary on a Centos 8 server hosting a vpn server.

Install Go on CentOS 8

Download Go tarball for installation from Go download’s page.

wget https://golang.org/dl/go1.15.2.linux-amd64.tar.gz

Extract it into /usr/local, creating a Go tree in /usr/local/go.

tar -C /usr/local -xzf go1.15.2.linux-amd64.tar.gz

Add /usr/local/go/bin to the PATH environment variable.

vim .bashrc_profile
...
# User specific environment and startup programs

PATH=$PATH:$HOME/bin:/usr/local/go/bin

export PATH

Source the file to effect the changes.

source .bash_profile

Go should now be on your PATH.

Download OpenVPN Node Exporter

Download OpenVPN node exporter from the Github releases page.

wget https://github.com/kumina/openvpn_exporter/archive/v0.3.0.tar.gz

Extract it;

tar xzf v0.3.0.tar.gz

Next, build the OpenVPN node exporter;

cd openvpn_exporter-0.3.0/

Set the path to OpenVPN server status log file;

vim main.og
...

func main() {
        var (
                listenAddress      = flag.String("web.listen-address", ":9176", "Address to listen on for web interface and telemetry.")
                metricsPath        = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
                // openvpnStatusPaths = flag.String("openvpn.status_paths", "examples/client.status,examples/server2.status,examples/server3.status", "Paths at which OpenVPN places its status files.")
                openvpnStatusPaths = flag.String("openvpn.status_paths", "/var/log/openvpn/openvpn-status.log", "Paths at which OpenVPN places its status files.")
...

Save and exit the file.

Build the exporter now;

go build -o openvpn_exporter main.go

This will create Prometheus OpenVPN node exporter binary, openvpn_eporter, in the current directory. Copy the binary to /usr/local/bin binary path.

cp openvpn_eporter /usr/local/bin/

By default, the exporter listens on TCP port 9176, hence you need to open this port on firewall.

firewall-cmd --add-port=9176/tcp --permanent
firewall-cmd --reload

Run the exporter in standalone mode to test it;

openvpn_exporter
2020/09/21 23:37:50 Starting OpenVPN Exporter
2020/09/21 23:37:50 Listen address: :9176
2020/09/21 23:37:50 Metrics path: /metrics
2020/09/21 23:37:50 openvpn.status_path: /var/log/openvpn/openvpn-status.log
2020/09/21 23:37:50 Ignore Individuals: false

Configure Prometheus to scrape OpenVPN Node exporter metrics;

vim /etc/prometheus/prometheus.yml
...
  # The job name is added as a label `job=` to any timeseries scraped from this config.
  - job_name: 'prometheus'

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    static_configs:
    - targets: ['localhost:9090']
  ## Add OpenVPN Node Exporter
  - job_name: 'openvpn-metrics'
    scrape_interval: 5s
    static_configs:
            - targets: ['192.168.60.6:9176']

Restart Prometheus;

systemctl restart prometheus

Check the Prometheus Target, http://server-IP:9090/targets.

Monitor OpenVPN Connections with Prometheus and Grafana

Monitor OpenVPN Connections with Prometheus and Grafana

Create OpenVPN Node Exporter Visualization on Grafana

You can create your own visualization dashboards or utilize the community created dashboards. For example, we use Grafana OpenVPN node exporter visualization dashboard from the Grafana community.

To import this dashboard, navigate to http://grafana-server-IP:3000/dashboard/import.

Paste the dashboard ID or JSON file, load and import it.

For instance, we took the the community dashboard from https://grafana.com/grafana/dashboards/10562 and modified it to fit our needs.

After modifying the above dashboard, this is how our simple OpenVPN connections dashabaord looks like;

Monitor OpenVPN Connections with Prometheus and Grafana

The JSON file for the above dashboard is provided below (modified version of dashboard 10562, all credit goes back to original author);

{
  "annotations": {
    "list": [
      {
        "builtIn": 1,
        "datasource": "-- Grafana --",
        "enable": true,
        "hide": true,
        "iconColor": "rgba(0, 211, 255, 1)",
        "name": "Annotations & Alerts",
        "type": "dashboard"
      }
    ]
  },
  "description": "OpenVPN Server status using Prometheus and OpenVPN exporter ",
  "editable": true,
  "gnetId": 10562,
  "graphTooltip": 0,
  "id": 3,
  "links": [
    {
      "icon": "external link",
      "tags": [
        "openvpn"
      ],
      "targetBlank": true,
      "type": "dashboards"
    }
  ],
  "panels": [
    {
      "cacheTimeout": null,
      "colorBackground": false,
      "colorPostfix": false,
      "colorPrefix": false,
      "colorValue": true,
      "colors": [
        "#d44a3a",
        "rgba(237, 129, 40, 0.89)",
        "#299c46"
      ],
      "datasource": "Prometheus",
      "fieldConfig": {
        "defaults": {
          "custom": {}
        },
        "overrides": []
      },
      "format": "none",
      "gauge": {
        "maxValue": 256,
        "minValue": 0,
        "show": false,
        "thresholdLabels": false,
        "thresholdMarkers": true
      },
      "gridPos": {
        "h": 2,
        "w": 3,
        "x": 0,
        "y": 0
      },
      "id": 2,
      "interval": null,
      "links": [],
      "mappingType": 1,
      "mappingTypes": [
        {
          "name": "value to text",
          "value": 1
        },
        {
          "name": "range to text",
          "value": 2
        }
      ],
      "maxDataPoints": 100,
      "nullPointMode": "connected",
      "nullText": null,
      "pluginVersion": "6.2.0-beta2",
      "postfix": " Clients",
      "postfixFontSize": "50%",
      "prefix": "",
      "prefixFontSize": "50%",
      "rangeMaps": [
        {
          "from": "null",
          "text": "N/A",
          "to": "null"
        }
      ],
      "sparkline": {
        "fillColor": "rgba(31, 118, 189, 0.18)",
        "full": true,
        "lineColor": "#5794F2",
        "show": true
      },
      "tableColumn": "openvpn_server_connected_clients{instance=\"192.168.60.6:9176\", job=\"openvpn-metrics\", status_path=\"/var/log/openvpn/openvpn-status.log\"}",
      "targets": [
        {
          "expr": "openvpn_server_connected_clients",
          "format": "time_series",
          "instant": true,
          "interval": "",
          "intervalFactor": 1,
          "legendFormat": "",
          "refId": "A"
        }
      ],
      "thresholds": "0, 30",
      "timeFrom": null,
      "timeShift": null,
      "title": "",
      "type": "singlestat",
      "valueFontSize": "150%",
      "valueMaps": [
        {
          "op": "=",
          "text": "N/A",
          "value": "null"
        },
        {
          "op": "=",
          "text": "No",
          "value": "0"
        }
      ],
      "valueName": "current"
    },
    {
      "cacheTimeout": null,
      "colorBackground": false,
      "colorPostfix": false,
      "colorPrefix": false,
      "colorValue": true,
      "colors": [
        "#FF9830",
        "#5794F2",
        "#5794F2"
      ],
      "datasource": "Prometheus",
      "fieldConfig": {
        "defaults": {
          "custom": {}
        },
        "overrides": []
      },
      "format": "none",
      "gauge": {
        "maxValue": 256,
        "minValue": 0,
        "show": false,
        "thresholdLabels": false,
        "thresholdMarkers": true
      },
      "gridPos": {
        "h": 2,
        "w": 3,
        "x": 3,
        "y": 0
      },
      "id": 16,
      "interval": null,
      "links": [],
      "mappingType": 1,
      "mappingTypes": [
        {
          "name": "value to text",
          "value": 1
        },
        {
          "name": "range to text",
          "value": 2
        }
      ],
      "maxDataPoints": 100,
      "nullPointMode": "connected",
      "nullText": null,
      "pluginVersion": "6.2.0-beta2",
      "postfix": " Users",
      "postfixFontSize": "50%",
      "prefix": "",
      "prefixFontSize": "50%",
      "rangeMaps": [
        {
          "from": "null",
          "text": "N/A",
          "to": "null"
        }
      ],
      "sparkline": {
        "fillColor": "rgba(31, 118, 189, 0.18)",
        "full": true,
        "lineColor": "#5794F2",
        "show": true
      },
      "tableColumn": "",
      "targets": [
        {
          "expr": "count(openvpn_server_client_received_bytes_total{common_name=~\"[a-zA-Z0-9]+\"}) or absent(count(openvpn_server_client_received_bytes_total{common_name=~\"[a-zA-Z0-9]+\"}))-1",
          "format": "time_series",
          "instant": true,
          "interval": "",
          "intervalFactor": 1,
          "legendFormat": "",
          "refId": "A"
        }
      ],
      "thresholds": "1, 2",
      "timeFrom": null,
      "timeShift": null,
      "title": "",
      "type": "singlestat",
      "valueFontSize": "150%",
      "valueMaps": [],
      "valueName": "current"
    },
    {
      "cacheTimeout": null,
      "colorBackground": false,
      "colorPrefix": false,
      "colorValue": true,
      "colors": [
        "#d44a3a",
        "rgba(237, 129, 40, 0.89)",
        "#299c46"
      ],
      "datasource": "Prometheus",
      "decimals": null,
      "fieldConfig": {
        "defaults": {
          "custom": {}
        },
        "overrides": []
      },
      "format": "none",
      "gauge": {
        "maxValue": 100,
        "minValue": 0,
        "show": false,
        "thresholdLabels": false,
        "thresholdMarkers": true
      },
      "gridPos": {
        "h": 2,
        "w": 7,
        "x": 6,
        "y": 0
      },
      "id": 12,
      "interval": null,
      "links": [],
      "mappingType": 1,
      "mappingTypes": [
        {
          "name": "value to text",
          "value": 1
        },
        {
          "name": "range to text",
          "value": 2
        }
      ],
      "maxDataPoints": 100,
      "nullPointMode": "connected",
      "nullText": null,
      "postfix": "",
      "postfixFontSize": "50%",
      "prefix": "Status:",
      "prefixFontSize": "150%",
      "rangeMaps": [
        {
          "from": "null",
          "text": "N/A",
          "to": "null"
        }
      ],
      "sparkline": {
        "fillColor": "rgba(31, 118, 189, 0.18)",
        "full": false,
        "lineColor": "rgb(31, 120, 193)",
        "show": false
      },
      "tableColumn": "openvpn_up{instance=\"192.168.60.6:9176\", job=\"openvpn-metrics\", status_path=\"/var/log/openvpn/openvpn-status.log\"}",
      "targets": [
        {
          "expr": "openvpn_up{job=\"openvpn-metrics\"}",
          "format": "time_series",
          "instant": true,
          "intervalFactor": 1,
          "legendFormat": "",
          "refId": "A"
        }
      ],
      "thresholds": "1, 1",
      "timeFrom": null,
      "timeShift": null,
      "title": "",
      "type": "singlestat",
      "valueFontSize": "150%",
      "valueMaps": [
        {
          "op": "=",
          "text": "N/A",
          "value": "null"
        },
        {
          "op": "=",
          "text": "UP",
          "value": "1"
        },
        {
          "op": "=",
          "text": "DOWN",
          "value": "0"
        }
      ],
      "valueName": "current"
    },
    {
      "aliasColors": {},
      "bars": false,
      "cacheTimeout": null,
      "dashLength": 10,
      "dashes": false,
      "datasource": "Prometheus",
      "decimals": 0,
      "fieldConfig": {
        "defaults": {
          "custom": {}
        },
        "overrides": []
      },
      "fill": 1,
      "fillGradient": 0,
      "gridPos": {
        "h": 7,
        "w": 11,
        "x": 13,
        "y": 0
      },
      "hiddenSeries": false,
      "id": 14,
      "legend": {
        "avg": false,
        "current": false,
        "max": false,
        "min": false,
        "show": false,
        "total": false,
        "values": false
      },
      "lines": true,
      "linewidth": 1,
      "links": [],
      "nullPointMode": "null as zero",
      "percentage": false,
      "pluginVersion": "7.1.5",
      "pointradius": 2,
      "points": false,
      "renderer": "flot",
      "seriesOverrides": [],
      "spaceLength": 10,
      "stack": false,
      "steppedLine": false,
      "targets": [
        {
          "expr": "sum(openvpn_server_connected_clients)",
          "format": "time_series",
          "instant": false,
          "interval": "",
          "intervalFactor": 1,
          "legendFormat": "Connected OpenVPN Clients",
          "refId": "A"
        }
      ],
      "thresholds": [],
      "timeFrom": null,
      "timeRegions": [],
      "timeShift": null,
      "title": "Clients Connected",
      "tooltip": {
        "shared": true,
        "sort": 0,
        "value_type": "individual"
      },
      "type": "graph",
      "xaxis": {
        "buckets": null,
        "mode": "time",
        "name": null,
        "show": true,
        "values": []
      },
      "yaxes": [
        {
          "decimals": 0,
          "format": "short",
          "label": "Clients",
          "logBase": 1,
          "max": null,
          "min": "0",
          "show": true
        },
        {
          "format": "short",
          "label": null,
          "logBase": 1,
          "max": null,
          "min": null,
          "show": true
        }
      ],
      "yaxis": {
        "align": false,
        "alignLevel": null
      }
    },
    {
      "cacheTimeout": null,
      "colorBackground": false,
      "colorPrefix": false,
      "colorValue": true,
      "colors": [
        "#5794F2",
        "#5794F2",
        "#5794F2"
      ],
      "datasource": "Prometheus",
      "decimals": 2,
      "fieldConfig": {
        "defaults": {
          "custom": {}
        },
        "overrides": []
      },
      "format": "bytes",
      "gauge": {
        "maxValue": 100,
        "minValue": 0,
        "show": false,
        "thresholdLabels": false,
        "thresholdMarkers": true
      },
      "gridPos": {
        "h": 2,
        "w": 6,
        "x": 0,
        "y": 2
      },
      "hideTimeOverride": true,
      "id": 19,
      "interval": null,
      "links": [],
      "mappingType": 1,
      "mappingTypes": [
        {
          "name": "value to text",
          "value": 1
        },
        {
          "name": "range to text",
          "value": 2
        }
      ],
      "maxDataPoints": 100,
      "nullPointMode": "connected",
      "nullText": null,
      "postfix": " sent today",
      "postfixFontSize": "50%",
      "prefix": "",
      "prefixFontSize": "50%",
      "rangeMaps": [
        {
          "from": "null",
          "text": "N/A",
          "to": "null"
        }
      ],
      "sparkline": {
        "fillColor": "rgba(31, 118, 189, 0.18)",
        "full": false,
        "lineColor": "rgb(31, 120, 193)",
        "show": false
      },
      "tableColumn": "",
      "targets": [
        {
          "expr": "sum(increase(openvpn_server_client_sent_bytes_total[1d]))",
          "format": "time_series",
          "instant": true,
          "intervalFactor": 1,
          "refId": "A"
        }
      ],
      "thresholds": "1, 2",
      "timeFrom": "24h",
      "timeShift": null,
      "title": "",
      "type": "singlestat",
      "valueFontSize": "80%",
      "valueMaps": [
        {
          "op": "=",
          "text": "N/A",
          "value": "null"
        }
      ],
      "valueName": "current"
    },
    {
      "cacheTimeout": null,
      "colorBackground": false,
      "colorPostfix": false,
      "colorPrefix": false,
      "colorValue": true,
      "colors": [
        "#FF9830",
        "#5794F2",
        "#5794F2"
      ],
      "datasource": "Prometheus",
      "fieldConfig": {
        "defaults": {
          "custom": {}
        },
        "overrides": []
      },
      "format": "none",
      "gauge": {
        "maxValue": 256,
        "minValue": 0,
        "show": false,
        "thresholdLabels": false,
        "thresholdMarkers": true
      },
      "gridPos": {
        "h": 5,
        "w": 3,
        "x": 6,
        "y": 2
      },
      "id": 15,
      "interval": null,
      "links": [],
      "mappingType": 1,
      "mappingTypes": [
        {
          "name": "value to text",
          "value": 1
        },
        {
          "name": "range to text",
          "value": 2
        }
      ],
      "maxDataPoints": 100,
      "nullPointMode": "connected",
      "nullText": null,
      "pluginVersion": "6.2.0-beta2",
      "postfix": " Servers",
      "postfixFontSize": "50%",
      "prefix": "",
      "prefixFontSize": "50%",
      "rangeMaps": [
        {
          "from": "null",
          "text": "N/A",
          "to": "null"
        }
      ],
      "sparkline": {
        "fillColor": "rgba(31, 118, 189, 0.18)",
        "full": true,
        "lineColor": "#5794F2",
        "show": true
      },
      "tableColumn": "",
      "targets": [
        {
          "expr": "count(openvpn_server_connected_clients{instance=~\"[0-9].+:[0-9]{0,}\"}) or absent(count(openvpn_server_connected_clients{instance=~\"[0-9].+:[0-9]{0,}\"}))-1",
          "format": "time_series",
          "instant": true,
          "interval": "",
          "intervalFactor": 1,
          "legendFormat": "",
          "refId": "A"
        }
      ],
      "thresholds": "1, 2",
      "timeFrom": null,
      "timeShift": null,
      "title": "",
      "type": "singlestat",
      "valueFontSize": "150%",
      "valueMaps": [
        {
          "op": "=",
          "text": "0",
          "value": "null"
        },
        {
          "op": "=",
          "text": "0",
          "value": "0"
        }
      ],
      "valueName": "current"
    },
    {
      "cacheTimeout": null,
      "colorBackground": false,
      "colorPostfix": false,
      "colorValue": true,
      "colors": [
        "#299c46",
        "rgba(237, 129, 40, 0.89)",
        "#d44a3a"
      ],
      "datasource": "Prometheus",
      "decimals": 0,
      "fieldConfig": {
        "defaults": {
          "custom": {}
        },
        "overrides": []
      },
      "format": "none",
      "gauge": {
        "maxValue": 100,
        "minValue": 0,
        "show": false,
        "thresholdLabels": false,
        "thresholdMarkers": true
      },
      "gridPos": {
        "h": 5,
        "w": 4,
        "x": 9,
        "y": 2
      },
      "id": 10,
      "interval": null,
      "links": [],
      "mappingType": 1,
      "mappingTypes": [
        {
          "name": "value to text",
          "value": 1
        },
        {
          "name": "range to text",
          "value": 2
        }
      ],
      "maxDataPoints": 100,
      "nullPointMode": "connected",
      "nullText": null,
      "postfix": "s since status update",
      "postfixFontSize": "50%",
      "prefix": "",
      "prefixFontSize": "30%",
      "rangeMaps": [
        {
          "from": "null",
          "text": "N/A",
          "to": "null"
        }
      ],
      "sparkline": {
        "fillColor": "rgba(31, 118, 189, 0.18)",
        "full": false,
        "lineColor": "rgb(31, 120, 193)",
        "show": false
      },
      "tableColumn": "{instance=\"192.168.60.6:9176\", job=\"openvpn-metrics\", status_path=\"/var/log/openvpn/openvpn-status.log\"}",
      "targets": [
        {
          "expr": "time()-openvpn_status_update_time_seconds{job=\"openvpn-metrics\"}",
          "format": "time_series",
          "instant": true,
          "intervalFactor": 1,
          "refId": "A"
        }
      ],
      "thresholds": "120, 180",
      "timeFrom": null,
      "timeShift": null,
      "title": "",
      "type": "singlestat",
      "valueFontSize": "150%",
      "valueMaps": [
        {
          "op": "=",
          "text": "N/A",
          "value": "null"
        }
      ],
      "valueName": "current"
    },
    {
      "cacheTimeout": null,
      "colorBackground": false,
      "colorValue": true,
      "colors": [
        "#5794F2",
        "#5794F2",
        "#5794F2"
      ],
      "datasource": "Prometheus",
      "decimals": 2,
      "fieldConfig": {
        "defaults": {
          "custom": {}
        },
        "overrides": []
      },
      "format": "bytes",
      "gauge": {
        "maxValue": 100,
        "minValue": 0,
        "show": false,
        "thresholdLabels": false,
        "thresholdMarkers": true
      },
      "gridPos": {
        "h": 3,
        "w": 6,
        "x": 0,
        "y": 4
      },
      "hideTimeOverride": true,
      "id": 18,
      "interval": null,
      "links": [],
      "mappingType": 1,
      "mappingTypes": [
        {
          "name": "value to text",
          "value": 1
        },
        {
          "name": "range to text",
          "value": 2
        }
      ],
      "maxDataPoints": 100,
      "nullPointMode": "connected",
      "nullText": null,
      "postfix": " received today",
      "postfixFontSize": "50%",
      "prefix": "",
      "prefixFontSize": "50%",
      "rangeMaps": [
        {
          "from": "null",
          "text": "N/A",
          "to": "null"
        }
      ],
      "sparkline": {
        "fillColor": "rgba(31, 118, 189, 0.18)",
        "full": false,
        "lineColor": "rgb(31, 120, 193)",
        "show": false
      },
      "tableColumn": "",
      "targets": [
        {
          "expr": "sum(increase(openvpn_server_client_received_bytes_total[1d]))",
          "format": "time_series",
          "instant": true,
          "intervalFactor": 1,
          "refId": "A"
        }
      ],
      "thresholds": "1, 2",
      "timeFrom": "24h",
      "timeShift": null,
      "title": "",
      "type": "singlestat",
      "valueFontSize": "80%",
      "valueMaps": [
        {
          "op": "=",
          "text": "N/A",
          "value": "null"
        }
      ],
      "valueName": "current"
    },
    {
      "columns": [],
      "datasource": "Prometheus",
      "fieldConfig": {
        "defaults": {
          "custom": {}
        },
        "overrides": []
      },
      "fontSize": "100%",
      "gridPos": {
        "h": 10,
        "w": 24,
        "x": 0,
        "y": 7
      },
      "id": 4,
      "links": [],
      "pageSize": 100,
      "scroll": true,
      "showHeader": true,
      "sort": {
        "col": 4,
        "desc": false
      },
      "styles": [
        {
          "alias": "Time",
          "align": "auto",
          "dateFormat": "YYYY-MM-DD HH:mm:ss",
          "pattern": "Time",
          "type": "hidden"
        },
        {
          "alias": "Username",
          "align": "auto",
          "colorMode": null,
          "colors": [
            "rgba(245, 54, 54, 0.9)",
            "rgba(237, 129, 40, 0.89)",
            "rgba(50, 172, 45, 0.97)"
          ],
          "decimals": 2,
          "pattern": "common_name",
          "thresholds": [],
          "type": "string",
          "unit": "short"
        },
        {
          "alias": "Login Time",
          "align": "auto",
          "colorMode": null,
          "colors": [
            "rgba(245, 54, 54, 0.9)",
            "rgba(237, 129, 40, 0.89)",
            "rgba(50, 172, 45, 0.97)"
          ],
          "dateFormat": "YYYY-MM-DD HH:mm:ss",
          "decimals": null,
          "mappingType": 1,
          "pattern": "connection_time",
          "thresholds": [],
          "type": "date",
          "unit": "dateTimeFromNow"
        },
        {
          "alias": "VPN Client Address",
          "align": "auto",
          "colorMode": null,
          "colors": [
            "rgba(245, 54, 54, 0.9)",
            "rgba(237, 129, 40, 0.89)",
            "rgba(50, 172, 45, 0.97)"
          ],
          "dateFormat": "YYYY-MM-DD HH:mm:ss",
          "decimals": 2,
          "mappingType": 1,
          "pattern": "virtual_address",
          "thresholds": [],
          "type": "string",
          "unit": "short"
        },
        {
          "alias": "",
          "align": "auto",
          "colorMode": null,
          "colors": [
            "rgba(245, 54, 54, 0.9)",
            "rgba(237, 129, 40, 0.89)",
            "rgba(50, 172, 45, 0.97)"
          ],
          "dateFormat": "YYYY-MM-DD HH:mm:ss",
          "decimals": 2,
          "mappingType": 1,
          "pattern": "Value",
          "thresholds": [],
          "type": "hidden",
          "unit": "short"
        },
        {
          "alias": "Rx",
          "align": "auto",
          "colorMode": null,
          "colors": [
            "rgba(245, 54, 54, 0.9)",
            "rgba(237, 129, 40, 0.89)",
            "rgba(50, 172, 45, 0.97)"
          ],
          "dateFormat": "YYYY-MM-DD HH:mm:ss",
          "decimals": 0,
          "mappingType": 1,
          "pattern": "Value #A",
          "thresholds": [],
          "type": "number",
          "unit": "bytes"
        },
        {
          "alias": "Tx",
          "align": "auto",
          "colorMode": null,
          "colors": [
            "rgba(245, 54, 54, 0.9)",
            "rgba(237, 129, 40, 0.89)",
            "rgba(50, 172, 45, 0.97)"
          ],
          "dateFormat": "YYYY-MM-DD HH:mm:ss",
          "decimals": 0,
          "mappingType": 1,
          "pattern": "Value #B",
          "thresholds": [],
          "type": "number",
          "unit": "bytes"
        },
        {
          "alias": "Real Client Address",
          "align": "auto",
          "colorMode": null,
          "colors": [
            "rgba(245, 54, 54, 0.9)",
            "rgba(237, 129, 40, 0.89)",
            "rgba(50, 172, 45, 0.97)"
          ],
          "dateFormat": "YYYY-MM-DD HH:mm:ss",
          "decimals": 2,
          "mappingType": 1,
          "pattern": "real_address",
          "thresholds": [],
          "type": "string",
          "unit": "short"
        }
      ],
      "targets": [
        {
          "expr": "sort_desc(sum(label_replace(openvpn_server_client_received_bytes_total{common_name!=\"UNDEF\"}, \"connection_time\", \"${1}000\", \"connection_time\", \"(.*)\"))by(common_name, connection_time, virtual_address, real_address))",
          "format": "table",
          "instant": true,
          "interval": "",
          "intervalFactor": 1,
          "legendFormat": "",
          "refId": "A"
        },
        {
          "expr": "sum(label_replace(openvpn_server_client_sent_bytes_total{common_name!=\"UNDEF\"}, \"connection_time\", \"${1}000\", \"connection_time\", \"(.*)\"))by(common_name, connection_time, virtual_address, real_address)",
          "format": "table",
          "instant": true,
          "interval": "",
          "intervalFactor": 1,
          "legendFormat": "",
          "refId": "B"
        }
      ],
      "timeFrom": null,
      "timeShift": null,
      "title": "Current OpenVPN Clients",
      "transform": "table",
      "type": "table-old"
    }
  ],
  "refresh": false,
  "schemaVersion": 26,
  "style": "dark",
  "tags": [
    "openvpn"
  ],
  "templating": {
    "list": []
  },
  "time": {
    "from": "now-6h",
    "to": "now"
  },
  "timepicker": {
    "refresh_intervals": [
      "5s",
      "10s",
      "30s",
      "1m",
      "5m",
      "15m",
      "30m",
      "1h",
      "2h",
      "1d"
    ],
    "time_options": [
      "5m",
      "15m",
      "1h",
      "6h",
      "12h",
      "24h",
      "2d",
      "7d",
      "30d"
    ]
  },
  "timezone": "",
  "title": "1  OpenVPN Server",
  "uid": "_1DM17HWk",
  "version": 4
}

The dashboard requires pie-chart plugin.

Install Grafana Pie Chart Plugin by executing the command below;

grafana-cli plugins install grafana-piechart-panel

Running OpenVPN Node Exporter as a Service

Create a systemd service for the OpenVPN Prometheus node exporter.

vim /etc/systemd/system/openvpn_exporter.service
[Unit]
Description=Prometheus OpenVPN Node Exporter
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/openvpn_exporter

[Install]
WantedBy=multi-user.target

Reload the systemd manager configuration.

systemctl daemon-reload

Start and enable Node Exporter to run on system boot.

systemctl enable --now openvpn_exporter.service

Check the status;

systemctl status openvpn_exporter
● openvpn_exporter.service - Prometheus OpenVPN Node Exporter
   Loaded: loaded (/etc/systemd/system/openvpn_exporter.service; enabled; vendor preset: disabled)
   Active: active (running) since Tue 2020-09-22 22:56:01 EAT; 13s ago
 Main PID: 5391 (openvpn_exporte)
    Tasks: 4 (limit: 23968)
   Memory: 9.1M
   CGroup: /system.slice/openvpn_exporter.service
           └─5391 /usr/local/bin/openvpn_exporter

Sep 22 22:56:01 centos8.kifarunix-demo.com systemd[1]: Stopped Prometheus OpenVPN Node Exporter.
Sep 22 22:56:01 centos8.kifarunix-demo.com systemd[1]: Started Prometheus OpenVPN Node Exporter.
Sep 22 22:56:01 centos8.kifarunix-demo.com openvpn_exporter[5391]: 2020/09/22 22:56:01 Starting OpenVPN Exporter
Sep 22 22:56:01 centos8.kifarunix-demo.com openvpn_exporter[5391]: 2020/09/22 22:56:01 Listen address: :9176
Sep 22 22:56:01 centos8.kifarunix-demo.com openvpn_exporter[5391]: 2020/09/22 22:56:01 Metrics path: /metrics
Sep 22 22:56:01 centos8.kifarunix-demo.com openvpn_exporter[5391]: 2020/09/22 22:56:01 openvpn.status_path: /var/log/openvpn/openvpn-status.log
Sep 22 22:56:01 centos8.kifarunix-demo.com openvpn_exporter[5391]: 2020/09/22 22:56:01 Ignore Individuals: false

Your OpenVPN connection status should be scrapped automatically.

That marks the end of our guide on how to monitor OpenVPN Connections with Prometheus and Grafana.

Other Related Tutorials

Install and Configure Prometheus on CentOS 8

Install and Configure Prometheus on Fedora 29/Fedora 28

Install and Configure Prometheus on Debian 9

Monitor Squid logs with Grafana and Graylog

Install and Setup TIG Stack on Fedora 30

SUPPORT US VIA A VIRTUAL CUP OF COFFEE

We're passionate about sharing our knowledge and experiences with you through our blog. If you appreciate our efforts, consider buying us a virtual coffee. Your support keeps us motivated and enables us to continually improve, ensuring that we can provide you with the best content possible. Thank you for being a coffee-fueled champion of our work!

Photo of author
koromicha
I am the Co-founder of Kifarunix.com, Linux and the whole FOSS enthusiast, Linux System Admin and a Blue Teamer who loves to share technological tips and hacks with others as a way of sharing knowledge as: "In vain have you acquired knowledge if you have not imparted it to others".

5 thoughts on “Monitor OpenVPN Connections with Prometheus and Grafana”

  1. Hi
    Thanks for sharing this tutorial, I would like to know how is the init of openvpn @ server on your server?
    I configured the openvpn exporter, but it doesn’t export any metrics, could you tell me if you have to change anything at the server start?

    Reply
  2. Hello,

    thanks for sharing this tutorial. Currently iam facing one issue, can someone tell me what I did wrong ?

    this is my error launching the exporter 2021/08/20 12:03:31 Failed to scrape showq socket: unexpected file contents: “OpenVPN CLIENT LIS”

    Reply
  3. root@ip-172-27-184-101:~# systemctl status openvpn_exporter
    ● openvpn_exporter.service – Prometheus OpenVPN Node Exporter
    Loaded: loaded (/etc/systemd/system/openvpn_exporter.service; enabled; vendor preset: enabled)
    Active: failed (Result: exit-code) since Mon 2022-11-07 11:36:41 UTC; 1min 39s ago
    Main PID: 21044 (code=exited, status=203/EXEC)

    Nov 07 11:36:41 ip-172-27-184-101 systemd[1]: Started Prometheus OpenVPN Node Exporter.
    Nov 07 11:36:41 ip-172-27-184-101 systemd[21044]: openvpn_exporter.service: Failed to execute command: No such file or directory
    Nov 07 11:36:41 ip-172-27-184-101 systemd[21044]: openvpn_exporter.service: Failed at step EXEC spawning /usr/local/bin/openvpn_exporter: No suc
    Nov 07 11:36:41 ip-172-27-184-101 systemd[1]: openvpn_exporter.service: Main process exited, code=exited, status=203/EXEC
    Nov 07 11:36:41 ip-172-27-184-101 systemd[1]: openvpn_exporter.service: Failed with result ‘exit-code’.

    Reply

Leave a Comment