ftp mirroring con lftp

He necesitado hacer un mirror de unos directorios que sólo estaban disponibles por ftp y necesito lanzar sincronizaciones periódicas de forma sencilla y cómoda, para ello he usado lftp una vieja herramienta que tenía ya enterrada.

Instalar lftp

apt-get install lftp

Lo más útil de lftp es la capacidad de copiar directorios de forma recursiva ya que por defecto el cliente ftp de toda la vida de las distrubicones de linux no lo soporta.

Como puedo hacer un mirroring:

lftp -u usuario,password ftp.midominio.tld
mirror /ruta/ftp/origen /ruta/local/b

 

ls: Fatal error: Certificate verification: Not trusted

Este error aparece cuando nuestro lftp no es capaz de verificar que el certificado remoto sea válido por lo que no nos permite continuar realizando operaciones en la máquina remota. Esto está bien, si el certificado es correcto, pero habitualmente nos encontraremos con certificados incorrectos o caducados, por lo que podremos desactivarlo, ya que no nos ofrece ningún nivel de seguridad, si el certificado no está correctamente instalado.

Para desactivarlo, yo he usado esta opción con éxito ( aunque hay varias variantes buscando en google ):

set ftp:ssl-allow no;

A lo que tengo que añadir que después de ejecutar este comando con el debug activo:

set debug 10

me ha sido imposible retornar el listado de directorios, y parece ser un bug. Este caso se observa al lanzar el comando `ls` y ver infinitamente el texto: Delaying before reconnect.

Desactivado el debug, funciona correctamente

set debug 0

Mirroring sites using ftp

Aquí os dejo un pequeño script para hacer migración de sitios usando ftp de una forma bastante rápida:

FTP_USER="tu usuario"
FTP_PASSWORD="tu password"
FTP_HOST=ftp.dominio.ltd
SOURCE=/ruta/remota/origen
DEST=/ruta/remota/destino
lftp -u"${FTP_USER},${FTP_PASSWORD}" -e "set ftp:ssl-allow no; mirror ${SOURCE} ${DEST} --only-newer --verbose --use-pget-n=10; quit" ${FTP_HOST}

A tener en cuenta:

  • -u»usuario,password» Es necesario usar las comillas ya que de esa forma escapamos los caracteres especiales que pueda tener el password
  • -e«ejecuta comandos una vez que abre la conexión»
  • set ftp:ssl-allow no No valida el certificado ssl del servidor ftp
  • –only-newer Sólo ficheros nuevos
  • –use-pget n=10 Usa 10 conexiones concurrentes

bloquear el acceso de un usuario al sistema GNU/Linux

Imagina que tienes un usuario en el sistema al que solo quieres acceder usando ssh, por ejemplo ‘desarrollo’, para securizar un poco más el sistema, mejor que sólo puedas acceder usando tu clave ssh.
Una forma muy rápida de bloquear el acceso al usuario es editando a mano el fichero /etc/shadow y substituyendo la segunda columna, a la que pertenece el hash de la password por una exclamación ‘!’.

Si este es el /etc/shadow original

desarrollo:$1$Xop0FYH9$IfxyQwBe9b8tiyIkt2P4F/:13262:0:99999:7:::
user2:$1$vXGZLVbS$ElyErNf/agUDsm1DehJMS/:13261:0:99999:7:::

lo cambiamos la segunda columna, por una exclamación

desarrollo:!:13262:0:99999:7:::
user2:$1$vXGZLVbS$ElyErNf/agUDsm1DehJMS/:13261:0:99999:7:::

El acceso al sistema usando login+password queda bloqueado, pero sigue siendo posible el acceso vía ssh

Como alternativa también se puede usar el comando ‘passwd -l’ para bloquear ( lock ) el acceso al sistema
[shell]
# lock user desarrollo
passwd -l desarrollo
[/shell]

Más información en
https://www.debian.org/doc/manuals/debian-reference/ch04.en.html

[sysstat] Invalid system activity file: /var/log/sysstat/sa10

