sábado, 26 de diciembre de 2015

Servidores DNS Raíz bajo ataque - Root DNS servers under attack

Leyendo algunos mails pendientes me encontré con una de los boletines de SANS que reportaba un ataque de DDOS a los servidores de nombre raíz. Efectivamente, entre el 30 de Noviembre y el 1 de Diciembre último, los servidores raíz fueron víctimas de un número inusual de consultas DNS.

Estos servidores mantienen la zona raíz, donde comienzan todas las consultas DNS recursivas y por lo tanto el correcto funcionamiento de estos servidores es crucial para la salud de Internet.

Podemos obtener la lista de servidores DNS raíz con una simple consulta desde la consola:

juan@juan-VirtualBox:~$ dig +short -t NS .
i.root-servers.net.
j.root-servers.net.
k.root-servers.net.
d.root-servers.net.
e.root-servers.net.
l.root-servers.net.
a.root-servers.net.
h.root-servers.net.
m.root-servers.net.
f.root-servers.net.
c.root-servers.net.
b.root-servers.net.
g.root-servers.net.
juan@juan-VirtualBox:~$


a partir de los nombres queda claro que cada uno de los servidores raíz se identifica por una letra en el abecedario. De momento hay servidores de la A a la M (13 servidores). Vale aclarar que en realidad detrás de cada uno de estos nombres hay en realidad muchas instancias del servidor respondiendo. Según Wikipedia a Octubre de 2014 había unos 504 servidores reales respondiendo consultas.

Horarios del ataque

 
  • El primer ataque comenzó a las 06:50 UTC del 30 de Noviembre y se extendió  hasta aproximadamente las 09:30 UTC.
  • El segundo ataque inició a las 05:10 UTC del 1 de Diciembre y se extendió hasta casi las 06:10 UTC.
Algunos sitios indican, erróneamente, que se trató de un ataque de amplificación y reflexión, sin embargo no parece haberlo sido. Según detalla la propia gente de www.root-servers.org en su informe, el ataque tuvo las siguientes características:
  • Durante el primer ataque, las consultas eran sobre un único dominio.
  • Durante el segundo ataque, las consultas involucraban diferentes dominios.
  • Buena parte de la flota de servidores recibieron este número excesivo de consultas, aunque no todos. Posiblemente por la naturaleza anycast de las direcciones IP de los servidores raíz. 
  • Extrañamente las direcciones origen de las consultas DNS parecían estar distribuidas aleatoriamente dentro del espacio de direcciones IPv4.
  • El pico máximo de consultas fue de alrededor de 5 millones de consultas por servidor raíz. 

Impacto


Afortunadamente el impacto del ataque fue casi nulo. Si confirmaron que algunos de los enlaces que conectan a los servidores raíz se vieron saturados, lo cual generó algunos retardos en las resoluciones destinadas a estos servidores. Una vez mas la robustez del mecanismo de anycast y la simplicidad de UDP mostraron ser un muy buena arquitectura para uno de los servicios mas críticos de Internet.

sábado, 12 de diciembre de 2015

Página12 bajo ataque...???

Hace ya un par de días la versión digital del diario Página12 se encuentra fuera de servicio o con un servicio muy reducido. Siendo el diario oficialista por excelencia (pero paradógicamente fundado por el mayor opositor del oficialismo ) la situación levanta muchas sospechas en un momento como el que está cursando Argentina.

Este post NO intenta resolver el misterio, dado que es bastante difícil/imposible de lograr tal cosa sin acceso preciso a los detalles, y como muchas veces en nuestro país posiblemente jamás se sepa la verdad. Sin embargo... hay cosas que podrían no ser cómo las venden.

El problema o ataque de denegación de servicio comenzó aparentemente el Martes 8 de Diciembre (hace unos 4 días), y desde entonces el sitio tuvo cortos períodos de funcionamiento. Un intento de conexión al sitio termina sencillamente en un timeout luego de intentar por unos segundos:

juan@juan-VirtualBox:~$ nc -vz www.pagina12.com.ar 80
nc: connect to www.pagina12.com.ar port 80 (tcp) failed: Connection timed out
juan@juan-VirtualBox:~$


Esto nos indica varias posibles situaciones:
  • El servidor se encuentra realmente ante un JODIDO ataque de denegación de servicio y no es capaz de aceptar nuevas conexiones. Vamos... 5 días de ataque?
  • El servidor se encuentra apagado y/o el tráfico no llega al mismo por algún motivo extra.
