En muchas ocasiones sucede que es necesario tener una política de cifrado de logs y no siempre es fácil 'convencer' a los servidores para que guarden la información cifrada.
Para abordar ese tipo de escenarios he liberado la versión 0.1 (mini-beta?) de libCryptoLog que permite manipular 'al vuelo' la forma en la que cualquier servidor guarda los logs. Y digo cualquiera porque en realidad, si bien este post va de Apache, el método que voy a explicar sirve para cualquier otro servicio (Postfix, Nginx ...).
De lo que se trata es de 'convencer' al servidor en cuestión de que toda información que vuelque al fichero que nos interese cifrar se almacene cifrada desde el minuto 0.
Después de pensar y re-pensar una forma que me convenciese para abordar este problema, programé libCryptoLog con la idea de que fuese 'plugeable', es decir, en vez de hardcodear en la librería las rutinas de cifrado, he decidido que eso caiga en un 'helper' externo para que cualquiera pueda adaptarlo fácilmente a sus requerimientos.
El funcionamiento es fácil: Por lo general, cuando Apache o cualquier otro servidor van a escribir a disco, suelen emplear dos funciones: fprintf() o write(), en el caso de Apache el 'meollo' está en write().
Como muchos de vosotros en este punto ya habréis deducido, lo que estoy haciendo es 'hookear' ambas funciones y alterar la forma en la que procesan los datos que van a almacenar a fichero.
El procedimiento es muy fácil de entender:
1- fprintf() y write() toman una cadena y la vuelcan a un fichero
2- libCryptoLog intercepta esas llamadas y extrae la cadena de texto
3- Esa cadena de texto se le pasa a un programa externo para que la cifre, le ponga colores o lo que sea
4- libCryptoLog toma el resultado de ese programa y ES ESO lo que almacena empleando fprintf o write
De esa forma los logs, antes de que 'toquen disco' ya están cifrados.
Este modelo permite que cualquiera, en cualquier lenguaje, programe sus propios helpers para decidir como transformar la cadena de texto. Junto con la librería van unos cuantos helpers para cifrar logs con RSA.
Vamos a ver un ejemplo con Apache en Centos 6.5:
Lo primero es descargar la librería desde aquí y descomprimirla con:
# tar -xvzf libCryptoLog.tgz
Para usar los helpers escritos en Perl, es necesario tener disponible la librería perl-Crypt-RSA, la instalamos
# yum -y install perl-Crypt-RSA
Una vez instalada, ya podemos generar nuestras claves publica y privada, es MUY CONVENIENTE que la clave privada no esté en el sitio donde vamos a cifrar por razones más que obvias. Esa clave es la llave para acceder al contenido de los logs por lo que se ha de guardar en un sitio externo donde se vayan a descifrar los logs.
Ejecutando:
# perl rsacreate.pl
Vemos que en el directorio han aparecido dos nuevos ficheros: key.public y key.private
Copiamos key.public a /usr/local/etc/
# cp key.public /usr/local/etc/
Igualmente copiamos el 'helper' a /usr/local/bin
# cp rsacrypt.pl /usr/local/bin/
Este es el script que se encargará de cifrar los logs 'al vuelo' usando la clave pública.
Con esto hecho, toca compilar la librería. Aquí hay una parte que se debe configurar. La función write() toma un ID como parámetro para saber en que descriptor debe escribir. No se puede manipular todas las llamadas a write() porque esa función se usa para muchas cosas, es necesario determinar los IDs que corresponden a los ficheros que vamos a cifrar.
Para obtener esta información debemos buscar un PID de Apache y consultar con lsof los IDs de los ficheros que tiene abiertos.
# ps aux | grep -i httpd
root 1508 0.0 1.6 135832 17328 ? Ss 09:26 0:00 /usr/sbin/httpd
apache 1540 0.0 0.6 135832 6932 ? S 09:26 0:00 /usr/sbin/httpd
apache 1541 0.0 0.6 135832 6932 ? S 09:26 0:00 /usr/sbin/httpd
apache 1542 0.0 0.6 135832 6932 ? S 09:26 0:00 /usr/sbin/httpd
apache 1543 0.0 0.6 135832 6932 ? S 09:26 0:00 /usr/sbin/httpd
apache 1544 0.0 0.6 135832 6932 ? S 09:26 0:00 /usr/sbin/httpd
apache 1545 0.0 0.6 135832 6932 ? S 09:26 0:00 /usr/sbin/httpd
apache 1546 0.0 0.6 135832 6932 ? S 09:26 0:00 /usr/sbin/httpd
apache 1547 0.0 0.6 135832 6932 ? S 09:26 0:00 /usr/sbin/httpd
Y luego
# lsof -p 1508
httpd 1508 root 0r CHR 1,3 0t0 3903 /dev/null
httpd 1508 root 1w CHR 1,3 0t0 3903 /dev/null
httpd 1508 root 2w REG 253,0 3522 398650 /var/log/httpd/error_log
httpd 1508 root 3r CHR 1,9 0t0 3908 /dev/urandom
httpd 1508 root 4u sock 0,6 0t0 10395 can't identify protocol
httpd 1508 root 5u IPv6 10396 0t0 TCP *:http (LISTEN)
httpd 1508 root 6r FIFO 0,8 0t0 10489 pipe
httpd 1508 root 7w FIFO 0,8 0t0 10489 pipe
httpd 1508 root 8w REG 253,0 265970 398649 /var/log/httpd/access_log
httpd 1508 root 1w CHR 1,3 0t0 3903 /dev/null
httpd 1508 root 2w REG 253,0 3522 398650 /var/log/httpd/error_log
httpd 1508 root 3r CHR 1,9 0t0 3908 /dev/urandom
httpd 1508 root 4u sock 0,6 0t0 10395 can't identify protocol
httpd 1508 root 5u IPv6 10396 0t0 TCP *:http (LISTEN)
httpd 1508 root 6r FIFO 0,8 0t0 10489 pipe
httpd 1508 root 7w FIFO 0,8 0t0 10489 pipe
httpd 1508 root 8w REG 253,0 265970 398649 /var/log/httpd/access_log
Justo al final podemos ver qué /var/log/httpd/error_log y
/var/log/httpd/access_log tienen asociados los IDs 2 y 8. Esto se puede ver en la cuarta columna en formato numero+w, en este caso 2w y 8w
/var/log/httpd/access_log tienen asociados los IDs 2 y 8. Esto se puede ver en la cuarta columna en formato numero+w, en este caso 2w y 8w
Como solo queremos que estos dos ficheros queden cifrados (son los que guardan la información sensible ...) editamos el fichero libCryptoLog.c y en la parte de:
int filedesyes[2] = {3, 10};
La cambiamos a
int filedesyes[2] = {2, 8};
Para que la librería sepa cuales llamadas a write debe modificar y cuales simplemente las debe dejar pasar sin hacer nada.
Una vez hecho eso, compilamos:
# gcc -Wall -fPIC -shared -o libCryptoLog.so libCryptoLog.c -ldl -lssl
Y copiamos nuestra flamante nueva librería en /usr/local/lib
# cp libCryptoLog.so /usr/local/lib/
¡¡ Ya casi casi estamos !!
Ahora, para que se produzca el 'hooking' tenemos que instruir al fichero de arranque de Apache para que defina la variable LD_PRELOAD correctamente en los procesos que genere.
# vi /etc/init.d/httpd
En la parte de start localizamos algo como
start() {
echo -n $"Starting $prog: "
LANG=$HTTPD_LANG daemon --pidfile=${pidfile} $httpd $OPTIONS
RETVAL=$?
echo
[ $RETVAL = 0 ] && touch ${lockfile}
return $RETVAL
}
echo -n $"Starting $prog: "
LANG=$HTTPD_LANG daemon --pidfile=${pidfile} $httpd $OPTIONS
RETVAL=$?
echo
[ $RETVAL = 0 ] && touch ${lockfile}
return $RETVAL
}
Y debemos modificarla para que quede algo así (he puesto en negrita lo que he añadido)
start() {
echo -n $"Starting $prog: "
LD_PRELOAD=/usr/local/lib/libCryptoLog.so LANG=$HTTPD_LANG daemon --pidfile=${pidfile} $httpd $OPTIONS
RETVAL=$?
echo
[ $RETVAL = 0 ] && touch ${lockfile}
return $RETVAL
}
echo -n $"Starting $prog: "
LD_PRELOAD=/usr/local/lib/libCryptoLog.so LANG=$HTTPD_LANG daemon --pidfile=${pidfile} $httpd $OPTIONS
RETVAL=$?
echo
[ $RETVAL = 0 ] && touch ${lockfile}
return $RETVAL
}
Y si todo ha ido bien, en cuanto hagamos un stop / start nuestros ficheros de log pasarían a quedar cifrados.
# service httpd stop
# service httpd start
Si hacemos un tail a /var/log/http/error.log ya podemos ver que Apache está guardando la información cifrada:
BEGINCRYPTO
gRNmfi/yS9Vaya37VJ7sM+iZtoYDG976SWPa4XTLnPGccBTd56J8Bk0uLZyK86vopcjdKp2JPDr7
oHWk/TKA00IStIgvTofUH9DeZGepqikIkjJg9wylAJ0ROjpcerozOX1LQWuj+ZoOxRu7K+UIeQmc
389SjDAyqNs/U8UHc75ntbVHy/A1e95fWUAHnkcD/1au463ugNHQmCJoSHA4NgwhDmwUJLafWSKr
T/L6BaOsruxDtkUqu0gBfROadVuc9oALSdRSc5WqA3T5HuS10a49szZ5zedqtQJiQFjikJCRo/v6
tzYHHs3Es+8yfpZti/l3pChW8+zHCxuPRKNccg==
ENDCRYPTO
BEGINCRYPTO
VB68V3MyG7yNHfYc8UR69ZbaC4ztBkOigWnKZzlKTMiXNdSBFEJ++TPKQXUFo4j8AfrgQPL6DQQ8
nd0yoMSaA3ojq+MvBY5cSLstVeEGaIJSXRboZMGyq6UpfOAqvWLvd48w63ND9cKKDBkEQcfUM3a7
S5KPss/qqSKYcsSHsqk=
ENDCRYPTO
BEGINCRYPTO
DN0d0oa8DJrVj7GGuiGUuhRMc8bDIPGUL39Ae51YFwZl1mRoq5EgipcTxyPThTngw7rOobUGrCaS
YY/MlO4FV4HGAsBWsQksfqYzsgbC7Lv+Ek4oe+01rhJ2L70BKPWPiRdr9oQHsWcL8aTDtLj8X/H4
R3XJ76mvNUSOPAPjijE2WZiAIRYmU+0fzTrbJTYaV3GrGYm2rWkoFQu7aImQHqWRsnSs9k9RmLij
3KoX/MpBBd+a0hOhBsZQmLf915VJgp5E2zrqXMwutdyS3+UJ7o+lesK8LfC2jl40aYvVZXZXNPv+
uDsNbkPJIRzXpfmwnR5KtMFcNOdEJ+1378BRrQ==
ENDCRYPTO
gRNmfi/yS9Vaya37VJ7sM+iZtoYDG976SWPa4XTLnPGccBTd56J8Bk0uLZyK86vopcjdKp2JPDr7
oHWk/TKA00IStIgvTofUH9DeZGepqikIkjJg9wylAJ0ROjpcerozOX1LQWuj+ZoOxRu7K+UIeQmc
389SjDAyqNs/U8UHc75ntbVHy/A1e95fWUAHnkcD/1au463ugNHQmCJoSHA4NgwhDmwUJLafWSKr
T/L6BaOsruxDtkUqu0gBfROadVuc9oALSdRSc5WqA3T5HuS10a49szZ5zedqtQJiQFjikJCRo/v6
tzYHHs3Es+8yfpZti/l3pChW8+zHCxuPRKNccg==
ENDCRYPTO
BEGINCRYPTO
VB68V3MyG7yNHfYc8UR69ZbaC4ztBkOigWnKZzlKTMiXNdSBFEJ++TPKQXUFo4j8AfrgQPL6DQQ8
nd0yoMSaA3ojq+MvBY5cSLstVeEGaIJSXRboZMGyq6UpfOAqvWLvd48w63ND9cKKDBkEQcfUM3a7
S5KPss/qqSKYcsSHsqk=
ENDCRYPTO
BEGINCRYPTO
DN0d0oa8DJrVj7GGuiGUuhRMc8bDIPGUL39Ae51YFwZl1mRoq5EgipcTxyPThTngw7rOobUGrCaS
YY/MlO4FV4HGAsBWsQksfqYzsgbC7Lv+Ek4oe+01rhJ2L70BKPWPiRdr9oQHsWcL8aTDtLj8X/H4
R3XJ76mvNUSOPAPjijE2WZiAIRYmU+0fzTrbJTYaV3GrGYm2rWkoFQu7aImQHqWRsnSs9k9RmLij
3KoX/MpBBd+a0hOhBsZQmLf915VJgp5E2zrqXMwutdyS3+UJ7o+lesK8LfC2jl40aYvVZXZXNPv+
uDsNbkPJIRzXpfmwnR5KtMFcNOdEJ+1378BRrQ==
ENDCRYPTO
Ahora queda la parte final ¿y como revierto el cifrado para tener el log original? Para eso, hay un script llamado rsadecrypt.pl que toma como parámetro un fichero entrada y otro salida, en este caso entrada sería error.log y salida error2.log, quedando en este último el formato descifrado.
# perl rsadecrypt.pl /var/log/httpd/error_log error2.log
Si hacemos un tail en error2.log
[Fri Jun 06 09:55:34 2014] [notice] suEXEC mechanism enabled (wrapper: /usr/sbin/suexec)
[Fri Jun 06 09:55:34 2014] [notice] Digest: generating secret for digest authentication ...
[Fri Jun 06 09:55:34 2014] [notice] Digest: done
[Fri Jun 06 09:55:34 2014] [notice] Apache/2.2.15 (Unix) DAV/2 PHP/5.3.3 mod_perl/2.0.4 Perl/v5.10.1 configured -- resuming normal operations
[Fri Jun 06 09:55:34 2014] [notice] Digest: generating secret for digest authentication ...
[Fri Jun 06 09:55:34 2014] [notice] Digest: done
[Fri Jun 06 09:55:34 2014] [notice] Apache/2.2.15 (Unix) DAV/2 PHP/5.3.3 mod_perl/2.0.4 Perl/v5.10.1 configured -- resuming normal operations
Vemos que lo que antes era base64 cifrada, ahora es plain text :)
Evidentemente no todo es gratis, el añadir este 'extra' de cifrado penaliza bastante el rendimiento porque las operaciones RSA son 'pesadas' y además desde un script aun más.
Hay que tener mucho cuidado en qué servidores se activa esta medida de seguridad y si tienen la capacidad adecuada para soportar el cambio.
Como última cosa, reiterar que en este ejemplo se están usando mis scripts para tratar los logs, en el código de la librería se puede ver el comando definido
char *command = "perl /usr/local/bin/rsacrypt.pl \"";
Pero ahí puede ir cualquier cosa que trate el texto de los logs.
7 comments :
Me lo guardo, para leer con calma que muy bien lo merece.
Saludos.
Muchas gracias, si algo no hace lo que se supone que debe hacer, por favor avísame :)
¡Enhorabuena! Pedazo de curro y herramienta fantástica! A favoritos :)
Muchas gracias !!
Quiero agradecer al Dr. Olokum para llevar alegría y felicidad a mi relación y mi familia. Perdí a mi novio, y yo requería ayuda hasta que me encontré con el Dr. Olokum el verdadero hechicero, Y él me aseguró que voy a tener mi novio de vuelta en dos días después de que el hechizo se ha fundido. dos días después, mi teléfono sonó, y así sorprendentemente, era mi novio, que no me ha llamado por mucho tiempo, e hizo una apología de la rotura del corazón, y me dijo que él está dispuesto a dedicar el resto de su vida conmigo. ola princetess lo soltó hasta saber lo mucho que me amaba y quería que él .. en este momento yo y mi novio es vivir una vida feliz y nuestro amor es ahora más fuerte que la forma en que eran incluso antes de nuestro descanso up..All agradecimiento a DR Olokum por el excesivo trabajo que ha hecho por mí. A continuación se muestra que la dirección de correo electrónico en la situación en la que está siendo sometido a una rotura del corazón, y te aseguro que a medida que la mina ha hecho por mí, ella definitivamente le ayudará también. Email: LAVENDERLOVESPELL@YAHOO.COM
Muy arriesgado el usar unicamente el numero de filedes. Me parece un poco sin sentido usar el base64 en openssl en la lib y luego tener que recurrir a un helper en perl para hacer el cifrado (ademas de deshacer y hacer base64 nuevamente en el helper)!
Hola, lo de arriesgado por el ID es relativo, si la cargas en ld.so.preload para *todos* los procesos claramente es un suicidio, por eso propongo cargarlo por variable y por cada proceso al que previamente has buscado los IDs, tengo varias instalaciones con meses de uptime sin problemas. Salvo que me pongas un ejemplo y demuestres lo contrario, yo diría que los IDs son fijos
Lo del base64 es simple y llanamente para curarme en salud y saber que los datos que llegan al helper son confiables y no llevan veneno. Es una medida de seguridad ya que voy a usar un comando externo y claro, eso suele ser problemático cuando toma parámetros.
Respecto a meter o no meter las rutinas de cifrado en la librería, ya he explicado los motivos, principalmente la flexibilidad de que cualquiera pueda crear un helper en C, Python o Perl y tratar el log como quiera, sin estar 'vendido' a que yo, por ejemplo, cifre con RSA y tu quieras usar otro algoritmo.
Publicar un comentario