Tips para administradores de sitios web

¡¡¡Webmasters!!! Hay que saber todo, porque sino dependemos de la profesionalidad de terceros o de su buena voluntad de terceros

Redsys, error 403 usando Cloudflare

Introducción

Hoy me ha llamado la atención cuando un cliente me ha expuesto el problema, en el que su pasarela Redsys, le daba un error en el que tras un pogo, el retorno de Redsys, no se efectuaba, y por tanto el carrito quedaba pagado pero no figuraba como tal en la tienda. Sus pedidos quedaban pendientes de pago, pese a estar debidamente procesados en Redsys.

Al ir a buscar información me encuentro con post que dan mil vueltas, pero no llegan a la raíz y otros que dieron la apertura fácil (gracias, más abajo os enlazo)

Redsys como siempre, amables, pero poco operativos, le indicaron el que podía ser pero con escueta y difusa información. El cliente entendió un error de java y que tenía un error 403, que pudo ver en la intranet de cliente en redsys.

Error en pasarela intranet

Análisis

Lo primero que me vino a la cabeza fue ver los logs (lo primero que hay que hacer en cualquier análisis técnico) y allí descubrí que no había error 403. ¿Cómo es eso?

Viendo los logs de Apache, donde no había ningún error 403 asociado las path, algo extraño ocurría. Así que la siguiente prueba fue determinar su resolución DNS.

Todo quedó claro: el cliente usa CloudFlare como servicio de distribución, cacheado y seguridad de contenidos.

Y aquí estaba claro que la respuesta o callback que retorna la pasarela Redsys no llegaba al servidor y esto suele tener nombre proxy o firewall (tipo mod_security o desarrollo propio como el caso de Cloudflare)

Primera salida para solventarlo

Algunos lo primero y único que hacen es, desactivar el firewall, el camino más habitual en muchos técnicos (algunos intitulados como Especialista en CiberSeguridad) lo cual es un craso error.

Jamás debemos ir a lo fácil, por esto será difícil, tarde o temprano.

Análisis de logs de Cloudflare

En nuestro dashboard de Cloudflare, tenemos un área para el Firewall, y allí, tenemos un sistema de log de eventos llamado Información general donde podemos reviar el log, consultar por rangos de fechas.

Cloudflare > Firewall > Información General

Podemos crear una regla con esos datos, y tambien hacerlo directamente desde los botones que aparecen si ponemos el puntero del ratón encima de Servicio pero prefiero hacerlo desde el menú Firewall > Reglas de firewall

Añadir una regla en el firewall

Debemos pues añadir dos reglas con la condicional Y

ASN que es el número de Sistema Autónomo de la red que en este caso si es de apropiado ponerla porque es una red bancaria, y es confiable, y además es posible que nos cambien o modifiquen alguna vez la IP, que en este caso para la red de Redsys-Sermepa es el AS31627, pero solo usaremos la parte numérica. Y el identificador URI - Identificador de recursos uniforme del callback de nuestra aplicación de comercio electrónico.

Cloudflare > Firewall > Reglas de firewall

Por último nos queda añadir la acción a esta regla, que como vimos en el log, debería ser Omitir > Comprobación de integridad del navegador

Enlaces

Aviso

Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos. También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad. El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de Castris

Si necesitas soporte profesional puedes contratar con Castris soporte profesional.

exception 'DOMPDF_Exception' with message 'No block-level parent found. Not good.

Introducción

Algunas veces con cambios entre servidores o proveedores de hosting, las aplicaciones de nuestros clientes, sufren ciertos descalabros, como por ejemplo el mostrar una página en blanco en lugar de renderizar un documento PDF.

Como norma general pensaremos en que hay un problema de nuestro servidor o hosting, en el que falta tal o cual extensión.

Craso error. Lo primero es lo primero. Hay que ver los logs.

Análisis

Podemos acceder vía SSH a nuestra shell o en su defecto via cPanel Acceso shell via cPAnel

Como podemos ver el error es claro:

[14-Jul-2021 05:49:24 UTC] Error al generar certificado : exception 'DOMPDF_Exception' with message 'No block-level parent found.  Not good.' in /home/dentista/public_html/dompdf/include/inline_positioner.cls.php:38
Stack trace:
#0 /home/dentista/public_html/dompdf/include/frame_decorator.cls.php(546): Inline_Positioner->position()
#1 /home/dentista/public_html/dompdf/include/text_frame_reflower.cls.php(331): Frame_Decorator->position()
#2 /home/dentista/public_html/dompdf/include/frame_decorator.cls.php(556): Text_Frame_Reflower->reflow(NULL)
#3 /home/dentista/public_html/dompdf/include/page_frame_reflower.cls.php(138): Frame_Decorator->reflow()
#4 /home/dentista/public_html/dompdf/include/frame_decorator.cls.php(556): Page_Frame_Reflower->reflow(NULL)
#5 /home/dentista/public_html/dompdf/include/dompdf.cls.php(817): Frame_Decorator->reflow()
#6 /home/dentista/public_html/pdfCerMaster.php(125): DOMPDF->render()
#7 {main}
Si nuestro proveedor usa PHP-FPM, los logs estarán en la carpeta `~/logs` con el formato `dominio_tld.php.error.log` 
Otros escenarios como fcgi, suPHP, etc, variarán según la configuración que el administrador haya impuesto por defecto o de manera personalizada

