IT post header

Proxy inverso con HAProxy + ACME en pfSense

En este post vamos a ver como configurar HAProxy y ACME en nuestro firewall pfSense para poder acceder a servicios alojados en nuestros servidores, por ejemplo a nuestra interface de Home Assistant o nuestro servidor web.

Para que la conexión sea segura vamos a utilizar un certificado de Let’s Encrypt. Esta parte es opcional pero muy recomendable; para esto no necesitamos tener un dominio o DNS dinámicas aunque si disponemos de una de estas dos cosas la configuración será mucho más sencilla. En caso de no disponer de ninguna de las dos opciones aun podremos utilizar el servidor para alojar el archivo de validación a través de la opción Webroot Local Folder o en el peor de los casos la opción Standalone.

Lo primero de todo será instalar los paquetes necesarios en pfSense. Para ello iremos a System → Package Manager → Available packages e instalaremos los paquetes ACME y HAProxy.

Configuración y obtención del certificado Let’s Encrypt (OPCIONAL)

En nuestro pfSense iremos a Services → Acme Certificates → Account keys y pulsaremos Add.

Elegiremos un nombre y como servidor ACME elegiremos Let’s Encrypt Production ACME v2, rellenaremos nuestra dirección de email y pulsaremos en Create para generar la clave de nuestra cuenta.

A continuación pulsaremos en Register ACME account key y después en Save.

Una vez que disponemos de la clave para nuestra cuenta podemos crear nuestro certificado. Para ello vamos a Certificates y pulsaremos Add.

Le daremos un nombre y una descripción, y nos aseguraremos de que la cuenta que acabamos de crear esté seleccionada en ACME account.

Bajaremos hasta Domain SAN List; aquí es donde vamos a validar que somos dueños del destino del certificado. Para el tutorial voy a utilizar mi dominio pero si no disponéis de uno y vuestro servicio DDNS acepta TXT records (como por ejemplo DuckDNS) también podéis utilizarlo. Si no, podéis utilizar los métodos directorio local Webroot o Standalone.

Vamos a generar un certificado comodín (wildcard) que será válido para el dominio y todos los subdominios. Para ello vamos a crear una entrada con *.nombre_de_dominio en el campo FQDN. En método elegiremos nuestro proveedor de DNS y rellenaremos los datos que nos pide. Si nuestro proveedor no está en la lista elegiremos manual.

Después haremos clic en Save y esto nos llevará de vuelta a la pantalla con la lista de certificados.

Una vez en esta pantalla veremos nuestro certificado con fecha de emisión 1 de Enero de 1970, pulsaremos en el botón Issue/Renew y si todo va bien nos aparecerá un mensaje verde en la parte superior de la pantalla.

Puede ser que en este mensaje tengamos unas líneas similares a estas:

[Sat Jun 19 17:10:38 ACDT 2021] Add the following TXT record:
[Sat Jun 19 17:10:38 ACDT 2021] Domain: ‘_acme-challenge.danatec.org’
[Sat Jun 19 17:10:38 ACDT 2021] TXT value: ‘EfUgo1h8THgJH78YUiJGYHgfRTf33YJHiH’

Si es así deberemos añadir una nueva entrada TXT DNS con el valor que se indica en TXT value en nuestro proveedor de DNS.

Después de añadir la entrada TXT (si ha sido necesario) pulsaremos de nuevo en Issue/Renew para ver que el certificado se renueva sin problemas; recargaremos la página y si todo ha ido bien veremos que la fecha de renovación coincide con la fecha actual.

Por ultimo en la pestaña General Settings activaremos Cron Entry para asegurarnos de que el certificado se renueva automáticamente.

Con esto damos por finalizada la configuración del certificado SSL. A continuación veremos cómo configurar HAProxy.

Configurar HAProxy

Para configurar HAProxy iremos a la pantalla Services → HAProxy → Settings.

En esta pantalla vamos a marcar la casilla Enable HAProxy y ajustar el valor de Maximum connections a 1000 y el de Max SSL Diffie-Hellman size a 2048. Despues pulsaremos el botón Save.

Configurar Backend

A continuación iremos a la pestaña Backend. En esta pestaña es donde vamos a definir nuestro servidor o servidores.

Para añadir un servidor pulsaremos el botón Add, le daremos un nombre (yo utilizo el del servidor o subdominio al que va a hacer referencia) y pulsaremos el botón con forma de flecha indicado en la siguiente imagen.

