1st commit

This commit is contained in:
Andreas Billmeier 2021-07-18 12:17:39 +02:00 committed by Andreas Billmeier
parent 1610ee389c
commit f0dee282eb
1 changed files with 299 additions and 0 deletions

299
check_openvpn Executable file
View File

@ -0,0 +1,299 @@
#! /usr/bin/env python
###############################################################################
# Nagios plugin check_openvpn
#
#
#
###############################################################################
__author__ = "Andreas Billmeier"
__email__ = "b@edevau.net"
__version__ = 0.1
# from optparse import OptionParser, OptionGroup
import argparse
import logging as log
import socket
import re
import sys
# NAGIOS return codes :
# https://nagios-plugins.org/doc/guidelines.html#AEN78
OK = 0
WARNING = 1
CRITICAL = 2
UNKNOWN = 3
## These will override any args passed to the script normally. Comment out after testing.
# testargs = '--help'
# testargs = '--version'
testargs = "'--password' 'phzqTdXuXkowuz1DIsUlSqHT8D' '--perfdata' '--port' '2195' '-H' '172.16.0.1' -v"
def main():
args = Get_Args()
log.debug(f"args: {args}")
global verbose
if args.v > 0:
verbose = True
message = ""
performancedata = {}
min = ""
max = ""
edevau = True
# Create a TCP socket to OpenVPN management
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((args.HOST, args.PORT))
if not s:
gtfo(3, "Could not connect.")
try:
if not ovpn_logon(s, args.password):
gtfo(3, "Login failed.")
status, clients, routing = ovpn_status2(s)
log.warning(status)
log.warning(clients)
log.warning(routing)
finally:
s.close()
# now we have all the data
if args.nomultiline:
ml_end = " "
else:
ml_end = "\n"
message += f"{status['TITLE']}{ml_end}"
if args.cn:
# search for a client
i = 0
performancedata['clients'] = 0
performancedata['in'] = 0
performancedata['out'] = 0
for cn in clients['Common Name']:
if re.findall(args.cn,cn):
message += f"Client {clients['Common Name'][i]} connected since: {clients['Connected Since'][i]}"
performancedata['clients'] += 1
performancedata['in'] = clients['Bytes Received'][i]
performancedata['out'] = clients['Bytes Sent'][i]
performancedata['connectedsince'] = clients['Connected Since (time_t)'][i]
i += 1
elif args.route:
# search for a specified route
gtfo(3,"Not implemented yet.")
else:
# default: report Clientcount
clientcount = len(clients['Common Name'])
message += f"Clients connected: {clientcount}"
if not args.nodetails:
message += f" {clients['Common Name']}"
performancedata['clients'] = clientcount
# special edevau counts
if edevau:
performancedata['k_clients'] = 0
performancedata['e_clients'] = 0
performancedata['o_clients'] = 0
for cn in clients['Common Name']:
if cn.startswith("k_"):
performancedata['k_clients'] += 1
elif cn.startswith("e_"):
performancedata['e_clients'] +=1
else:
performancedata['o_clients'] +=1
# add performancedata
# 'label'=value[UOM];[warn];[crit];[min];[max]
# https://nagios-plugins.org/doc/guidelines.html#PLUGOUTPUT
message += f" |"
for key in performancedata.keys():
if key.endswith("clients"):
message += f" {key}={performancedata[key]};{args.warn};{args.crit};{min};{max}"
else:
message += f" {key}={performancedata[key]};;;;"
# go Home
gtfo(0,message)
def ovpn_status2(s):
try:
request = "status 2\r\n"
s.sendall(request.encode())
except socket.error:
gtfo(3, "Send failed")
status = {}
clients = {}
routing = {}
end = False
while not end:
data = s.recv(1024)
if not data:
break
lines = data.decode()
log.debug(f"lines...:{lines}")
for line in lines.split("\n"):
line = line.rstrip("\r")
if not re.findall(",", line):
# no Komma Found
end = True
break
token, payload = line.split(",", 1)
log.debug(f"{token} {payload}")
if token == "TITLE" or token == "GLOBAL_STATS" or token == "TIME":
status[token] = payload
continue
elif token == "HEADER":
header, hcontent = payload.split(",", 1)
if header == "CLIENT_LIST":
for i in hcontent.split(","):
clients[i] = []
elif header == "ROUTING_TABLE":
for i in hcontent.split(","):
routing[i] = []
else:
log.warning(f"unknown HEADER found: {header}")
continue
elif token == "CLIENT_LIST" or token == "ROUTING_TABLE":
i = 0
while i < len(hcontent.split(",")):
log.debug(
f"{token} {hcontent.split(',')[i]} -> {payload.split(',')[i]}"
)
if token == "CLIENT_LIST":
clients[hcontent.split(",")[i]].append(payload.split(",")[i])
else:
routing[hcontent.split(",")[i]].append(payload.split(",")[i])
i += 1
continue
elif token == "ERROR":
log.warning(f"{token} {payload}")
continue
else:
log.error(f"unknown token {token} -> {payload}")
return status, clients, routing
def ovpn_logon(s, password):
connected = False
while not connected:
line = s.recv(1024).decode()
log.debug(f"line..:{line}")
token = line.split(":")[0]
log.debug(f"token..:{token}")
if token == "ENTER PASSWORD":
if password:
log.debug("Logon requested...")
try:
request = f"{password}\r\n"
s.sendall(request.encode())
except socket.error:
gtfo(3, "Send Password failed")
else:
gtfo(3, "Logon requested, none given.")
elif token == "SUCCESS":
connected = True
break
elif token == ">INFO":
connected = True
break
elif token == ">ERROR":
# what the hell...
break
return connected
def save_obj(obj, name ):
with open('obj/'+ name + '.pkl', 'wb') as f:
pickle.dump(obj, f, pickle.HIGHEST_PROTOCOL)
def load_obj(name ):
with open('obj/' + name + '.pkl', 'rb') as f:
return pickle.load(f)
def Get_Args():
parser = argparse.ArgumentParser(
description="check_openvpn", usage=f"usage: {sys.argv[0]} [-v|vv|vvv] [options]"
)
parser.add_argument(
"-v",
"--verbosity",
action="count",
dest="v",
default=0,
help="increase output verbosity [-v|vv|vvv]",
)
parser.add_argument("-H", help="Hostname or IP address", required=True, dest="HOST")
parser.add_argument(
"-p",
"--port",
required=True,
help="TCP port of management interface",
type=int,
dest="PORT",
default=2195,
)
parser.add_argument(
"-P", "--password", help="password for management interface", default=None
)
parser.add_argument(
"-C", "--cn", help="Common Name (in certificate) to seek", default=None
)
parser.add_argument("-R", "--route", help="Network to seek route for", default=None)
parser.add_argument(
"-A", "--addr", help="IP Address of client system to seek", default=None
)
parser.add_argument(
"-d",
"--perfdata",
help="Output performance data",
action="store_true",
default=False,
)
parser.add_argument(
"--nomultiline",
help="Force single line output",
action="store_true",
default=False,
)
parser.add_argument(
"--nodetails",
help="simplify output",
action="store_true",
default=False,
)
parser.add_argument('-w', '--warn', help='warning limit', default=40)
parser.add_argument('-c', '--crit', help='critical limit', default=50)
# parser.add_argument('-n', '--neg ', help='negate the return code', action="store_true", dest="neg", default=False)
args = parser.parse_args()
if args.v > 3:
args.v = 3
log.getLogger().setLevel([log.ERROR, log.WARNING, log.INFO, log.DEBUG][args.v])
log.debug(f"Parsed arguments: {args}")
return args
def gtfo(exitcode, message=""):
"""Exit gracefully with exitcode and (optional) message"""
log.debug(f"Exiting with status {exitcode}. Message: {message}")
if message:
print(message)
sys.exit(exitcode)
if __name__ == "__main__":
## Initialize logging before hitting main, in case we need extra debuggability
log.basicConfig(
level=log.DEBUG,
format="%(asctime)s - %(funcName)s - %(levelname)s - %(message)s",
)
main()