Alguna vez nos encontramos con este error en algunos VPS. Suele ser cuando el proveedor nos hace una parada por actualización de los nodos virtuozzo. Seguramente coincidirá el apagado con algún proceso de rotación de ficheros o escritura de fichero de estadisticas.

/usr/bin/sar.sysstat
Invalid system activity file: /var/log/sysstat/sa10

Para solucionarlo, directamente vamos a por el mal menor y lo que hacemos es mover el fichero y relanzar el demonio

mv /var/log/sysstat/sa10 /var/log/sysstat/sa10.old
/etc/init.d/sysstat restart

y ya funciona correctamente :

/usr/bin/sar.sysstat
Linux 2.6.32-042stab065.3 (hostingaldescubierto.com) 	12/10/2012 	_i686_	(16 CPU)

11:18:26 AM       LINUX RESTART

 

Error migration Plesk 11 : Failed to execute scout.

Parallels Plesk Panel 11 migration error

Esta mañana migrando unos dominios a un servidor con Plesk 11 ( de Axarnet estamos probando este proveedor ) nos ha saltado un TOTAL FAIL ! migrando los dominios. El error es este :

 

 

 

 

= STDERR ====================
Failed to execute scout.
The return code is 9
The output on STDERR is
Unable to determine block size in df output. First line looks like ‘S.ficheros Bloques de 512 Usado Libre Ocupado Montado en
‘ at – line 560.

Nos parece tristísimo que a estas alturas, un software con la trayectoria de Plesk cometa estos fallos de principiante. ¿ De que hablamos ? De que la máquina de origen tiene el idioma por defecto como es_ES.UTF-8. Por eso cuando laza un df -h para ver si hay espacio salta el error en castellano…

Como somos perros viejos y no nos las sabemos todas, pero sí algunas, hemos ido directamente a cambiar el idioma en el servidor de origen a ver si cuela. Es una ubuntu así que lo hemos así :

vps:~# update-locale LANG=en_US.UTF-8 LANGUAGE=en_US.UTF-8 LC_ALL=en_US.UTF-8 LC_MESSAGES=POSIX
vps:~# locale-gen
Generating locales...
  en_US.UTF-8... up-to-date
  es_ES.UTF-8... up-to-date
Generation complete.

y ha funcionado perfectamente. A título personal, no está de más que en los scripts que lanza el agente migrador de plesk incluyan algo como:

export LANG=en_US.UTF-8
export LANGUAGE=en_US.UTF-8
export LC_ALL=en_US.UTF-8

Como siempre Plesk, sigue decepcionando… en estos pequeños detalles. Esperemos que no aparezcan SQLi’s que nos dejen en bastante mala posición a los usuarios.

[debian] xulrunner

Otro error actualizando Debian esta mañana.

Al actualizar el sistema y abrir Iceweasel…. cataplas !!!!!

Error de lectura XML: entidad no definida
Ubicación: jar:file:///usr/lib/xulrunner-7.0/omni.jar!/chrome/toolkit/content/global/netError.xhtml
Número de línea 338, columna 43:        <h1 id="et_corruptedContentError">&corruptedContentError.title;</h1>
------------------------------------------^

Así que lo de siempre, googlear y encuentro un bug reportado en http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=643862

Hacen referencia a que es causa de un paquete e idiomas que no está actualizado, así que para evitar estar dando vueltas, directamente los desinstalo y otro día… ya veremos 😀

  iceweasel-l10n-es-ar* iceweasel-l10n-es-es*

y a funcionar….

[ubuntu] locale: Cannot set LC_CTYPE to default locale: No such file or directory

Vaya con ubuntu… no me esperaba esto. Normalmente en Debian cuando no está bien configurado el idioma local, basta con reconfigurar el paquete de esta manera :

sudo dpkg-reconfigure --frontend=dialog --priority=low locales

Con las opciones forzamos a que los mensajes sean vía dialogo con libncurses y la prioridad es que nos pregunte cualquier opción necesaria para configurar el paquete , no sólo las importantes. Como decíamos en debian, con esto es suficiente.

El error que normalmente aparece es algo asi :

perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
	LANGUAGE = (unset),
	LC_ALL = (unset),
	LANG = "en_US.UTF-8"
    are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory

El caso es que hoy con un servidor ubuntu…. esto no hace na de naaaaa y he estado indagando y parace que hace falta definir qué idiomas son los válidos en el directorio /var/lib/locales/supported.d.
En la documentación de Ubuntu nos encontramos información, pero no es muy clara https://wiki.ubuntu.com/LocalesThatDontSuck

La configuración correcta la podemos hacer así :

Instalamos el paquete con los idiomas en nuestro caso inglés

apt-get install language-pack-en

Como el inglés tiene bastante variantes le vamos a dejar sólo la americana en_US. Para ello editamos el fichero /var/lib/locales/supported.d/en y dejamos sólo esta linea:

en_US.UTF-8 UTF-8

Ahora configuramos el idioma por defecto con

update-locale LANG=en_US

Ya tenemos generado el fichero que necesitabamos en /etc/default/locale

#  File generated by update-locale
LANG=en_US

y con esto el problema queda solucionado.

[plesk] ERROR: WDExc fallo en pack-sysstats

Aunque debería estar corregido ya en todas las instalaciones puede que alguno os encontreis con alguna versión de Plesk 9.5.x o 10.x con este error que se genera en el fichero :

<b>/opt/psa/libexec/modules/watchdog/cp/pack-sysstats </b>

La consula sql no está bien formada y falla al ejecutar, concretamente el error es este :