Nos aparecerá un desplegable en el que rellenaremos al menos los siguientes parámetros:

  • Name: Aquí rellanaremos el subdominio o nombre del servidor.
  • Address: La dirección IP de nuestro servidor.
  • Port: El puerto en el que está escuchando el servidor.

No será necesario rellanar ninguno de los campos referentes a los certificados puesto que de esto se encarga HAProxy y no los servidores.

Nota: Mi servidor web está escuchando en el puerto 80, pero si vuestro servidor está escuchando en otro puerto deberéis de rellenarlo aquí.

En esta pantalla hay muchas opciones, echadles un vistazo y probad vosotros mismos las que os parezcan interesantes.

Modificaciones para Home Assistant

Cuando estaba configurando el Backend de Home Assistant me he encontrado con un problema. El método para comprobar la salud del servidor que se asigna por defecto (Http check method → OPTIONS) no funcionaba correctamente y cuando intentaba acceder a Home Assistant en el navegador aparecía un error 503.

Esto lo he solucionado cambiando el método de comprobación de salud del servidor a Http check method → GET.

Configurar Frontend

Primeramente vamos a crear un frontend común para todo el tráfico HTTPS.

Vamos a ir a la pestaña Frontend y pulsaremos el botón Add.

Como nombre del servicio vamos a utilizar https_shared.

En puerto seleccionaremos el puerto 443 y marcaremos la casilla SSL Offloading. A continuación bajaremos hasta la sección SSL Offloading y seleccionaremos el certificado que hemos creado anteriormente.

Si no usamos certificado SSL dejaremos la casilla SSL Offloading desmarcada y no seleccionaremos nada en la sección SSL Offloading.

A continuación vamos a crear otro Frontend para redireccionar el tráfico HTTP a HTTPS.

Crearemos una nueva regla llamada http_redirect que escuche en el puerto 80 de la interface WAN, con la casilla SSL Offloading desmarcada.

Nos desplazaremos hasta la sección actions y crearemos una nueva acción pulsando la flecha verde. Como acción elegiremos http-request redirect y en rule escribiremos scheme https.

Salvaremos y aplicaremos la configuración.

A continuación podremos crear los frontend de nuestros servidores o servicios.

Para ello creamos un nuevo frontend, le daremos un nombre, marcaremos la casilla Shared Frontend y seleccionaremos https_shared.

A continuación Añadiremos una entrada en Access Control lists pulsando la flecha verde.

Aquí definimos criterios que van a servir como filtro para las acciones que definiremos despues. Por ejemplo quiero que si alguien escribe www.danatec.org o danatec.org acceda a la web alojada en mi servidor, para esto he creado una entrada llamada web-server con la expresion Host matches: y como valor danatec.org y otra entrada llamada www-web-server con la expresion Host matches: y como valor www.danatec.org.

Hay bastantes más opciones así que podéis elegir la que mejor se ajuste a vuestro caso.

Después de esto vamos a añadir las siguientes acciones, una para cada una de las reglas que hemos definido arriba:

  • Action → Use Backend
  • Condition acl names → Nombre de la entrada creada en Access Control lists
  • Backend → El servicio o servidor que queremos exponer cuando se cumpla la regla

Por ultimo en Default Backend podríamos elegir si queremos mostrar otro backend en caso de que el anterior no respondiese.

Pulsaremos Save y aplicaremos los cambios.

Despues de haber creado los servicios que necesitemos vamos a crear algunas reglas en el Firewall.

Frontend WordPress

Uno de mis servidores es un servidor WordPress, al cual accedía a través de Traefik, otro proxy inverso que tenía configurado en un contenedor Docker y el cual he decidido cambiar por HAProxy para simplificar las cosas.

WordPress ya estaba configurado para usar una conexión SSL pero como ahora la conexión SSL la gestiona HAProxy, WordPress no sabe que la conexión es SSL y al intentar acceder a él recibía el error «Too Many Redirects». Después de dar muchas vueltas he conseguido que funcione añadiendo las siguientes acciónes en el Frontend (es la misma acción repetida para cada una de las reglas definidas en Access Control lists):

  • Action → http-request header add
  • Condition acl names → Nombre de la entrada creada en Access Control lists
  • name → X-Forwarded-Proto
  • fmt → https

Creación de reglas en el Firewall

Vamos a la pantalla Firewall → Rules.

Crearemos una nueva regla dentro de la pestaña WAN con los siguientes parámetros:

  • Action → Pass
  • Interface → WAN
  • Address Family → IPV4
  • Protocol → TCP
  • Destination → This Firewall (self)
  • Destination Port Range From → HTTP (80)
  • Destination Port Range To → HTTP (80)
  • Description → HAProxy HTTP