Por supuesto que no responde ni ping, ni permite conexiones a otros puertos conocidos:

 juan@juan-VirtualBox:~$ ping -c 3 www.pagina12.com.ar
PING www.pagina12.com.ar (138.0.155.10) 56(84) bytes of data.

--- www.pagina12.com.ar ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2017ms

juan@juan-VirtualBox:~$ nc -vz www.pagina12.com.ar 22 -w 5
nc: connect to www.pagina12.com.ar port 22 (tcp) timed out: Operation now in progress
juan@juan-VirtualBox:~$ nc -vz www.pagina12.com.ar 443 -w 5
nc: connect to www.pagina12.com.ar port 443 (tcp) timed out: Operation now in progress
juan@juan-VirtualBox:~$



Pero bueno, es cierto que todo esto podría estar filtrado en el firewall y por eso no hay respuesta del servidor. Otro detalle no menor es el hecho de que el dominio www.pagina12.com.ar y m.pagina12.com.ar resuelven siempre con la misma IP:

juan@juan-VirtualBox:~$ dig +short www.pagina12.com.ar
138.0.155.10
juan@juan-VirtualBox:~$


no hay ningún tipo de balanceo por DNS o de redirección regional, nada de eso. Esto último lo pueden corroborar con https://dnschecker.org/#A/www.pagina12.com.ar



Dicha IP es propiedad de la gente de Gigared en Bs As:

juan@juan-VirtualBox:~$ whois 138.0.155.10

#
# ARIN WHOIS data and services are subject to the Terms of Use
# available at: https://www.arin.net/whois_tou.html
#
# If you see inaccuracies in the results, please report at
# http://www.arin.net/public/whoisinaccuracy/index.xhtml
#


#
# The following results may also be obtained via:
# http://whois.arin.net/rest/nets;q=138.0.155.10?showDetails=true&showARIN=false&showNonArinTopLevelNet=false&ext=netref2
#

NetRange:       138.0.0.0 - 138.0.255.255
CIDR:           138.0.0.0/16
NetName:        LACNIC-ERX-138-0-0-0
NetHandle:      NET-138-0-0-0-1
Parent:         NET138 (NET-138-0-0-0-0)
NetType:        Transferred to LACNIC
OriginAS:      
Organization:   Latin American and Caribbean IP address Regional Registry (LACNIC)
RegDate:        2010-11-19
Updated:        2010-11-19
Comment:        This IP address range is under LACNIC responsibility
Comment:        for further allocations to users in LACNIC region.
Comment:        Please see http://www.lacnic.net/ for further details,
Comment:        or check the WHOIS server located at http://whois.lacnic.net
Ref:            http://whois.arin.net/rest/net/NET-138-0-0-0-1

ResourceLink:  http://lacnic.net/cgi-bin/lacnic/whois
ResourceLink:  whois.lacnic.net

OrgName:        Latin American and Caribbean IP address Regional Registry
OrgId:          LACNIC
Address:        Rambla Republica de Mexico 6125
City:           Montevideo
StateProv:     
PostalCode:     11400
Country:        UY
RegDate:        2002-07-27
Updated:        2011-09-24
Ref:            http://whois.arin.net/rest/org/LACNIC

ReferralServer:  whois://whois.lacnic.net
ResourceLink:  http://lacnic.net/cgi-bin/lacnic/whois

OrgTechHandle: LACNIC-ARIN
OrgTechName:   LACNIC Whois Info
OrgTechPhone:  999-999-9999
OrgTechEmail:  whois-contact@lacnic.net
OrgTechRef:    http://whois.arin.net/rest/poc/LACNIC-ARIN

OrgAbuseHandle: LACNIC-ARIN
OrgAbuseName:   LACNIC Whois Info
OrgAbusePhone:  999-999-9999
OrgAbuseEmail:  whois-contact@lacnic.net
OrgAbuseRef:    http://whois.arin.net/rest/poc/LACNIC-ARIN


#
# ARIN WHOIS data and services are subject to the Terms of Use
# available at: https://www.arin.net/whois_tou.html
#
# If you see inaccuracies in the results, please report at
# http://www.arin.net/public/whoisinaccuracy/index.xhtml
#



Found a referral to whois.lacnic.net.


% Joint Whois - whois.lacnic.net
%  This server accepts single ASN, IPv4 or IPv6 queries

% LACNIC resource: whois.lacnic.net


% Copyright LACNIC lacnic.net
%  The data below is provided for information purposes
%  and to assist persons in obtaining information about or
%  related to AS and IP numbers registrations
%  By submitting a whois query, you agree to use this data
%  only for lawful purposes.
%  2015-12-12 09:28:12 (BRST -02:00)

