Laboratorio iPXE Flisol 2014

Este Sábado 26 de Abril se realiza en Buenos Aires el “Festival Latinoamericano de instalación de Software Libre” o Flisol para simplificar.

Como en muchas ocasiones en ediciones anteriores me he encargado de la configuración y administración de red, mirror, repositorios, etc (con mayor o menor éxito) aunque puedo decir que cada año he utilizado una configuración cada vez mas sofisticada.

Para este año he decidido publicar con tiempo toda la configuración que usará el server el día del evento en un repositorio de Github con la idea de que cualquiera pueda armar algo similar y con la remota esperanza de que también sea de utilidad a otras sedes, pueden verlo en el siguiente link:
https://github.com/Mstaaravin/flisol-ipxe

Pero he aqui que no todo el mundo tiene la posibilidad de disponer de hardware dedicado (switches, PCs, etc) a armar un laboratorio para probar que el booteo por PXE funcione para todas las distribuciones por lo que decidí simplificar utilizando KVM + Libvirt mas un pequeño hack en la configuración de red que utiliza libvirt para simular una red real.

Yo utilizo KVM para virtualizar y libvirt + virt-manager para administrar todas las VMs en mi desktop/server personal.

Por default cuando utilizamos KVM tenemos disponible una red “default”

1
2
3
4
root@lhome:~# virsh net-list
Name                 State      Autostart
-----------------------------------------
default              active     yes       
root@lhome:~# virsh net-list
Name                 State      Autostart
-----------------------------------------
default              active     yes       

Que podemos ver con mas detalles en virt-manager

lhome Connection Details_001

El objetivo de este post no es explicar todo lo relacionado a la configuración de red que utiliza libvirt, sino modificar la red x default para que nos permita tener booteo PXE en esa red virtual.

Lo primero que debemos hacer es detener la red y su bridge asociado (virbr0)

1
2
root@lhome:~# virsh net-destroy default
Network default destroyed
root@lhome:~# virsh net-destroy default
Network default destroyed

Vemos la configuración que está usando por default

1
2
3
4
5
6
7
8
9
10
11
12
13
root@lhome:~# virsh net-dumpxml default
<network>
  <name>default</name>
  <uuid>5a014735-f658-7a87-c7d4-e6b0f488c332</uuid>
  <forward mode='nat'/>
  <bridge name='virbr0' stp='on' delay='0' />
  <mac address='52:54:00:B5:24:7B'/>
  <ip address='192.168.122.1' netmask='255.255.255.0'>
    <dhcp>
      <range start='192.168.122.2' end='192.168.122.99' />
    </dhcp>
  </ip>
</network>
root@lhome:~# virsh net-dumpxml default
<network>
  <name>default</name>
  <uuid>5a014735-f658-7a87-c7d4-e6b0f488c332</uuid>
  <forward mode='nat'/>
  <bridge name='virbr0' stp='on' delay='0' />
  <mac address='52:54:00:B5:24:7B'/>
  <ip address='192.168.122.1' netmask='255.255.255.0'>
    <dhcp>
      <range start='192.168.122.2' end='192.168.122.99' />
    </dhcp>
  </ip>
</network>

Y le agregamos soporte para PXE con 2 nuevos parámetros
“tftp root=” y “bootp file=”

Hay 2 formas de editar la red (siempre que esté apagada), con:

1
root@lhome:~# virsh net-edit default
root@lhome:~# virsh net-edit default

O manualmente en /etc/libvirt/qemu/networks/default.xml

Y debería quedar así:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
root@lhome:~# virsh net-dumpxml default
<network>
  <name>default</name>
  <uuid>5a014735-f658-7a87-c7d4-e6b0f488c332</uuid>
  <forward mode='nat'/>
  <bridge name='virbr0' stp='on' delay='0' />
  <mac address='52:54:00:B5:24:7B'/>
  <ip address='192.168.122.1' netmask='255.255.255.0'>
    <tftp root='/home/tftp' />
    <dhcp>
      <range start='192.168.122.2' end='192.168.122.99' />
      <bootp file='pxelinux.0' />
    </dhcp>
  </ip>