Este error no tiene nada que ver con la existencia o no de determinada extensión, sino con un error de en el analizador (parsing) de HTML5.

Corrección

Debemos localizar el fichero de configuración que defina la variable DOMPDF_ENABLE_HTML5PARSER o en su defecto si nuestra app no la tiene acudiremos a la de la propia librería.

Buscamos el posible fichero

$ find . -type f -exec grep -il "DOMPDF_ENABLE_HTML5PARSER" {} \;
./dompdf/include/dompdf.cls.php
./dompdf/changelog.txt
./dompdf/dompdf_config.custom.inc.php
./dompdf/dompdf_config.inc.php
./lib/dompdf/include/dompdf.cls.php
./lib/dompdf/www/setup.php
./lib/dompdf/changelog.txt
./lib/dompdf/dompdf_config.custom.inc.php
./lib/dompdf/dompdf_config.inc.php

Por los nombres me acerco a pensar en ./dompdf/dompdf_config.inc.php el cual paso a editar:

define("DOMPDF_ENABLE_HTML5PARSER", true);

Esto puede que funcione o no, pero al menos, me mostrará los errores de las etiquetas de HTML de una forma más adecuada.

Enlaces

Aviso

Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos. También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad. El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de Castris

Si necesitas soporte profesional puedes contratar con Castris soporte profesional.

Gmail Ips address ranges

Gmail

Si, gmail tambien tiene sus lios con fallos en los SPF, con algun problema como todos. Y a diferencia del resto de los ISP, a Google como Microsoft, les tenemos tanto miedo que tenemos que protegernos de que sus ip caigan en lista negra o que incumplan lo que a nosotros se nos exige.

Esto es asi. Es imposible lidiar con un cliente, o con un administrador de una empresa que no pueda enviarnos correos porque el servidor XXX de Gmail, no ha firmado correctamente el correo con DKIM, o porque hay una falla en el SPF.

Como obtener las IP de gMail

❯ dig +short gmail.com txt
gmail.com.              300     IN      TXT     "globalsign-smime-dv=CDYX+XFHUw2wml6/Gb8+59BsH31KzUr6c1l2BPvqKX8="
gmail.com.              300     IN      TXT     "v=spf1 redirect=_spf.google.com"

❯ dig +short _spf.google.com TXT
"v=spf1 include:_netblocks.google.com include:_netblocks2.google.com include:_netblocks3.google.com ~all"

❯ dig +short _netblocks.google.com TXT
"v=spf1 ip4:35.190.247.0/24 ip4:64.233.160.0/19 ip4:66.102.0.0/20 ip4:66.249.80.0/20 ip4:72.14.192.0/18 ip4:74.125.0.0/16 ip4:108.177.8.0/21 ip4:173.194.0.0/16 ip4:209.85.128.0/17 ip4:216.58.192.0/19 ip4:216.239.32.0/19 ~all"
❯ dig +short _netblocks2.google.com TXT
"v=spf1 ip6:2001:4860:4000::/36 ip6:2404:6800:4000::/36 ip6:2607:f8b0:4000::/36 ip6:2800:3f0:4000::/36 ip6:2a00:1450:4000::/36 ip6:2c0f:fb50:4000::/36 ~all"
❯ dig +short _netblocks3.google.com TXT
"v=spf1 ip4:172.217.0.0/19 ip4:172.217.32.0/20 ip4:172.217.128.0/19 ip4:172.217.160.0/20 ip4:172.217.192.0/19 ip4:172.253.56.0/21 ip4:172.253.112.0/20 ip4:108.177.96.0/19 ip4:35.191.0.0/16 ip4:130.211.0.0/22 ~all"

Google public DNS

Pendiente hacer el trabajo con un scriot como lo tengo con los google dns, pero incluyendo los Google gmail.

Listado de IP de google y gmail (IPV4)

