openvpn: Add metrics script

This script is called when an OpenVPN Roadwarrior client
connects or disconnect and logs the start and duration
of the session.

This can be used to monitor session duration and data transfer.

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Signed-off-by: Arne Fitzenreiter <arne_f@ipfire.org>
This commit is contained in:
Michael Tremer
2020-04-13 11:50:17 +00:00
committed by Arne Fitzenreiter
parent f81fe76354
commit 708f2b7368
6 changed files with 180 additions and 0 deletions

171
src/scripts/openvpn-metrics Executable file
View File

@@ -0,0 +1,171 @@
#!/usr/bin/python3
############################################################################
# #
# This file is part of the IPFire Firewall. #
# #
# IPFire is free software; you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation; either version 2 of the License, or #
# (at your option) any later version. #
# #
# IPFire is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with IPFire; if not, write to the Free Software #
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #
# #
# Copyright (C) 2007-2020 IPFire Team <info@ipfire.org>. #
# #
############################################################################
import argparse
import logging
import logging.handlers
import os
import sqlite3
import sys
_ = lambda x: x
DEFAULT_DATABASE_PATH = "/var/ipfire/ovpn/clients.db"
def setup_logging(level=logging.INFO):
l = logging.getLogger("openvpn-metrics")
l.setLevel(level)
# Log to console
h = logging.StreamHandler()
h.setLevel(logging.DEBUG)
l.addHandler(h)
# Log to syslog
h = logging.handlers.SysLogHandler(address="/dev/log",
facility=logging.handlers.SysLogHandler.LOG_DAEMON)
h.setLevel(logging.INFO)
l.addHandler(h)
# Format syslog messages
formatter = logging.Formatter("openvpn-metrics[%(process)d]: %(message)s")
h.setFormatter(formatter)
return l
# Initialise logging
log = setup_logging()
class OpenVPNMetrics(object):
def __init__(self):
self.db = self._open_database()
def parse_cli(self):
parser = argparse.ArgumentParser(
description=_("Tool that collects metrics of OpenVPN Clients"),
)
subparsers = parser.add_subparsers()
# client-connect
client_connect = subparsers.add_parser("client-connect",
help=_("Called when a client connects"),
)
client_connect.add_argument("file", nargs="?",
help=_("Configuration file")
)
client_connect.set_defaults(func=self.client_connect)
# client-disconnect
client_disconnect = subparsers.add_parser("client-disconnect",
help=_("Called when a client disconnects"),
)
client_disconnect.add_argument("file", nargs="?",
help=_("Configuration file")
)
client_disconnect.set_defaults(func=self.client_disconnect)
# Parse CLI
args = parser.parse_args()
# Print usage if no action was given
if not "func" in args:
parser.print_usage()
sys.exit(2)
return args
def __call__(self):
# Parse command line arguments
args = self.parse_cli()
# Call function
try:
ret = args.func(args)
except Exception as e:
log.critical(e)
# Return with exit code
sys.exit(ret or 0)
def _open_database(self, path=DEFAULT_DATABASE_PATH):
db = sqlite3.connect(path)
# Create schema if it doesn't exist already
db.executescript("""
CREATE TABLE IF NOT EXISTS sessions(
common_name TEXT NOT NULL,
connected_at INTEGER NOT NULL,
duration INTEGER,
bytes_received INTEGER,
bytes_sent INTEGER
);
-- Create index for speeding up searches
CREATE INDEX IF NOT EXISTS sessions_common_name ON sessions(common_name);
""")
return db
def _get_environ(self, key):
if not key in os.environ:
sys.stderr.write("%s missing from environment\n" % key)
raise SystemExit(1)
return os.environ.get(key)
def client_connect(self, args):
common_name = self._get_environ("common_name")
# Time
time_ascii = self._get_environ("time_ascii")
time_unix = self._get_environ("time_unix")
log.info("Opening session for %s at %s" % (common_name, time_ascii))
c = self.db.cursor()
c.execute("INSERT INTO sessions(common_name, connected_at) \
VALUES(?, ?)", (common_name, time_unix))
self.db.commit()
def client_disconnect(self, args):
common_name = self._get_environ("common_name")
duration = self._get_environ("time_duration")
# Collect some usage statistics
bytes_received = self._get_environ("bytes_received")
bytes_sent = self._get_environ("bytes_sent")
log.info("Closing session for %s after %ss and receiving/sending %s/%s bytes" \
% (common_name, duration, bytes_received, bytes_sent))
c = self.db.cursor()
c.execute("UPDATE sessions SET duration = ?, bytes_received = ?, \
bytes_sent = ? WHERE common_name = ? AND duration IS NULL",
(duration, bytes_received, bytes_sent, common_name))
self.db.commit()
def main():
m = OpenVPNMetrics()
m()
main()