</network>
root@lhome:~# virsh net-dumpxml default
<network>
  <name>default</name>
  <uuid>5a014735-f658-7a87-c7d4-e6b0f488c332</uuid>
  <forward mode='nat'/>
  <bridge name='virbr0' stp='on' delay='0' />
  <mac address='52:54:00:B5:24:7B'/>
  <ip address='192.168.122.1' netmask='255.255.255.0'>
    <tftp root='/home/tftp' />
    <dhcp>
      <range start='192.168.122.2' end='192.168.122.99' />
      <bootp file='pxelinux.0' />
    </dhcp>
  </ip>
</network>

tftp root=’/home/tftp’ Por supuesto es el root path donde tenemos todos los archivos necesarios para que PXE funcione, hay muchos tutoriales en internet que explicar cómo utilizar dnsmasq (es lo que utiliza libvirt para sus redes) también pueden descargarse toda la configuración desde mi repositorio github mencionado al inicio del post.

Probamos que funcione (no olvidar seleccionar PXE como primera opción de boot en virt-manager)

Filomena

Les presento a Filomena, que me lleva y me trae todos los dias al/del laburo.

Click para agrandar

Click para agrandar

#Cybermonday Argentina, análisis del fiasco…

Este es mi análisis netamente técnico de lo acontecido hoy durante el llamado Cybermonday anunciado en todas partes, pero lo mas destacado en mi opinión fueron 3 cosas.

En primer lugar lo notoriamente mal que ha funcionado la infraestructura de muchos sino la mayoría de los sitios.

En segundo lugar los paupérrimos descuentos, creo que la gente que trabaja en Marketing (he trabajado con ellos y los detesto) tienen lo que literalmente denomino “un moco en la cabeza”, no pueden ser tan pero tan pelotudos de pensar que la gente va a comprar algo a precios inflados cuando en USA o Europa aprovechan fechas como estas para sacarse de encima todo el stock viejo a como dé lugar pero aquí no, aquí intentan maximizar las ganancias como sea.

Y en tercer lugar las expresiones de quejas de la gente, se puede usar twitter para seguir minuto a minuto el desencanto y disconformidad del público como por ejemplo siguiendo este link: cybermonday argentina

Esta mañana apenas comenzó el horario laboral y en vistas de que prácticamente todo el mundo estaría accediendo a los distintos sitios publicados desde http://cybermondayarg.com.ar/ me dediqué a hacer un rápido análisis de la configuración de esos sitios con un simple curl para obtener los headers de las páginas principales.

A medida que lo iba haciendo lo iba publicando en mi cuenta de twitter con un escueto screenshot del resultado pero sin explicar qué era cada cosa, porque básicamente el resultado de lo que mostraba al ejecutar

1
2
3
4
5
6
7
8
9
10
cmiranda@marvin:~$ curl -I http://cybermondayarg.com.ar/
HTTP/1.1 200 OK
Server: Apache/2.2
Content-Type: text/html; charset=UTF-8
Date: Mon, 02 Dec 2013 22:25:20 GMT
Accept-Ranges: bytes
Connection: Keep-Alive
Set-Cookie: X-Mapping-pindfigh=A0570588EDFA4906B8EBB80CFE5F7FDD; path=/
Last-Modified: Mon, 02 Dec 2013 22:16:42 GMT
Content-Length: 37199
cmiranda@marvin:~$ curl -I http://cybermondayarg.com.ar/
HTTP/1.1 200 OK
Server: Apache/2.2
Content-Type: text/html; charset=UTF-8
Date: Mon, 02 Dec 2013 22:25:20 GMT
Accept-Ranges: bytes
Connection: Keep-Alive
Set-Cookie: X-Mapping-pindfigh=A0570588EDFA4906B8EBB80CFE5F7FDD; path=/
Last-Modified: Mon, 02 Dec 2013 22:16:42 GMT
Content-Length: 37199