# google
34.64.0.0/24 
34.64.1.0/24 
34.64.2.0/24 
34.101.0.0/24 
34.101.1.0/24 
34.101.2.0/24 
74.125.16.128/26 
74.125.16.192/26 
74.125.17.128/26 
74.125.17.192/26 
74.125.18.0/25 
74.125.18.128/26 
74.125.18.192/26 
74.125.19.0/25 
74.125.19.128/25 
74.125.40.0/25 
74.125.40.128/26 
74.125.40.192/26 
74.125.41.0/24 
74.125.42.0/24 
74.125.43.0/25 
74.125.43.128/25 
74.125.44.0/24 
74.125.45.0/24 
74.125.46.0/24 
74.125.47.0/24 
74.125.72.0/24 
74.125.73.0/24 
74.125.74.0/24 
74.125.75.0/24 
74.125.76.0/24 
74.125.77.0/24 
74.125.78.0/24 
74.125.79.0/24 
74.125.80.0/24 
74.125.81.0/24 
74.125.92.0/24 
74.125.112.0/24 
74.125.113.0/24 
74.125.114.128/26 
74.125.114.192/26 
74.125.115.0/24 
74.125.177.0/24 
74.125.178.0/25 
74.125.178.128/25 
74.125.179.0/25 
74.125.179.128/26 
74.125.179.192/26 
74.125.180.0/24 
74.125.181.0/25 
74.125.181.128/26 
74.125.181.192/26 
74.125.182.0/24 
74.125.183.0/24 
74.125.184.0/24 
74.125.185.0/25 
74.125.185.128/26 
74.125.185.192/26 
74.125.186.0/25 
74.125.186.128/26 
74.125.186.192/26 
74.125.187.0/25 
74.125.187.128/26 
74.125.187.192/26 
74.125.189.0/24 
74.125.190.0/24 
74.125.191.0/24 
172.217.32.0/25 
172.217.32.128/26 
172.217.32.192/26 
172.217.33.0/25 
172.217.33.128/25 
172.217.34.0/26 
172.217.34.64/26 
172.217.34.128/26 
172.217.34.192/26 
172.217.35.0/26 
172.217.35.64/26 
172.217.35.128/26 
172.217.35.192/26 
172.217.36.0/24 
172.217.37.0/25 
172.217.37.128/26 
172.217.37.192/26 
172.217.38.0/25 
172.217.38.128/26 
172.217.38.192/26 
172.217.39.0/25 
172.217.39.128/26 
172.217.39.192/26 
172.217.40.0/25 
172.217.40.128/26 
172.217.40.192/26 
172.217.41.0/25 
172.217.41.128/26 
172.217.41.192/26 
172.217.42.0/25 
172.217.42.128/26 
172.217.42.192/26 
172.217.43.0/25 
172.217.43.128/26 
172.217.43.192/26 
172.217.44.0/25 
172.217.44.128/26 
172.217.44.192/26 
172.217.45.0/25 
172.217.45.128/25 
172.217.46.0/24 
172.217.47.0/25 
172.217.47.128/25 
172.253.0.0/25 
172.253.0.128/25 
172.253.1.0/25 
172.253.1.128/26 
172.253.1.192/26 
172.253.2.0/25 
172.253.2.128/26 
172.253.2.192/26 
172.253.3.0/25 
172.253.3.128/25 
172.253.4.0/25 
172.253.4.128/25 
172.253.5.0/25 
172.253.5.128/25 
172.253.6.0/25 
172.253.6.128/25 
172.253.7.0/25 
172.253.7.128/26 
172.253.7.192/26 
172.253.8.0/25 
172.253.8.128/26 
172.253.8.192/26 
172.253.9.0/25 
172.253.9.128/26 
172.253.9.192/26 
172.253.10.0/25 
172.253.10.128/25 
172.253.11.0/25 
172.253.11.128/26 
172.253.11.192/26 
172.253.12.0/25 
172.253.12.128/25 
172.253.13.0/25 
172.253.13.128/26 
172.253.13.192/26 
172.253.14.0/25 
172.253.14.128/26 
172.253.14.192/26 
172.253.15.0/25 
172.253.15.128/26 
172.253.15.192/26 
172.253.192.0/24 
172.253.193.0/25 
172.253.193.128/26 
172.253.193.192/26 
172.253.194.0/25 
172.253.194.128/26 
172.253.194.192/26 
172.253.195.0/25 
172.253.195.128/26 
172.253.195.192/26 
172.253.196.0/25 
172.253.196.128/26 
172.253.196.192/26 
172.253.197.0/25 
172.253.197.128/26 
172.253.197.192/26 
172.253.198.0/25 
172.253.198.128/26 
172.253.198.192/26 
172.253.199.0/25 
172.253.199.128/26 
172.253.199.192/26 
172.253.200.0/25 
172.253.200.128/26 
172.253.200.192/26 
172.253.201.0/25 
172.253.201.128/25 
172.253.202.0/24 
172.253.204.0/25 
172.253.204.128/26 
172.253.204.192/26 
172.253.205.0/24 
172.253.206.0/24 
172.253.209.0/25 
172.253.209.128/26 
172.253.209.192/26 
172.253.210.0/25 
172.253.210.128/25 
172.253.211.0/25 
172.253.211.128/26 
172.253.211.192/26 
172.253.212.0/25 
172.253.212.128/26 
172.253.212.192/26 
172.253.213.0/25 
172.253.213.128/25 
172.253.214.0/25 
172.253.214.128/26 
172.253.214.192/26 
172.253.215.0/25 
172.253.215.128/26 
172.253.215.192/26 
172.253.216.0/25 
172.253.216.128/26 
172.253.216.192/26 
172.253.217.0/25 
172.253.217.128/25 
172.253.218.0/25 
172.253.218.128/26 
172.253.218.192/26 
172.253.219.0/25 
172.253.219.128/26 
172.253.219.192/26 
172.253.220.0/25 
172.253.220.128/26 
172.253.220.192/26 
172.253.221.0/25 
172.253.221.128/26 
172.253.221.192/26 
172.253.222.0/25 
172.253.222.128/26 
172.253.222.192/26 
172.253.223.0/25 
172.253.223.128/26 
172.253.223.192/26 
172.253.224.0/24 
172.253.225.0/24 
172.253.226.0/24 
172.253.227.0/24 
172.253.228.0/24 
172.253.229.0/24 
172.253.230.0/24 
172.253.231.0/24 
172.253.232.0/24 
172.253.233.0/24 
172.253.234.0/24 
172.253.235.0/24 
172.253.236.0/24 
172.253.237.0/24 
172.253.238.0/24 
172.253.239.0/24 
172.253.240.0/24 
172.253.241.0/24 
172.253.242.0/24 
172.253.243.0/24 
172.253.244.0/24 
172.253.245.0/24 
172.253.246.0/24 
172.253.247.0/24 
172.253.248.0/24 
172.253.249.0/24 
172.253.250.0/24 
172.253.251.0/24 
172.253.252.0/24 
172.253.253.0/24 
172.253.254.0/24 
172.253.255.0/24 
173.194.90.0/24 
173.194.91.0/24 
173.194.93.0/24 
173.194.94.0/24 
173.194.95.0/24 
173.194.96.0/25 
173.194.96.128/25 
173.194.97.0/24 
173.194.98.0/24 
173.194.99.0/24 
173.194.100.0/24 
173.194.101.0/24 
173.194.102.0/24 
173.194.103.0/24 
173.194.168.0/25 
173.194.168.128/26 
173.194.168.192/26 
173.194.169.0/24 
173.194.170.0/24 
173.194.171.0/24 
108.177.16.0/24
108.177.17.0/24
142.250.220.0/24
142.250.221.0/24
64.18.0.0/20
64.233.160.0/19
66.102.0.0/20
66.249.80.0/20
72.14.192.0/18
74.125.0.0/16
108.177.8.0/21
108.177.96.0/19
172.217.0.0/19
173.194.0.0/16
207.126.144.0/20
209.85.128.0/17
216.58.192.0/19
216.239.32.0/19
# end google ipv4

