some shell scripts replaced ovpn_status.py

This commit is contained in:
Pavel Muhortov 2023-07-27 13:53:33 +03:00
parent 70a9dd0106
commit 36869d518b
5 changed files with 869 additions and 406 deletions

View File

@ -0,0 +1,597 @@
zabbix_export:
version: '6.0'
date: '2023-07-27T10:47:37Z'
groups:
-
uuid: a571c0d144b14fd4a87a9d9b2aa9fcd6
name: Templates/Applications
templates:
-
uuid: c3272861e3ff46e2b3daa302066c53c7
template: 'OpenVPN by Zabbix agent'
name: 'OpenVPN by Zabbix agent'
description: 'OpenVPN by Zabbix agent'
groups:
-
name: Templates/Applications
items:
-
uuid: 51151af0bb704668a1bb3b390cee2039
name: 'OpenVPN stats'
key: discovery.ovpn
history: 14d
trends: '0'
value_type: TEXT
tags:
-
tag: Application
value: OpenVPN
-
tag: 'OpenVPN stats'
value: raw
-
uuid: 877b2f94cd4645fcaae13543f42d79be
name: 'OpenVPN clients limit'
key: ovpn.clients.limit
delay: 1h
history: 14d
units: client
tags:
-
tag: Application
value: OpenVPN
-
tag: 'OpenVPN server'
value: 'clients limit'
-
uuid: 00c5526a838e4f7791b4edafc20bb094
name: 'OpenVPN expiration ca'
key: ovpn.expiration.ca
delay: 1h
history: 14d
units: s
tags:
-
tag: Application
value: OpenVPN
-
tag: 'OpenVPN server'
value: expiration
triggers:
-
uuid: 34fbc5a346d0458c8e529b92f0aa39c5
expression: 'last(/OpenVPN by Zabbix agent/ovpn.expiration.ca,#1)<86400'
name: 'OpenVPN ca certificate expires in 1 day'
priority: DISASTER
tags:
-
tag: Application
value: OpenVPN
-
tag: 'OpenVPN stats'
value: expiration
-
uuid: 016c7d7c40b342c883ff81d0a5817b75
expression: 'last(/OpenVPN by Zabbix agent/ovpn.expiration.ca,#1)<604800'
name: 'OpenVPN ca certificate expires in 7 days'
priority: HIGH
tags:
-
tag: Application
value: OpenVPN
-
tag: 'OpenVPN stats'
value: expiration
-
uuid: 2d9f9da08348499ab9b7584a9386abfc
expression: 'last(/OpenVPN by Zabbix agent/ovpn.expiration.ca,#1)<2592000'
name: 'OpenVPN ca certificate expires in 30 days'
priority: AVERAGE
tags:
-
tag: Application
value: OpenVPN
-
tag: 'OpenVPN stats'
value: expiration
-
uuid: 65912ffeb36a4b2c8bae996c0b865f69
expression: 'last(/OpenVPN by Zabbix agent/ovpn.expiration.ca,#1)<7776000'
name: 'OpenVPN ca certificate expires in 90 days'
priority: WARNING
tags:
-
tag: Application
value: OpenVPN
-
tag: 'OpenVPN stats'
value: expiration
-
uuid: 591d03b0553645788c5b178670cb8bc9
name: 'OpenVPN expiration cert'
key: ovpn.expiration.cert
delay: 1h
history: 14d
units: s
tags:
-
tag: Application
value: OpenVPN
-
tag: 'OpenVPN server'
value: expiration
triggers:
-
uuid: 6a2bc06ed9944e95bfdec45af330bd53
expression: 'last(/OpenVPN by Zabbix agent/ovpn.expiration.cert,#1)<86400'
name: 'OpenVPN cert certificate expires in 1 day'
priority: DISASTER
tags:
-
tag: Application
value: OpenVPN
-
tag: 'OpenVPN stats'
value: expiration
-
uuid: da6ae766472541e8addb2712584289c7
expression: 'last(/OpenVPN by Zabbix agent/ovpn.expiration.cert,#1)<604800'
name: 'OpenVPN cert certificate expires in 7 days'
priority: HIGH
tags:
-
tag: Application
value: OpenVPN
-
tag: 'OpenVPN stats'
value: expiration
-
uuid: 5571d2aa00a2479889bdd853b37d7160
expression: 'last(/OpenVPN by Zabbix agent/ovpn.expiration.cert,#1)<2592000'
name: 'OpenVPN cert certificate expires in 30 days'
priority: AVERAGE
tags:
-
tag: Application
value: OpenVPN
-
tag: 'OpenVPN stats'
value: expiration
-
uuid: 083b3ed043db4d209b093056c04605c3
expression: 'last(/OpenVPN by Zabbix agent/ovpn.expiration.cert,#1)<7776000'
name: 'OpenVPN cert certificate expires in 90 days'
priority: WARNING
tags:
-
tag: Application
value: OpenVPN
-
tag: 'OpenVPN stats'
value: expiration
-
uuid: 5ba0b1d455444ec8851c4cdda408ed24
name: 'Clients count'
type: DEPENDENT
key: ovpn.stats.clients_count
delay: '0'
history: 14d
units: clients
preprocessing:
-
type: JSONPATH
parameters:
- $.clients_count
master_item:
key: discovery.ovpn
tags:
-
tag: Application
value: OpenVPN
-
tag: 'OpenVPN stats'
value: 'clients count'
-
uuid: 731f61d192f944769aaf82c2fb05676b
name: 'Clients found'
type: DEPENDENT
key: ovpn.stats.clients_found
delay: '0'
history: 14d
units: clients
preprocessing:
-
type: JSONPATH
parameters:
- $.clients_found
master_item:
key: discovery.ovpn
tags:
-
tag: Application
value: OpenVPN
-
tag: 'OpenVPN stats'
value: 'clients found'
-
uuid: f38e82ba64f14385bd60a1397eda278c
name: 'Stats updated'
type: DEPENDENT
key: ovpn.stats.updated
delay: '0'
history: 14d
trends: '0'
value_type: TEXT
preprocessing:
-
type: JSONPATH
parameters:
- $.stats_updated
master_item:
key: discovery.ovpn
tags:
-
tag: Application
value: OpenVPN
-
tag: 'OpenVPN stats'
value: updated
discovery_rules:
-
uuid: f6b3ac3373544c1f820c207234177816
name: 'Discovery openvpn clients'
type: DEPENDENT
key: get.ovpn.stats
delay: '0'
item_prototypes:
-
uuid: fc55509717fc4ee7bef6f684932ee01a
name: 'OpenVPN client "{#OVPN_CLIENT_NAME}" bytes recieved'
type: DEPENDENT
key: 'ovpn.client.b_rx.name[{#OVPN_CLIENT_NAME}]'
delay: '0'
history: 14d
units: B
preprocessing:
-
type: JSONPATH
parameters:
- '$.data.[?(@.name=="{#OVPN_CLIENT_NAME}")].b_rx.first()'
master_item:
key: discovery.ovpn
tags:
-
tag: Application
value: OpenVPN
-
tag: 'OpenVPN client'
value: '{#OVPN_CLIENT_NAME}'
-
uuid: e75ed01ce6cd45e0822e021f6733b115
name: 'OpenVPN client "{#OVPN_CLIENT_NAME}" bytes transmitted'
type: DEPENDENT
key: 'ovpn.client.b_tx.name[{#OVPN_CLIENT_NAME}]'
delay: '0'
history: 14d
units: B
preprocessing:
-
type: JSONPATH
parameters:
- '$.data.[?(@.name=="{#OVPN_CLIENT_NAME}")].b_tx.first()'
master_item:
key: discovery.ovpn
tags:
-
tag: Application
value: OpenVPN
-
tag: 'OpenVPN client'
value: '{#OVPN_CLIENT_NAME}'
-
uuid: 332feedbbd314a479ba27d43c7bb7523
name: 'OpenVPN client "{#OVPN_CLIENT_NAME}" real ip'
type: DEPENDENT
key: 'ovpn.client.r_ip.name[{#OVPN_CLIENT_NAME}]'
delay: '0'
history: 14d
trends: '0'
value_type: TEXT
preprocessing:
-
type: JSONPATH
parameters:
- '$.data.[?(@.name=="{#OVPN_CLIENT_NAME}")].r_ip.first()'
master_item:
key: discovery.ovpn
tags:
-
tag: Application
value: OpenVPN
-
tag: 'OpenVPN client'
value: '{#OVPN_CLIENT_NAME}'
-
tag: 'OpenVPN stats'
value: 'real ip'
-
uuid: 0dc7671cdc9b47c6a7c06b47a8de25ca
name: 'OpenVPN client "{#OVPN_CLIENT_NAME}" connect duration'
type: DEPENDENT
key: 'ovpn.client.t_cd.name[{#OVPN_CLIENT_NAME}]'
delay: '0'
history: 14d
units: s
preprocessing:
-
type: JSONPATH
parameters:
- '$.data.[?(@.name=="{#OVPN_CLIENT_NAME}")].t_cd.first()'
master_item:
key: discovery.ovpn
tags:
-
tag: Application
value: OpenVPN
-
tag: 'OpenVPN client'
value: '{#OVPN_CLIENT_NAME}'
-
uuid: 127fd46950194d9ba5325f183cb6e940
name: 'OpenVPN client "{#OVPN_CLIENT_NAME}" connect time'
type: DEPENDENT
key: 'ovpn.client.t_cs.name[{#OVPN_CLIENT_NAME}]'
delay: '0'
history: 14d
trends: '0'
value_type: TEXT
preprocessing:
-
type: JSONPATH
parameters:
- '$.data.[?(@.name=="{#OVPN_CLIENT_NAME}")].t_cs.first()'
master_item:
key: discovery.ovpn
tags:
-
tag: Application
value: OpenVPN
-
tag: 'OpenVPN client'
value: '{#OVPN_CLIENT_NAME}'
-
uuid: 2bb9be1dd4a041b09e68f7e9022676d4
name: 'OpenVPN client "{#OVPN_CLIENT_NAME}" virtual ip'
type: DEPENDENT
key: 'ovpn.client.v_ip.name[{#OVPN_CLIENT_NAME}]'
delay: '0'
history: 14d
trends: '0'
value_type: TEXT
preprocessing:
-
type: JSONPATH
parameters:
- '$.data.[?(@.name=="{#OVPN_CLIENT_NAME}")].v_ip.first()'
master_item:
key: discovery.ovpn
tags:
-
tag: Application
value: OpenVPN
-
tag: 'OpenVPN client'
value: '{#OVPN_CLIENT_NAME}'
-
tag: 'OpenVPN stats'
value: 'virtual ip'
graph_prototypes:
-
uuid: 0e740374d0d7435990fba99d19211947
name: 'OpenVPN client "{#OVPN_CLIENT_NAME}" connect duration'
show_work_period: 'NO'
show_triggers: 'NO'
graph_items:
-
color: FFBF00
item:
host: 'OpenVPN by Zabbix agent'
key: 'ovpn.client.t_cd.name[{#OVPN_CLIENT_NAME}]'
-
uuid: 48b62a9621c64082919a6e041defc546
name: 'OpenVPN client "{#OVPN_CLIENT_NAME}" traffic'
graph_items:
-
drawtype: GRADIENT_LINE
color: 00FF00
item:
host: 'OpenVPN by Zabbix agent'
key: 'ovpn.client.b_rx.name[{#OVPN_CLIENT_NAME}]'
-
sortorder: '1'
drawtype: BOLD_LINE
color: 0080FF
item:
host: 'OpenVPN by Zabbix agent'
key: 'ovpn.client.b_tx.name[{#OVPN_CLIENT_NAME}]'
master_item:
key: discovery.ovpn
lld_macro_paths:
-
lld_macro: '{#OVPN_CLIENT_NAME}'
path: $..name.first()
-
lld_macro: '{#OVPN_CLIENT_R_IP}'
path: $..r_ip.first()
-
lld_macro: '{#OVPN_CLIENT_V_IP}'
path: $..v_ip.first()
-
lld_macro: '{#OVPN_CLIENT_B_RX}'
path: $..b_rx.first()
-
lld_macro: '{#OVPN_CLIENT_B_TX}'
path: $..b_tx.first()
-
lld_macro: '{#OVPN_CLIENT_T_CS}'
path: $..t_cs.first()
-
lld_macro: '{#OVPN_CLIENT_T_CD}'
path: $..t_cd.first()
dashboards:
-
uuid: d25222f632c74c83ac80c8cbce480db0
name: OpenVPN
auto_start: 'NO'
pages:
-
name: Server
widgets:
-
type: ITEM
width: '5'
hide_header: 'YES'
fields:
-
type: ITEM
name: itemid
value:
key: ovpn.expiration.ca
host: 'OpenVPN by Zabbix agent'
-
type: ITEM
x: '6'
width: '5'
hide_header: 'YES'
fields:
-
type: ITEM
name: itemid
value:
key: ovpn.expiration.cert
host: 'OpenVPN by Zabbix agent'
-
type: GRAPH_CLASSIC
'y': '2'
width: '11'
height: '5'
hide_header: 'YES'
fields:
-
type: GRAPH
name: graphid
value:
name: 'OpenVPN certificates expiration'
host: 'OpenVPN by Zabbix agent'
-
name: Clients
widgets:
-
type: ITEM
width: '5'
hide_header: 'YES'
fields:
-
type: ITEM
name: itemid
value:
key: ovpn.stats.clients_count
host: 'OpenVPN by Zabbix agent'
-
type: ITEM
x: '6'
width: '5'
hide_header: 'YES'
fields:
-
type: ITEM
name: itemid
value:
key: ovpn.stats.clients_found
host: 'OpenVPN by Zabbix agent'
-
type: GRAPH_PROTOTYPE
'y': '7'
width: '11'
height: '5'
fields:
-
type: INTEGER
name: columns
value: '1'
-
type: GRAPH_PROTOTYPE
name: graphid
value:
name: 'OpenVPN client "{#OVPN_CLIENT_NAME}" traffic'
host: 'OpenVPN by Zabbix agent'
-
type: GRAPH_CLASSIC
'y': '2'
width: '11'
height: '5'
hide_header: 'YES'
fields:
-
type: GRAPH
name: graphid
value:
name: 'OpenVPN clients sum'
host: 'OpenVPN by Zabbix agent'
-
type: URL
x: '11'
width: '13'
height: '12'
hide_header: 'YES'
fields:
-
type: STRING
name: url
value: '/zabbix.php?name=virtual ip&tags%5B0%5D%5Btag%5D=Application&tags%5B0%5D%5Boperator%5D=0&tags%5B0%5D%5Bvalue%5D=OpenVPN&show_tags=0&action=latest.view&kiosk=1'
triggers:
-
uuid: 022e1211349c48a4be951588cda2dba0
expression: 'max(/OpenVPN by Zabbix agent/ovpn.stats.clients_count,#1)>=max(/OpenVPN by Zabbix agent/ovpn.clients.limit,#1)'
name: 'Maximum number of OpenVPN clients reached'
priority: AVERAGE
tags:
-
tag: Application
value: OpenVPN
-
tag: 'OpenVPN server'
value: 'clients limit'
graphs:
-
uuid: a23bdc304f5d49e1b42597f9cbd4e840
name: 'OpenVPN certificates expiration'
graph_items:
-
drawtype: GRADIENT_LINE
color: FFBF00
item:
host: 'OpenVPN by Zabbix agent'
key: ovpn.expiration.ca
-
sortorder: '1'
drawtype: BOLD_LINE
color: FF8000
item:
host: 'OpenVPN by Zabbix agent'
key: ovpn.expiration.cert
-
uuid: 94d1e1cc40424214a291067790d3db89
name: 'OpenVPN clients sum'
graph_items:
-
color: FF8000
item:
host: 'OpenVPN by Zabbix agent'
key: ovpn.stats.clients_count
-
sortorder: '1'
color: FFBF00
item:
host: 'OpenVPN by Zabbix agent'
key: ovpn.stats.clients_found

