1st commit
This commit is contained in:
parent
1610ee389c
commit
f0dee282eb
|
@ -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()
|
Loading…
Reference in New Issue