Prestashop: Expected response code 354 but got code 530, with message 530 Relaying not allowed

Introducción

No hemos tocado nada, ¿o sí?

Bueno, es probable que simplemente hayamos cambiado la dirección de correo electrónico que usábamos antes para enviar correos, y poco más.

La realidad es que si ponemos la tienda en debug poco más que lo que nos indica el panel de control de Prestahsop, obtendremos, pero es suficiente.

Expected response code 354 but got code “530”, with message “530 Relaying not allowed ”

530 Relaying not allowed

Análisis

El mensaje es claro, el servidor de correo nos prohíbe el envío del correo por que la necesaria paridad entre el emisor o usuario SMTP usado “xxxxx@dominio.tld” no corresponde. Son distintos.

Por supuesto, indica también que el servidor tiene configurado un sistema que no permite el relay de correo, como debe ser en un servidor de correo que se precie de ser cuando menos respetuosos con las más elementales normas de seguridad.

Prestashop te ilumina sobre su capacidad de tener distintos contactos, pero olvida que su sistema no es válido cuando un servidor o cuando la tienda se configura con SMTP AUTENTIFICADO. Para ello, Prestashop debería estar preparado para configurar cada una de las cuentas emisoras, y actualmente no lo esta (versión 1.7.8.3) cosa fácil de hacer si se lo propusieran.

Prestahop :: Gestión de contactos

En la versión 1.7 he visto que es posible hacer una vuelta a la cuestión, pero no apta para no programadores, ni para “expertos informáticos”. En ella se detalla una solución patatera (debería estar en el tablero de mandos y no en un manual del Core)

Bueno, si ya hemos llegado hasta aquí, y hemos sumado dos y dos son cuatro, la solución está vista.

'{email}' => Configuration::get('PS_SHOP_EMAIL'), // sender email address

Así que viendo la variable, ya tenemos claro por dónde van las cosas.

Prestashop permite la creación de distinto contactos para el envío de correos, pero estos no son usados en esa variable, lo cual no lleva al su inutilidad, pues spolo funcionará cuando usemos /usr/bin/sendmail o correo no autenticado o en su defecto el servidor donde nos encontremos, no tiene activas las protecciones de relay.

Solución

La solución pasa por editar la tabla configuration de nuestra tienda y modificar la variable PS_SHOP_EMAIL para que coincida con la dirección que hemos configurado como dirección autenticada.

Como norma general, esta variable tendrá la dirección de correo electrónico que se usó al instalar la tienda, y que no es modificable (o al menos yo no lo vi claro en su documentación) desde el panel de control.

En la imagen de abajo, como hacerlo con Mysql Workbench (no soy amigo de phpMyAdmin)

Prestahop :: Modificar la tabla configuration de prestahsop

Aviso

Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos. También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad. El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de Castris

Si necesitas soporte profesional puedes contratar con Castris soporte profesional.

Actualizando Kimai Timetracker

Kimai Timetracker

Es un buen software como timetracker, y aunque podría mejorar mucho lo estoy usando desde hace tiempo, y no puedo ponerme a hacerme uno a medida.

Pero, yo suelo tener mi máquina actualizada a la última versión de PHP, y eso en el software, ya sea OpenSource o Comercial, muchas veces no es lo mejor.

En este caso, Kimai no está preparado para usar la PHP 8.1

Actualización.

Como siempre importante tener un backup rápido de los ficheros y de mysql. (mysqldump y rsync a nuestro servicio)

rsync -avv --progress --delete-after . ~/backups/home
mysqludmp –opt -u <user> -p database_kmai > database_kimai.sql

Leer la documentación

Importante leer la documentación de upgrades ya que en el caso de este software deberemos ir paso a paso, tag a tag.

Error por PHP Version

Use the `composer fund` command to find out more!

Synchronizing package.json with PHP packages
Don't forget to run npm install --force or yarn install --force to refresh your JavaScript dependencies!
Run composer recipes at any time to see the status of your Symfony recipes.

Executing script cache:clear [KO]
 [KO]