ERROR: WDExc
Error occurred while processing database query: 'MySQL query failed: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'group by service_id, type, round(unix_timestamp(time) / 200, 0) having count(val' at line 3'
0: wdlib.php:1088
       wd__db_query(string 'select service_id, type, unix_timestamp(min(time)) as min_time, unix_timestamp(max(time)) as max_time, avg(value) as avg
                                       from module_watchdog_sys_stat where
                                       group by service_id, type, round(unix_timestamp(time) / 200, 0) having count(value) > 1 limit 10000;')
1: pack-sysstats:63
       pack_statistics(integer '200', boolean  false, boolean  false)
2: pack-sysstats:44

Existe un microupdate de corrige el problema, para aplicarlo, directamente ejecutamos el autoupdate de plesk de la siguiente forma :

/usr/local/psa/admin/sbin/autoinstaller --select-release-current --install-component base

Artículo de kb de Parallels: http://kb.parallels.com/en/9554 Cron job failure on watchdog jobs

[plesk] ERROR: PleskFatalException: Solucionar incosistencias en la base de datos

Plesk sigue teniendo algunos defectos , uno de ellos es que a veces se pierde la integridad referencial en algunas tablas como las tablas mail y accounts, que provoca el fallo de las cuentas de correo. Este fallo puede ser :

  • The error that the other server returned was: 550 550 sorry, no mailbox here by that name. (#5.7.17)
  • The error that the other server returned was: 451 451 qq internal bug (#4.3.0)
  • O bien que al acceder a las propiedades de la cuenta de correo o intentar eliminar la cuenta, obtengamos este error en Plesk :
    ERROR: PleskFatalException
    Error: Can't create Account object: Account: unable to select: no such row in the table
    
    0: common_func.php3:146
        psaerror(string 'Error: Can't create Account object: Account: unable to select: no such row in the table')
    1: client.domain.mail.mailname.php:86
        plesk__client__domain__mail__mailname->accessItemOverview(string 'GET', NULL null)
    2: client.domain.mail.mailname.php:160
        __plesk__client__domain__mail__mailname->accessItem(string 'GET', NULL null)
    3: UIPointer.php:596
        UIPointer->access(string 'GET')
    4: plesk.php:38
    

Pongamos el supuesto que en mi servidor tengo dos dominios uno que se llama hostingaldescubierto.com y otro senin.org y que la cuenta que falla con el error de antes es contacto@senin.org.
Vamos a lanzar esta consulta en la base de datos para localizar los registros huérfanos en la taba de mail con accounts.

select domains.name, mail.mail_name, accounts.id, accounts.password from domains, mail left join accounts on mail.account_id = accounts.id where domains.id = mail.dom_id and accounts.id IS null;
+--------------------------+-------------+------+----------+
| name                     | mail_name   | id   | password |
+--------------------------+-------------+------+----------+
| hostingaldescubierto.com | basura      | NULL | NULL     | 
| senin.org                | contacto    | NULL | NULL     | 
+--------------------------+-------------+------+----------+
2 rows in set (0.01 sec)

Ahora podemos hacer dos cosas o bien borrar la cuenta de correo de la tabla mail o bien insertar una entrada en la tabla accounts. El único problema que puede haber si borramos la entrada en la tabla mail, es que si volvemos a crear la cuenta de correo podría ser que plesk eliminase el buzón con el contenido y lo crease de nuevo, pero no estoy totalmente seguro de esto o si ocurriría en todas las versiones. Yo opto por insertar los registros que falten.

Podríamos proceder generando los valores a insertar en la tabla accounts, y generando unas passwords aleatorias :

select mail.account_id, 'plain', SUBSTRING(MD5(RAND()) FROM 1 FOR 8) from domains, mail left join accounts on mail.account_id = accounts.id where domains.id = mail.dom_id and accounts.id IS null;

Nos devuelve :

+------------+------+-------------------------------------+
| account_id | text | SUBSTRING(MD5(RAND()) FROM 1 FOR 8) |
+------------+------+-------------------------------------+
|       1550 | text | 764dd7d1                            | 
|       2310 | text | cdc15fc4                            | 
+------------+------+-------------------------------------+

Con lo que montamos la sentencia INSERT y quedaría así:

INSERT INTO accounts select mail.account_id, 'plain', SUBSTRING(MD5(RAND()) FROM 1 FOR 8) from domains, mail left join accounts on mail.account_id = accounts.id where domains.id = mail.dom_id and accounts.id IS null;

Query OK, 2 rows affected (0.04 sec)
Records: 2  Duplicates: 0  Warnings: 0

y ahora verificamos que haya quedado corregido con la primera consulta:

select domains.name, mail.mail_name, accounts.id, accounts.password from domains, mail left join accounts on mail.account_id = accounts.id where domains.id = mail.dom_id and accounts.id IS null;
Empty set (0.01 sec)

Y así tenemos corregido el problema con tan solo copiar y pegar.

[wordpress]

WordPress ya está maduro y cosas como esta hacen darle un voto positivo.

Instalado un template en un cliente me aparece este error:

Warning: touch() [function.touch]: SAFE MODE Restriction in effect. The script whose uid is 10277 is not allowed to access /tmp owned by uid 0 in /var/www/vhosts/dominio.comm/httpdocs/wordpress/wp-admin/includes/file.php on line 199  Download failed. Could not create Temporary file.

Es normal en los alojamientos con safe_mode activado y seguramente no quieran desactivarlo por seguridad.

Para ello, nos creamos nuestro propio tmp ( teniendo en cuenta los problemas de seguridad que podría ocasionar )

mkdir /var/www/vhosts/dominio.com/httpdocs/tmp 

# ajustar el usuario real para asignarle al directorio
chown dominio.com:psacln  /var/www/vhosts/dominio.com/httpdocs/tmp

# para que el usuario apache pueda escribir tambien
chmod 777 /var/www/vhosts/dominio.com/httpdocs/tmp

Ahora lo más interesante ,a gregar a nuestro fichero wp-config.php, esta linea :

define('WP_TEMP_DIR','/var/www/vhosts/dominio.com/httpdocs/tmp');

Con este cambio lo que hemos hecho ha sido preprar un tmp e indicarle a wordpress que por defecto ese es el directorio temporal.

Me ha gustado mucho encontrarme con estos detalles que demuestran la madured del proyecto.

[php] Depurando / Profiling en php ( II )

En este caso analizamos un hecho real, una web que tarda en cargar aleatoriamente entre 10 y 12 segundos. El problema es que al medir los tiempos en otro servidor no llega a 2 segundos. Después de revisar conectividad, carga de sistema, carga de apache … y demás parámetros habituales; todo estaba perfecto. Así que decidimos relizar el profiling de la web para localizar el cuello de botella dentro de los scripts php.

En el artículo anterior [php] Depurando / Profiling en php ( I ) dejamos instalado el módulo APD para capturar la información necesaria y luego procesarla. Recordamos que hay que usar la función apd_set_pprof_trace(); para que se generen los datos. En nuestro caso hemos seleccionado la ruta /tmp en donde se almacenarán estos ficheros.

Ejecutamos unos cuantas veces el script php que nos interesa. Generamos una captura de una ejecución en 1-2 segundos (/tmp/pprof.32231.1) y seguimos ejecutando hasta que conseguimos una captura de datos de una ejecución que tardó unos 10-11 segundos (/tmp/pprof.01666.0)

Vamos a comparar la captura de los datos :

La ejecución buena :

# pprofp -R /tmp/pprof.32231.1

Trace for /var/www/vhosts/hostingaldescubierto.com/httpdocs/index.php
Total Elapsed Time = 0.36
Total System Time  = 0.03
Total User Time    = 0.07

Real         User        System             secs/    cumm
%Time (excl/cumm)  (excl/cumm)  (excl/cumm) Calls    call    s/call  Memory Usage Name
--------------------------------------------------------------------------------------
100.0 0.00 0.36  0.00 0.07  0.00 0.03     1  0.0000   0.3589            0 main
96.1 0.00 0.34  0.00 0.08  0.00 0.03     5  0.0000   0.0690            0 require_once
59.1 0.00 0.21  0.00 0.01  0.00 0.00     8  0.0000   0.0265            0 include
58.9 0.00 0.21  0.00 0.01  0.00 0.00     1  0.0000   0.2113            0 call_user_func_array
58.9 0.00 0.21  0.00 0.01  0.00 0.00     1  0.0000   0.2113            0 Pages->index
58.9 0.00 0.21  0.00 0.01  0.00 0.00     1  0.0000   0.2112            0 Pages->show
58.9 0.00 0.21  0.00 0.01  0.00 0.00     1  0.0000   0.2112            0 Template->build
58.8 0.00 0.21  0.00 0.01  0.00 0.00     2  0.0000   0.1054            0 MY_Loader->view
58.6 0.00 0.21  0.00 0.01  0.00 0.00     2  0.0000   0.1052            0 MY_Loader->_ci_load
56.8 0.00 0.20  0.00 0.00  0.00 0.00     1  0.0000   0.2038            0 weather_google_api
56.8 0.20 0.20  0.00 0.00  0.00 0.00     1  0.2037   0.2037            0 simplexml_load_file
29.4 0.00 0.11  0.00 0.05  0.00 0.02     1  0.0000   0.1054            0 Pages->Pages
29.2 0.00 0.10  0.00 0.05  0.00 0.02     1  0.0000   0.1048            0 Pages->Public_Controller
13.9 0.00 0.05  0.00 0.03  0.00 0.02     1  0.0000   0.0497            0 Pages->MY_Controller
13.4 0.00 0.05  0.00 0.02  0.00 0.03     2  0.0000   0.0241            0 MY_Loader->_ci_autoloader
12.2 0.00 0.04  0.00 0.01  0.00 0.00     3  0.0000   0.0146            0 Banners_model->get_by_section
11.7 0.00 0.04  0.00 0.00  0.00 0.00    25  0.0000   0.0017            0 CI_DB_mysql_driver->query
11.5 0.00 0.04  0.00 0.01  0.00 0.00     6  0.0000   0.0069            0 Banners_model->add_hit
11.4 0.04 0.04  0.03 0.03  0.01 0.01    65  0.0006   0.0006            0 defined
10.9 0.00 0.04  0.00 0.00  0.00 0.00    25  0.0000   0.0016            0 CI_DB_mysql_driver->simple_query

El fichero de 10-12 segundos :

# pprofp -R /tmp/pprof.01666.0

Trace for /var/www/vhosts/hostingaldescubierto.com/httpdocs/index.php
Total Elapsed Time = 10.34
Total System Time  = 0.02
Total User Time    = 0.08

Real         User        System             secs/    cumm
%Time (excl/cumm)  (excl/cumm)  (excl/cumm) Calls    call    s/call  Memory Usage Name
--------------------------------------------------------------------------------------
100.1 0.00 10.35  0.00 0.09  0.00 0.02     5  0.0000   2.0692            0 require_once
100.0 0.00 10.34  0.00 0.08  0.00 0.02     1  0.0000   10.3394            0 main
99.1 0.00 10.24  0.00 0.01  0.00 0.00     8  0.0000   1.2802            0 include
99.0 0.00 10.24  0.00 0.01  0.00 0.00     1  0.0000   10.2409            0 call_user_func_array
99.0 0.00 10.24  0.00 0.01  0.00 0.00     1  0.0000   10.2409            0 Pages->index
99.0 0.00 10.24  0.00 0.01  0.00 0.00     1  0.0000   10.2409            0 Pages->show
99.0 0.00 10.24  0.00 0.01  0.00 0.00     1  0.0000   10.2409            0 Template->build
99.0 0.00 10.24  0.00 0.01  0.00 0.00     2  0.0000   5.1202            0 MY_Loader->view
99.0 0.00 10.24  0.00 0.01  0.00 0.00     2  0.0000   5.1200            0 MY_Loader->_ci_load
99.0 0.00 10.23  0.00 0.00  0.00 0.00     1  0.0000   10.2335            0 weather_google_api
99.0 10.23 10.23  0.00 0.00  0.00 0.00     1  10.2333   10.2333            0 simplexml_load_file
0.7 0.00 0.08  0.00 0.06  0.00 0.01     1  0.0000   0.0766            0 Pages->Pages
0.7 0.00 0.08  0.00 0.05  0.00 0.01     1  0.0000   0.0761            0 Pages->Public_Controller
0.5 0.00 0.05  0.00 0.04  0.00 0.01     2  0.0000   0.0266            0 MY_Loader->_ci_autoloader
0.5 0.00 0.05  0.00 0.04  0.00 0.01     1  0.0000   0.0524            0 Pages->MY_Controller
0.4 0.04 0.04  0.04 0.04  0.01 0.01    65  0.0006   0.0006            0 defined
0.4 0.00 0.04  0.00 0.02  0.00 0.01    38  0.0000   0.0010            0 MY_Loader->helper
0.3 0.00 0.04  0.00 0.03  0.00 0.00    10  0.0000   0.0036            0 MY_Loader->model
0.3 0.00 0.03  0.00 0.02  0.00 0.01     1  0.0000   0.0312            0 Pages->Controller
0.3 0.00 0.03  0.00 0.02  0.00 0.01     1  0.0000   0.0311            0 Pages->_ci_initialize

Vemos claramente esta linea que es la que penaliza el tiempo de ejecución del script:

99.0 10.23 10.23  0.00 0.00  0.00 0.00     1  10.2333   10.2333            0 simplexml_load_file

Perfecto, ya sabemos lo que tenemos que buscar el uso de la función simplexml_load_file. Buscamos los ficheros que usan esta funcion, por ejemplo así :

find -name "*php" -exec grep -l simplexml_load_file {} ;

Entre los resultados encontramos uno, que llama especialmente la atención, en el que solicita un fichero xml de google http://www.google.com/ig/api?weather=madrid&oe=utf-8.
Vamos a medir cuanto tiempo tarda en descargarlo :

# time wget 'http://www.google.com/ig/api?weather=madrid&oe=utf-8'
--2010-07-26 18:27:47--  http://www.google.com/ig/api?weather=madrid&oe=utf-8
Resolving www.google.com... 66.249.92.104
Connecting to www.google.com|66.249.92.104|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/xml]
Saving to: `api?weather=madrid&oe=utf-8.1'

[ <=>                                                                                                                                      ] 1,291       --.-K/s   in 0s

2010-07-26 18:27:57 (12.0 MB/s) - `api?weather=madrid&oe=utf-8.1' saved [1291]

real    0m10.157s
user    0m0.002s
sys    0m0.001s

Parece bastante claro que la ejecución se ralentiza por la petición a google… una caché que se actualice cada día, hora o cada minuto solucionaría este problema.