Este artículo explica cómo listar todos los usuarios conectados a un servidor OpenVPN utilizando un pequeño script Bash.
Existe una forma de comunicarse con el demonio de openvpn, a fin de obtener estadísticas de conexiones, sin necesidad de tener habilitada la interfaz de administración. Consiste en enviar la señal SIGUSR2 al proceso correspondiente al demonio openvpn. De acuerdo a la documentación oficial de OpenVPN, esta señal está reservada para que el demonio vuelque las estadísticas de conexiones en el syslog.
En los sistemas Unix, las señales a procesos se envían con el comando kill
. El cual, a pesar de su nombre, no sirve sólo para matar procesos, sino para enviar todo tipo de señales:
root@debian7:~# /bin/kill -L 1 HUP 2 INT 3 QUIT 4 ILL 5 TRAP 6 ABRT 7 BUS 8 FPE 9 KILL 10 USR1 11 SEGV 12 USR2 13 PIPE 14 ALRM 15 TERM 16 STKFLT 17 CHLD 18 CONT 19 STOP 20 TSTP 21 TTIN 22 TTOU 23 URG 24 XCPU 25 XFSZ 26 VTALRM 27 PROF 28 WINCH 29 POLL 30 PWR 31 SYS
Como se observa, la opción -L
permite listar todas las señales disponibles en formato de tabla.
Tener en cuenta que algunas shells (como Bash) incluyen su propia versión embebida de kill
, la cual posee diferentes argumentos a la versión de kill
provista por el paquete util-linux
(/bin/kill
).
Para listar las señales utilizando la versión embebida de Bash, se debe utilizar la opción -l
:
root@debian7:~# kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX
Esta versión permite enviar el doble de señales. Notar, sin embargo, que los nombres cambian. Por ejemplo SIGUSR2 corresponde con USR2.
Veamos entonces cómo enviar una señal SIGUSR2 a un demonio openvpn utilizando kill
(esta vez en un sistema OpenBSD). Primero es necesario determinar el PID (process ID) del demonio:
# ps -ax | grep "[o]penvpn --daemon" 66790 ?? Ss 114:09.05 /usr/local/sbin/openvpn --daemon --config /etc/openvpn/server.conf # ps -ax -o pid,command | grep "[o]penvpn" | cut -d' ' -f1 66790
Luego enviar la señal USR2 utilizando kill
:
# kill -USR2 66790
Al enviar esta señal, el demonio envía información estadística sobre conexiones al log del sistema:
# less /var/log/messages
Al abrir el log del sistema con algún visor de textoi, al final del log se encontrará información como la siguiente:
Sep 11 09:02:54 obsd openvpn[66790]: OpenVPN CLIENT LIST Sep 11 09:02:54 obsd openvpn[66790]: Updated,Tue Sep 11 09:02:54 2018 Sep 11 09:02:54 obsd openvpn[66790]: Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since Sep 11 09:02:54 obsd openvpn[66790]: juan.topo,192.168.123.45:49598,12591,12503,Tue Sep 11 08:24:05 2018 Sep 11 09:02:54 obsd openvpn[66790]: reverendo.alegria,192.168.123.33:33376,3871714,3806955,Tue Sep 4 09:25:52 2018 Sep 11 09:02:54 obsd openvpn[66790]: juan.topo,192.168.123.13:65424,1860375,1233229,Sat Sep 8 09:40:25 2018 Sep 11 09:02:54 obsd openvpn[66790]: hombre.duff,192.168.123.201:24263,30744,29671,Tue Sep 11 07:24:21 2018 Sep 11 09:02:54 obsd openvpn[66790]: ROUTING TABLE Sep 11 09:02:54 obsd openvpn[66790]: Virtual Address,Common Name,Real Address,Last Ref Sep 11 09:02:54 obsd openvpn[66790]: 10.8.0.10,reverendo.alegria,192.168.123.33:33376,Tue Sep 4 09:25:54 2018 Sep 11 09:02:54 obsd openvpn[66790]: 10.8.0.18,juan.topo,192.168.123.45:49598,Tue Sep 11 08:24:05 2018 Sep 11 09:02:54 obsd openvpn[66790]: 10.8.0.14,hombre.duff,192.168.123.201:24263,Tue Sep 11 07:24:23 2018 Sep 11 09:02:54 obsd openvpn[66790]: 10.8.0.6,juan.topo,192.168.123.13:65424,Sat Sep 8 09:40:26 2018 Sep 11 09:02:54 obsd openvpn[66790]: GLOBAL STATS Sep 11 09:02:54 obsd openvpn[66790]: Max bcast/mcast queue length,0 Sep 11 09:02:54 obsd openvpn[66790]: END
La salida muestra una lista de clientes y tabla de ruteo.
Con el objetivo de simplificar y automatizar este proceso, me dispuse a crear un pequeño script. La idea es recuperar del log de sistema sólo la salida correspondiente a la respuesta del demonio a la señal USR2. Para ello se me ocurrió enviar un mensaje especifico al log del sistema, a modo de flag que permita delimitar claramente el comienzo de la respuesta. Este mensaje debe ser único para cada ejecución del script, con lo cual la fecha actual en un formato único es una buena opción.
Primero es necesario recuperar el PID del demonio openvpn en una variable:
# OPENVPN_PID=$(ps -ax -o pid,command | grep "[o]penvpn" | cut -d' ' -f1) # echo $OPENVPN_PID 66790
Luego crear el flag utilizando la fecha actual en un formato particular:
# OPENVPN_STATUS=$(date +%Y%m%d%H%M%S) # echo $OPENVPN_STATUS 20180911114324
Finalmente se debe enviar la señal USR2 igual que en el ejemplo, y cortar el log desde la aparición del flag hasta la próxima aparición de la cadena "END".
El código fuente del script es el siguiente:
#!/bin/sh # Configuration # WAIT: Amount of seconds to wait for the client list # SYSLOG: OpenVPN daemon logs location # # On most Linux systems: # SYSLOG=/var/log/syslog # # On OpenBSD systems: # SYSLOG=/var/log/messages # WAIT=2 SYSLOG=/var/log/syslog # Get openvpn daemon PID OPENVPN_PID=$(ps -ax -o pid,command | grep "[o]penvpn --daemon" | cut -d' ' -f1) if [ -z "$OPENVPN_PID" ]; then echo "Server not running?" exit 1 fi # Get current datetime OPENVPN_STATUS=$(date +%Y%m%d%H%M%S) # Send starting message (flag) to syslog logger "OpenVPN Server Status - $OPENVPN_STATUS" # Signal openvpn daemon kill -USR2 $OPENVPN_PID # Wait x seconds sleep $WAIT # Recover openvpn log messages grep -A 1000 $OPENVPN_STATUS $SYSLOG | grep -B 1000 'END'
A su vez, lo he publicado en GitHub: linuxitux / scripts / security / openvpn_status.sh
Ejemplo de uso:
# ./openvpn_status.sh Sep 11 13:01:02 obsd bowman: OpenVPN Server Status - 20180911130102 Sep 11 13:01:02 obsd openvpn[66790]: OpenVPN CLIENT LIST Sep 11 13:01:02 obsd openvpn[66790]: Updated,Tue Sep 11 13:01:02 2018 Sep 11 13:01:02 obsd openvpn[66790]: Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since Sep 11 13:01:02 obsd openvpn[66790]: juan.topo,192.168.123.45:49598,83128,81351,Tue Sep 11 08:24:05 2018 Sep 11 13:01:02 obsd openvpn[66790]: reverendo.alegria,192.168.123.33:33376,3963434,3897123,Tue Sep 4 09:25:52 2018 Sep 11 13:01:02 obsd openvpn[66790]: juan.topo,192.168.123.13:65424,1964241,1301889,Sat Sep 8 09:40:25 2018 Sep 11 13:01:02 obsd openvpn[66790]: hombre.duff,192.168.123.201:24263,101226,98479,Tue Sep 11 07:24:21 2018 Sep 11 13:01:02 obsd openvpn[66790]: ROUTING TABLE Sep 11 13:01:02 obsd openvpn[66790]: Virtual Address,Common Name,Real Address,Last Ref Sep 11 13:01:02 obsd openvpn[66790]: 10.8.0.10,reverendo.alegria,192.168.123.33:33376,Tue Sep 4 09:25:54 2018 Sep 11 13:01:02 obsd openvpn[66790]: 10.8.0.18,juan.topo,192.168.123.45:49598,Tue Sep 11 08:24:05 2018 Sep 11 13:01:02 obsd openvpn[66790]: 10.8.0.14,hombre.duff,192.168.123.201:24263,Tue Sep 11 07:24:23 2018 Sep 11 13:01:02 obsd openvpn[66790]: 10.8.0.6,juan.topo,192.168.123.13:65424,Sat Sep 8 09:40:26 2018 Sep 11 13:01:02 obsd openvpn[66790]: GLOBAL STATS Sep 11 13:01:02 obsd openvpn[66790]: Max bcast/mcast queue length,0 Sep 11 13:01:02 obsd openvpn[66790]: END
Así como utilicé grep
podría haber utilizado sed
o awk
. Tener en cuenta que (al resolver el corte con grep
, en lugar de utilizar una herramienta más potente) si hay unos 500 usuarios conectados, el script probablemente falle al recuperar el texto. En tal caso será necesario optar por una mejor solución con awk
o sed
. Se aceptan sugerencias, o mejor pull requests, para recuperar el texto desde la línea que contiene $OPENVN_STATUS hasta la que contiene la cadena "END".
Referencias
- Controlling a running OpenVPN process
- Gestión de Procesos en GNU/Linux
- Cómo enviar un mensaje al log del sistema en GNU/Linux
man signal
man kill