Script cache:clear returned with error code 255
!!  PHP Warning:  include_once(/home/kimai/web/timetracker.midominio.tld/src/Command/CreateReleaseCommand.php): Failed to open stream: No such file or directory in /home/kimai/web/timetracker.midominio.tld/var/cache/prod/ContainerGZoxyYu/getCreateReleaseCommandService.php on line 10
!!  PHP Warning:  include_once(): Failed opening '/home/kimai/web/timetracker.midominio.tld/src/Command/CreateReleaseCommand.php' for inclusion (include_path='.:/usr/share/php') in /home/kimai/web/timetracker.midominio.tld/var/cache/prod/ContainerGZoxyYu/getCreateReleaseCommandService.php on line 10
!!  PHP Warning:  include_once(/home/kimai/web/timetracker.midominio.tld/src/Command/ResetCommand.php): Failed to open stream: No such file or directory in /home/kimai/web/timetracker.midominio.tld/var/cache/prod/ContainerGZoxyYu/getResetCommandService.php on line 10
!!  PHP Warning:  include_once(): Failed opening '/home/kimai/web/timetracker.midominio.tld/src/Command/ResetCommand.php' for inclusion (include_path='.:/usr/share/php') in /home/kimai/web/timetracker.midominio.tld/var/cache/prod/ContainerGZoxyYu/getResetCommandService.php on line 10

Ni que mirar más, problema de uso de un PHP que no está soportado por kimai.

Cambiar la versión de PHP para el usuario

¿Por qué tener usuarios independientes para nuestro host virtuales? Por estas cosas. Imagínate que ahora por ahorrar un poco tienes todo en la misma cuenta de usuario. Uf, que problemon.

Si tienes multiples versiones de PHP en tu sistema (que deberías) puedes usar este tip [Cambiar la version php para nuestro shell y composer](] https://wiki.castris.com/books/tips-para-programadores/page/cambiar-la-version-php-para-el-shell-composer)

Tras hacerlo solo deberás usar el alias creado para usar la versión deseada, (en este caso php74)

c74 update
. . . 
136 packages you are using are looking for funding.
Use the `composer fund` command to find out more!

What about running composer global require symfony/thanks && composer thanks now?
This will spread some   by sending a ★  to the GitHub repositories of your fellow package maintainers.

Run composer recipes at any time to see the status of your Symfony recipes.

Unpacking Symfony packs
 - Unpacked symfony/profiler-pack
Loading composer repositories with package information
Updating dependencies
Nothing to modify in lock file
Installing dependencies from lock file (including require-dev)
Package operations: 0 installs, 0 updates, 1 removal
 - Removing symfony/profiler-pack (v1.0.6)
Package doctrine/reflection is abandoned, you should avoid using it. Use roave/better-reflection instead.
Package symfony/inflector is abandoned, you should avoid using it. Use EnglishInflector from the String component instead.
Package zendframework/zend-escaper is abandoned, you should avoid using it. Use laminas/laminas-escaper instead.
Package fzaninotto/faker is abandoned, you should avoid using it. No replacement was suggested.
Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.
Generating autoload files
composer/package-versions-deprecated: Generating version class...
composer/package-versions-deprecated: ...done generating version class
135 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
Failed to download recipe: The "https://flex.symfony.com/p/zendframework,zend-eventmanager,r3.2.0;zendframework,zend-code,r3.3.0;symfony,profiler-pack,rv1.0.3;symfony,ldap,rv4.2.8;php,r7.2.
9;ocramius,proxy-manager,r2.1.1;ocramius,package-versions,r1.2.0;composer,package-versions-deprecated,i1.11.99.5,1642428864;composer,pcre,i1.0.1,1642796677;symfony,polyfill-php81,iv1.25.0,1
631541491" file could not be downloaded (HTTP/2 410 )                                                                                                                                         

Run composer recipes at any time to see the status of your Symfony recipes.

Info from https://repo.packagist.org: #StandWithUkraine
Executing script cache:clear [OK]
Executing script assets:install [OK]

Después continuar con los pasos exigidos por Kimai para actualizar.

Aviso

Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos. También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad. El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de Castris

Si necesitas soporte profesional puedes contratar con Castris soporte profesional.

QGIS, Navicat, y otros problemas de conexión con MySQL 8

Introducción

Muchas veces no solemos encontrar respuesta a problemas de conexión con mysql con distintos programas, ya sea para acceso a mysql como herramientas de trabajo, on en mi caso para un programa como QGIS en el cual queriamos configurar una conexión mysql con un servidor basado en MySQL 8.

Error

Error en QGIS MySQL

Críptico, inútil, y deficitario mensaje de error.

Pero es lo habitual.

Este me llamó la atención, ya que con otro servidor basado en Cpanel si conectaba. Así que revise la configuración MySQL.

mysql
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2882
Server version: 8.0.30-0ubuntu0.20.04.2 (Ubuntu)