inetnum:     138.0.152/22
status:      allocated
aut-num:     N/A
owner:       Gigared S.A.
ownerid:     AR-GISA2-LACNIC
responsible: Roberto Feijoo
address:     Donado, 840,
address:     C1427CZB - Capital Federal -
country:     AR
phone:       +54 11 63106000 [6071]
owner-c:     FER
tech-c:      FER
abuse-c:     FER
inetrev:     138.0.152/22
nserver:     NS1.GIGARED.COM 
nsstat:      20151210 AA
nslastaa:    20151210
nserver:     NS2.GIGARED.COM 
nsstat:      20151210 AA
nslastaa:    20151210
created:     20150102
changed:     20150102

nic-hdl:     FER
person:      Feijoo Roberto
e-mail:      rfeijoo@GIGARED.COM.AR
address:     Donado, 840,
address:     C1427CZB - Capital Federal - BA
country:     AR
phone:       +54 11 604006000 [6030]
created:     20030110
changed:     20150303

% whois.lacnic.net accepts only direct match queries.
% Types of queries are: POCs, ownerid, CIDR blocks, IP
% and AS numbers.

juan@juan-VirtualBox:~$


Osea que bien podrían comunicarse con la gente de Gigared para pedirles una mano y con un poco de maña y recursos podrían reducir el impacto de tal ataque.

Sus DNSs


Simpáticamente la gente de Página12 usa los DNS de Cloudflare (deberían haber usado el CDN también xD, o se estarán mudando gradualmente a Cloudflare???):

juan@juan-VirtualBox:~$ dig -t NS pagina12.com.ar
; <<>> DiG 9.9.5-3ubuntu0.5-Ubuntu <<>> -t NS pagina12.com.ar
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- 33465="" br="" id:="" noerror="" opcode:="" query="" status:="">;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;pagina12.com.ar.        IN    NS

;; ANSWER SECTION:
pagina12.com.ar.    2084    IN    NS    bill.ns.cloudflare.com.
pagina12.com.ar.    2084    IN    NS    edna.ns.cloudflare.com.


;; Query time: 16 msec
;; SERVER: 127.0.1.1#53(127.0.1.1)
;; WHEN: Sat Dec 12 11:37:17 GMT 2015
;; MSG SIZE  rcvd: 88

juan@juan-VirtualBox:~$


esto es una muy buena práctica, pero claro... solo DNS con alta disponibilidad no es suficiente. Insisto deberían haber ampliado al servicio de CDN, podría haberle ahorrado un dolor de cabeza con los beneficios de Cloudflare ante ataques DDOS.

Historia de la IP


En Internet hay mucha historia, por suerte. Así como hay sitios que se encargan de guardar copias de otros sitios para luego comparar como fueron cambiando con el tiempo, hay sitios que guardan la historia de DNS. ViewDNS.info es uno de ellos; qué tiene ese sitio para contarnos sobre www.pagina12.com.ar?:



Al parecer el dominio cambió de IP 3 veces desde 2014, es cierto que podría ser información no muy precisa. Pero.... según viewdns.info el dominio www.pagina12.com.ar cambio IP por última vez hace no muchos días. Previamente (por lo menos hasta el 4 de Diciembre) se encontraba en la IP 190.57.233.170, que corresponde al operador EDITORIAL LA PAGINA S.A. (aunque dentro del SA de Gigared S.A.):

juan@juan-VirtualBox:~$ whois 190.57.233.170

% Joint Whois - whois.lacnic.net
%  This server accepts single ASN, IPv4 or IPv6 queries

% LACNIC resource: whois.lacnic.net


% Copyright LACNIC lacnic.net
%  The data below is provided for information purposes
%  and to assist persons in obtaining information about or
%  related to AS and IP numbers registrations
%  By submitting a whois query, you agree to use this data
%  only for lawful purposes.
%  2015-12-12 09:50:11 (BRST -02:00)

inetnum:     190.57.233.160/28
status:      reallocated
owner:       EDITORIAL LA PAGINA S.A.
ownerid:     AR-ELPS-LACNIC
responsible: LUIS COMAND
address:     SOLIS, 1525,
address:     C1134ADG - CABA -
country:     AR
phone:       +54 011 67724400 [4481]
owner-c:     LUC52
tech-c:      LUC52
abuse-c:     LUC52
created:     20140930
changed:     20140930
inetnum-up:  190.57.224/19