Sólo lo iban a entender este tipo de especímenes: sysadmins, diseñadores (de los buenos), programadores web y pará de contar.

Porqué me interesé en los headers…?
Bueno, veamos.

Headers

Sólo una pequeña introducción a los headers que pueden leerla en wikipedia: http://es.wikipedia.org/wiki/Hypertext_Transfer_Protocol
Otro link/imagen interesante que explica qué sucede cada vez que hacemos una petición http a un webserver es el siguiente:

Para los sysadmins o personas que trabajamos en infraestructura web, los headers de un webserver nos dan información muy valiosa sobre la configuración y comportamiento de estos servidores porque tenemos acceso a información que nos permite interpretar muy bien dónde hacer mejoras, encontrar fallas, etc en la configuración de estos mismos webservers.

La información que podemos obtener nos permite tomar deciciones correctas en el caso de proyectar la perfomance y desempeño de un webserver como veremos mas adelante.

Webservers

Todo el que trabaja relaciondo con sitios de “alto” tráfico y me refiero por alto a mantener servidores con unas cuantas centenas de miles de visitas diarias conoce de términos como Proxys reversos, cache web, memcache, apc, etc. Todas estas son palabras de tecnologías relacionadas a nuestro trabajo y que nos facilitan el día a día.
Tambien sabemos todos los que trabajamos y mantenemos sitios de alta perfomance que Apache (el webserver mas utilizado) realmente no es muy eficiente que digamos en manejar muchas peticiones simultáneas, en otras palabras explota por todos lados. Es casi 100% compatible con casi todo lo publicable vía web (excepto .NET) pero como decía no es el Oooooh de los webserver en lo que a perfomance refiere.

Y cuando de perfomance pura estamos hablando es donde entran en juego otras palabras mágicas tales como nginx, Varnish, squid, etc. Los cuales son ampliamente utilizados como proxies reversos que a grandes rasgos es como esto:

Pero vayamos al quid de la cuestión que es la configuración.

Si, en el caso de hoy donde se sabía que la afluencia de visitas iba a ser bastante alta uno como sysadmin debe prepararse para eso y buscar obtener el máximo provecho que las distinas herramientas mencionadas ponen a nuestra disposición.

Varnish se utiliza para cachear en memoria contenido estático (imágenes, css, js, html, etc) para que sea más rápido “servir” ese contenido desde la ram y evitar sobrecargar al propio Apache procesando todas las peticiones dejando a este último el contenido dinámico.
Varnish es un excelente software pero para funcionar correctamente necesita de una gran cantidad de ram porque NO lee desde el disco todo el contenido.
Pero el contenido cacheado “desde” el webserver no es lo único importante como veremos.

Veamos algunos casos:

caso Fravega

El sitio de Fravega fue uno de los que peor funcionó, en ningún momento del dia de las varias veces que intenté entrar estaba funcional.
Analizemos el resultado de los headers de la página principal.

fravega
O con Google Chrome por ejemplo al html principal.

Lo que realmente nos interesa de la información que vemos alli es que sí, estan utilizando Varnish y consideran que eso es mas que suficiente para mantener el sitio online con la avalancha de usuarios que ha habido hoy cuando han ignorado olímpicamente lo que he remarcado en rojo que es el Cache-Control de http.
Porqué digo que han desaprovechado el uso de Cache-Control…?
Bueno, aquí aquí y aquí tienen todos los detalles técnicos en profundidad.
Pero puedo decirles que habilitando la cache del contenido estático uno puede forzar a los browsers de los clientes y a todo proxy de contenidos que haya entre el webserver y el browser del cliente a mantener una copia en cada uno de los intermediarios.