Copyright (c) 2000, 2022, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> SHOW VARIABLES LIKE '%ssl%';
+-------------------------------------+-----------------+
| Variable_name                       | Value           |
+-------------------------------------+-----------------+
| admin_ssl_ca                        |                 |
| admin_ssl_capath                    |                 |
| admin_ssl_cert                      |                 |
| admin_ssl_cipher                    |                 |
| admin_ssl_crl                       |                 |
| admin_ssl_crlpath                   |                 |
| admin_ssl_key                       |                 |
| have_openssl                        | YES             |
| have_ssl                            | YES             |
| mysqlx_ssl_ca                       |                 |
| mysqlx_ssl_capath                   |                 |
| mysqlx_ssl_cert                     |                 |
| mysqlx_ssl_cipher                   |                 |
| mysqlx_ssl_crl                      |                 |
| mysqlx_ssl_crlpath                  |                 |
| mysqlx_ssl_key                      |                 |
| performance_schema_show_processlist | OFF             |
| ssl_ca                              | ca.pem          |
| ssl_capath                          |                 |
| ssl_cert                            | server-cert.pem |
| ssl_cipher                          |                 |
| ssl_crl                             |                 |
| ssl_crlpath                         |                 |
| ssl_fips_mode                       | OFF             |
| ssl_key                             | server-key.pem  |
| ssl_session_cache_mode              | ON              |
| ssl_session_cache_timeout           | 300             |
+-------------------------------------+-----------------+
27 rows in set (0.01 sec)

¡Qué bonito! La sutil diferencia estaba en que este servidor tiene activada la conexión SSL.

Es un servidor montado a pelo, sin panel de control, pero sin tunear la seguridad ya que como norma general, es sólo accesible a una serie de IP fijas y de hostnames. Por defecto está todo cerrado.

Como norma general animo a mis compañeros a usar un túnel SSH para conectar a mysql, pero aun así esto falla, y es por que a fin de cuentas con túnel o sin túnel la conexión trata de conectarse via SSL.

Solución rápida: deshabilitar SSL en el servidor MySQL

Sólo tenemos que añadir skip_ssl a nuestro fichero de configuración del servidor y hacer un restart del servidor mysql

Esta es una solución rápida, bajo la premisa de que el tráfico entre puntos está bajo SSL (túnel) y que el acceso está reducido mediante políticas de acceso denegado-a-todos/abierto-sólo-autorizados

Después de reiniciar

mysql> SHOW VARIABLES LIKE '%ssl%';
+-------------------------------------+----------+
| Variable_name                       | Value    |
+-------------------------------------+----------+
| admin_ssl_ca                        |          |
| admin_ssl_capath                    |          |
| admin_ssl_cert                      |          |
| admin_ssl_cipher                    |          |
| admin_ssl_crl                       |          |
| admin_ssl_crlpath                   |          |
| admin_ssl_key                       |          |
| have_openssl                        | DISABLED |
| have_ssl                            | DISABLED |
| mysqlx_ssl_ca                       |          |
| mysqlx_ssl_capath                   |          |
| mysqlx_ssl_cert                     |          |
| mysqlx_ssl_cipher                   |          |
| mysqlx_ssl_crl                      |          |
| mysqlx_ssl_crlpath                  |          |
| mysqlx_ssl_key                      |          |
| performance_schema_show_processlist | OFF      |
| ssl_ca                              |          |
| ssl_capath                          |          |
| ssl_cert                            |          |
| ssl_cipher                          |          |
| ssl_crl                             |          |
| ssl_crlpath                         |          |
| ssl_fips_mode                       | OFF      |
| ssl_key                             |          |
| ssl_session_cache_mode              | ON       |
| ssl_session_cache_timeout           | 300      |
+-------------------------------------+----------+

Este tip tiene una consecuencia. Si tenemos usuarios en nuestro servidor que usen Auhthentication caching_sha_password no podrán logearse con ese usuario, ya que el plugin requiere de un conexión segura. Si no podemos recrear el usuario con la autentificación estandar, no podrá logearse.

Cambiar el modo de autentificación de un usuario

Si optamos por la via rápida, y ya teniamos algun uso del usuario en modo remoto con algun software (TablePlus, MySQlWorkbench, Navicat, DBeaver, ...) fallará. Si sigue siendo una via rápida valida, de acuerdo a nuestras configuraciones de seguridad, posibles usos, y otros elementos de valoración, podemos cambiar el usuario alterandolo en la linea de comandos de mysql.

mysql> ALTER user 'user'@'127.0.0.1' IDENTIFIED WITH mysql_native_password BY 'mypasswordcomplejo';
mysql > FLUSH PRIVILEGES;

Solución: usar certificados SSL no autofirmados

Aviso

Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos. También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad. El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de Castris

Si necesitas soporte profesional puedes contratar con Castris soporte profesional.

Mysql 8, SSL con Let’s Encrypt

Lo mejor es trabajar en un entorno seguro.

La aparición de nuevas versiones de muchos softwares, que fuerzan al uso de una seguridad mayor, sobre todo en las comunicaciones entre nodos, habida cuenta del auge de los microservicios, y la amplitud de posibilidades para este tema, nos lleva a un camino ya conocido: la desactivación de la seguridad en cuanto surge algo que nos molesta, en lugar de aprender el camino del cómo hacerlo con el método seguro.

En el anterior artículo QGIS, Navicat, y otros problemas de conexión con MySQL 8 hablamos de una salida rápida, pero la realidad es que es mejor dedicar un tiempo y hacer la correcta.

Aquí te explico cómo securizar con SSL de Let’s Encrypt un servidor MySQL 8.X, aunque estoy seguro de que te vale con algún matiz, para MariaDb o Percona.

Instalando Let’s Encrypt en MySQL

No es el alcance explicar cómo se obtiene un certificado Let’s Encrypt en un servidor pero te dejo algunos tips, al final del documento.