nic-hdl:     LUC52
person:      Luis Comand
e-mail:      comande@PAGINA12.COM.AR
address:     Sol�s, 1525,
address:     C1134ADG - Buenos aires -
country:     AR
phone:       +54 11 67724481 []
created:     20140930
changed:     20140930

% whois.lacnic.net accepts only direct match queries.
% Types of queries are: POCs, ownerid, CIDR blocks, IP
% and AS numbers.

juan@juan-VirtualBox:~$


Por supuesto que no podemos saber a ciencia cierta porque se dio este cambio, pero el cambio existió (podemos asegurarlo porque la IP actual es diferente). Ahora revolviendo un poco mas en el cajón de los recuerdos... vemos...





Todo esto indicaría que la gente de Pagina12 podría haber estado migrando el servidor de un lugar después del 4 de Diciembre. Cambiaron de IP y de DNS registrados en NIC. Podría deberse a esto la caída del servicio? Todos sabemos que las migraciones no suelen ser una tarea sencilla.

Reportes de usuarios


Revolviendo un poco mas, me encontré con un simpático personaje de tweeter (hellr00t) que allá por Marzo se encargó de publicar ciertas falencias en el servidor web de Página12. El tweet en particular es https://twitter.com/hellr00t/status/577938582377271297 donde se puede ver lo que serían las estadísticas del servidor web Apache hospedando Página12. El tweet pasó bastante desapercibido pero dejó en evidencia una buena prueba de lo mal administrado que se encontraba el servidor.

Conclusión


La verdad, desde mi humilde opinión y dejando de lado las teorías conspirativas, es que no se puede saber con precisión qué está pasando con el sitio de Página12. Si alguien me pidiese mi punto de vista (que a poca gente le va a importar xD), NO creo que se trate de un ataque de denegación de servicio como se está hablando. Mas bien me parece que una de las siguientes cosas sucedió:

  • Migración con problemas. Es muy fácil que una migración se complique, malos cálculos de recursos, versiones nuevas de software que generan incompatibilidades, etc, etc etc.
  • No descarto un ataque al sitio, todos sabemos que hay gente mala pululando por ahí. Pero... 5 días de downtime habla peor de los administradores que de los atacantes.
Posiblemente el tiempo aclare alguna de las dudas planteadas, o no. Estoy mas que abierto a opiniones y sugerencias, en caso de que vean algo que yo no vi.

Saludos!!!

Actualización 14 de Diciembre


Al parecer no estaba tan equivocado con respecto a la posible migración en proceso de la versión digital de Pagina12. Al día de hoy el sitio web www.pagina12.com.ar se encuentra respondiendo a través del servicio de CDN de CloudFlare. Podemos confirmarlo primero que nada con las nuevas IPs:

juan@juan-VirtualBox:~$ dig +short www.pagina12.com.ar
104.20.76.37
104.20.75.37

juan@juan-VirtualBox:~$


estas IPs pertenecen a CloudFlare

juan@juan-VirtualBox:~$ whois 104.20.75.37|grep -i orgname
OrgName:        CloudFlare, Inc.

juan@juan-VirtualBox:~$ whois 104.20.76.37|grep -i orgname
OrgName:        CloudFlare, Inc.
juan@juan-VirtualBox:~$


de momento la vieja IP sigue funcionando y se puede acceder sencillamente:

juan@juan-VirtualBox:~$ curl -I 138.0.155.10/index.php -H 'Host:www.pagina12.com.ar'
HTTP/1.1 302 Found
Date: Mon, 14 Dec 2015 22:09:10 GMT
Server: Apache
X-Powered-By: PHP/5.3.3-7+squeeze14
Location: /diario/ultimas/index.html
Vary: Accept-Encoding
Content-Type: text/html; charset=ISO-8859-1

juan@juan-VirtualBox:~$

Al parececr lo que han hecho, básicamente es utilizar el servicio de CDN the CloudFlare para darle una mejor performance al sitio, actuando como cache y además es una muy buena idea para mitigar algunos tipos de ataques.

Pequeña recomendación para los sysadmins del sitio... restrinjan el acceso al puerto 80 del servidor backend solamente a las IPs de los caches de Cloudflare. Si no lo hacen sigue siendo muy sencillo atacar el servidor donde realmente se encuentra hospedado el sitio.

A ciencia cierta esto NO devela del todo el misterio... Mi predicción de migración fue acertada :D, pero probablemente jamás sepamos si la migración fue como acción para mitigar un ataque o... el ataque nunca existió y el problema que se hacía visible era por la migración misma.

Escucho ideas!!!

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!!!