Ahondemos un poco mas.
Todo contenido estático se puede cachear en 2 lugares distintos.

  • “EN” el webserver
  • “EN” el browser del cliente
  • “EN/ENTRE” el medio de ambos

Cómo… uhh me perdí, no eran 2…?

En el primer caso que mencioné a Varnish, es un ejemplo de contenido estático cacheado en webserver. No es por supuesto el único que puede hacer esto yo particularmente tengo predilección por nginx (mi favorito) que tiene un método de cache que usa el filesystem en vez de ram como Varnish (tiene sus pros y sus contras) pero que usando cierto artilugios que todo Linux/Unix nos permite también podemos cachear contenido en la ram.

En el segundo caso que es cachear el contenido “en” el browser del cliente, es allí donde entran en juego los headers http que mencioné mas arriba, porque manipulando cómo se debe/permite cachear este contenido estático (*.jpg, *.png, *.gif, *.js, *.css, *.fonts, etc) uno puede hacer que la infraestructura de los clientes y de muchas empresa trabaje para nosotros SIN sobrecargar nuestro webserver + cache server

qué cómo…?

imgur_9CUtI

Veamos un ejemplo práctico con una imagen cualquiera alojada en este blog:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cmiranda@marvin:~$ curl -I http://blog.ngen.com.ar/wp-content/uploads/2013/09/virsh_list.png
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 03 Dec 2013 00:00:19 GMT
Content-Type: image/png
Content-Length: 7421
Last-Modified: Mon, 02 Sep 2013 00:38:38 GMT
Connection: keep-alive
ETag: "5223de0e-1cfd"
Expires: Tue, 10 Dec 2013 00:00:19 GMT
Cache-Control: max-age=604800
Pragma: public
Cache-Control: public
X-Server: lrserver01
Accept-Ranges: bytes
cmiranda@marvin:~$ curl -I http://blog.ngen.com.ar/wp-content/uploads/2013/09/virsh_list.png
HTTP/1.1 200 OK
Server: nginx
Date: Tue, 03 Dec 2013 00:00:19 GMT
Content-Type: image/png
Content-Length: 7421
Last-Modified: Mon, 02 Sep 2013 00:38:38 GMT
Connection: keep-alive
ETag: "5223de0e-1cfd"
Expires: Tue, 10 Dec 2013 00:00:19 GMT
Cache-Control: max-age=604800
Pragma: public
Cache-Control: public
X-Server: lrserver01
Accept-Ranges: bytes
  • curl -I http://blog.ngen.com.ar/wp-content/uploads/2013/09/virsh_list.png es el comando que ejecuté en mi Linux Desktop (by Debian) para obtener información sobre esa imagen.
  • Expires: Me indica cuándo va a expirar esa imagen en la cache del cliente, en este caso el 10 de diciembre del corriente año. Esto significa que si el mismo usuario con el mismo browser ingresa mañana, pasado mañana o el 9 de Diciembre (día de mi cumpleaños dicho sea de paso) a mi blog y visualiza esa imagen, esta se mostrará desde la caché de su propio browser en vez de verla desde mi webserver y/o cache server (nginx, Varnish, squid, etc)
  • Cache-Control: Justamente la duración en segundos del objeto en la cache del browser del cliente, en este caso corresponde a una semana.
  • Pragma: public Directamente relacionado al siguiente ítem
  • Cache-Control: public Este parámetro junto max-age es lo que produce la magia, hacer que el contenido estático pueda ser públicamente cacheado significa que cualquier instancia entre el webserver y el browser del cliente que sea capaz de cachear contenido pueda hacerlo, ejemplo: El proxy de una empresa, o el proxy de un ISP. Uno de los usos mas comunes y erróneos en mi opinión es definir este parámetro como “private” que significa que sólo la sesión (identificada con Etag + session id (en algunos casos) puede cachear el contenido o sea sólo el browser del usuario que pidió ese objeto, algo poco práctico en sitios con contenido estático de acceso público.

Con básicamente esos parámetros el 90% de los browsers y web proxies en el medio van a ser capaces de cachear contenido ahorrando tráfico y peticiones a nuestro webserver.
Hay por supuesto algunos parámetros específicos para controlar el comportamiento de los proxies como por ejemplo “Max-Forwards” que indica la cantidad de veces que el mismo objeto puede pasar de proxy a proxy antes de expirar y solicitarlo directamente al host que lo aloja.

Así como analizamos sólo un caso, pueden extrapolar este comportamiento al resto de todos los sitios que se subieron a la ola del cybermonday, algunos increíblemente ni siquiera disponían de un cache server, y como seguramente sucedió por incompetencia de mandos medios algún gerente o jefe de turno decidió que la mejor forma de prepararse para un dia de alto tráfico era aumentar la ram de/los webservers.

Utilizar todas estas herramientas que estan literalmente al alcance de los dedos de cualquier sysadmin no garantizan una perfecta perfomance de un website con alto tráfico pero ayudan bastante.

Puedo decirles por experiencia que trabajando a la par de un equipo de buenos desarrolladores (escasean bastante) he sido capaz de publicar un sitio hecho en WordPress con picos de 90.000 visitas diarias con un server en Rackspace Cloud con 1gb de ram consumiendo un máximo de 700mb.
Puede sonar increíble pero les aseguro que es perfectamente posible cuando lo que publicamos no tiene errores de de código y tenemos un memcache + APC trabajando a la par de una buena configuración de nginx.

43477830

nginx

Aunque Varnish tiene su propio método para sobrescribir los headers de un webserver en backend aqui les muestro cómo hago esto con nginx.
Sólo tienen que editar la configuración de su VirtualHost y agregar algo como esto:

1
2
3
4
location ~*  \.(jpg|jpeg|png|gif|ico|css|js)$ {
        expires 7d;
        add_header Cache-Control public;
}
location ~*  \.(jpg|jpeg|png|gif|ico|css|js)$ {
        expires 7d;
        add_header Cache-Control public;
}

Apache

Un buen sitio donde aprender a configurar correctamente todos los intringulis de Apache es este: http://www.askapache.com/htaccess/htaccess.html

Espero que este corto artículo les haya sido de ayuda, porque como dice la frase “El diablo está en los detalles”

Configuración segura de un webserver con nginx + php-fpm

Desde la semana pasada estoy como sysadmin freelance de una serie de webservers de una empresa de diseño que hostea los sitios webs de sus propios clientes.

El objetivo es actualizar, optimizar y asegurar esos pocos webservers (menos de 10) y justo cuando tomé el control de los mismos me estreno con un deface a TODOS los sitios de varios de ellos apareciendo una portada como la siguiente:

defaced

Por supuesto, se fue al garete todo el plan de upgrades que tenia pensado y tuve que ponerme a trabajar contrareloj para solventar este inconveniente y permitir que los sitios esten nuevamente online.
En un principio sólo restauré archivos borrados y databases y los sitios volvieron a estar online pero a las pocas horas nuevamente volvieron a comprometer los sitios asi que había una cuestión de fondo que resolver.

Para el ejemplo sólo usaré dominios bajo mi control para mantener en privado los reales sobre los que realizé el trabajo

En la GRAN mayoría de los casos cuando nos encontramos con sitios defaced, la causa es una combinación de frameworks sin actualizar y por consiguiente vulnerables (WordPress, Joomla, Drupal, etc) + algo típico en sysadmins sin mucha experiencia: servers mal configurados.

Este caso no fue la excepción por lo que nada podía hacer si la propia empresa no actualizaba esos WordPress & Joomla (la mayoría) que estaban siendo vulnerados a cada momento.
Por lo que tuve que implementar una solución para al menos mitigar estas vulnerabilidades y en el caso de que comprometan un sitio, no puedan acceder a los demás.

Esto último lo explico de esta forma:
Por defecto (incluso yo mismo he hecho esto en el pasado) la mayoría de los webservers con apache+php o nginx+php tienen definido en el php.ini una variable open_basedir que en el caso de los servidores Debian es /var/www esto hace que si logran comprometer un sitio y NO estan deshabilitadas las funciones exec,passthru,shell_exec,eval,system los defacers pueden ganar acceso a otros directorios donde se alojen otros sitios y poder comprometerlos sin necesidad de que esos otros sitios tengan vulnerabilidades php, sql injections, etc.
Se entiende entonces la importancia de definir correctamente variables y funciones en la configuración de PHP para aumentar las chances en la seguridad de un sitio.

Lo primero fue dar de baja Apache+PHP e instalar nginx+php-fpm, los servidores son Debian Squeeze por lo que tuve que instalar todos los paquetes desde los repositorios de dotdeb

Usuarios

Una vez instalado nginx+php-fpm quería ejecutar un pool por cada site y con usuarios (sin privilegios) y paths distintos, para lo cual tuve que crear usuarios nuevos iguales a nobody (por ejemplo) comenzando con el usuario y grupo.

1
2
root@lrserver01:~# groupadd --gid 65531 site1
root@lrserver01:~# useradd -M -d /nonexistent -c site1 -s /bin/sh -u 65531 -g 65531 site1
root@lrserver01:~# groupadd --gid 65531 site1
root@lrserver01:~# useradd -M -d /nonexistent -c site1 -s /bin/sh -u 65531 -g 65531 site1

Nota importante, el usuario que acabamos de crear tiene el mismo perfil que nobody:nogroup

Permisos

Si el sitio se publica desde /var/www/site1.ngen.com.ar debemos cambiar los permisos y owner de todos los archivos y directorios.

1
2
root@lrserver01:~# find /var/www/site1.ngen.com.ar -type d -exec chmod 755 {} \; -exec chown site1. {} \;
root@lrserver01:~# find /var/www/site1.ngen.com.ar -type f -exec chmod 664 {} \; -exec chown site1. {} \;
root@lrserver01:~# find /var/www/site1.ngen.com.ar -type d -exec chmod 755 {} \; -exec chown site1. {} \;
root@lrserver01:~# find /var/www/site1.ngen.com.ar -type f -exec chmod 664 {} \; -exec chown site1. {} \;

Pools

Llegó el momento de configurar un pool, cuando se instala php-fpm por defecto se crea un pool “www” en /etc/php5/fpm/pool.d/www.conf, podemos mantenerlo, modificarlo o crear uno nuevo y tantos como necesitemos y/o soporte nuestro server.

Si decidimos crear un nuevo pool con hacer una copia de ese archivo (/etc/php5/fpm/pool.d/site1.conf) y editarlo es suficiente, los valores a editar van a ser:

1
2
3
4
5
6
7
8
listen = /var/run/php-fpm/site1.sock
listen.owner = site1
listen.group = site1
listen.mode = 0666
user = site1
group = site1
chroot =
chdir = /var/www/site1.ngen.com.ar
listen = /var/run/php-fpm/site1.sock
listen.owner = site1
listen.group = site1
listen.mode = 0666
user = site1
group = site1
chroot =
chdir = /var/www/site1.ngen.com.ar

listen = /var/run/php-fpm/site1.sock es para diferenciar los distintos sockets de cada pool.

Una vez que tienen aceitado el procedimiento para crear distintos pooles separados con sus usuarios pueden ver los permisos de los directorios de cada sitio y los procesos (con su owner).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
root@lrserver01:/etc/php5/fpm/pool.d# ls -lh /var/www/
total 16K
drwxr-xr-x 7 blog     blog     4.0K Nov 16 08:07 blog.ngen.com.ar
drwxr-xr-x 4 mavi     mavi     4.0K Nov  3 20:54 mavi.ngen.com.ar
drwxr-xr-x 3 www-data www-data 4.0K Dec  1  2012 ngen.com.ar
drwxr-xr-x 3 site1    site1    4.0K Nov 17 11:29 site1.ngen.com.ar
 
root@lrserver01:/etc/php5/fpm/pool.d# ps -eaf |grep '[p]hp-fpm'
root      7766     1  0 11:53 ?        00:00:00 php-fpm: master process (/etc/php5/fpm/php-fpm.conf)
mavi      7767  7766  0 11:53 ?        00:00:00 php-fpm: pool mavi
mavi      7768  7766  0 11:53 ?        00:00:00 php-fpm: pool mavi
site1     7769  7766  0 11:53 ?        00:00:00 php-fpm: pool site1
site1     7771  7766  0 11:53 ?        00:00:00 php-fpm: pool site1
www-data  7772  7766  0 11:53 ?        00:00:00 php-fpm: pool www-data
www-data  7773  7766  0 11:53 ?        00:00:00 php-fpm: pool www-data
blog      7777  7766  0 11:53 ?        00:00:01 php-fpm: pool blog
blog      7812  7766  0 11:53 ?        00:00:01 php-fpm: pool blog
blog      7814  7766  0 11:53 ?        00:00:01 php-fpm: pool blog
root@lrserver01:/etc/php5/fpm/pool.d# ls -lh /var/www/
total 16K
drwxr-xr-x 7 blog     blog     4.0K Nov 16 08:07 blog.ngen.com.ar
drwxr-xr-x 4 mavi     mavi     4.0K Nov  3 20:54 mavi.ngen.com.ar
drwxr-xr-x 3 www-data www-data 4.0K Dec  1  2012 ngen.com.ar
drwxr-xr-x 3 site1    site1    4.0K Nov 17 11:29 site1.ngen.com.ar

root@lrserver01:/etc/php5/fpm/pool.d# ps -eaf |grep '[p]hp-fpm'
root      7766     1  0 11:53 ?        00:00:00 php-fpm: master process (/etc/php5/fpm/php-fpm.conf)
mavi      7767  7766  0 11:53 ?        00:00:00 php-fpm: pool mavi
mavi      7768  7766  0 11:53 ?        00:00:00 php-fpm: pool mavi
site1     7769  7766  0 11:53 ?        00:00:00 php-fpm: pool site1
site1     7771  7766  0 11:53 ?        00:00:00 php-fpm: pool site1
www-data  7772  7766  0 11:53 ?        00:00:00 php-fpm: pool www-data
www-data  7773  7766  0 11:53 ?        00:00:00 php-fpm: pool www-data
blog      7777  7766  0 11:53 ?        00:00:01 php-fpm: pool blog
blog      7812  7766  0 11:53 ?        00:00:01 php-fpm: pool blog
blog      7814  7766  0 11:53 ?        00:00:01 php-fpm: pool blog

php.ini

Una de las ventajas que nos da utilizar php-fpm es que podemos ser lo más restrictivos posibles en la configuración usada en /etc/php5/fpm/php.ini y a medida que necesitemos determinada configuración mas permisiva podemos habilitarla en forma individual en cada pool.

Por lo que en este caso reemplazo la configuración x default de estos valores.

1
2
3
4
5
6
7
8
9
10
11
12
disable_functions = pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,exec,passthru,shell_exec,eval,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source
allow_url_fopen = Off
allow_url_include = Off
expose_php = Off
log_error = Off
file_uploads = Off
sql.safe_mode = On
magic_quotes_gpc = Off
max_execution_time =  30
max_input_time = 30
memory_limit = 40M
cgi.force_redirect = On
disable_functions = pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,exec,passthru,shell_exec,eval,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source
allow_url_fopen = Off
allow_url_include = Off
expose_php = Off
log_error = Off
file_uploads = Off
sql.safe_mode = On
magic_quotes_gpc = Off
max_execution_time =  30
max_input_time = 30
memory_limit = 40M
cgi.force_redirect = On

pool.d/blog.conf

Esta es la configuración que estoy utilizando en este blog a modo de ejemplo, donde muestro qué valores utilizo y cómo aplicar cambios para sobreescribir el php.ini el cual tiene una configuración MUY restrictiva.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[blog]
user = blog
group = blog
listen = /var/run/php-fpm/blog.sock
listen.owner = blog
listen.group = blog
listen.mode = 0666
chroot =
chdir = /var/www/blog.ngen.com.ar
php_admin_value[file_uploads] = on
php_admin_value[memory_limit] = 64M
php_flag[display_errors] = off
php_admin_value[sql.safe_mode] = off
php_admin_value[memory_limit] = 64M
php_admin_value[open_basedir] = /var/www/blog.ngen.com.ar
php_admin_value[cgi.force_redirect] = on
php_admin_value[allow_url_fopen] = on
php_admin_value[allow_url_include] = off
php_admin_value[magic_quotes_gpc] = off
php_admin_value[upload_tmp_dir] = /tmp
php_admin_value[disable_functions] = exec,passthru,shell_exec,eval,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority
[blog]
user = blog
group = blog
listen = /var/run/php-fpm/blog.sock
listen.owner = blog
listen.group = blog
listen.mode = 0666
chroot =
chdir = /var/www/blog.ngen.com.ar
php_admin_value[file_uploads] = on
php_admin_value[memory_limit] = 64M
php_flag[display_errors] = off
php_admin_value[sql.safe_mode] = off
php_admin_value[memory_limit] = 64M
php_admin_value[open_basedir] = /var/www/blog.ngen.com.ar
php_admin_value[cgi.force_redirect] = on
php_admin_value[allow_url_fopen] = on
php_admin_value[allow_url_include] = off
php_admin_value[magic_quotes_gpc] = off
php_admin_value[upload_tmp_dir] = /tmp
php_admin_value[disable_functions] = exec,passthru,shell_exec,eval,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority

Es importante definir en los pools valores de php distintos a los que hay en /etc/php5/fpm/php.ini sino muchas aplicaciones no funcionarán correctamente, tal es el caso del valor “sql.safe_mode” con WordPress que debe estar en off para que funcione.

Tambien es MUY importante el valor “open_basedir” porque en combinación con el “chroot” del propio php-fpm aísla el acceso de la aplicación php a solamente el path que definamos, en el ejemplo /var/www/blog.ngen.com.ar y no se permite que los scripts php puedan escalar directorios.

Con estos simples pasos pude aislar exitosamente los distintos sitios de los servers, en todos los casos excepto en 1 pude evitar que los sitios se vuelvan a ser comprometidos, ademas tuve que probar y flexibilizar bastante los valores definidos en “disabled_functions” para que otros sitios funcionaran correctamente, eso depende de cada caso en particular.
Por lo menos en el caso del único sitio que quedó vulnerable (Joomla 1.1) y volvía a ser comprometido una y otra vez no afectó el normal funcionamiento de los demás, y en cuestión de dias fue actualizado y no presentó mas problemas.

Hay formas de aplicar el mismo tipo de seguridad con Apache+php-fpm pero prácticamente no utilizo Apache asi que no sé cómo hacerlo.

Algo que también se puede implementar para incrementar la seguridad de nginx y de las aplicaciones es utilizar naxsi un WAF (Web Application Firewall) similar a mod-security para Apache, algo que estoy implementando en este blog y testeando antes de ponerlo en producción en otros servers.

Links de interés

http://php.net/manual/en/install.fpm.configuration.php
http://www.cyberciti.biz/tips/php-security-best-practices-tutorial.html
http://myjeeva.com/php-fpm-configuration-101.html
https://docs.newrelic.com/docs/php/per-directory-settings

Viernessssss

Hora de un:

1
:~# echo "work" > /dev/null && modprobe weekend
:~# echo "work" > /dev/null && modprobe weekend

tumblr_inline_mmawrllsKw1qz4rgp