Entendemos para este artículo que tenemos un servidor o un sistema de certificados para el hostname basado en Let’s Encrypt, ya está ya instalado y que se renueva regularmente.

Ubicación de los certificados Let’s Encrypt

Esto es sólo un ejemplo, para entenderlo. En realidad debes tener claro dónde está en tu servidor.

# ls  -lisa /etc/letsencrypt/live/<mydomain.tld>/
total 12
428663 4 drwxr-xr-x 2 root root 4096 sep 15 14:32 .
428659 4 drwx------ 3 root root 4096 mar  3  2022 ..
399891 0 lrwxrwxrwx 1 root root   54 sep 15 14:32 cert.pem -> ../../archive/mydomain.tld/cert5.pem
399898 0 lrwxrwxrwx 1 root root   55 sep 15 14:32 chain.pem -> ../../archive/mydomain.tld/chain5.pem
399930 0 lrwxrwxrwx 1 root root   59 sep 15 14:32 fullchain.pem -> ../../archive/mydomain.tld/fullchain5.pem
399892 0 lrwxrwxrwx 1 root root   57 sep 15 14:32 privkey.pem -> ../../archive/mydomain.tld/privkey5.pem

El problema es que estando en esa ubicación, mediante enlaces a los ficheros reales, correspondientes a cada una de las versiones (renovaciones) no conseguí que funcionaran.

Así que vamos a solucionarlo

Despliegue de los certificados Let’s Encrypt para MySQL

Configuración mysql

Configurar el fichero /etc/my.cnfo el usado por su distribución. En mi caso para una Ubuntu 20.04 /etc/mysql/mysql.conf.d/mysqld.cnf

[mysqld]
ssl_ca=/var/lib/mysql/chain.pem
ssl_cert=/var/lib/mysql/cert.pem
ssl_key=/var/lib/mysql/privkey.pem
Crear los ficheros de los certificados

Notas y descargo de responsabilidad

# domain=mydomain.tld
# cert_dir=/var/lib/mysql
# user=mysql.mysql
# cp /etc/letsencrypt/live/$domain/cert.pem $cert_dir
# cp /etc/letsencrypt/live/$domain/privkey.pem $cert_dir
# openssl x509 -in /etc/letsencrypt/live/$domain/chain.pem > $cert_dir/chain.pem
# chown $user $cert_dir/*.pem
# chmod 600 $cert_dir/*.pem
# mysql --login-path=root@localhost --execute="ALTER INSTANCE RELOAD TLS" 

Si todo ha ido bien, no saldrá ningún mensaje de error, y MySQL 8.0 ya está preparado para usar SSL en su trabajo.

Entendemos que tenemos y usamos un fichero ~/.my.cnf para acceder sin password como root a mysql

Comprobación MySQL
mysql > SHOW VARIABLES LIKE '%ssl%';
+-------------------------------------+----------------------------+
| Variable_name                       | Value                      |
+-------------------------------------+----------------------------+
| admin_ssl_ca                        |                            |
| admin_ssl_capath                    |                            |
| admin_ssl_cert                      |                            |
| admin_ssl_cipher                    |                            |
| admin_ssl_crl                       |                            |
| admin_ssl_crlpath                   |                            |
| admin_ssl_key                       |                            |
| have_openssl                        | YES                        |
| have_ssl                            | YES                        |
| mysqlx_ssl_ca                       |                            |
| mysqlx_ssl_capath                   |                            |
| mysqlx_ssl_cert                     |                            |
| mysqlx_ssl_cipher                   |                            |
| mysqlx_ssl_crl                      |                            |
| mysqlx_ssl_crlpath                  |                            |
| mysqlx_ssl_key                      |                            |
| performance_schema_show_processlist | OFF                        |
| ssl_ca                              | /var/lib/mysql/chain.pem   |
| ssl_capath                          |                            |
| ssl_cert                            | /var/lib/mysql/cert.pem    |
| ssl_cipher                          |                            |
| ssl_crl                             |                            |
| ssl_crlpath                         |                            |
| ssl_fips_mode                       | OFF                        |
| ssl_key                             | /var/lib/mysql/privkey.pem |
| ssl_session_cache_mode              | ON                         |
| ssl_session_cache_timeout           | 300                        |
+-------------------------------------+----------------------------+
27 rows in set (0,00 sec)
Modificación del script de renovación de Let’s Encrypt

Obtenemos el path de de los hooks de Let’s Encrypt.

## Versión de certbot recomendada con snap, pero puede variar si usamos package de la distro o binario. Por eso buscamos
# systemctl | grep cer
  snap-certbot-2344.mount                                                                          loaded active mounted   Mount unit for certbot, revision 2344
  snap-certbot-2414.mount                                                                          loaded active mounted   Mount unit for certbot, revision 2414
  snap.certbot.renew.timer                                                                         loaded active waiting   Timer renew for snap application certbot.renew
#  systemctl status snap.certbot.renew.timer
● snap.certbot.renew.timer - Timer renew for snap application certbot.renew
     Loaded: loaded (/etc/systemd/system/snap.certbot.renew.timer; enabled; vendor preset: enabled)
     Active: active (waiting) since Tue 2022-10-04 20:35:18 UTC; 1 weeks 2 days ago
    Trigger: Fri 2022-10-14 11:31:00 UTC; 1h 53min left
   Triggers: ● snap.certbot.renew.service

