PfCentinel V 1.0 Beta – Manager Update Platform/Packages for pfSense Multisite
-
Hola
Una aportación a la causa :)
pfCentinel V 1.0 Beta – Manager Update Platform/Packages for pfSense Multisite
pfCentinel V 1.0 Beta – Gestor de actualizaciones de plataforma/paquetes para varios sitios pfSenseEsta utilidad sirve para tener un control de actualizaciones de la plataforma y los paquetes de varios pfSense.
Son dos scripts en php que se ejecutan desde un host pfSense (al que llamaré central) y efectua vía php conexiones ssh2 sobre los hosts pfSense remotos a controlar sus actualizaciones con comandos ssh/bash
Condiciones necesarias:
Tanto el host pfSense (central) que corre los scripts como los hosts pfSense remotos para comprobar las actualizaciones deben de tener activada la Consola ssh
( Enable Secure Shell – sshd ) y ser accesibles vía puerto tcp22.(Esto es fácil de comprobar: hacer desde la shell del pfSense central un ssh sobre otro pfSense remoto
para el usuario root: # ssh root@ip–otro-pfSense y ver si hay conectividad y acceso/validación ok)Condiciones recomendables:
Se recomienda que el host que ejecuta los scripts se conecte a los otros hosts vía Tunel (openVPN o IPsec), ya que se supone que los otros hosts son remotos y externos a la LAN del pfSense Central. Y por defecto pfSense bloquea en su interfaz WAN este tipo de acceso.
Es decir evitar dejar un acceso ssh vía WAN a los pfSense y trabajar de forma segura vía tunnel.La utilidad se compone de 2 scripts php
1.- pfCentinel-Setup.php
2.- pfCentinel-central.phpEl 1.- pfCentinel-Setup.php sirve para crear la base de datos de los hosts a controlar o añadir más host a esa bbdd.
La bbdd es un simpe fichero csv, alojado en la ruta /scripts/machines.csv
La bbdd guarda en sus registros los campos:
“descr”;”host”;”platform”;”packages”;”user”;”password”
-Descripción del pfSense remoto, por ejemplo: pfMadrid
-Host del pfSense remoto, la IP del pfSense remoto (altamente recomendable que sea una IP de un tunel openVPN o IPSec)
-Plataforma: la plataforma del pfSense remoto y si necesita update y la versión disponible del update
Valores posibles (por ejemplo):CURRENT-2.3.1_5 = Esta al día, no hay update
2.3-UPDATE-to-2.3.1_5 = HAY UN UPDATE (versión actual – UPDATE a – versión disponible)
NO_CONEXION = No hay conectividad al host remoto
ERROR_AUTENTICACION = Hay conectividad pero hay un error de validación ssh2 de usurio:clave en host remoto
-Packages: Si existe algún paquete para actualizar
Valores posibles:CURRENT = Está al día, no hay updates de paquetes
NO_CONEXION = No hay conectividad al host remoto
ERROR_AUTENTICACION = Hay conectividad pero hay un error de validación ssh2 de usurio:clave en host remoto
UPDATES = Hay actualizaciones de paquetes disponibles.
-User, el usuario para conectarse vía ssh2 al host remoto, usualmente root
-Password, la clave de User para acceder vía ssh2 al host remoto.
En este campo se guarda la clave encriptada para cada hostSe recomienda alojar este script php cli: pfCentinel-Setup.php en un directorio no accesible desde web gui, como por ejemplo el directorio /scripts , o /tmp
Para ejecutar este script (se hace vía shell), si estuviera alojado en la carpeta scripts:
/usr/local/bin/php /scripts/pfCentinel-Setup.php
Al ejecutar viá shell o cli o consola el script pfCentinel-Setup.php, la primera vez, nos irá preguntando por los datos de cada host
(cuando pregunte la clave, no se verá por consola, y se guarda de forma encriptada en machines.csv, por seguridad)[2.3-RELEASE][root@pfLEON.localdomain.local]/root: /usr/local/bin/php /scripts/pfCentinel-Setup.php
###################################################
# Setup pfCentinel 2016 #
###################################################
#by Javier Castan?on.javcasta https://javcasta.com#
###################################################
###################################################
# host pfCentinel-Setup.php in /tmp folder #
###################################################run via shell: # php /tmp/pfCentinel-Setup.php
###################################################
…
Introduccion de datos para Host 1
Introduce descripcion Host (p.e: pfMadrid): pftest1
Introduce IP Host (p.e: 10.0.0.254): 10.20.10.204
Introduce el usuario para el host pftest1 (p.e: root): root
Introduce la clave para el usuario root:
Testeando conectividad, usurio:clave ...
Host: 10.20.10.204NO CONEXION
An?adimos pftest1 a /scripts/machines.csv ?(y/n):n
Continuamos? (y/n): y
...
Introduccion de datos para Host 2
Introduce descripcion Host (p.e: pfMadrid):
...Y hará un test de conectividad, si este da NO CONEXION es que no hay conectividad a ese host vía ssh2,
pero se puede añadir a la bbdd ( An?adimos pftest1 a /scripts/machines.csv ?(y/n):y ) u optar por no añadirloSi da un ERROR DE AUTENTICACION, idem al caso anterior, lo normal seria no añadirlo a la bbdd hasta que nos de un Test OK
Introduccion de datos para Host 3
Introduce descripcion Host (p.e: pfMadrid): pferraut
Introduce IP Host (p.e: 10.0.0.254): 192.168.56.243
Introduce el usuario para el host pferraut (p.e: root): root2
Introduce la clave para el usuario root2:
Testeando conectividad, usurio:clave …
Host: 192.168.56.243ERROR DE AUTENTICACION
An?adimos pferraut a /scripts/machines.csv ?(y/n):n
Continuamos? (y/n):yY si el test es OK nos mostrará el estado de UPDATES de plataforma y paquetes
…
Introduccion de datos para Host 1
Introduce descripcion Host (p.e: pfMadrid): pfTest5
Introduce IP Host (p.e: 10.0.0.254): 192.168.56.243
Introduce el usuario para el host pfTest5 (p.e: root): root
Introduce la clave para el usuario root:
Testeando conectividad, usurio:clave ...
Host: 192.168.56.243HAY UPDATES PAQUETES: Paquetes a actualizar:
pfSense-pkg-openvpn-client-export-1.3.7_1
pfSense-pkg-pfBlockerNG-2.0.10
pfSense-pkg-squid-0.4.16_2
pfSense-pkg-squidGuard-1.14_2
pfSense-pkg-suricata-3.0_6Version pfSense actual: 2.3
Version pfSense disponible: 2.3.1_5
HAY UPDATE PLATAFORMA
An?adimos pfTest5 a /scripts/machines.csv ?(y/n):y
Para cortar el bucle del script, a la pregunta:
Continuamos? (y/n):n
Contestar n ( = no), y para continuar, contestar y ( = yes)
Si ya existe el fichero de la bbdd /scripts/machines.csv al volver a ejecutarse este script,
nos avisará mostrando la bbdd y dandonos opción a añadir más hostsUna vez tengamos la bbdd lista, ya podemos ejecutar desde web gui el script:
2.- pfCentinel-central.php
Este script hay que alojarlo en la ruta:
/usr/local/www/
o
/usr/local/www/pfCentinel/ (u otra subcarpeta debajo de www)para que sea accesible vía web gui.
Accedemos a la web gui del pfSense central, nos validamos user:pass, y en el navegador indicamos la ruta al script:
Por ejemplo:https://10.20.10.203/pfCentinel/pfCentinel-central.php
si lo hubiésemos alojado el script en /usr/local/www/pfCentinel-central.php el link seria:
https://10.20.10.203/pfCentinel-central.php
Para 5 hosts tardará de media 1 minuto o menos el script pfCenter-central.php
Por cada host se toma de 10 a 30 sg, en mi entorno de red, para unos 9 host me ha tardado un minuto (58sg).
Hasta que el script no haya recorrido todos los registros de /scripts/machines.csv no va a mostrar nada en el navegador
así que pacienciaAsí que recomiendop no tener una bbdd de más de 15 registros, ya que puede dar el error Gateway Time Out el script y no mostrar nada.
Por lo que para empezar yo probaria la primera vez el script con no más de 10 host pfSense en la bbdd, con 5 seria un buen test.
(De todas formas pocos admins tienen más de 15 pfSense que administrar, aunque haberlos haylos)
(En otra versión más adelante cuando controle mejor AJAX (solo con php no se puede mostrar cada registro uno a uno),
podré subsanar esto haciendo que se vaya mostrando cada registro en el navegador en tiempo real)Una vez termine el scriopt se mostrarán los resultados, y en caso de algún registro con un UPDATE presentará el link para su actualización
(el link funcionará si hay una web gui validada a ese host pfSense)Se podria automatizar la actualización de la platforma y/o paquetes vía script, pero creo que es un asunto delicado dejar automatizada
updates en un firewall, ya que es la piedra angular o factor crítico de cualquier infraestructura de una red.
Mejor que sea asistida por un técnico o admin, siempre pueden haber sorpresas…La página que muestra tiene un campo: Remote Date Last refresh. Muestra la última comprobación de Update del script, pero con la fecha y hora remota
,no la del host centralLa tabla que se muestra en la página se puede ordenar clikando en los campos, como por ejemplo en el campo: Remote Date Last refresh
La página tiene un botón Refresh, para volver a lanzar el script, no recomiendo hacer esto más de una o dos veces al día en un pfSense central en
producción con alta carga de red, ya que el script abre sockets (tcp22) y consume tiempo de cpu y algo de cantidad de ram, y ya se sabe que un firewall, cuanto
menos se le sature, mejor.Si se ejecutara este script pfCentinel-central.php sin que exista /scripts/machines.csv daria el aviso:
Ejecute vía shell pfCentinel-Setup.php para crear fichero machines.csv, y depues de crearlo, refresque esta página.
EXECUTE via shell pfCentinel-setup.php for CREATE FILE machines.csv:
after create it, refresh this pageSalu2
Referencia:
https://www.javcasta.com/pfcentinel-v-1-0-beta-manager-update-platformpackages-for-pfsense-multisite/Descarga/Download pfCentinel-V1-Beta: http://www.javcasta.com/?smd_process_download=1&download_id=33256
-
Me funciona bien. Andaba buscando algo parecido hace tiempo. Gracias
-
Hola.
Gracias a ti por comunicar su funcionalidad. :)
Salu2
-
Hola
Posteo el código (está en mi site, pero a modo de backup y poder leerlo sin hacer download es lo mejor)
/scripts/pfCentinel-Setup.php
/* pfCentinel-Setup.php Copyright (c) 2016 Javier Castañon javier@javcasta.com - https://javcasta.com/ All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1\. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2\. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ set_time_limit(0); ini_set('max_execution_time', '0'); //si no existe dir /scripts lo creamos if(!is_dir('/scripts')) { mkdir('/scripts'); } $existemachines = false; if (file_exists('/scripts/machines.csv')) { $existemachines = true; } echo "\033[34m################################################### \033[0m \n"; echo "\033[31m# Setup pfCentinel 2016 # \033[0m \n"; echo "\033[34m################################################### \033[0m \n"; echo "\033[34m#by Javier Castan?on.javcasta https://javcasta.com# \033[0m \n"; echo "\033[34m################################################### \033[0m \n"; echo "\033[34m################################################### \033[0m \n"; echo "\033[34m# host pfCentinel-Setup.php in /tmp folder # \033[0m \n"; echo "\033[34m################################################### \033[0m \n"; echo "\033[34m# run via shell: # php /tmp/pfCentinel-Setup.php # \033[0m \n"; echo "\033[34m################################################### \033[0m \n"; // rutina introduccion datos > /scrips/machines.csv $goon = true; $i = 0; // condiciones necesarias: // cada host tenga habilitado sshd (Enable Secure Shell) en puerto tcp22 // y no tengan restricciones a conectarse vía ssh al tcp22 desde otro host // por lo que es muy recomendable que las IPs de los hosts sean accesibles // vía tunel openVPN o IPSec , etc //Para 5 hosts de media tardará 1 minuto el script pfCenter-central.php //por cada host se toma de 10 a 30 sg, para 5 hosts: [ 50 .. 150 ] sg if ($existemachines) { echo "\n Ya existe /scripts/machines.csv \n"; $muestra = shell_exec("/bin/cat /scripts/machines.csv"); echo $muestra . "\n"; $adddata = readline("Introducimos mas hosts a /scripts/machines.csv ? (y/n): "); if ($adddata == "n") die("\nFin.\n"); } while ( $goon ) { //&& $i <= 4 $i++; echo "...\n"; echo "\033[31m Introduccion de datos para Host $i \033[0m \n"; $descripcion = readline("Introduce descripcion Host (p.e: pfMadrid): "); $elhost = readline("Introduce IP Host (p.e: 10.0.0.254): "); $eluser = readline("Introduce el usuario para el host $descripcion (p.e: root): "); echo "Introduce la clave para el usuario $eluser: "; `/bin/stty -echo`; $lapass = readline(); `/bin/stty echo`; echo "\n"; echo "Testeando conectividad, usurio:clave ...\n"; @fdoit($elhost, 22, $eluser, $lapass); $escribir = readline("An?adimos $descripcion a /scripts/machines.csv ?(y/n):"); if ($escribir == "y") fmachinesupdate($descripcion, $elhost, "xxx", "xxx", $eluser, fenydesencripta($lapass, true)); $continuar = readline("Continuamos? (y/n): "); if ( $continuar == "n" ) $goon = false; } echo "END.\n"; function fdoit($host, $port, $username, $password) { $paquetes = ""; $comando = "pkg version > /tmp/pfcenter-status.tmp"; fshh($host, $port, $username, $password, $comando); echo "Host: $host\n"; echo "=====================\n"; $comando = "cat /tmp/pfcenter-status.tmp | grep '^pfSense-pkg' | grep '<' | cut -f 1 -d ' '"; $paquetes = @fshh($host, $port, $username, $password, $comando); if (strpos($paquetes, "ERROR_AUTENTICACION") !== false) { echo "ERROR DE AUTENTICACION\n"; return "ERROR DE AUTENTICACION"; } if (strpos($paquetes, "NO_CONEXION") !== false) { echo "NO CONEXION\n"; return "NO CONEXION"; } if (strlen($paquetes) < 5) { echo "NO HAY UPDATES PAQUETES\n"; echo " \n"; } else echo "HAY UPDATES PAQUETES: Paquetes a actualizar:\n" . $paquetes . "\n"; $comando = "cat /tmp/pfcenter-status.tmp | grep '^pfSense-2' | cut -c 9- | cut -f 1 -d ' '"; $vactual = fshh($host, $port, $username, $password, $comando); echo "Version pfSense actual: " . $vactual . "\n"; $comando = "pkg rquery %v pfSense"; $vdisponible = fshh($host, $port, $username, $password, $comando); echo "Version pfSense disponible: " . $vdisponible . "\n"; if ($vactual !== $vdisponible) echo "HAY UPDATE PLATAFORMA\n"; echo " \n"; } function fpingssh($phost, $pport) { $waitTimeoutInSeconds = 2; try { if($fp = @fsockopen($phost,$pport,$errCode,$errStr,$waitTimeoutInSeconds)){ // It worked return true; } else { // It didn't work return false; } fclose($fp); } catch(Exception $e) { return false; } } function fshh ( $vhost, $vport, $vuser, $vpass, $vcomando ){ //if(!$connection) $connection = ssh2_connect($vhost, $vport)or die("The SSH2 connection could not be established."); if(fpingssh($vhost, $vport)) { set_time_limit(30); if(!$connection) { $connection = ssh2_connect($vhost, $vport); if (!$authentication) { $errorautenticacion = false; $authentication = @ssh2_auth_password($connection, $vuser, $vpass) or $errorautenticacion = true; //die("Could not authenticate '{$vuser}'"); if ($errorautenticacion) return "ERROR_AUTENTICACION"; $stream = ssh2_exec($connection, $vcomando) or die("Error comando..."); stream_set_blocking( $stream, true ); $cmd = stream_get_contents($stream); return $cmd; fclose($stream); sleep(1); } else return "NO_CONEXION"; } } else { return "NO_CONEXION"; } //flush(); } function fenydesencripta($vcadena, $modo) { //AES-256 / CBC / ZeroBytePadding - ref http://php.net/manual/es/function.mcrypt-encrypt.php $key = pack('H*', "dcb34c7d113acd07b53763052cef08cc66ace029fddbae4e1d427a1cfb2a10b2"); $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC); $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND); if ($modo) { // $modo = true => encripta $ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $vcadena, MCRYPT_MODE_CBC, $iv); $ciphertext = $iv . $ciphertext; $ciphertext_base64 = base64_encode($ciphertext); return $ciphertext_base64; } else { // $modo = false => desencripta $ciphertext_dec = base64_decode($vcadena); $iv_dec = substr($ciphertext_dec, 0, $iv_size); $ciphertext_dec = substr($ciphertext_dec, $iv_size); $plaintext_dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec); return $plaintext_dec; } } function fmachinesupdate($vdescr, $vhost, $vplatform, $vpackage, $vuser, $vpass) { $cabeza = '"descr";"host";"platform";"packages";"user";"password"'."\n"; if (!file_exists('/scripts/machines.csv')) file_put_contents('/scripts/machines.csv', $cabeza ); $vlinea = '"'.$vdescr.'"'.';'.'"'.$vhost.'"'.';'.'"'.$vplatform.'"'.';'.'"'.$vpackage.'"'.';'.'"'.$vuser.'"'.';'.'"'.$vpass.'"'."\n"; file_put_contents('/scripts/machines.csv', $vlinea, FILE_APPEND | LOCK_EX); } ?>[/code] Salu2
-
Hola
Y el código de /usr/local/www/pfCentinel-central.phpEste script php, tarda entre 10sg a 30sg por host... paciencia :)"."[Feel Free to paypal me][0] [ --- By Javier Castan?on - @JavCasta - 2016][1]"; $form = new Form; $section = new Form_Section('pfCentinel: CENTRAL (highly recommended to work with tunnels openVPN or IPsec)'); $form->add($section); //si no existe dir /scripts lo creamos if(!is_dir('/scripts')) { mkdir('/scripts'); } $existemachines = false; if (file_exists('/scripts/machines.csv')) { $existemachines = true; } //csv file to array function csv_in_array($url, $delm=";", $encl="\"", $head=true) { $out = Array(); //ref http://php.net/manual/es/function.file.php#105772 $csvxrow = file($url); // ---- csv rows to array ---- $csvxrow[0] = chop($csvxrow[0]); $csvxrow[0] = str_replace($encl,'',$csvxrow[0]); $keydata = explode($delm,$csvxrow[0]); $keynumb = count($keydata); if ($head === true) { $anzdata = count($csvxrow); $z=0; for($x=1; $x
Salu2
-
Estimado. Si pongo nombre de máquina en algún caso en vez de la ip de tunel de la máquina no funciona, da no conexion.
que puede ser?. Gracias por su ayuda. -
Hola
Si pones nombre de máquina o fqdn en el campo host, dependiendo de los registros de tu servicio dns, traducirá ese nombre de host tal vez a una ip pública o una privada (que no sea la del tunel), y puede que en esa interfaz de ese pfSense no tengas habilitado vía reglas del firewall el acceso a sshd (tcp22). Revisa los logs del firewall. Por eso es preferible poner la IP del tunel del host.
Por cierto, no se recomienda dejar acceso a sshd vía ip pública wan en un firewall pfsense, ya que siempre hay scaneos e intentos de acceso vía ssh por fuerza bruta y si sale un zero day para ssh/freebsd, pues peor todavía :) .
Salu2
-
Gracias estimado.
-
Hola
De nada.
En unos días (o semanas, depende). Pondré la versión 2 de este script.
En lugar de un fichero csv, usaré un fichero de bbdd sqlite3 y espero implementar bién lo que me falta de AJAX para que se vea en tiempo real el progreso de todos los registros de los hosts.
Salu2
-
Hola
Una duda que me ha llegado por correo (email).
Me preguntan si el propio pfSense (central) que ejecuta el script se puede incluir en la bbdd machines.csv.
La respuesta es sí (siempre que se defina para ese host en machines.csv la IP de una interfaz LAN o de Tunel y no haya restricciones al tcp/22)
Salu2
-
Con poner como ip la de localhost 127.0.0.1 ya va bien para el central. lo he comprobado.
-
Hola
Pues gracias por el dato, no lo había probado con 127.0.0.1 :)
Salu2
-
jajaja, era de cajón. gracias por el tool.
;D -
Ya tenés la v2 de la tool?
-
Hola
Estoy en ello, tengo nivel medio en php (backend), pero poco nivel en AJAX y javascript (frontend), así que tardaré un poco más :)
Salu2
-
+1
-
Ok. Otra forma de desir que andás en vacaciones. ;D Esperamos
-
Hola
Tras la actualización de pfSense a la v 2.3.2, el script, si se ejecuta desde un pfSense 2.3.2, ya no soporta usar localhost (127.0.0.1) como uno de los registros de machines.csv.
También he comprobado que si se ejecuta el script desde un pfSense con versión menor a 2.3.2, la conexión php ssh (ssh2_connect) contra un pfSense 2.3.2 da error, esto es debido a que pfSense 2.3.2 está usando como algoritmo de intercambio de key ssh2 a curve25519
Por lo tanto, si alguien usa este script, conviene que sepa estas circunstancias y lo ejecute desde un pfSense actualizado a su última versión (desde un 2.3.2 la conexión php ssh a una versión menor 2.3 o 2.3.1 o 2.3.1_5 va bien, lo he testeado).
El script no lo he testeado con versiones 2.2.6 y menores. En un firewall, dejar de actualizar durante mucho tiempo o dejar pasar varias versiones es una brecha de seguridad potencial :)
Salu2