Crearemos otra regla también en la interface WAN con estos parámetros:

  • Action → Pass
  • Interface → WAN
  • Address Family → IPV4
  • Protocol → TCP
  • Destination → This Firewall (self)
  • Destination Port Range From → HTTPS (443)
  • Destination Port Range To → HTTPS (443)
  • Description → HAProxy HTTPS

Una vez creadas las reglas y aplicados los cambios nuestros servidores y/o servicios serán accesibles desde el exterior de nuestra red.

Bonus: Proteger Backend con usuario y contraseña

Es posible que deseemos acceder a algún servicio en nuestra red pero que este no disponga de ningún tipo de autentificación por lo que si lo hacemos accesible cualquiera podría acceder a él. Para evitar esto vamos a ver como poder proteger este servicio con un usuario y contraseña.

Lo primero de todo será crear una lista de usuarios siguiendo las instrucciones de la documentación de HAProxy. Podemos utilizar contraseñas en texto plano aunque esto no es aconsejable ya que se van a almacenar asi. Lo mejor es utilizar las contraseñas encriptadas en formato DES, MD5, SHA-256, o SHA-512.

Aquí podemos ver dos ejemplos de lista de usuarios llamada Danatec con contraseñas encriptadas y en texto plano:

# Lista de usuarios con contraseñas encriptadas
 
userlist Danatec
user User1 password Contraseña_Encriptada
user User2 password Contraseña_Encriptada

# Lista de usuarios con contraseñas en texto plano

userlist Danatec
user User1 insecure-password Contraseña_Plana
user User2 insecure-password Contraseña_Plana

Generar las contraseñas encriptadas

Para generar las contraseñas encriptadas podemos utilizar el siguiente comando en nuestra distribución Linux:

printf TheSuperSecretPasswordHere | mkpasswd --stdin --method=sha-512

Nos quedará una lista de usuarios similar a esta:

userlist Danatec
group is-user
user Danatec password CONTRASEÑA_AQUI groups is-user

Una vez que tenemos nuestra lista de usuarios la pegaremos en campo Settings → Global Advanced pass thru → Custom options y guardaremos y aplicaremos los cambios.

Nota: La lista de usuarios siempre debe de estar al final de las Custom Options.

Modificar Backend

Ahora vamos a modificar el Backend que queremos proteger con usuario y contraseña.

Editaremos el backend y crearemos una nueva entrada en Access Control lists con los parámetros:

  • Name → BackendPassword (cualquier otro nombre es posible)
  • Expression → Custom acl:
  • Value → http_auth(Nombre_lista_de_usuarios), en mi caso http_auth(Danatec)

También crearemos una acción con los parámetros:

  • Action: http-request auth
  • realm: realm Nombre_lista_de_ususarios unless Nombre_Custom_ACL, en mi caso realm Danatec unless BackendPassword

Guardaremos y aplicaremos los cambios y ya estaría listo. Ahora al intentar acceder a nuestro Backend nos solicitará usuario y contraseña.

Todos los usuarios que estén en la lista de usuarios tendrán acceso a este Backend; si queremos también podemos crear diferentes grupos en la lista de usuarios de la siguiente forma:

userlist Danatec
group is-user
group is-admin
user Danatec password CONTRASEÑA_AQUI groups is-user
user AdminDanatec password CONTRASEÑA_AQUI groups is-admin

Para dar acceso al Backend solamente al grupo de administradores haríamos lo siguiente:

Modificaremos la entrada en Access Control lists con los parámetros:

  • Name → AdminAccess (cualquier otro nombre es posible)
  • Expression → Custom acl:
  • Value → http_auth_group(Nombre_de_la_lista_de_usuarios) nombre_del_grupo, en mi caso http_auth_group(Danatec) is-admin

Y modificaremos la acción con los parámetros:

  • Action: http-request auth
  • realm: realm Nombre_Lista_de_Ususarios unless Nombre_del_Custom_ACL, en mi caso realm Danatec unless AdminAccess

Con esta configuración solo los usuarios miembros del grupo is-admin podrían autentificarse.

Si habéis llegado hasta aquí, muchas gracias! Si tenéis alguna pregunta no dudéis en dejarla en los comentarios y haré todo lo posible por ayudar.

Y no olvidéis suscribiros para recibir un correo cuando se publiquen nuevos artículos.

No te pierdas los nuevos post!

We don’t spam! Read more in our privacy policy

Deja un comentario

Tu dirección de correo electrónico no será publicada.