Warning: journal has been rotated since unit was started, output may be incomplete.

Y después creamos el fichero /etc/letsencrypt/renewal-hooks/deploy/mysqld-deploy.sh que se encargará de ajustar tras la renovación los ficheros para MySQL.

#!/bin/sh
domain=mydomain.tld
cert_dir=/var/lib/mysql
user=mysql.mysql
cp /etc/letsencrypt/live/$domain/cert.pem $cert_dir
cp /etc/letsencrypt/live/$domain/privkey.pem $cert_dir

# Only keep 1st certificate (C=US/O=Let's Encrypt/CN=R3), that is, get rid
# of 2nd certificate "ISRG Root X1" issued by "DST Root CA X3" which is expired.
# https://letsencrypt.org/2020/12/21/extending-android-compatibility.html

openssl x509 -in /etc/letsencrypt/live/$domain/chain.pem > $cert_dir/chain.pem
chown $user $cert_dir/*.pem
chmod 600 $cert_dir/*.pem
mysql --login-path=root@localhost --execute="ALTER INSTANCE RELOAD TLS"

Y le damos permisos de ejecución

chmod 755 /etc/letsencrypt/renewal-hooks/deploy/mysqld-deploy.sh
Comprobación
openssl s_client -starttls mysql -showcerts -connect mydomain.tld:3306
CONNECTED(00000003)
depth=2 C = US, O = Internet Security Research Group, CN = ISRG Root X1
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = R3
verify return:1
depth=0 CN = api.mydomain.tld
verify return:1
---
Certificate chain
 0 s:CN = api.mydomain.tld
   i:C = US, O = Let's Encrypt, CN = R3
-----BEGIN CERTIFICATE-----
MIIFZjCCBE6gAwIBAgISBJPIsuc3JOh84ALWyVAXpFjxMA0GCSqGSIb3DQEBCwUA
…
…
BpPXg5qzChYB5e2/wRhXRZb3IejNUDg8tQzU3hL6sJPcNtwnYAFyxDcg
-----END CERTIFICATE-----
 1 s:C = US, O = Let's Encrypt, CN = R3
   i:C = US, O = Internet Security Research Group, CN = ISRG Root X1
-----BEGIN CERTIFICATE-----
MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw
…
…
MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX
nLRbwHOoq7hHwg==
-----END CERTIFICATE-----
---
Server certificate
subject=CN = api.mydomain.tld

issuer=C = US, O = Let's Encrypt, CN = R3

---
No client certificate CA names sent
Requested Signature Algorithms: ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512:Ed25519:Ed448:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512:ECDSA+SHA224:RSA+SHA224
Shared Requested Signature Algorithms: ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512:Ed25519:Ed448:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 3415 bytes and written 468 bytes
Verification: OK
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 2048 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : TLS_AES_256_GCM_SHA384
    Session-ID: AD8278545269458B04E87DF04C50140678DEE6C2E2A5BC9017329D1D170A80B1
    Session-ID-ctx:
    Resumption PSK: DFB2E7716E7B0579E911AB32999D976CBA419B43C0F2A69FAB68D7C13DF7E52FF58A43E3709F62B7E80E6269707AD002
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - 9b 98 52 5a 9f bc be 7c-b9 37 3a 3a e7 bf 49 7c   ..RZ...|.7::..I|
    0010 - c9 de 90 6b 1d 08 c8 9e-f7 dc c4 04 c6 e4 48 41   ...k..........HA
    …
    …
    00b0 - 09 68 bb 27 18 d3 3e f6-2e e9 f4 6e ee 3f 49 26   .h.'..>....n.?I&
    00c0 - 7c 55 be 6b 46 1b 3c b3-0d 72 d4 93 da 7e 6f c2   |U.kF.<..r...~o.

    Start Time: 1665740692
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: no
    Max Early Data: 0
---
read R BLOCK
---
Post-Handshake New Session Ticket arrived:
SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : TLS_AES_256_GCM_SHA384
    Session-ID: 2C2F40E7309289A3368A250BC710F04021E66B9D821DFA74694AACFE05E9E742
    Session-ID-ctx:
    Resumption PSK: DE4C98072B6A8DC2A3636ADFBDF3612090C3C1A5BCB1CD267C1B688015F12FAE5946F2541FB0A428BEDE5483E1DA7A46
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - 9b 98 52 5a 9f bc be 7c-b9 37 3a 3a e7 bf 49 7c   ..RZ...|.7::..I|
    0010 - 40 00 eb f6 2b 3e 84 fc-4a 7d 6e 00 e7 96 af ce   @...+>..J}n.....
    …
    …
    00b0 - 14 4a 48 3e 33 4b 19 b4-df 14 24 bb 28 bc 55 29   .JH>3K....$.(.U)
    00c0 - 73 23 37 e2 3c e7 0b ea-ed 25 5e 3d 28 cc b5 0d   s#7.<....%^=(...

    Start Time: 1665740692
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: no
    Max Early Data: 0
---
read R BLOCK
Enlaces externos
Aviso

Esta documentación y su contenido, no implica que funcione en tu caso o determinados casos. También implica que tienes conocimientos sobre lo que trata, y que en cualquier caso tienes copias de seguridad. El contenido el contenido se entrega, tal y como está, sin que ello implique ningún obligación ni responsabilidad por parte de Castris

Si necesitas soporte profesional puedes contratar con Castris soporte profesional.