Certificados Let’s Encrypt en más de un servidor …

… vaya problema tonto con el que no contaba.

En las redes que gestiono tengo una máquina que actúa como frontal de acceso a y hacia Internet al resto de la red. Lo hice así en su momento porque descubrí el módulo mod_proxy del programa Apache y me di cuenta de que era muy cómodo tener los servicios web en una máquina interior y podía controlar mejor los accesos desde el exterior.

Para el caso de los certificados que tenían que estar en varios sitios al mismo tiempo lo había resuelto con un paquete Debian que reconstruía cada vez que los renovaba e instalaba en ambas máquinas. Ahora, como estoy empleando los certificados de Let’s Encrypt y no tengo tanta maña con ellos, me he limitado a usar el cliente letsencrypt.sh que me ha dado buenos resultados en servidores independientes.

Pero se me acaba de presentar el caso de tener dichos certificados en dos sitios al mismo tiempo antes de lo que pensaba. La instalación principal está en un servidor web interno y la secundaria se encuentra en el servidor que hace de frontal con la red. Tenía (y tengo) pensado situar algo como un acceso unificado hacia la inttranet y por eso preciso que exista también allí una copia de los certificados.

Bueno, por eso no exactamente. El módulo que utilizo en el servidor frontal para conectar con el servidor interno emplea técnicas de proxy (sí, es mod_proxy) y quiero que exista concordancia de conexiones entre ambos. Es decir, si alguien desde fuera conecta bajo cifrado la conexión entre servidores debe estar cifrada también.

El programa letsencrypt.sh dispone de un mecanismo para efectuar tareas tras la renovación de un certificado; un archivo llamado hook.sh que define varias funciones bash que puedo utilizar para efectuar una copia de los nuevos archivos y para reiniciar los servicios. Y este es el punto. Reiniciar los servicios en local es sencillo, la renovación la realiza el administrador, pero ¿ y en remoto ?

No me va a quedar más remedio que escribir un script que se encargue de reiniciar servicios en la segunda máquina tras renovar los certificados en ésta. Claro que, bien pensado, lo mismo el script me sirve también para reiniciarlos en local.

Y así ha sido. Lamentablemente no puedo mostrar el código porque lo he personalizado mucho para la empresa pero a grandes rasgos lo que hace es:

  1. Prepara el entorno para el programa letsencrypt.sh.
  2. Prepara el servidor Apache para el desafío ACME.
  3. Clona el software de letsencrypt.sh y lo instala.
  4. Configura el reinicio de los servicios del sistema que emplean los certificados y lo dispone así en la configuración.

Tras la renovación

Tengo dos escenarios: en el primero se encuentra el servidor donde se renuevan los certificados y que también proporciona servicios que hacen uso de ellos. En el segundo (o tercero o cuarto) sólo se hacen uso de dichos certificados.

He dividido la funcionalidad del paquete de manera que sea posible configurar e instalar letsencrypt.sh y configurar apache de manera independiente. De esta forma en el primer servidor es donde creo y renuevo certificados y en los segundos sólo reinicio servicios. Para ello uso el archivo hook.sh de letsencrypt.sh con la siguiente configuración:

function deploy_cert {
    local DOMAIN="${1}" KEYFILE="${2}" CERTFILE="${3}" FULLCHAINFILE="${4}" CHAI
NFILE="${5}" TIMESTAMP="${6}"

    #   Reiniciamos los servicios locales 
    after-renew-certs

    #   y vemos si es necesario transferirlos a otras máquinas
    if [ -e /etc/empresa/remote-certs.conf ]; then 
        sync-letsencrypt /etc/venexma/remote-certs.conf 
    fi
}

El archivo remote-certs.conf contiene una lista de máquinas con las que conectar, transferir archivos y reiniciar servicios.

Reiniciando servicios

Pues aprovechando que Debian Jessie tiene a systemd integrado y funcional he escrito un pequeño script que lee un archivo de configuración simple, donde se detallan los servicios que hacen uso de los certificados, y si encuentra que alguno está activo lo reinicia.

El archivo es algo tan simple como:

#   Servidor web
apache2

#   Correo electrónico: recepción y almacenamiento
dovecot
exim4
qpsmptd

#   Mensajería instantánea
prosody

Mientras que la parte del script encarga de reiniciarlos es como sigue:

read_config()
{
    local config="$*"

    clean_text=$(egrep -v '^#|^$' $config)

    $clean_text
}

# Procesamos el archivo de configuración servicio a servicio
for service in $(read_config $config)
do
    if systemctl --quit is-active $service ; then
        _debug "Trying restart ${service} service"
        if [ $dry_mode -eq 0 ]; then 
            systemctl restart $service   
        fi
    fi
done