101
README.md
View File

@ -2,45 +2,70 @@
OpenVPN management and monitoring utils.
* [`ovpn-cert-expiration`.sh](https://git.hmp.today/pavel.muhortov/openvpn-management#ovpn-cert-expiration-sh)
* [`ovpn_status`.py](https://git.hmp.today/pavel.muhortov/openvpn-management#ovpn_status-py)
* [`ovpn-client-management`.sh](https://git.hmp.today/pavel.muhortov/openvpn-management#ovpn-client-management-sh)
* [`ovpn-connect-handling`.sh](https://git.hmp.today/pavel.muhortov/openvpn-management#ovpn-connect-handling-sh)
____
## `ovpn-cert-expiration`.sh
## `ovpn_status`.py
**Description:**
> Checking openvpn server certificates expiration and preparing stats for monitoring.
> OpenVPN server status parser.
**Dependencies:**
>
> * privileged rights
> * [openssl](https://www.openssl.org/) (tested version 1.1.1k on [Debian GNU/Linux 11](http://ftp.debian.org/debian/dists/bullseye/))
> * [Python 3](https://www.python.org/downloads/) (tested version 3.9.5 on [Debian GNU/Linux 11](http://ftp.debian.org/debian/dists/bullseye/))
> * Existing `status`, `server`, `ca`, `cert` options in [server.conf](https://openvpn.net/community-resources/reference-manual-for-openvpn-2-5/#options)
| POSITION | PARAMETERS | DESCRIPTION | DEFAULT |
|-----------|--------------|------------------------|---------------|
| 1 |**[qn]**|execution without pauses||
| 2 |**[/path/to/conf]**|openvpn server config file path|/etc/openvpn/server/server.conf|
|**[-s, --server_conf]**|path to OpenVPN server configuration file|**REQUIRED**|
|**[-f, --filter]**|client names filter by regex|`.*`|
|**[-g, --geo]**|check client real ip geo location (may be slow)|`.*`|
Example usage:
Example usage with Zabbix agent:
```bash
# add options to openvpn server config file
sudo tee -a /etc/openvpn/server/server.conf > /dev/null <<'EOF'
status /var/log/openvpn/openvpn-status.log
status-version 2
server 10.0.0.0 255.0.0.0
ca /etc/openvpn/easy-rsa/pki/ca.crt
cert /etc/openvpn/easy-rsa/pki/issued/server.crt
EOF
sudo systemctl restart openvpn@server
```
```bash
# download
sudo wget https://git.hmp.today/pavel.muhortov/openvpn-management/raw/branch/master/ovpn-cert-expiration.sh -O /etc/openvpn/server/ovpn-cert-expiration.sh
sudo chmod +x /etc/openvpn/server/ovpn-cert-expiration.sh
sudo wget https://git.hmp.today/pavel.muhortov/openvpn-management/raw/branch/master/ovpn_status.py -O /etc/openvpn/server/ovpn_status.py
sudo chmod +x /etc/openvpn/server/ovpn_status.py
```
```bash
# sudo crontab -e
0 * * * * bash /etc/openvpn/server/ovpn-cert-expiration.sh qn
# edit sudoers
sudo sh -c "echo '
zabbix ALL=(ALL) NOPASSWD:/etc/openvpn/server/ovpn_status.py -s /etc/openvpn/server/server.conf
' > /etc/sudoers.d/zabbix_agentd"
# check permission
sudo -u zabbix sudo /etc/openvpn/server/ovpn_status.py -s /etc/openvpn/server/server.conf
```
```bash
# check stats
watch cat /var/log/openvpn/ovpn-cert-expiration.log
# add UserParameter to Zabbix agent
sudo sh -c "echo '
Timeout=30
AllowRoot=0
UserParameter=discovery.ovpn, sudo /etc/openvpn/server/ovpn_status.py -s /etc/openvpn/server/server.conf
' >> /etc/zabbix/zabbix_agentd.conf"
sudo systemctl restart zabbix-agent
```
Download [OpenVPN_by_Zabbix_agent.yaml](https://git.hmp.today/pavel.muhortov/openvpn-management/raw/branch/master/OpenVPN_by_Zabbix_agent.yaml) template
Zabbix Server -> Configuration -> Templates -> Import template
____
## `ovpn-client-management`.sh
@ -92,51 +117,3 @@ sudo ./ovpn del username -f
# check journal
tail -f /var/log/openvpn/ovpn.log
```
____
## `ovpn-connect-handling`.sh
**Description:**
> Handling client connection and preparing stats for monitoring.
**Dependencies:**
>
> * executing by [openvpn](https://openvpn.net/) server (tested version 2.5.1 on [Debian GNU/Linux 11](http://ftp.debian.org/debian/dists/bullseye/))
> * [jq](https://github.com/stedolan/jq) (tested version 1.6 on [Debian GNU/Linux 11](http://ftp.debian.org/debian/dists/bullseye/))
> * [grepcidr](https://github.com/ryantig/grepcidr) (tested version 2.0 on [Debian GNU/Linux 11](http://ftp.debian.org/debian/dists/bullseye/))
> * [Python 3](https://www.python.org/downloads/) (tested version 3.9.5 on [Debian GNU/Linux 11](http://ftp.debian.org/debian/dists/bullseye/))
> * existing [/usr/local/bin/sendmail.py](https://git.hmp.today/pavel.muhortov/utils#sendmail-py)
> * [bash](https://www.gnu.org/software/bash/) (tested versions: 5.1.4 on [Debian GNU/Linux 11](http://ftp.debian.org/debian/dists/bullseye/), 5.0.17 on [Ubuntu 20](https://wiki.ubuntu.com/FocalFossa/ReleaseNotes), 4.2.46 on [CentOS 7](https://wiki.centos.org/Manuals/ReleaseNotes/CentOS7.2009))
| POSITION | PARAMETERS | DESCRIPTION | DEFAULT |
|-----------|--------------|------------------------|---------------|
| 1 |**inc\|dec**|increment or decrement counter|**REQUIRED**|
| 2 |**</path/to/dir>**|root path for counter, names, log|**REQUIRED**|
| 3 |**[mail]**|send email notification||
| 4 |**[geo]**|check client address geolocation||
Example usage:
```bash
# download
sudo wget https://git.hmp.today/pavel.muhortov/openvpn-management/raw/branch/master/ovpn-connect-handling.sh -O /etc/openvpn/server/ovpn-connect-handling.sh
sudo chmod +x /etc/openvpn/server/ovpn-connect-handling.sh
```
```bash
# add options to openvpn server config file
sudo tee -a /etc/openvpn/server/server.conf > /dev/null <<'EOF'
script-security 2
client-connect "/etc/openvpn/server/ovpn-connect-handling.sh inc /var/log/openvpn mail geo"
client-disconnect "/etc/openvpn/server/ovpn-connect-handling.sh dec /var/log/openvpn - -"
EOF
sudo systemctl restart openvpn@server
```
```bash
# check counter and names
watch cat /var/log/openvpn/openvpn-counts.log
# check journal
tail -f /var/log/openvpn/ovpn-connect-handling.log
```

View File

@ -1,149 +0,0 @@
#!/usr/bin/env bash
# DESCRIPTION:
# checking openvpn server certificates expiration
# and
# preparing stats for monitoring
#
# DEPENDENCIES:
# - privileged rights
# - openssl
#
# PARAMETERS:
# 1: "qn" - execution without pauses
# 2: openvpn server config file path
#
# FUNCTIONS:
#
#######################################
# Print message and add to log.
# Globals:
# logs
# Arguments:
# 1: message to print and logging
#######################################
addtologs() {
echo "$(date +'%Y.%m.%d-%H:%M:%S') $1" | tee -a "${logs}"
}
#######################################
# Waiting for press [ENTER].
# Globals:
# None
# Arguments:
# None
#######################################
execpause() {
read -r -p "Press [ENTER] to continue... "
}
#######################################
# Exit procedure.
# Globals:
# show
# Arguments:
# None
#######################################
execquite() {
addtologs "execution time is $(($(date +%s)-time)) seconds, exit"
if [ "${show}" != "qn" ]; then
execpause
fi
exit
}
#######################################
# Error exit procedure
# Globals:
# None
# Arguments:
# 1: message to print and logging
#######################################
execerror() {
addtologs "error: $1"
execquite
}
#######################################
# Parsing config file and creating global vars.
# Globals:
# None
# Arguments:
# None
#######################################
getconfig() {
cacrpath=$(grep ^ca "${conf}" | cut -d' ' -f2)
certpath=$(grep ^cert "${conf}" | cut -d' ' -f2)
statfile="$(dirname "$(grep ^log /etc/openvpn/server/server.conf | cut -d' ' -f2)")/$(basename -s .sh "$0").log"
}
#######################################
# Checking user rights.
# Globals:
# None
# Arguments:
# None
# return:
# 0 - if privileged rights, 1 - if not privileged rights
#######################################
checkroot() {
if [ "${EUID}" -ne 0 ]; then
return 1 # false
else
return 0 # true
fi
}
#######################################
# Print certificate expiration date in epoch
# Globals:
# None
# Arguments:
# 1: certificate path
#######################################
checkcert() {
printf '%s\n' "$(date -d "$(openssl x509 -text -noout -in "${1}" | grep 'Not After' | cut -d':' -f2-)" +%s)"
}
#
# VARIABLES:
#
show=$1
conf=$2
logs=/dev/null
if [ -z "${conf}" ] || [ "${conf}" == "-" ]; then
conf=/etc/openvpn/server/server.conf
fi
time=$(date +%s)
cd "$(dirname "$(realpath "$0")")" || execerror
if [ ! -e "${conf}" ]; then
execerror "${conf} not found"
else
getconfig
fi
if ! command -v openssl &> /dev/null; then
execerror "Not found dependencies"
fi
#
# MAIN:
#
if checkroot; then
cacrtime=$(checkcert "${cacrpath}")
certtime=$(checkcert "${certpath}")
cacrremain=$(( cacrtime - time ))
certremain=$(( certtime - time))
addtologs "${cacrpath} remains only ${cacrremain} seconds"
addtologs "${certpath} remains only ${certremain} seconds"
printf '%s\n' "ca=${cacrremain}" > "${statfile}"
printf '%s\n' "cert=${certremain}" >> "${statfile}"
addtologs "stats wrote to ${statfile}"
execquite
else
execerror "Restart this as root!"
fi

View File

@ -1,195 +0,0 @@
#!/usr/bin/env bash
# DESCRIPTION:
# handling client connection
# and
# preparing stats for monitoring
#
# DEPENDENCIES:
# - executing by openvpn server
# - jq
# - grepcidr
# - Python 3
# - existing /usr/local/bin/sendmail.py
#
# PARAMETERS:
# 1: "inc|dec" - increment or decrement counter
# 2: root path for counter, names, log
# 3: "mail" - send email notification
# 4: "geo" - check client address geolocation
#
# FUNCTIONS:
#
#######################################
# Print message and add to log.
# Globals:
# logs
# Arguments:
# 1: message to print and logging
#######################################
addtologs() {
echo "$(date +'%Y.%m.%d-%H:%M:%S') $1" | tee -a "${logs}"
}
#######################################
# Exit procedure.
# Globals:
# show
# Arguments:
# None
#######################################
execquite() {
addtologs "execution time is $(($(date +%s)-time)) seconds, exit"
exit
}
#######################################
# Error exit procedure
# Globals:
# None
# Arguments:
# 1: message to print and logging
#######################################
execerror() {
addtologs "error: $1"
execquite
}
#######################################
# Incrementing counter with adding client name
# Globals:
# counts_file
# common_name (variable by openvpn server)
# ifconfig_pool_remote_ip (variable by openvpn server)
# Arguments:
# None
#######################################
# shellcheck disable=SC2154
incremcounter() {
summary_cur=$(grep 'total=' "${counts_file}" | cut -d= -f2)
summary_new=${summary_cur} && (( summary_new += 1 ))
counts_temp=$(sed -e "s/total=${summary_cur}/total=${summary_new}/g" "${counts_file}" \
| sed -e '$a'"${common_name}"'_'"${ifconfig_pool_remote_ip}"'')
addtologs "client ${common_name} connected, counter increment to ${summary_new}"
printf "%s\n" "${counts_temp}" > "${counts_file}"
}
#######################################
# Decrementing counter with deleting client name
# Globals:
# counts_file
# common_name (variable by openvpn server)
# Arguments:
# None
#######################################
decremcounter(){
summary_cur=$(grep 'total=' "${counts_file}" | cut -d= -f2)
summary_new=${summary_cur} && (( summary_new -= 1 ))
counts_temp=$(sed -e "s/total=${summary_cur}/total=${summary_new}/g" "${counts_file}" \
| sed '0,/'"${common_name}"'/{/'"${common_name}"'/d}')
addtologs "client ${common_name} disconnected, counter decrement to ${summary_new}"
printf "%s\n" "${counts_temp}" > "${counts_file}"
}
#######################################
# Get information about client address
# Globals:
# flaggeol
# untrusted_ip (variable by openvpn server)
# Arguments:
# None
#######################################
# shellcheck disable=SC2154
expandaddress() {
ipinfo="Source address is ${untrusted_ip}"
localnetworks="10.0.0.0/8
100.64.0.0/10
127.0.0.1/8
172.16.0.0/12
192.168.0.0/16
"
if ! grepcidr "${localnetworks}" <(echo "${untrusted_ip}") >/dev/null; then
if [ "${flaggeol}" == "geo" ]; then
ipinfo=$(curl "https://api.ipbase.com/v1/json/${untrusted_ip}")
if [ "$(jq -r '.country_name' <<< "$ipinfo")" != "" ]; then
z=$(jq -r '.zip_code' <<< "$ipinfo")
c=$(jq -r '.country_name' <<< "$ipinfo")
r=$(jq -r '.region_name' <<< "$ipinfo")
t=$(jq -r '.city' <<< "$ipinfo")
ipinfo="Source address ${untrusted_ip} is from ${z}, ${c}, ${r}, ${t}"
fi
fi
fi
addtologs "client ${common_name} checked. ${ipinfo}"
}
#######################################
# Send email notification about client connect
# Globals:
# ipinfo
# common_name (variable by openvpn server)
# ifconfig_pool_remote_ip (variable by openvpn server)
# Arguments:
# None
#######################################
startsendmail() {
subj="[VPN Connected] $(cat /etc/hostname): ${common_name} connect to ${ifconfig_pool_remote_ip}"
(
python3 /usr/local/bin/sendmail.py \
-u "$(grep "from=" /usr/local/bin/sendmail.config | cut -d= -f2)" \
-p "$(grep "pass=" /usr/local/bin/sendmail.config | cut -d= -f2)" \
-d "$(grep "dest=" /usr/local/bin/sendmail.config | cut -d= -f2)" \
--smtp "$(grep "smtp=" /usr/local/bin/sendmail.config | cut -d= -f2)" \
--port "$(grep "port=" /usr/local/bin/sendmail.config | cut -d= -f2)" \
--stls "True" \
--subj "${subj}" \
--text "${ipinfo}" \
>> /dev/null 2>&1 &
)
addtologs "sent mail with subject '${subj}'"
}
#
# VARIABLES:
#
flagmath=$1
pathroot=$2
flagmail=$3
flaggeol=$4
time=$(date +%s)
logs="${pathroot}/$(basename -s .sh "$0").log"
counts_file="${pathroot}/ovpn-counts.log"
if [ -z "${pathroot}" ]; then
logs=/dev/null
execerror "Usage example: $0 'inc|dec' '/var/log/openvpn' '-' '-'"
elif [ ! -e "${logs}" ]; then
touch "${logs}"
fi
if ! command -v curl &> /dev/null || \
! command -v /usr/local/bin/sendmail.py &> /dev/null || \
! command -v python3 &> /dev/null || \
! command -v grepcidr &> /dev/null || \
! command -v jq &> /dev/null; then
execerror "Not found dependencies"
fi
#
# MAIN:
#
if [ "${flagmath}" == "inc" ]; then
incremcounter
expandaddress
if [ "${flagmail}" == "mail" ]; then
startsendmail
fi
elif [ "${flagmath}" == "dec" ]; then
decremcounter
else
execerror "Usage example: $0 'inc|dec' '/var/log/openvpn' '-' '-'"
fi
execquite

233
ovpn_status.py Normal file
View File

@ -0,0 +1,233 @@
#!/usr/bin/env python3
"""It's the OpenVPN server status parser.
"""
import json
import re
import time
from argparse import ArgumentParser
from ipaddress import IPv4Network
from os import path
from cryptography import x509
import requests
def status(stats_file: str, client_filter: str = '.*', client_geo: bool = False) -> dict:
"""OpenVPN status log parser.
Args:
stats_file (str): path to OpenVPN status log file.
client_filter (str, optional): client names filter by regex. Defaults to '.*'.
client_geo (bool, optional): check client real ip geo location Defaults to False.
Returns:
dict: {
'stats_updated': timestamp,
'clients_count': int,
'clients_found': int,
'data': [
{
"name": str,
"r_ip": str,
"v_ip": str,
"b_rx": int,
"b_tx": int,
"t_cs": timestamp,
"t_cd": int
},
]
}
"""
with open(stats_file, mode='r', encoding='utf-8') as file:
stats_data = file.read()
if re.match("^OpenVPN CLIENT LIST", stats_data):
# status-version 1
stats_vers = 1
dlm = ','
elif re.match("^TITLE,", stats_data):
# status-version 2
stats_vers = 2
dlm = ','
elif re.match("^TITLE\t", stats_data):
# status-version 3
stats_vers = 3
dlm = '\t'
else:
stats_vers = 0
clients_array = []
clients_count = -1
clients_found = 0
stats_updated = -1
if stats_vers == 0:
pass
elif stats_vers == 1:
updated_r = re.search('Updated' + dlm + '.*', stats_data).group(0)
stats_updated = updated_r.replace('Updated' + dlm, '')
updated_t = time.mktime(time.strptime(stats_updated, "%Y-%m-%d %H:%M:%S"))
clients_s = 'Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since'
clients_e = 'ROUTING TABLE'
clients_r = re.search(clients_s + "(.*)" + clients_e, stats_data, re.DOTALL).group(0)
stats_clients = re.sub(clients_s + '\n', '', re.sub(clients_e, '', clients_r))
routing_s = 'Virtual Address,Common Name,Real Address,Last Ref'
routing_e = 'GLOBAL STATS'
routing_r = re.search(routing_s + "(.*)" + routing_e, stats_data, re.DOTALL).group(0)
stats_routing = re.sub(routing_s + '\n', '', re.sub(routing_e, '', routing_r))
clients_count = len(stats_clients.splitlines())
if clients_count > 0:
for client in stats_clients.splitlines():
client_name = client.split(dlm)[0]
client_r_ip = client.split(dlm)[1].split(':')[0]
client_r_cc = '--'
if client_geo:
client_r_cc = ip_geo(addr=client_r_ip)
if re.findall(client_filter, client_name):
regex_v_ip = re.compile('.*' + dlm.join(client.split(dlm)[:2]) + '.*')
clients_array.append(
{
'name': client_name,
'r_ip': client_r_ip,
'r_cc': client_r_cc,
'v_ip': regex_v_ip.search(stats_routing).group(0).split(dlm)[0],
'b_rx': int(client.split(dlm)[2]),
'b_tx': int(client.split(dlm)[3]),
't_cs': client.split(dlm)[4],
't_cd': int(updated_t) - int(
time.mktime(
time.strptime(client.split(dlm)[4], "%Y-%m-%d %H:%M:%S")
)
)
}
)
clients_found += 1
else:
updated_r = re.search('TIME' + dlm + '.*', stats_data).group(0)
updated_t = updated_r.split(dlm)[2]
stats_updated = updated_r.split(dlm)[1]
stats_clients = '\n'.join(re.findall("^CLIENT_LIST.*", stats_data, re.MULTILINE))
clients_count = len(stats_clients.splitlines())
if clients_count > 0:
for client in stats_clients.splitlines():
client_name = client.split(dlm)[1]
client_r_ip = client.split(dlm)[2].split(':')[0]
client_r_cc = '--'
if client_geo:
client_r_cc = ip_geo(addr=client_r_ip)
if re.search(client_filter, client_name):
clients_array.append(
{
'name': client_name,
'r_ip': client_r_ip,
'r_cc': client_r_cc,
'v_ip': client.split(dlm)[3],
'b_rx': int(client.split(dlm)[5]),
'b_tx': int(client.split(dlm)[6]),
't_cs': client.split(dlm)[7],
't_cd': int(updated_t) - int(client.split(dlm)[8])
}
)
clients_found += 1
clients_stats = {
'stats_updated': stats_updated,
'clients_count': clients_count,
'clients_found': clients_found,
'data': clients_array,
}
return clients_stats
def ip_num(addr: str, mask: (str, int)) -> int:
"""OpenVPN client ip limit calculator (without --ifconfig-pool-linear).
Args:
addr (str): server subnet.
mask (str, int): server mask.
Returns:
int: ip limit.
"""
return int(IPv4Network(addr + '/' + mask).num_addresses/4-1)
def ce_exp(cert_path: str) -> int:
"""Get certificate expiration time.
Args:
cert_path (str): path to certificate file.
Returns:
int: certificate expiration time in epoch.
"""
with open(cert_path, mode='rb') as cert_file:
cert_data = x509.load_pem_x509_certificate(cert_file.read())
return int(cert_data.not_valid_after.timestamp())
def ip_geo(addr: str) -> str:
"""Get ip address geo location.
Args:
addr (str): ip address.
Returns:
str: country code.
"""
try:
request = 'https://geolocation-db.com/json/' + addr
response = requests.get(request, timeout=5)
result = json.loads(response.content.decode())
return result['country_code']
except requests.exceptions.RequestException:
return '--'
if __name__ == "__main__":
args = ArgumentParser(
prog='ovpn_status',
description='OpenVPN server status parser',
epilog='Dependencies: '
'- Python 3 (tested version 3.9.5), '
)
args.add_argument('-s', '--server_conf', type=str, required=True,
help='path to OpenVPN server configuration file')
args.add_argument('-f', '--filter', type=str, default='.*', required=False,
help='client names filter by regex')
args.add_argument('-g', '--geo', action='store_true', required=False,
help='check client real ip geo location (may be slow)')
args = vars(args.parse_args())
if path.exists(args['server_conf']):
with open(args['server_conf'], mode='r', encoding='utf-8') as conf_file:
conf_data = conf_file.read()
st_file_conf = re.search(r'status\s+\S*', conf_data, re.MULTILINE).group(0)
st_file_path = re.sub(r'status\s+', '', st_file_conf)
json_data = status(
stats_file=st_file_path,
client_filter=args['filter'],
client_geo=args['geo']
)
ca_file_conf = re.search(r'ca\s+\S*', conf_data, re.MULTILINE).group(0)
ca_file_path = re.sub(r'ca\s+', '', ca_file_conf)
json_data['ca_expiration'] = ce_exp(cert_path=ca_file_path)
ce_file_conf = re.search(r'cert\s+\S*', conf_data, re.MULTILINE).group(0)
ce_file_path = re.sub(r'cert\s+', '', ce_file_conf)
json_data['ce_expiration'] = ce_exp(cert_path=ce_file_path)
network_conf = re.search(r'server\s+\S*\s+\S*', conf_data, re.MULTILINE).group(0)
network_pool = re.sub(r'server\s+', '', network_conf)
network_addr = re.sub(r'\s+\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}', '', network_pool)
network_mask = re.sub(r'\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}\s+', '', network_pool)
json_data['clients_limit'] = ip_num(addr=network_addr, mask=network_mask)
print(json.dumps(json_data, indent=2))