domingo, 29 de noviembre de 2015

Asegura tu información en tránsito - VPN IPSec con OpenSwan

La idea de este post es mostrar una manera sencilla y económica de levantar un tunel IPSec con OpenSwan para conectar dos redes separadas geográficamente.

Podría tratarse de dos sucursales en una misma ciudad o en lugares diferentes del mundo. En este caso en particular se van a tratar de dos redes en dos regiones diferentes de AWS. Una en eu-west-1 (Dublin) y otra en us-east-1 (North Virginia). A continuación una imagen que posiblemente aclare un poco mas el entorno (estrenando pizarra jajaj):


Qué hace falta?


Dado que se va a tratar de un entorno virtual, los requerimientos son sencillos:
  • Dos redes privadas diferentes separadas geográficamente de tal forma que la hostil Internet sea el único medio viable para comunicarlas.
  • Dos endpoints uno en cada región:
    • En este caso dos instancias EC2 en VPCs con redes diferentes.
    • Cada endpoint debe contar con una EIP asociada.
    • Ubuntu 14.04 LTS con OpenSwan instalado en ambos endpoints (apt-get install -y openswan)
  • El tráfico a los puertos UDP 500 y 4500 debe estar permitido entre los endpoints. Estos puertos se utilizaran para establecer las Asociaciones de Seguridad y transportar los paquetes ESP cifrados (respectivamente). 

Configuración del kernel


Dado que los endpoints oficiaran de routers en este caso, es necesario setear algunos parámetros extras en el kernel de los mismos. OpenSwan provee los mismos en el archivo /etc/ipsec.d/examples/sysctl.conf, por lo tanto podemos copiarlos a /etc/sysctl.d (para garantizarnos que se cargan luego en caso de reinicio) y ejecutarlos manualmente:

root@ip-172-31-16-163:/etc/ipsec.d# cp /etc/ipsec.d/examples/sysctl.conf /etc/sysctl.d/11-openswan.conf
root@ip-172-31-16-163:/etc/ipsec.d# sysctl -p /etc/sysctl.d/11-openswan.conf
net.ipv4.ip_forward = 1
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.conf.default.log_martians = 0
net.ipv4.conf.all.log_martians = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.neigh.default.gc_thresh1 = 1024
net.ipv4.neigh.default.gc_thresh2 = 2048
net.ipv4.neigh.default.gc_thresh3 = 4096
root@ip-172-31-16-163:/etc/ipsec.d#


Con el kernel ya listo procedemos a configurar OpenSwan

Configuración de OpenSwan:


Por defecto OpenSwan viene casi listo para arrancar y con el soporte para NAT-Traversal activado así que no nos tenemos que preocupar por esa parte. Para corroborar que la instalación fue exitosa podemos ver los puertos en estado LISTEN en el sistema:

ubuntu@ip-10-0-61-63:~$ sudo netstat -ntpul
sudo: unable to resolve host ip-10-0-61-63
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1071/sshd
tcp6       0      0 :::22                   :::*                    LISTEN      1071/sshd
udp        0      0 0.0.0.0:11854           0.0.0.0:*                           589/dhclient
udp        0      0 0.0.0.0:68              0.0.0.0:*                           589/dhclient
udp        0      0 127.0.0.1:4500          0.0.0.0:*                           2372/pluto
udp        0      0 10.0.61.63:4500         0.0.0.0:*                           2372/pluto
udp        0      0 127.0.0.1:500           0.0.0.0:*                           2372/pluto
udp        0      0 10.0.61.63:500          0.0.0.0:*                           2372/pluto
udp6       0      0 ::1:500                 :::*                                2372/pluto
udp6       0      0 :::1255                 :::*                                589/dhclient

ubuntu@ip-10-0-61-63:~$

en ambos endpoints deberíamos ver los puertos UDP 500 y 4500.

Como primer paso debemos editar el archivo /etc/ipsec.conf para indicarle donde pondremos las configuraciones extra de nuestros túneles, agregamos la siguiente linea al final:

include /etc/ipsec.d/*.conf

Ahora creamos el archivo /etc/ipsec.d/eu-west-1_us-east-1.conf con el siguiente contenido:

En el endpoint de eu-west-1:

conn tunnel
    authby=secret
    type=tunnel
    left=172.31.16.163
    leftsubnet=172.31.0.0/16
    right=52.23.178.X
    rightid=10.0.61.63
    rightsubnet=10.0.0.0/16


En el endpoint de us-east-1:

conn tunnel
    type=tunnel
    auto=start
    authby=secret
    left=54.77.119.X
    leftid=172.31.16.163
    leftsubnet=172.31.0.0/16
    right=10.0.61.63
    rightsubnet=10.0.0.0/16


Como pueden ver las configuraciones son similares, pero a la vez espejadas en algunos campos como los left/right y las subnets. El modo de la VPN sera Tunel, se iniciará automáticamente y se autenticará con pre-shared key (el método mas sencillo de autenticación).

El último paso consiste en definir la autenticación en el archivo /etc/ipsec.secrets de la siguiente manera:

En el endpoint de eu-west-1 agregar:

10.0.61.63 172.31.16.163: PSK "bjimQ0jc"

 En el endpoint the us-east-1 agregar:

172.31.16.163 10.0.61.63: PSK "bjimQ0jc"

Ahora solo basta reiniciar el servicio ipsec en ambos endpoints:

root@ip-172-31-16-163:/home/ubuntu# service ipsec restart
ipsec_setup: Stopping Openswan IPsec...
ipsec_setup: Starting Openswan IPsec 2.6.38...
ipsec_setup: No KLIPS support found while requested, desperately falling back to netkey
ipsec_setup: NETKEY support found. Use protostack=netkey in /etc/ipsec.conf to avoid attempts to use KLIPS. Attempting to continue with NETKEY
root@ip-172-31-16-163:/home/ubuntu#


root@ip-10-0-61-63:/home/ubuntu# service ipsec restart
ipsec_setup: Stopping Openswan IPsec...
ipsec_setup: Starting Openswan IPsec 2.6.38...
ipsec_setup: No KLIPS support found while requested, desperately falling back to netkey
ipsec_setup: NETKEY support found. Use protostack=netkey in /etc/ipsec.conf to avoid attempts to use KLIPS. Attempting to continue with NETKEY
root@ip-10-0-61-63:/home/ubuntu# 


Y si todo salio bien, deberíamos poder ver los túneles de la siguiente manera:

root@ip-172-31-16-163:/home/ubuntu# service ipsec status
IPsec running  - pluto pid: 17635
pluto pid 17635
2 tunnels up
some eroutes exist
root@ip-172-31-16-163:/home/ubuntu#


root@ip-10-0-61-63:/home/ubuntu# service ipsec status
IPsec running  - pluto pid: 11655
pluto pid 11655
2 tunnels up
some eroutes exist
root@ip-10-0-61-63:/home/ubuntu#


Para mas detalles puede ejecutar ipsec auto --status.

Probando el túnel


Dado que el túnel se encuentra listo, ahora probemos hacer ping desde un endpoint al otro pero mediante las IPs privadas:

root@ip-172-31-16-163:/etc/ipsec.d# ping -c 5 10.0.61.63
PING 10.0.61.63 (10.0.61.63) 56(84) bytes of data.
64 bytes from 10.0.61.63: icmp_seq=1 ttl=64 time=81.3 ms
64 bytes from 10.0.61.63: icmp_seq=2 ttl=64 time=81.3 ms
64 bytes from 10.0.61.63: icmp_seq=3 ttl=64 time=81.3 ms
64 bytes from 10.0.61.63: icmp_seq=4 ttl=64 time=81.3 ms
64 bytes from 10.0.61.63: icmp_seq=5 ttl=64 time=81.1 ms

--- 10.0.61.63 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4005ms
rtt min/avg/max/mdev = 81.161/81.321/81.378/0.267 ms
root@ip-172-31-16-163:/etc/ipsec.d#


Vemos que todo funciona perfectamente, y con una latencia de unos 81ms, mas que razonable si consideramos que los endpoints están separadas por un buen pedazo de mundo. Con tcpdump en otra ventana capturé el tráfico que sale del endpoint en eu-west-1 con destino al otro endpoint :

root@ip-172-31-16-163:/home/ubuntu# tcpdump -n host 52.23.178.X
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
22:52:38.065238 IP 172.31.16.163.4500 > 52.23.178.X.4500: UDP-encap: ESP(spi=0x71e9743c,seq=0x1), length 132
22:52:38.146578 IP 52.23.178.X.4500 > 172.31.16.163.4500: UDP-encap: ESP(spi=0x8261d38d,seq=0x1), length 132
22:52:39.066729 IP 172.31.16.163.4500 > 52.23.178.X.4500: UDP-encap: ESP(spi=0x71e9743c,seq=0x2), length 132
22:52:39.148078 IP 52.23.178.X.4500 > 172.31.16.163.4500: UDP-encap: ESP(spi=0x8261d38d,seq=0x2), length 132
22:52:40.068222 IP 172.31.16.163.4500 > 52.23.178.X.4500: UDP-encap: ESP(spi=0x71e9743c,seq=0x3), length 132
22:52:40.149553 IP 52.23.178.X.4500 > 172.31.16.163.4500: UDP-encap: ESP(spi=0x8261d38d,seq=0x3), length 132
22:52:41.069703 IP 172.31.16.163.4500 > 52.23.178.X.4500: UDP-encap: ESP(spi=0x71e9743c,seq=0x4), length 132
22:52:41.151020 IP 52.23.178.X.4500 > 172.31.16.163.4500: UDP-encap: ESP(spi=0x8261d38d,seq=0x4), length 132
22:52:42.071171 IP 172.31.16.163.4500 > 52.23.178.X.4500: UDP-encap: ESP(spi=0x71e9743c,seq=0x5), length 132
22:52:42.152306 IP 52.23.178.X.4500 > 172.31.16.163.4500: UDP-encap: ESP(spi=0x8261d38d,seq=0x5), length 132

^C
10 packets captured
10 packets received by filter
0 packets dropped by kernel
root@ip-172-31-16-163:/home/ubuntu#


Cada uno de estos paquetes UDP 4500 lleva los mensajes ICMP enviados anteriormente dentro de un paquete ESP Algo como (((((ICMP)IP)ESP)UDP)IP). Por eso vemos 5 mensajes que salen del endpoint y 5 que vuelven.


En este punto ya tenemos la conectividad resuelta de endpoint a endpoint perfectamente, ahora sólo nos queda por resolver la conectividad con las otras instancias en las redes en cuestión. Esto tiene dos aristas:

Dado que el primer punto es trivial voy a mostrar los pasos del segundo unicamente xD.

Basicamente hay que identificar las tablas de ruteo en ambas regiones y modificarlas de la siguiente manera:

  • Tabla en eu-west-1, dirigimos todo el tráfico con destino a 10.0.0.0/16 hacia la instancia endpoint de la región:



  • Tabla en us-east-1, dirigimos todo el tráfico destinado a 172.31.0.0/16 hacia la instancia endpoint de la región:



y ahora hacemos ping entre las instancias internas libremente:

[ec2-user@ip-172-31-17-235 ~]$ ping -c 5 10.0.207.203
PING 10.0.207.203 (10.0.207.203) 56(84) bytes of data.
64 bytes from 10.0.207.203: icmp_seq=1 ttl=62 time=82.1 ms
64 bytes from 10.0.207.203: icmp_seq=2 ttl=62 time=82.0 ms
64 bytes from 10.0.207.203: icmp_seq=3 ttl=62 time=82.2 ms
64 bytes from 10.0.207.203: icmp_seq=4 ttl=62 time=82.1 ms
64 bytes from 10.0.207.203: icmp_seq=5 ttl=62 time=82.2 ms

--- 10.0.207.203 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4087ms
rtt min/avg/max/mdev = 82.089/82.190/82.286/0.192 ms
[ec2-user@ip-172-31-17-235 ~]$


Reiniciar uno de los endpoints


Para corroborar que la configuración sea correcta y todo inicie normalmente mandé a reiniciar uno de los endpoints mientras hacía ping entre las instancias internas. Eh aquí van los resultados:

[ec2-user@ip-172-31-17-235 ~]$ ping 10.0.207.203
PING 10.0.207.203 (10.0.207.203) 56(84) bytes of data.
64 bytes from 10.0.207.203: icmp_seq=1 ttl=62 time=77.2 ms
64 bytes from 10.0.207.203: icmp_seq=2 ttl=62 time=77.1 ms
64 bytes from 10.0.207.203: icmp_seq=3 ttl=62 time=77.2 ms
64 bytes from 10.0.207.203: icmp_seq=4 ttl=62 time=77.6 ms
64 bytes from 10.0.207.203: icmp_seq=5 ttl=62 time=77.2 ms
64 bytes from 10.0.207.203: icmp_seq=6 ttl=62 time=77.1 ms
64 bytes from 10.0.207.203: icmp_seq=7 ttl=62 time=77.1 ms
64 bytes from 10.0.207.203: icmp_seq=8 ttl=62 time=77.3 ms
64 bytes from 10.0.207.203: icmp_seq=9 ttl=62 time=77.4 ms
64 bytes from 10.0.207.203: icmp_seq=10 ttl=62 time=77.5 ms
64 bytes from 10.0.207.203: icmp_seq=11 ttl=62 time=77.1 ms
64 bytes from 10.0.207.203: icmp_seq=13 ttl=62 time=77.1 ms
64 bytes from 10.0.207.203: icmp_seq=14 ttl=62 time=77.1 ms
64 bytes from 10.0.207.203: icmp_seq=15 ttl=62 time=77.2 ms


64 bytes from 10.0.207.203: icmp_seq=69 ttl=62 time=77.3 ms
64 bytes from 10.0.207.203: icmp_seq=70 ttl=62 time=77.2 ms
64 bytes from 10.0.207.203: icmp_seq=71 ttl=62 time=76.9 ms
64 bytes from 10.0.207.203: icmp_seq=72 ttl=62 time=77.6 ms
64 bytes from 10.0.207.203: icmp_seq=73 ttl=62 time=77.3 ms
64 bytes from 10.0.207.203: icmp_seq=74 ttl=62 time=77.0 ms
64 bytes from 10.0.207.203: icmp_seq=75 ttl=62 time=77.1 ms
64 bytes from 10.0.207.203: icmp_seq=76 ttl=62 time=76.9 ms
^C
--- 10.0.207.203 ping statistics ---
76 packets transmitted, 22 received, 71% packet loss, time 76043ms
rtt min/avg/max/mdev = 76.929/77.259/77.671/0.255 ms
[ec2-user@ip-172-31-17-235 ~]$


podemos ver que los paquetes entre el 15 y el 69 se perdieron por la caída del endpoint. Pero a su vez sin intervención alguna la conexión se reestableció exitosamente!!!

Overhead de la VPN


Nada es perfecto ni gratis en este mundo, y las VPNs no son la excepci'on. Por lo tanto, es esperable que el uso de la VPN venga con un costo asociado.

En pequeñas transferencias no para ser perceptible:
  • Ping entre los endpoints sin utilizar el tunel:
root@ip-172-31-16-163:/etc/ipsec.d# ping -c 5 52.23.178.X
PING 52.23.178.X (52.23.178.X) 56(84) bytes of data.
64 bytes from 52.23.178.X: icmp_seq=1 ttl=52 time=76.0 ms
64 bytes from 52.23.178.X: icmp_seq=2 ttl=52 time=76.3 ms
64 bytes from 52.23.178.X: icmp_seq=3 ttl=52 time=76.1 ms
64 bytes from 52.23.178.X: icmp_seq=4 ttl=52 time=76.1 ms
64 bytes from 52.23.178.X: icmp_seq=5 ttl=52 time=76.0 ms

--- 52.23.178.X ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4005ms
rtt min/avg/max/mdev = 76.021/76.164/76.349/0.206 ms
root@ip-172-31-16-163:/etc/ipsec.d#

  • Ping entre los endpoints utilizando el tunel:
root@ip-172-31-16-163:/etc/ipsec.d# ping -c 5 10.0.61.63
PING 10.0.61.63 (10.0.61.63) 56(84) bytes of data.
64 bytes from 10.0.61.63: icmp_seq=1 ttl=64 time=76.1 ms
64 bytes from 10.0.61.63: icmp_seq=2 ttl=64 time=76.1 ms
64 bytes from 10.0.61.63: icmp_seq=3 ttl=64 time=76.2 ms
64 bytes from 10.0.61.63: icmp_seq=4 ttl=64 time=76.0 ms
64 bytes from 10.0.61.63: icmp_seq=5 ttl=64 time=76.1 ms

--- 10.0.61.63 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4005ms
rtt min/avg/max/mdev = 76.086/76.166/76.245/0.053 ms
root@ip-172-31-16-163:/etc/ipsec.d#


Con tráfico un poco mas denso la diferencia comienza a notarse, en este caso hay 6 segundos de diferencia en una transferencia de 500 MBytes:

root@ip-172-31-16-163:/home/ubuntu# time nc 10.0.61.63 80 < 500MB_file

real    0m44.957s
user    0m0.013s
sys     0m1.591s
root@ip-172-31-16-163:/home/ubuntu# time nc 52.23.178.X 80 < 500MB_file

real    0m38.693s
user    0m0.004s
sys     0m0.429s
root@ip-172-31-16-163:/home/ubuntu#


Conclusión


Hoy por hoy con soluciones Open Source como OpenSwan no hay motivo alguno para que no utilicemos VPNs para asegurar nuestra información en tránsito. Es cierto que en algunos escenarios las VPNs con SSL pueden ser mas sencillas de desplegar, pero IPSec tarde o temprano las va a reemplazar, así que gente... ponganse a probar OpenSwan!!!

miércoles, 25 de noviembre de 2015

Dell vendió computadoras con un Certificado Raiz y su private key dentro xD

Si, mas parece una broma que algo cierto, pero ellos mismos lo reconocen aquí mismo. Lo mas gracioso de todo esto es que hace una semana compré una Inspiron 7000 Series... ergo... tengo un certificado raíz de Dell gratis!!!!  Ah, y la clave privada...

Al parecer los amigos de Dell en un esfuerzo por mejorar la experiencia de soporte de sus usuarios  a través de su servicio Dell Fundational Services incluyeron por error en el repositorio de certificados de Windows nada mas y nada menos que un certificado raíz junto con su clave privada... Pero bueno, según el post citado antes, un update a partir del 24 de Noviembre debería encargarse de remover dicho certificado de los sistemas. Sin embargo he aquí un screenshot tomado hace unos minutos (18:17 UTC 25 de Nov)...




Simpático, no? Y se supone que iba a ser válido hasta 2039... Lógicamente los amigos de Dell están pidiendo a gritos que lo eliminemos (pasos aquí) para reducir posibles dolores de cabeza.

El potencial problema aquí es que alguien utilice dicho certificado y clave para firmar otros certificados y luego pueda realizar ataques tipos MiTM haciéndose pasar por un servicio de Dell. Si bien el certificado y la clave privada están ambos en el repositorio de windows sólo es posible exportar el certificado, dado que la clave está protegida para no poder ser exportada...




Según cuentan las malas lenguas en Internet, es posible exportar la clave privada haciendo uso de algún software que hay por ahí... Ya con la clave privada y el certificado, crear certificados y firmarlos es solo cuestión de paciencia.

La forma mas sencilla de solucionar esto es con el aplicativo provisto por Dell, con un simple click nos elimina el certificado y hace el resto del hosekeeping necesario (antes por las dudas lo exporté :P). Lo raro es que el aplicativo sólo nos garantiza que el certificado es removido del sistema y de los certificados en los que confía Windows, esto podría ser suficiciente. Pero just in case, lo vamos a incluir formalmente dentro de los certificados en los que Windows NO debe confiar, simplemente importando el certificado dentro de "Untrusted Certificates", quedando de la siguiente manera:




Moraleja?... Empresas grandes hacen cagadas grandes jajaja.

viernes, 20 de noviembre de 2015

Zombie Processes - Procesos Zombies

No, no se trata de procesos que se mueven de manera errática y que convierten a otros procesos si los muerden (aunque sería muy divertido).

Cuando hablamos de procesos zombies nos referimos a procesos que han completado su ejecución (estado terminated) pero aun se encuentran registrados en la tabla de procesos del kernel. ¿Por qué sucede esto? Básicamente sucede para poder garantizar que el proceso padre pueda obtener el estado final de sus procesos hijos, para saber cuál fue el resultado. Una vez que el proceso padre lee el estado de salida del hijo (a través de la syscall wait) este último será removido de la tabla de procesos y podrá descansar finalmente en paz, e ir al cielo de los procesos.

Algunos puntos para resaltar:
  • Un proceso zombie es un proceso cuya ejecución ha finalizado y el estado del proceso es TERMINATED. Puede haber terminado por las buenas o por las malas (con kill por ejemplo).
  • La memoria ocupada por el proceso ha sido liberada.
  • Podemos ver procesos zombies utilizando ps aux, los reconoceremos por la Z en la columna de STAT.
  • Todo proceso que termina su ejecución se vuelve zombie, aunque raras veces lo notaremos dado que por lo general el proceso padre estará esperándolo con la llamada wait.

Creando zombies


A modo de prueba de concepto veamos el siguiente código, que será nuestro generador de zombies:

#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

int main(void)
{
        pid_t pid;
        int pid_status;
        pid = fork();
        if (pid == 0) {
                sleep(10);
                exit(9);
        }
        printf("PID del hijo %d\n",pid);
        sleep(40);
        waitpid(pid, &pid_status, 0);
        printf("Estado de salida %d\n",WEXITSTATUS(pid_status));
        return 0;
}

El código es sencillo, luego de la llamada a fork tendremos en el sistema corriendo dos procesos idénticos. Uno de ellos, el hijo, ejecutará el código dentro del if, mientras que el padre irá directamente al primer printf, luego  dormirá por 40 segundos y ejecutará waitpid para obtener el estado de terminación del hijo e imprimirlo.

Veamos un poco qué sucede durante la ejecución:

[ec2-user@ip-172-31-16-177 ~]$ gcc -o zombies zombies.c
[ec2-user@ip-172-31-16-177 ~]$ ./zombies
PID del hijo 23037
Estado de salida 9
[ec2-user@ip-172-31-16-177 ~]$

El proceso padre imprimió el PID del hijo (23037), luego durmió por 40 segundos, ejecutó waitpid para recojer el valor de salida del proceso hijo (9) y lo imprimió en pantalla. Usando ps antes de que termine la ejecución del proceso hijo podemos ver un poco mas en detalle lo que sucede:

La relación entre los procesos (23036 es el proceso padre y 23037 es el hijo):

[ec2-user@ip-172-31-16-177 ~]$  ps -eo pid,ppid,cmd|grep zombie
23036  2407 ./zombies
23037  23036 ./zombies
23293 22781 grep --color=auto zombie
[ec2-user@ip-172-31-16-177 ~]$

Luego aun dentro de los primeros 10 segundos vemos lo siguiente:

[ec2-user@ip-172-31-16-177 ~]$ ps aux|grep zombies
ec2-user 23036  0.0  0.0   4176   620 pts/0    S+   22:22   0:00 ./zombies
ec2-user 23037  0.0  0.0   4172    80 pts/0    S+   22:22   0:00 ./zombies
ec2-user 23039  0.0  0.2 110460  2196 pts/1    S+   22:22   0:00 grep --color=auto zombies
[ec2-user@ip-172-31-16-177 ~]$

ambos procesos se encuentran en estado Sleeping (S+), y podemos ver también que ambos tienen el mismo valor de VSZ (virtual memory size), lo cual tiene sentido dado que son idénticos luego de la llamada fork. Pasados los 10 segundos nos encontramos con la siguiente situación:

[ec2-user@ip-172-31-16-177 ~]$ ps aux|grep zombies
ec2-user 23036  0.0  0.0   4176   620 pts/0    S+   22:22   0:00 ./zombies
ec2-user 23037  0.0  0.0      0     0 pts/0    Z+   22:22   0:00 [zombies] <defunct>
ec2-user 23041  0.0  0.2 110460  2124 pts/1    S+   22:22   0:00 grep --color=auto zombies
[ec2-user@ip-172-31-16-177 ~]$

el proceso hijo se encuentra ahora en Zombie state (Z+) y su VSZ es 0 (dado que toda la memoria ocupada fue liberada).

Una vez que se cumplen los 40 segundos, el proceso padre y el hijo desaparecen del sistema.


[ec2-user@ip-172-31-16-177 ~]$ ps aux|grep zombies
[ec2-user@ip-172-31-16-177 ~]$

Cómo matar un proceso zombie?


Como se imaginaran, cortarle la cabeza al proceso no parece ser una opción viable en este contexto. El equivalente en el mundo de los procesos es la señal SIGKILL (9), pero veamos qué sucede cuando la usamos para matar un proceso zombie:

Ejecución del binario zombie:

[ec2-user@ip-172-31-22-1 ~]$ ./zombie
PID del hijo 2765
Estado de salida 9
[ec2-user@ip-172-31-22-1 ~]$


Intentos fallidos de acabar con la existencia del proceso 2765:

[ec2-user@ip-172-31-22-1 ~]$ ps aux|grep zombie
ec2-user  2764  0.0  0.0   4176   692 pts/1    S+   21:06   0:00 ./zombie
ec2-user  2765  0.0  0.0   4172    80 pts/1    S+   21:06   0:00 ./zombie
ec2-user  2769  0.0  0.0 110460  2196 pts/0    S+   21:06   0:00 grep --color=auto zombie
[ec2-user@ip-172-31-22-1 ~]$ ps aux|grep zombie
ec2-user  2764  0.0  0.0   4176   692 pts/1    S+   21:06   0:00 ./zombie
ec2-user  2765  0.0  0.0      0     0 pts/1    Z+   21:06   0:00 [zombie]
ec2-user  2771  0.0  0.0 110460  2152 pts/0    S+   21:06   0:00 grep --color=auto zombie
[ec2-user@ip-172-31-22-1 ~]$
[ec2-user@ip-172-31-22-1 ~]$ kill -9 2765
[ec2-user@ip-172-31-22-1 ~]$ ps aux|grep zombie
ec2-user  2764  0.0  0.0   4176   692 pts/1    S+   21:06   0:00 ./zombie
ec2-user  2765  0.0  0.0      0     0 pts/1    Z+   21:06   0:00 [zombie]
ec2-user  2773  0.0  0.0 110460  2140 pts/0    S+   21:06   0:00 grep --color=auto zombie
[ec2-user@ip-172-31-22-1 ~]$ kill -9 2765
[ec2-user@ip-172-31-22-1 ~]$ ps aux|grep zombie
ec2-user  2764  0.0  0.0   4176   692 pts/1    S+   21:06   0:00 ./zombie
ec2-user  2765  0.0  0.0      0     0 pts/1    Z+   21:06   0:00 [zombie]
ec2-user  2775  0.0  0.0 110460  2192 pts/0    S+   21:07   0:00 grep --color=auto zombie
[ec2-user@ip-172-31-22-1 ~]$


Claramente kill -9 no esta siendo capaz de terminar el proceso, la señal está siendo enviada al proceso  y no recibimos ningun mensaje de error o algo similar.

Cómo podríamos deshacernos de ellos?


Lamentablemente, la única manera de deshacernos de ellos es matando el proceso padre. Dando muerte al proceso padre, los procesos zombies se convierten en procesos huérfanos y serán adoptados por init, luego init ejecutará waitpid y los procesos descansarán finalmente en paz.

A continuación un pequeño ejemplo:

[ec2-user@ip-172-31-16-177 ~]$  ps -eo pid,ppid,cmd|grep zombie
23346  2407 ./zombies1
23347 23346 ./zombies1
23348 23346 ./zombies1
23349 23346 ./zombies1
23350 23346 ./zombies1
23351 23346 ./zombies1
23353 22781 grep --color=auto zombie
[ec2-user@ip-172-31-16-177 ~]$ ps aux|grep zombies
ec2-user 23346  0.0  0.0   4172   600 pts/0    S+   23:40   0:00 ./zombies1
ec2-user 23347  0.0  0.0      0     0 pts/0    Z+   23:40   0:00 [zombies1]
ec2-user 23348  0.0  0.0      0     0 pts/0    Z+   23:40   0:00 [zombies1]

ec2-user 23349  0.0  0.0      0     0 pts/0    Z+   23:40   0:00 [zombies1]

ec2-user 23350  0.0  0.0      0     0 pts/0    Z+   23:40   0:00 [zombies1]

ec2-user 23351  0.0  0.0      0     0 pts/0    Z+   23:40   0:00 [zombies1]

ec2-user 23355  0.0  0.2 110460  2124 pts/1    S+   23:41   0:00 grep --color=auto zombies
[ec2-user@ip-172-31-16-177 ~]$ kill -9 23346
[ec2-user@ip-172-31-16-177 ~]$ ps aux|grep zombies
ec2-user 23357  0.0  0.2 110460  2156 pts/1    S+   23:41   0:00 grep --color=auto zombies
[ec2-user@ip-172-31-16-177 ~]$


Podemos ver que tenemos 5 procesos en estado zombie y cuyo padre es el proceso 23346. Una vez que enviamos la señal (con kill) para matar el proceso padre, todos los hijos desaparecen.

El código utilizado fue el siguiente:

#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
        pid_t pid;
        int pid_status=0;
        int i;
        for(i=0;i<5;i++)
        {
                pid = fork();
                if (pid == 0) {
                        sleep(3);
                        exit(9);
                }
        }
        sleep(600);
        return 0;
}

Por suerte, dado que los procesos Zombies son proceso que técnicamente ya no se encuentran consumiendo recursos (salvo por las estructuras de kernel donde se encuentran representados) no deberíamos preocuparnos demasiado. Sin embargo podríamos caer en una situación donde tenemos muchos de ellos acumulados...

Zombie fork bomb!


Ok, los procesos en estado zombie en teoría no consumen memoria, pero siguen estando representados dentro del kernel de alguna manera (de lo contrario no serian visibles para ps por ejemplo) por lo que algo de memoria deben consumir. Qué sucede si creamos tantos zombies como procesos podemos crear según ulimit?

[ec2-user@ip-172-31-22-1 ~]$ ulimit -u
31877
[ec2-user@ip-172-31-22-1 ~]$


Según ulimit este usuario puede crear hasta 31877 procesos,  de los cuales ya hay:

[ec2-user@ip-172-31-22-1 ~]$ ps aux | grep ^ec2-user | wc -l
6[ec2-user@ip-172-31-22-1 ~]$


Entonces con el proceso padre hacemos 7 y por lo tanto veamos qué pasa si ocupamos todos los procesos, intentemos crear 31870 zombies (seguro me van a llamar de The walking dead después de esto), usando el siguiente código:

#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

int main(void)
{
        pid_t pid;
        int bomb;
        for(bomb=0;bomb<31870;bomb++)
        {
                pid = fork();
                if (pid == 0) {
                        sleep(30);
                        exit(9);
                }
        }
        getchar();
        return 0;
}

Ejecutamos (abrí una consola como root, just in case):

[ec2-user@ip-172-31-22-1 ~]$ gcc -o zombie_fork_bomb zombie_fork_bomb.c
[ec2-user@ip-172-31-22-1 ~]$ ./zombie_fork_bomb

Y desde la consola de root vemos lo siguiente:

[root@ip-172-31-22-1 ec2-user]# free -m
             total       used       free     shared    buffers     cached
Mem:          7987        417       7569          0         13        320
-/+ buffers/cache:         83       7903
Swap:            0          0          0

[root@ip-172-31-22-1 ec2-user]# ps aux|grep ^ec2-user|wc -l
31877
[root@ip-172-31-22-1 ec2-user]# ps aux|
grep ^ec2-user|grep " Z+ "|wc -l
31870
[root@ip-172-31-22-1 ec2-user]# free -m
             total       used       free     shared    buffers     cached
Mem:          7987       1445       6541          0         13        320
-/+ buffers/cache:       1111       6875
Swap:            0          0          0
[root@ip-172-31-22-1 ec2-user]#


Se pudieron los 31870 zombies y con eso alcanzamos el limite de los 31877 procesos disponibles para el usuario ec2-user. Eso lo podemos comprobar con la segunda consola del usuario:

[ec2-user@ip-172-31-22-1 ~]$ ls
-bash: fork: retry: No child processes
-bash: fork: retry: No child processes
^C-bash: fork: retry: No child processes
-bash: fork: retry: No child processes
-bash: fork: Resource temporarily unavailable


[ec2-user@ip-172-31-22-1 ~]$


Claramente bash no esta pudiendo hacer el fork para ejecutar ls. Por otro lado también podemos ver que el consumo de memoria aumento considerablemente de 417MBytes a 1445MBytes, prácticamente triplicado, pero claro, estamos hablando de 31871 procesos en estado zombie.

Con esto podemos inferir que cada proceso en estado zombie nos cuesta alrededor de 33Kbytes de memoria ((1445-417)/31871*1024 Kbytes) y lo que es tal vez mas importante, cuenta como un proceso más en ejecución a la hora de controlar los límites del usuario.

Conclusión:

Los procesos zombies solo pueden ser eliminados si su padre o init ejecutan la función wait para ese process ID. A pesar de que los procesos zombies son proceso cuya ejecución ha finalizado, siguen ocupando lugar en el kernel y podrían ocasionar problemas mayores si se tratara de un gran numero de ellos.

Como breve comentario final, no hay que confundir los proceso en estado Z (zombie) con los procesos en estado D (uninterruptible). Estos últimos por lo general se encuentran esperando alguna operación de E/S y por lo tanto no hay manera de interrumpirlos. Deshacerse de uno de estos procesos es BASTANTE mas complicado y podría llegar a ser necesario reiniciar el sistema. En otra entrada vamos a ver de que se trata eso.


sábado, 14 de noviembre de 2015

Snapshots automáticos y consistentes en AWS

Los snapshots pueden ser una forma muy tentadora de backups, dado que proveen una imagen de un volumen en un punto particular del tiempo. Pero como todo tipo de backup debemos asegurarnos que se hagan correctamente, de lo contrario el día que lo precisemos podríamos arrepentirnos de no haberlo hecho xD.

AWS permite crear snapshots de los volúmenes EBS que tengamos bajo nuestro control. Estos son algunos de los detalles al respecto:
  • Se almacenan en S3, por lo tanto la durabilidad y disponibilidad de los mismos es mas que razonable.
  • Son incrementales, por lo tanto son eficientes en espacio y tiempo.
  • Restaurar un snapshot es muy sencillo, simplemente hay que crear un nuevo volumen del snapshot.
Los mismos documentos de AWS sugieren que para obtener snapshots consistentes y evitar la corrupción de los datos se deben detener las operaciones de escritura sobre el volumen por el tiempo que demore la creación del snapshot. Una forma muy sencilla de hacer esto es deteniendo totalmente la instancia, por ejemplo, o al menos desmontando el volumen del cual se está tomando el snapshot.

En casos donde detener la instancia o desmontar los volúmenes no sea posible se puede optar por congelar el sistema de archivos durante la creación del snapshot haciendo uso de xfs_freeze.

xfs_freeze permite detener las operaciones de escrituras sobre un sistema de archivos (-f), y luego retomarlas (-u).
En este post se va a describir una prueba de concepto de como se puede lograr un snapshot consistente deteniendo las operaciones de escritura sobre el volumen.

Role IAM para las instancias


Para poder tomar los snapshots de manera automática y desde las mismas instancias debemos permitirles ciertas operaciones (API calls) como CreateSnapshot, DescribeInstances y DescribeSnapshots. Para lograr esta parte podemos valernos de un Role IAM y adjuntarle la siguiente Policy:
 
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1447448059000",
            "Effect": "Allow",
            "Action": [
                "ec2:CreateSnapshot",
                "ec2:DescribeInstances",
                "ec2:DescribeSnapshots"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

El rol nos permitirá darle a las instancias la capacidad de acceder a este subconjunto de la API sin tener que preocuparnos por mantener las credenciales.

Una vez creado el rol las instancias deben lanzarse utilizando dicho rol. Para comprobar que el rol se encuentra aplicado a la instancia correctamente podemos hacer lo siguiente:

[ec2-user@ip-172-31-20-132 ~]$ curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
EC2-BackupRole

[ec2-user@ip-172-31-20-132 ~]$

Se puede ver que el role EC2-BackupRole se encuentra asociado a la instancia. Para comprobar la autorización podemos, por ejemplo, describir el volumen del que tomaremos el snapshot:

[ec2-user@ip-172-31-20-132 ~]$ aws ec2 describe-instances --instance-ids i-8161a338  --query 'Reservations[0].Instances[0].BlockDeviceMappings[?DeviceName==`/dev/sdb`]' --output json
[
    {
        "DeviceName": "/dev/sdb",
        "Ebs": {
            "Status": "attached",
            "DeleteOnTermination": false,
            "VolumeId": "vol-e3490220",
            "AttachTime": "2015-11-14T10:28:41.000Z"
        }
    }
]
[ec2-user@ip-172-31-20-132 ~]$


Script para la creación de los snapshots


Una vez solucionada la parte de la autorización con IAM, sólo queda escribir el script que cree los snapshots. Los siguientes puntos se tendrán en cuenta:
  • El script debe crear un snapshot del volumen /dev/sdb, o cualquiera sea el volumen en cuestión.
  • El snapshot debe ser consistente. Por lo tanto debe detener las operaciones de escritura.
  • Se debe poder definir un timeout que permita recuperar las operaciones de escritura en caso de que la creación del snapshot demore demasiado tiempo. Por supuesto, esto pone en riesgo la integridad del snapshot, pero garantiza que se conocerá el tiempo máximo que el servicio se encontrará degradado (sin posibilidades de escribir).
A continuación el script:
#!/bin/bash

VOLUME='/dev/sdb'
MOUNT='/mnt'
LOGS=/var/log/backups.log
DONE=0
TIMEOUT=300
TIMEDOUT=0
SLEEP=30
INSTANCEID=`curl http://169.254.169.254/latest/meta-data/instance-id 2>/dev/null`
REGION=`curl http://169.254.169.254/latest/dynamic/instance-identity/document 2>/dev/null | grep region | awk -F\" '{print $4}'`
VOLUMEID=`aws ec2 describe-instances --instance-ids $INSTANCEID --region $REGION --query "Reservations[0].Instances[0].BlockDeviceMappings[?DeviceName=='$VOLUME'].Ebs.{ID:VolumeId}" --output text`
DESCRIPTION="$INSTANCEID-$VOLUMEID-$(date +%F)"

echo "$(date) Iniciando backup de: $INSTANCEID $REGION $VOLUMEID"

#Detener los servicios que quieras detener aqui

###

sync
xfs_freeze -f $MOUNT
echo "$(date) Escrituras detenidas."
SNAPSHOTID=`aws ec2 create-snapshot --volume-id $VOLUMEID --region $REGION --description $DESCRIPTION --query '{ID:SnapshotId}' --output text`
OUT=$?
if [ $OUT -ne 0 ]; then
        echo "$(date) La creacion del snapshot fallo."
        xfs_freeze -u $MOUNT
        echo "$(date) Escrituras reestablecidas."
        exit
fi
while [ $DONE = "0" ]; do
        PROGRESS=`aws ec2 describe-snapshots --snapshot-id $SNAPSHOTID --region $REGION --query 'Snapshots[0].{Progress:Progress}' --output text`
        OUT=$?
        if [ $OUT -ne 0 ]; then
                echo "$(date) Snapshot $SNAPSHOTID aun no esta disponible."
                sleep $SLEEP
                TIMEOUT=`echo "$TIMEOUT-$SLEEP" | bc`
        else
                if [ $PROGRESS = "100%" ]; then
                        DONE="1"
                        echo "$(date) Snapshot $SNAPSHOTID listo"
    else
                        echo "$(date) Snapshot $SNAPSHOTID $PROGRESS"
                        sleep $SLEEP
                        TIMEOUT=`echo "$TIMEOUT-$SLEEP" | bc`
                fi
        fi
        if [ $TIMEOUT -le 0 ]; then
                DONE="1"
                TIMEDOUT="1"
        fi
done
xfs_freeze -u $MOUNT
echo "$(date) Escrituras reestablecidas."
if [ $TIMEDOUT = "1" ]; then
        echo "$(date) Snapshot $SNAPSHOTID timed out!!! Podria ser inconsistente"
else
        echo "$(date) Snapshot $SNAPSHOTID terminado exitosamente"
fi

Prueba 1: Primer snapshot


Dada la naturaleza incremental de los snapshots, mientras mas bloques "Sucios" haya por copiar mas va a demorar el snapshot. Esto se hace mas evidente generalmente en el primer snapshot que se tome de un volumen.

Lanzamos una escritura aleatoria en background de unos 12GB de la siguiente manera

[ec2-user@ip-172-31-20-132 ~]$ sudo dd if=/dev/urandom of=/mnt/archivo_borrar bs=1M count=12000 &
[2] 4783

[ec2-user@ip-172-31-20-132 ~]$ 

cuando llevan escritos unos 8GB

[ec2-user@ip-172-31-20-132 ~]$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/xvda1      7.8G  1.1G  6.6G  15% /
devtmpfs        3.9G   60K  3.9G   1% /dev
tmpfs           3.9G     0  3.9G   0% /dev/shm
/dev/xvdb        99G  8.4G   85G   9% /mnt
[ec2-user@ip-172-31-20-132 ~]$


lanzamos el snapshot

[ec2-user@ip-172-31-20-132 ~]$ sudo ./backups.sh
Sat Nov 14 17:13:24 UTC 2015 Iniciando backup de: i-8161a338 eu-west-1 vol-e3490220
Sat Nov 14 17:13:26 UTC 2015 Escrituras detenidas.
Sat Nov 14 17:13:26 UTC 2015 Snapshot snap-27582071 0%
Sat Nov 14 17:13:57 UTC 2015 Snapshot snap-27582071 0%
Sat Nov 14 17:14:27 UTC 2015 Snapshot snap-27582071 0%
Sat Nov 14 17:14:58 UTC 2015 Snapshot snap-27582071 0%
Sat Nov 14 17:15:28 UTC 2015 Snapshot snap-27582071 0%
Sat Nov 14 17:15:58 UTC 2015 Snapshot snap-27582071 0%
Sat Nov 14 17:16:29 UTC 2015 Snapshot snap-27582071 0%
Sat Nov 14 17:16:59 UTC 2015 Snapshot snap-27582071 0%
Sat Nov 14 17:17:30 UTC 2015 Snapshot snap-27582071 0%
Sat Nov 14 17:18:00 UTC 2015 Snapshot snap-27582071 0%
Sat Nov 14 17:18:30 UTC 2015 Escrituras reestablecidas.
Sat Nov 14 17:18:30 UTC 2015 Snapshot snap-27582071 timed out!!! Podria ser inconsistente
[ec2-user@ip-172-31-20-132 ~]$


este primer snapshot no pudo terminar en el lapso de los 300 segundos, por lo tanto podría tratarse de un snapshot inconsistente.

Poco antes de lanzar la creación del snapshot, en una segunda consola puse a correr iostat para ver el comportamiento de las operaciones de escritura, aquí están los resultados:

[ec2-user@ip-172-31-20-132 ~]$ sudo iostat -x -d /dev/xvdb 5 30
Linux 4.1.10-17.31.amzn1.x86_64 (ip-172-31-20-132)      11/14/2015      _x86_64_        (2 CPU)

Device:         rrqm/s   wrqm/s     r/s     w/s   rsec/s   wsec/s avgrq-sz avgqu-sz   await  svctm  %util
xvdb              0.00    10.24    0.12   78.65     1.00 20046.09   254.48     9.79  124.28   0.92   7.27

Device:         rrqm/s   wrqm/s     r/s     w/s   rsec/s   wsec/s avgrq-sz avgqu-sz   await  svctm  %util
xvdb              0.00     1.60    0.00  637.40     0.00 163038.40   255.79    84.64  132.79   0.95  60.24

Device:         rrqm/s   wrqm/s     r/s     w/s   rsec/s   wsec/s avgrq-sz avgqu-sz   await  svctm  %util
xvdb              0.00     0.20    0.00    0.40     0.00     4.80    12.00     0.00    0.00   0.00   0.00

Device:         rrqm/s   wrqm/s     r/s     w/s   rsec/s   wsec/s avgrq-sz avgqu-sz   await  svctm  %util
xvdb              0.00     0.20    0.00   54.80     0.00 13931.20   254.22     4.92   55.08   0.64   3.52

Device:         rrqm/s   wrqm/s     r/s     w/s   rsec/s   wsec/s avgrq-sz avgqu-sz   await  svctm  %util
xvdb              0.00     2.40    0.00  171.60     0.00 43056.00   250.91    21.20  134.67   0.95  16.32

Device:         rrqm/s   wrqm/s     r/s     w/s   rsec/s   wsec/s avgrq-sz avgqu-sz   await  svctm  %util
xvdb              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00   0.00   0.00

Device:         rrqm/s   wrqm/s     r/s     w/s   rsec/s   wsec/s avgrq-sz avgqu-sz   await  svctm  %util
xvdb              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00   0.00   0.00

Device:         rrqm/s   wrqm/s     r/s     w/s   rsec/s   wsec/s avgrq-sz avgqu-sz   await  svctm  %util
xvdb              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00   0.00   0.00

...

Device:         rrqm/s   wrqm/s     r/s     w/s   rsec/s   wsec/s avgrq-sz avgqu-sz   await  svctm  %util
xvdb              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00   0.00   0.00

Device:         rrqm/s   wrqm/s     r/s     w/s   rsec/s   wsec/s avgrq-sz avgqu-sz   await  svctm  %util
xvdb              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00   0.00   0.00

Device:         rrqm/s   wrqm/s     r/s     w/s   rsec/s   wsec/s avgrq-sz avgqu-sz   await  svctm  %util
xvdb              0.00     0.00    0.00    0.20     0.00     1.60     8.00     0.00    0.00   0.00   0.00


Device:         rrqm/s   wrqm/s     r/s     w/s   rsec/s   wsec/s avgrq-sz avgqu-sz   await  svctm  %util
xvdb              0.00     0.80    0.00  101.40     0.00 25816.00   254.60     7.65   75.46   0.78   7.92


Device:         rrqm/s   wrqm/s     r/s     w/s   rsec/s   wsec/s avgrq-sz avgqu-sz   await  svctm  %util
xvdb              0.00     0.80    0.00    0.40     0.00     9.60    24.00     0.00    0.00   0.00   0.00

Device:         rrqm/s   wrqm/s     r/s     w/s   rsec/s   wsec/s avgrq-sz avgqu-sz   await  svctm  %util
xvdb              0.00     0.20    0.00    0.40     0.00     4.80    12.00     0.00    0.00   0.00   0.00

^C[ec2-user@ip-172-31-20-132 ~]$


se puede ver la linea en negrita (azul), como a partir de ese punto no hay mas operaciones de escritura sobre el disco. Los altos valores de escrituras en ese momento se deben a las operaciones sync y xfs_freeze. Las operaciones de escritura se re establecen mas adelante (también en negrita, roja) cuando el timeout se cumple en el script.

Prueba 2: Segundo snapshot


El segundo snapshot termina antes del timeout, por lo que se lo puede considerar aboslutamente consistente.

[ec2-user@ip-172-31-20-132 ~]$ sudo ./backups.sh
Sat Nov 14 18:14:26 UTC 2015 Iniciando backup de: i-8161a338 eu-west-1 vol-e3490220
Sat Nov 14 18:14:26 UTC 2015 Escrituras detenidas.
Sat Nov 14 18:14:27 UTC 2015 Snapshot snap-39405312 0%
Sat Nov 14 18:14:57 UTC 2015 Snapshot snap-39405312 0%
Sat Nov 14 18:15:27 UTC 2015 Snapshot snap-39405312 0%
Sat Nov 14 18:15:58 UTC 2015 Snapshot snap-39405312 0%
Sat Nov 14 18:16:28 UTC 2015 Snapshot snap-39405312 0%
Sat Nov 14 18:16:59 UTC 2015 Snapshot snap-39405312 0%
Sat Nov 14 18:17:29 UTC 2015 Snapshot snap-39405312 listo
Sat Nov 14 18:17:29 UTC 2015 Escrituras reestablecidas.
Sat Nov 14 18:17:29 UTC 2015 Snapshot snap-39405312 terminado exitosamente
[ec2-user@ip-172-31-20-132 ~]$
 

Resumen


Esto es una simple prueba de concepto y no fue realmente probado en ambientes de producción. Desde mis pruebas puedo decir que dd (el proceso bloqueado por xfs_freeze) no sufrió mas que el bloqueo que era de esperarse, posiblemente esto no sea tan factible en una partición donde trabaja una BD que realiza muchos inserts por ejemplo. El script podría lanzarse sencillamente desde un cronjob y tendriamos todo automatizado y aceitado!!!