OpenBSD en alta disponibilidad (High Availability)
OpenBSD en alta disponibilidad (High Availability)
Licenciado bajo los terminos de Creative Commons Attribution 4.0 International
Este trabajo fue desarrollado integramente en OpenBSD Release 7.5
Contacto: adrianali arroba fortix.com.ar
Índice
1. Introducción
2. Que es CARP
2.1. Sobre los programadores de CARP
2.2. Conceptos y parámetros de configuración del protocolo CARP
2.3. Entendiendo CARP con un ejemplo sencillo
2.3.1. Secuencia de comandos en el host "B" del grupo de redundancia
2.3.2. Secuencia de comandos en el host "A" del grupo de redundancia
2.4. Posibles estados de la interfaz del protocolo CARP
2.5. Que tener en cuenta en la elección del Master
2.6. Guardar la configuración de CARP de forma persistente en el sistema operativo
2.7. Monitoreo y logs
2.7.1. Analizar tráfico CARP con tcpdump
2.7.2. Cambios de estado y consulta en logs
2.7.3. Incrementar verbosidad de logs
2.7.4. Generar conmutación por error usando contador de degradación de CARP
3. Que es pfsync
3.1. Sobre los programadores de pfsync
3.2. Estados en PF
3.2.1. Ejemplo de seguimiento de un estado
3.2.2. Como impacta los cambios de reglas, flush de estados en conexiones ya establecidas
3.3. Modos de conexión entre hosts pfsync
3.3.1. Conexión con protocolo multicast
3.3.2. Conexión con protocolo unicast
3.4. Segurizar conexión pfsync
4. Ejemplo práctico del uso de CARP y pfsync
4.1. Configuración inicial de los hosts
4.2. Descripción general de los pasos de configuración
4.2.1. Configuración de variables de estados en el kernel (sysctl)
4.2.2. Configuración de las interfaces de red de la VM
4.2.3. Configuración de las interfaces del protocolo CARP
4.2.4. Configuración de túnel VPN Wireguard para segurizar pfsync
4.2.5. Configuración de la interfaz del protocolo pfsync
4.2.6. Configuración de PF en función de los estados a sincronizar
4.2.7. Probar la configuración de HA en OpenBSD
4.2.7.a. Primer test host "A" en estado Master
4.2.7.b. Segundo test degradar host "A" al estado de Backup
4.2.7.c. Tercer test volver host "A" al estado de Master
5. Sincronización de archivos entre hosts
5.1. Unison
5.1.1. Sobre los programadores de Unison
5.2. Instalar Unison
5.3. Diseñar esquema de sincronización
5.4. Configurar Unison
5.4.1. Usuario para ejecutar la sincronización
5.4.1.a. Creación de usuario
5.4.1.b. Generación del par de llaves ssh
5.4.1.c. Método de autenticación ssh por publickey entre ambos hosts
5.4.2. Configurar en Unison una tarea de sincronización
5.4.3. Script de arranque RC para Unison
6. Lista de referencias
1. Introducción
Surgió la necesidad en mi trabajo de realizar una implementación de OpenBSD en alta disponibilidad, la llamada en ingles High Availability, abreviada como HA, como vengo usando OpenBSD hace unos años se que el mejor lugar para encontrar información es la documentación oficial, así que rápidamente me dirigí ahí y en un minuto ya estaba leyendo la sección:
OpenBSD PF - Firewall Redundancy (CARP and pfsync)
del manual oficial:
Por otro lado, mientras aprendo un tema nuevo me gusta ir haciendo anotaciones en español de lo que voy leyendo y probando en la computadora, ayuda a que mi cerebro retenga mejor ese tema nuevo que si solamente lo leo en ingles y lo implemento. Entonces pensé en no ser tan egoísta y hacer un post en español en mi sitio que quizás pueda servir a otro hispanohablante.
A diferencia de la documentación oficial, en este documento vamos a realizar varios ejercicios de configuración para al final lograr una implementación completa y funcional de HA sobre OpenBSD. La idea no es repetir lo que ya dice la documentación oficial, sino sumar ejercicios que nos lleven a conclusiones que nos permitan aprender estos temas más rápidamente.
Al ser un apasionado de Unix y del software que corre en él me gusta leer las historias que hay por detrás del desarrollo de un programa, por ello también encontraras en este documento algunas referencias para que el que comparte esta pasión.
Ademas de lo anterior también voy a traducir algunos párrafos de la documentación oficial, los cuales creo necesarios para que no haya dudas sobre la interpretación de conceptos base.
Ya que voy a traducir partes del manual oficial de CARP del sitio de OpenBSD y pegarlo en mi propio sitio voy a copiar la licencia del mismo:
file: faq/pf/carp.html
Copyright (c) 2005-2007 Joel Knight <enabled@myrealbox.com>
Permission to use, copy, modify, and distribute this
documentation for any purpose with or without fee is
hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE DOCUMENTATION IS PROVIDED "AS IS" AND THE AUTHOR
DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
DOCUMENTATION INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS DOCUMENTATION
2. Que es CARP
CARP es un protocolo que hace posible tener una misma dirección IP en dos o más hosts en una misma red LAN. Existen otros protocolos que realizan funciones similares, como:
Los dos primeros son propiedad de la empresa CISCO y si bien el tercero fue desarrollado por la IETF está también bajo las patentes de la empresa CISCO. Por ese motivo el proyecto OpenBSD decide desarrollar CARP, para no estar bajo el dominio de las molestas patentes de CISCO. En el proyecto OpenBSD realmente se preocupan para que el software sea realmente libre y eso significa no solo tener una licencia libre, si no también estar libre de las ataduras de las patentes. Hay que tener en cuenta que de cada protocolo puede haber varias implementaciones, por ejemplo de VRRP en el mundo Linux podemos nombrar la más popular que es keepalived. Pero también tenemos uvrrpd y vrrpd.
Con CARP también tenemos varias implementaciones, la original o inicial, que es la que se desarrolló en el proyecto OpenBSD, que es la que aprenderemos a configurar en este manual. También tenemos las implementaciones de CARP en los proyectos NetBSD y FreeBSD, estas implementaciones heredaron código de la original y en el caso de la implementación de FreeBSD la misma fue significativamente reescrita en este último. Encontramos ademas una implementación en el espacio de usuario de CARP en Linux llamada UCARP, esta implementación está mínimamente relacionada a nivel de código con la original, se hizo llamar portable CARP y aparentemente podía correr en varios sistemas operativos:
"The software has been successfully tested on Linux 2.4, Linux 2.6, MacOS X, OpenBSD, MirBSD and NetBSD."[6]
Fue usada y venía en los repositorios de paquetes de varias distribuciones Linux, ejemplo de página de man de UCARP en Ubuntu:
https://manpages.ubuntu.com/manpages/bionic/man8/ucarp.8.html
Esta implementación fue cerrada el 24 de abril de 2019, el propietario del repositorio y desarrollador principal decidió archivar el repositorio, dejándolo solo lectura, lo que en otras palabras significa que cancelo el desarrollo de UCARP.
Para aclarar aún más la función de CARP en OpenBSD vamos a empezar leyendo el primer punto del manual oficial:
"Introducción a CARP
CARP es un protocolo y sus siglas significan Common Address Redundancy Protocol. Su objetivo principal es permitir que varios hosts en el mismo segmento de red compartan una dirección IP. CARP es una alternativa segura y libre a VRRP Virtual Router Redundancy Protocol (VRRP) y al protocolo Hot Standby Router Protocol (HSRP). CARP funciona permitiendo que un grupo de hosts en el mismo segmento de red compartan una dirección IP. Este grupo de hosts se denomina "grupo de redundancia". Al grupo de redundancia se le asigna una dirección IP que se comparte entre los miembros del grupo. Dentro del grupo, un host se designa como "Master" y el resto como "Backups". El Master host es el que actualmente "posee" la IP compartida; responde a cualquier tráfico o solicitud ARP dirigida hacia esa dirección IP. Cada host puede pertenecer a más de un grupo de redundancia a la vez.
Un uso común de CARP es crear un grupo de firewalls redundantes. La IP virtual asignada al grupo de redundancia se configura en las computadoras clientes como puerta de enlace predeterminada. En caso de que el firewall maestro sufra una falla o se desconecte, la IP se moverá a uno de los firewalls de respaldo y el servicio continuará sin verse afectado.
CARP es compatible con IPv4 e IPv6."[1]
También me resulta interesante citar este par de párrafos relacionados a la historia de este protocolo sacado de la Wikipedia:
"A finales de la década de 1990, el Grupo de Trabajo de Ingeniería de Internet (IETF) comenzó a trabajar en un protocolo para la redundancia de enrutadores. En 1997, Cisco informó al IETF que tenía patentes en esta área y, en 1998, señaló su patente sobre HSRP. No obstante, el IETF continuó trabajando en el VRRP. Después de algún debate, el grupo de trabajo VRRP del IETF decidió aprobar el estándar, a pesar de su dependencia de técnicas patentadas, siempre y cuando Cisco pusiera la patente a disposición de terceros bajo términos de licencia razonables y no discriminatorios. Debido a que VRRP solucionó problemas con el protocolo HSRP, Cisco comenzó a utilizar VRRP en su lugar, sin dejar de reclamarlo como propio."[5]
"Cisco informó a los desarrolladores de OpenBSD que haría cumplir su patente sobre HSRP. La posición de Cisco pudo deberse a su demanda con Alcatel. Como los términos de la licencia de Cisco impedían la implementación de VRRP de código abierto, los desarrolladores de OpenBSD comenzaron a desarrollar CARP. OpenBSD se centra en la seguridad. Diseñaron CARP para utilizar criptografía. Esto hizo que CARP fuera fundamentalmente diferente de VRRP y aseguró que CARP no infringiera la patente de Cisco. CARP estuvo disponible en octubre de 2003. Posteriormente, se integró en FreeBSD (lanzado por primera vez en mayo de 2005 con FreeBSD 5.4), NetBSD y Linux (UCARP). Si bien la patente estadounidense de Cisco expiró en 2014, los dos protocolos incompatibles continúan coexistiendo."[5]
Si se tiene más curiosidad sobre CARP y sus inicios recomiendo leer la historia completa con el relato de lo que pasó entre IETF, Cisco y OpenBSD y porque esto dio nacimiento a CARP:
http://www.openbsd.org/lyrics.html#35
2.1. Sobre los programadores de CARP
La idea de nombrar a los programadores es hacer un reconocimiento a su excelente trabajo, seguramente también colaboraron muchas personas no solo codificando, sino haciendo pruebas, documentando, etc. a los cuales va todo nuestro agradecimiento ya que hacen nuestra vida más fácil administrando OpenBSD.
Como podemos leer en el encabezado del archivo de código de CARP:
netinet/ip_carp.c
figuran estos tres programadores:
* Copyright (c) 2002 Michael Shalayeff. All rights reserved.
* Copyright (c) 2003 Ryan McBride. All rights reserved.
* Copyright (c) 2006-2008 Marco Pfatschbacher. All rights reserved.
y en el encabezado del archivo de código principal de pfsync:
net/if_pfsync.c
figuran:
Copyright (c) 2002 Michael Shalayeff
Copyright (c) 2008 David Gwynne <dlg@openbsd.org>
Al escribir este manual nos enteramos que Michael "Mickey" Shalayeff falleció en el 2017. En su momento se envió este mensaje de correo avisando en la lista del New York City *BSD User Group (NYC*BUG):
copio aquí el mensaje original:
[talk] mickey@ OpenBSD RIP
George Rosamond george at ceetonetechnology.com
Sat Aug 12 23:59:00 EDT 2017
Mickey Shalayeff passed recently.
He was a longtime OpenBSD developer and had his
fingerprints throughout the operating system. He lived in
Brooklyn for many years and was involved in
infrastructures around NYC. He was at Calyx (.com) when
Nick Merrill got the NSL.
He did a great NYC*BUG meeting for us many years ago.
I'll give more details later, and there's so many crazy
stories about Mickey.
He'll be sorely missed. He was talented and eternally
devoted to good code. Personally, I'll miss the long
rambling emails we had so many times hitting tech,
politics and food.
He was the only person I ever met who put salt and pepper
in coffee. He communicated in mickeyese. Unintelligible
to many, but hysterical to read and listen to.
mickey.lucifer.net is filled with some of his gems,
including his take on the alleged OpenBSD IPSec backdoor.
http://images.kd85.com/mickey/IMAG0484.jpg
RIP mickey@
su repositorio github con algunos de sus trabajos:
Se puede contar su nombre en 257 archivos de código en el kernel de OpenBSD, una gran perdida para este proyecto y para el software FLOSS en general.
2.2. Conceptos y parámetros de configuración del protocolo CARP
Vamos a leer algo de teoría antes de realizar configuraciones, para ello copio la traducción del punto "Configurando CARP" del manual oficial:
"Configurando CARP
Cada grupo de redundancia está representado por una interfaz de red virtual carp(4). Como tal, CARP se configura usando ifconfig(8).
# ifconfig carpN create
# ifconfig carpN vhid vhid [pass password] [carpdev carpdev] \
[advbase advbase] [advskew advskew] [state state] [group|-group group] \
ipaddress netmask mask
-
carpN El nombre de la interfaz virtual carp(4) donde N es un número entero que representa el número de la interfaz (por ejemplo, carp10).
-
vhid La identificación del host virtual (Virtual Host ID). Este es un número único que se utiliza para identificar el grupo de redundancia ante otros hosts del grupo y para ante otros grupos en la misma red. Los valores aceptables son del 1 al 255. Debe ser el mismo en todos los miembros del grupo.
-
password La contraseña de autenticación que se utilizará al hablar con otros hosts habilitados para CARP en este grupo de redundancia. Este debe ser el mismo para todos los miembros del grupo.
-
carpdev Este parámetro opcional especifica la interfaz de red física que pertenece a este grupo de redundancia. De forma predeterminada, CARP intentará determinar qué interfaz usar buscando una interfaz física que esté en la misma subred que la combinación de dirección IP y máscara proporcionada a la interfaz carp(4).
-
advbase Este parámetro opcional especifica con qué frecuencia, en segundos, anunciar que somos miembros del grupo de redundancia. El valor predeterminado es 1 segundo. Los valores aceptables son del 1 al 255.
-
advskew Este parámetro opcional especifica cuánto sesgar la base de datos al enviar anuncios CARP. Manipulando advskew, se puede elegir el anfitrión CARP maestro. Cuanto mayor sea el número, menos preferido será el anfitrión a la hora de elegir un maestro. El valor predeterminado es 0. Los valores aceptables son de 0 a 254.
-
state Fuerza una interfaz carp(4) a un estado determinado. Los estados válidos son Init, Backup y Master.
-
group, -group Agregue o elimine una interfaz carp(4) a un determinado grupo de interfaces. De forma predeterminada, todas las interfaces de CARP se agregan al grupo carp. Cada grupo tiene un contador que afecta a todas las interfaces de CARP que pertenecen a ese grupo. Si una interfaz física habilitada para CARP deja de funcionar, CARP aumentará el contador de degradación en 1 en los grupos de interfaces de los que es miembro la interfaz carp(4), lo que de hecho provocará que todos los miembros del grupo conmuten por error juntos.
-
ipaddress Esta es la dirección IP compartida asignada al grupo de redundancia. Esta dirección no tiene que estar en la misma subred que la dirección IP en la interfaz física (si está presente). Sin embargo, esta dirección debe ser la misma en todos los hosts del grupo.
-
mask La máscara de subred de la IP compartida.
Se puede controlar más comportamiento de CARP mediante sysctl(8).
-
net.inet.carp.allow Aceptar paquetes CARP entrantes o no. El valor predeterminado es 1 (sí).
-
net.inet.carp.preempt Permitir que un host dentro de un grupo de redundancia, que tengan mejores valores en advbase y advskew sea seleccionado como Maestro. net.inet.carp.preempt viene por defecto en 0 (deshabilitado) de forma predeterminada.
-
net.inet.carp.log Cambio de estado, paquetes defectuosos y otros errores. Este valor puede estar entre 0 y 7, correspondiente a las prioridades de syslog(3). El valor predeterminado es 2 (solo mostrar cambios de estado)."[1]
2.3. Entendiendo CARP con un ejemplo sencillo
Antes de pasar al ejemplo leamos el siguiente párrafo del manual oficial:
"Como funciona CARP (traducido del ingles tal cual el manual oficial)
El Master host del grupo envía anuncios sistemáticamente a la red local para que los Backup hosts sepan que todavía está activo. Si los Backup hosts no escuchan un anuncio del Master host durante un período de tiempo determinado, uno de ellos asumirá las funciones del Master (cualquier Backup host que tenga los valores de advbase y advskew configurados más bajos). Es posible que existan varios grupos CARP en el mismo segmento de red. Los anuncios CARP contienen el ID del virtual host que permite a los miembros del grupo identificar a qué grupo de redundancia pertenece el anuncio. Para evitar que un usuario malintencionado en el segmento de la red falsifique los anuncios CARP, en cada grupo se puede configurar un password. Cada paquete CARP enviado al grupo está protegido por un HMAC SHA1."[1]
Ahora que ya leimos como funciona CARP veamos un ejemplo sencillo, tenemos dos host con las siguientes IPs para management:
- obsda:
10.0.0.100/24
- obsdb:
10.0.0.200/24
y una IP de LAN que van a compartir con CARP: 192.168.46.254/24
el ejemplo será implementando usando maquinas virtuales, notar que por eso las interfaces de red son del tipo "vio".
Diagrama:
10.0.0.200/24 .---.192.168.46.254/24.---.
vio0| B |<--------------->| A |vio0
'---' carp0 '---' 10.0.0.100/24
2.3.1. Secuencia de comandos en el host "B" del grupo de redundancia
Primero consultamos con el comando ifconfig(8) el estado de las interfaces de red:
obsdb ~ root # ifconfig -A
lo0: flags=2008049<UP,LOOPBACK,RUNNING,MULTICAST,LRO> mtu 32768
index 3 priority 0 llprio 3
groups: lo
inet6 ::1 prefixlen 128
inet6 fe80::1%lo0 prefixlen 64 scopeid 0x3
inet 127.0.0.1 netmask 0xff000000
vio0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 12:e4:07:63:53:79
index 1 priority 0 llprio 3
groups: egress
media: Ethernet autoselect
status: active
inet 10.0.0.200 netmask 0xffffff00 broadcast 10.0.0.255
enc0: flags=0<>
index 2 priority 0 llprio 3
groups: enc
status: active
pflog0: flags=141<UP,RUNNING,PROMISC> mtu 33136
index 5 priority 0 llprio 3
groups: pflog
obsdb ~ root #
Por defecto, por lo menos en OpenBSD 7.5 que es la versión que usé para escribir este manual, CARP se encuentra activo por default en el kernel, lo consultamos con el comando:
obsdb ~ root # sysctl net.inet.carp.allow
net.inet.carp.allow=1
obsdb ~ root #
por cualquier actualización a futuro, que traiga cambios en este comportamiento por defecto, podemos dejar persistente que siempre este activo CARP de la siguiente forma:
obsdb ~ root # echo 'net.inet.carp.allow=1' >> /etc/sysctl.conf
obsdb ~ root #
otro parámetro que nos interesa configurar es:
obsdb ~ root # sysctl net.inet.carp.preempt
net.inet.carp.preempt=0
obsdb ~ root #
que viene deshabilitado por defecto, con este parámetro hacemos que el Master sea el host con mejores valores en advbase y advskew. Lo configuamos en caliente:
obsdb ~ root # sysctl net.inet.carp.preempt=1
net.inet.carp.preempt: 0 -> 1
obsdb ~ root #
y dejamos persistente la configuración:
obsdb ~ root # echo 'net.inet.carp.preempt=1' >> /etc/sysctl.conf
obsdb ~ root #
para que no haya duda hemos agregado al archivo:
/etc/sysctl.conf
estos dos valores:
net.inet.carp.allow=1
net.inet.carp.preempt=1
ahora vamos a proceder a crear la interfaz para CARP:
obsdb ~ root # ifconfig carp0 create
obsdb ~ root #
y por último vamos a proceder a configurarlo:
obsdb ~ root # ifconfig carp0 vhid 86 pass 4PAmAmVfRaRG carpdev vio0 advskew 20 192.168.46.254 netmask 255.255.255.0
obsdb ~ root #
ahora consultamos con el comando ifconfig(8) como quedó configurado:
obsdb ~ root # ifconfig carp0
carp0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:00:5e:00:01:56
index 6 priority 15 llprio 3
carp: MASTER carpdev vio0 vhid 86 advbase 1 advskew 20
groups: carp
status: master
inet 192.168.46.254 netmask 0xffffff00 broadcast 192.168.46.255
obsdb ~ root #
Como vemos en el campo status: al ser el host "B" el primero en levantar en el grupo de redundancia, éste queda con el estado de Master, aunque las transición es primero Init, luego backup y Master si corresponde. Ver más adelante una explicación de los estados de las interfaces del protocolo CARP.
Para una explicación detallada de las opciones del comando ifconfig(8) para la creación de la interfaz del protocolo CARP, leer la sección CARP en la página del manual:
https://man.openbsd.org/ifconfig#CARP
Para borrar la interfaz CARP el comando sería:
obsdb ~ root # ifconfig carp0 destroy
obsdb ~ root #
2.3.2. Secuencia de comandos en el host "A" del grupo de redundancia
Primero consultamos con el comando ifconfig(8) el estado de las interfaces de red:
obsda ~ root # ifconfig -A
lo0: flags=2008049<UP,LOOPBACK,RUNNING,MULTICAST,LRO> mtu 32768
index 3 priority 0 llprio 3
groups: lo
inet6 ::1 prefixlen 128
inet6 fe80::1%lo0 prefixlen 64 scopeid 0x3
inet 127.0.0.1 netmask 0xff000000
vio0: flags=8b43<UP,BROADCAST,RUNNING,PROMISC,ALLMULTI,SIMPLEX,MULTICAST> mtu 1500
lladdr fe:e1:bb:d1:a9:b9
index 1 priority 0 llprio 3
groups: egress
media: Ethernet autoselect
status: active
inet 10.0.0.100 netmask 0xffffff00 broadcast 10.0.0.255
enc0: flags=0<>
index 2 priority 0 llprio 3
groups: enc
status: active
pflog0: flags=141<UP,RUNNING,PROMISC> mtu 33136
index 5 priority 0 llprio 3
groups: pflog
obsda ~ root #
Como hicimos en el host "B" verificamos que CARP este habilitado en el kernel y dejamos persistente esa configuración:
obsda ~ root # sysctl net.inet.carp.allow
net.inet.carp.allow=1
obsda ~ root #
obsda ~ root # echo 'net.inet.carp.allow=1' >> /etc/sysctl.conf
obsda ~ root #
ademas dejamos persistente esta otra configuración:
obsda ~ root #
obsda ~ root # sysctl net.inet.carp.preempt=1
net.inet.carp.preempt: 0 -> 1
obsda ~ root #
obsda ~ root # echo 'net.inet.carp.preempt=1' >> /etc/sysctl.conf
obsda ~ root #
ahora creamos la interfaz del protocolo CARP:
obsda ~ root #
obsda ~ root # ifconfig carp0 create
obsda ~ root #
vemos que si bien esta creada, no esta configurada, vemos que su estado es invalid:
obsda ~ root # ifconfig carp0
carp0: flags=8802<BROADCAST,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:00:00:00:00:00
index 6 priority 15 llprio 3
groups: carp
status: invalid
obsda ~ root #
ahora la configuramos:
obsda ~ root #
obsda ~ root # ifconfig carp0 vhid 86 pass 4PAmAmVfRaRG carpdev vio0 advskew 10 192.168.46.254 netmask 255.255.255.0
obsda ~ root #
y consultamos como quedo la configuración:
obsda ~ root # ifconfig carp0
carp0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:00:5e:00:01:56
index 6 priority 15 llprio 3
carp: MASTER carpdev vio0 vhid 86 advbase 1 advskew 10
groups: carp
status: master
inet 192.168.46.254 netmask 0xffffff00 broadcast 192.168.46.255
obsda ~ root #
como vemos levantó y con el estado de Master, eso porque tiene el parámetro advskew en 10 o sea menor que el host "B" que lo tiene configurado en 20. Al levantar el host "A" como Master el host "B" pasó al estado Backup:
obsdb ~ root # ifconfig carp0
carp0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:00:5e:00:01:56
index 6 priority 15 llprio 3
carp: BACKUP carpdev vio0 vhid 86 advbase 1 advskew 20
groups: carp
status: backup
inet 192.168.46.254 netmask 0xffffff00 broadcast 192.168.46.255
obsdb ~ root #
estos cambios se registran en los logs del sistema, en el archivo:
/var/log/messages
ejemplo:
obsdb ~ root # tail -f /var/log/messages | grep carp
Mar 26 01:29:47 obsdb /bsd: carp0: state transition: BACKUP -> MASTER
Mar 26 01:45:12 obsdb /bsd: carp0: state transition: MASTER -> BACKUP
Mar 26 01:57:51 obsdb /bsd: carp0: state transition: BACKUP -> MASTER
Mar 26 01:58:55 obsdb /bsd: carp0: state transition: BACKUP -> MASTER
Mar 26 02:01:16 obsdb /bsd: carp0: state transition: MASTER -> BACKUP
2.4. Posibles estados de la interfaz del protocolo CARP
- Invalid: no configurado aún.
- Backup: Backup del grupo, porque se descubrió un Master activo.
- Master: Master del grupo.
- Init: iniciando, pasa a modo Backup inicialmente y en ese modo busca un Master, si lo encuentra queda en Backup sino pasa a modo Master.
2.5. Que tener en cuenta en la elección del Master
La elección del host Master en el grupo de redundancia depende de los siguientes dos parámetros que ya los vimos en la documentación oficial, copio la traducción de la misma:
"
-
advbase Este parámetro opcional especifica con qué frecuencia, en segundos, anunciar que somos miembros del grupo de redundancia. El valor predeterminado es 1 segundo. Los valores aceptables son del 1 al 255.
-
advskew Este parámetro opcional especifica cuánto sesgar la base de datos al enviar anuncios CARP. Manipulando advskew, se puede elegir el anfitrión CARP maestro. Cuanto mayor sea el número, menos preferido será el anfitrión a la hora de elegir un maestro. El valor predeterminado es 0. Los valores aceptables son de 0 a 254.
"[1]
Solo agregar que en la elección del Master el parámetro advbase tiene preferencia sobre advskew, ejemplo:
Host A:
obsda ~ root # ifconfig carp0
carp0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:00:5e:00:01:56
index 7 priority 15 llprio 3
carp: BACKUP carpdev vio0 vhid 86 advbase 2 advskew 10
groups: carp
status: backup
inet 192.168.46.254 netmask 0xffffff00 broadcast 192.168.46.255
obsda ~ root #
Host B:
obsdb ~ root # ifconfig carp0
carp0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:00:5e:00:01:56
index 4 priority 15 llprio 3
carp: MASTER carpdev vio0 vhid 86 advbase 1 advskew 20
groups: carp
status: master
inet 192.168.46.254 netmask 0xffffff00 broadcast 192.168.46.255
obsdb ~ root #
fijarse que en el host "A" si bien se tiene el parámetro advskew con el valor 10 o sea con más preferencia que en el host "B" que tiene 20, este último tiene el parámetro advbase en 1 segundo, o sea con más preferencia que en el host "A" que lo tiene en 2 segundos. Por este motivo el host "B" gana la elección de Master.
2.6. Guardar la configuración de CARP de forma persistente en el sistema operativo
Para conseguir que la configuración del punto anterior sea tomada al inicio del sistema operativo o sea en tiempo de boot time, debemos guardar la misma en un archivo de configuración hostname.if(5), los cuales nos posibilitan configurar distintos tipos de interfaces de red de forma persistente. Entonces en el host "A":
- obsda
vamos a crear el siguiente archivo:
/etc/hostname.carp0
con el siguiente contenido:
inet 192.168.46.254 255.255.255.0 192.168.46.255 vhid 86 advskew 10 carpdev vio0 pass 4PAmAmVfRaRG
Ahora en el host "B":
- obsdb
creamos el mismo archivo, recordar que en este host, para forzar que el mismo tenga el estado de Backup, ponemos un valor de advskew en 20 y no en 10 como en el host "A". Archivo de configuración:
inet 192.168.46.254 255.255.255.0 192.168.46.255 vhid 86 advskew 20 carpdev vio0 pass 4PAmAmVfRaRG
recordar que en el punto tres de este manual también dejamos fija la configuración de CARP en el kernel:
obsda ~ aali $ cat /etc/sysctl.conf
net.inet.carp.allow=1
net.inet.carp.preempt=1
obsda ~ aali $
esta configuración es la misma en ambos hosts.
NOTA: Los archivos hostname.if(5) no solo se procesan en el tiempo de boot time, también podemos cargar la configuración que almacenan con el comando netstart(8) de la siguiente forma:
sh /etc/netstart carp0
2.7. Monitoreo y logs
En este punto veremos como obtener información del funcionamiento de CARP, aprenderemos como incrementar la información enviada a syslogd(8), ademas de usar herramientas de análisis de tráfico como el comando tcpdump.
2.7.1. Analizar tráfico CARP con tcpdump
NOTA importante: El comando tcpdump (man de tcpdump en OpenBSD) en OpenBSD pertenece a la base del sistema y es diferente al tcpdump (man de tcpdump de los Linux) que viene en los Linux, la diferencia radica en que si bien tienen un ancestro común son ramas diferentes de desarrollo. El tcpdump originalmente fue desarrollado por:
"Van Jacobson van@ee.lbl.gov, Craig Leres leres@ee.lbl.gov, and Steven McCanne mccanne@ee.lbl.gov, all of the Lawrence Berkeley Laboratory, University of California, Berkeley, CA."[8]
Cuando la linea de comando o salida del mismo sea diferente entre OpenBSD y Linux lo haré notar.
Con tcpdump podemos descubrir en el tráfico de red que host está con el estado de Master y enviando anuncios CARP. Estos anuncios son enviados vía multicast, o sea a una dirección multicast específica, podemos averiguar cual es esa dirección con el siguiente comando en OpenBSD:
puffypc ~ root # tcpdump -i em0 -v net 224.0.0.0/4
tcpdump: listening on em0, link-type EN10MB
18:36:31.600845 carp 10.0.0.100 > vrrp.mcast.net: CARPv2-advertise 36: vhid=86 advbase=1 advskew=10 demote=0 (DF) [tos 0x10] (ttl 255, id 22128, len 56)
18:36:32.641089 carp 10.0.0.100 > vrrp.mcast.net: CARPv2-advertise 36: vhid=86 advbase=1 advskew=10 demote=0 (DF) [tos 0x10] (ttl 255, id 16996, len 56)
18:36:33.681033 carp 10.0.0.100 > vrrp.mcast.net: CARPv2-advertise 36: vhid=86 advbase=1 advskew=10 demote=0 (DF) [tos 0x10] (ttl 255, id 32614, len 56)
...
En Linux de forma similar:
tuxpc ~ root # tcpdump -i eth0 net 224.0.0.0/4
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
18:37:12.748078 IP 10.0.0.100 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 86, prio 10, authtype none, intvl 1s, length 36
18:37:13.778081 IP 10.0.0.100 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 86, prio 10, authtype none, intvl 1s, length 36
18:37:14.818120 IP 10.0.0.100 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 86, prio 10, authtype none, intvl 1s, length 36
...
como vemos la dirección multicast destino donde se envían los anuncios es:
vrrp.mcast.net
la cual tiene la IP:
puffypc ~ root # host vrrp.mcast.net
vrrp.mcast.net has address 224.0.0.18
puffypc ~ root #
entonces para analizar el tráfico CARP en nuestra red podemos ejecutar un comando tcpdump más específico, cosa que en la salida no nos aparezca otro tráfico multicast, ejemplo en OpenBSD:
puffypc ~ root # tcpdump -i em0 -v host 224.0.0.18
tcpdump: listening on em0, link-type EN10MB
18:39:05.450948 carp 10.0.0.100 > vrrp.mcast.net: CARPv2-advertise 36: vhid=86 advbase=1 advskew=10 demote=0 (DF) [tos 0x10] (ttl 255, id 11373, len 56)
18:39:06.491096 carp 10.0.0.100 > vrrp.mcast.net: CARPv2-advertise 36: vhid=86 advbase=1 advskew=10 demote=0 (DF) [tos 0x10] (ttl 255, id 51322, len 56)
18:39:07.531042 carp 10.0.0.100 > vrrp.mcast.net: CARPv2-advertise 36: vhid=86 advbase=1 advskew=10 demote=0 (DF) [tos 0x10] (ttl 255, id 40209, len 56)
...
En Linux de forma similar:
tuxpc ~ root # tcpdump -i eth0 host 224.0.0.18
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
18:39:17.469809 IP 10.0.0.100 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 86, prio 10, authtype none, intvl 1s, length 36
18:39:18.509947 IP 10.0.0.100 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 86, prio 10, authtype none, intvl 1s, length 36
18:39:19.549671 IP 10.0.0.100 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 86, prio 10, authtype none, intvl 1s, length 36
...
Tener en cuenta que la dirección multicast es vrrp.mcast.net, VRRP y CARP usan la misma, el nombre (vrrp) fue reservado haciendo referencia solo al protocolo VRRP, pero es valida para CARP.
Como se dijo anteriormente, lo presentado por el comando tcpdump va a depender de la implementación de tcpdump que estemos usando, por ejemplo en OpenBSD la linea se ve así:
18:39:05.450948 carp 10.0.0.100 > vrrp.mcast.net: CARPv2-advertise 36: vhid=86 advbase=1 advskew=10 demote=0 (DF) [tos 0x10] (ttl 255, id 11373, len 56)
en Linux, el mismo tráfico se ve así:
18:39:17.469809 IP 10.0.0.100 > vrrp.mcast.net: VRRPv2, Advertisement, vrid 86, prio 10, authtype none, intvl 1s, length 36
es fácil cotejar los nombres de los parámetros que representan lo mismo en ambas implementaciones:
OpenBSD Linux
------- -----
vhid vrid
advbase intvl
advskew prio
Los parámetros y sus valores presentados por tcpdump, son fáciles de reconocer en la linea de configuración de ifconfig(8) cuando creamos la interfaz del protocolo CARP, por ejemplo, esta linea de tcpdump(OpenBSD):
18:39:05.450948 carp 10.0.0.100 > vrrp.mcast.net: CARPv2-advertise 36: vhid=86 advbase=1 advskew=10 demote=0 (DF) [tos 0x10] (ttl 255, id 11373, len 56)
El primer campo "18:39:05.450948" es el timestamp. Después nos informa el protocolo "carp". Luego tenemos el origen y el destino con "10.0.0.100 > vrrp.mcast.net". El tipo de mensaje "CARPv2-advertise 36". El ID del grupo de redundancia con "vhid=86". Con "advbase" nos informa cada cuantos segundos se envía el anuncio. Y con "advskew=10" que es como la prioridad de VRRP.
Ejercicio: como podemos ver en las salidas anteriores en el timestamp de cada linea, se envía un anuncio por segundo, recordar que este valor de un segundo es un valor por defecto que lo podemos cambiar con el argumento "advbase". Por ejemplo, vamos a realizar este cambio en ambos host, primeramente en el host "B", que esta con el estado de "Backup", haremos un "destroy" de la interfaz carp0 y la volvemos a crear con el parámetro "advbase" configurado en 2 segundos, comandos:
obsdb ~ root # ifconfig carp0
carp0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:00:5e:00:01:56
index 6 priority 15 llprio 3
carp: BACKUP carpdev vio0 vhid 86 advbase 1 advskew 20
groups: carp
status: backup
inet 192.168.46.254 netmask 0xffffff00 broadcast 192.168.46.255
obsdb ~ root #
obsdb ~ root # ifconfig carp0 destroy
obsdb ~ root #
obsdb ~ root # ifconfig carp0 vhid 86 pass 4PAmAmVfRaRG carpdev vio0 advskew 20 advbase 2 192.168.46.254 netmask 255.255.255.0
obsdb ~ root #
obsdb ~ root # ifconfig carp0
carp0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:00:5e:00:01:56
index 7 priority 15 llprio 3
carp: BACKUP carpdev vio0 vhid 86 advbase 2 advskew 20
groups: carp
status: backup
inet 192.168.46.254 netmask 0xffffff00 broadcast 192.168.46.255
obsdb ~ root #
como vemos en el listado de comandos, con ifconfig(8) usamos ahora el argumento advbase con el valor de 2. En comandos anteriores no lo usamos puesto que queríamos que la frecuencia de los anuncios esté con el valor por defecto que es 1 segundo. Ahora procedemos a cambiar en el Master:
obsda ~ root # ifconfig carp0
carp0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:00:5e:00:01:56
index 6 priority 15 llprio 3
carp: MASTER carpdev vio0 vhid 86 advbase 1 advskew 10
groups: carp
status: master
inet 192.168.46.254 netmask 0xffffff00 broadcast 192.168.46.255
obsda ~ root #
obsda ~ root # ifconfig carp0 destroy
obsda ~ root #
obsda ~ root # ifconfig carp0 vhid 86 pass 4PAmAmVfRaRG carpdev vio0 advskew 10 advbase 2 192.168.46.254 netmask 255.255.255.0
obsda ~ root #
obsda ~ root # ifconfig carp0
carp0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:00:5e:00:01:56
index 7 priority 15 llprio 3
carp: MASTER carpdev vio0 vhid 86 advbase 2 advskew 10
groups: carp
status: master
inet 192.168.46.254 netmask 0xffffff00 broadcast 192.168.46.255
obsda ~ root #
como podemos apreciar en el listado anterior el argumento advbase de la interfaz de red carp0 pasó a valer 2 segundos. Ahora vemos con tcpdump(OpenBSD) la salida:
puffypc ~ root # tcpdump -i em0 -v host 224.0.0.18
tcpdump: listening on em0, link-type EN10MB
18:49:01.900924 carp 10.0.0.100 > vrrp.mcast.net: CARPv2-advertise 36: vhid=86 advbase=2 advskew=10 demote=0 (DF) [tos 0x10] (ttl 255, id 24989, len 56)
18:49:03.940959 carp 10.0.0.100 > vrrp.mcast.net: CARPv2-advertise 36: vhid=86 advbase=2 advskew=10 demote=0 (DF) [tos 0x10] (ttl 255, id 34382, len 56)
18:49:05.980893 carp 10.0.0.100 > vrrp.mcast.net: CARPv2-advertise 36: vhid=86 advbase=2 advskew=10 demote=0 (DF) [tos 0x10] (ttl 255, id 16994, len 56)
...
fijarse en el timestamp que ahora entre cada linea hay una diferencia de 2 segundos.
2.7.2. Cambios de estado y consulta en logs
En una instalación estándar de OpenBSD los logs de CARP están en el archivo:
/var/log/messages
En ambos servidores, el host "A" y el B vamos a dejar corriendo el comando tail de forma interactiva así vamos viendo como cambian de estado las interfaces del protocolo CARP, el comando sería:
tail -f /var/log/messages | grep carp
Ahora que ya estamos viendo de forma interactiva el archivo de log, podemos enterarnos al instante como cambia el estado de las interfaces de CARP, para realizar estos cambios podemos usar el parámetro state, forzando a una interface CARP a cambiar de estado. Ejemplo, tenemos el host "A" del grupo de redundancia 86 (vhid) en estado de Master:
obsda ~ root # ifconfig carp0
carp0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:00:5e:00:01:56
index 7 priority 15 llprio 3
carp: MASTER carpdev vio0 vhid 86 advbase 2 advskew 10
groups: carp
status: master
inet 192.168.46.254 netmask 0xffffff00 broadcast 192.168.46.255
obsda ~ root #
y el host "B", del mismo grupo en estado de Backup:
obsdb ~ root # ifconfig carp0
carp0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:00:5e:00:01:56
index 7 priority 15 llprio 3
carp: BACKUP carpdev vio0 vhid 86 advbase 2 advskew 20
groups: carp
status: backup
inet 192.168.46.254 netmask 0xffffff00 broadcast 192.168.46.255
obsdb ~ root #
ahora procedemos a cambiar el estado de la interface CARP del host "A" a estado de Backup:
obsda ~ root # ifconfig carp0 state backup
resultado:
obsda ~ root # ifconfig carp0
carp0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:00:5e:00:01:56
index 7 priority 15 llprio 3
carp: BACKUP carpdev vio0 vhid 86 advbase 2 advskew 10
groups: carp
status: backup
inet 192.168.46.254 netmask 0xffffff00 broadcast 192.168.46.255
obsda ~ root #
NOTA: Tener en cuenta que el estado de Backup no dura más que unos pocos segundos en el host "A", puesto que éste tiene más prioridad en el parámetro advskew que el host "B".
log en host "A":
May 4 18:52:06 obsda /bsd: carp0: state transition: MASTER -> BACKUP
log en host "B":
May 4 18:52:10 obsdb /bsd: carp0: state transition: BACKUP -> MASTER
Como dijimos este estado no dura más de unos segundos, lo comprobamos examinando los logs en el host "B":
May 4 18:52:10 obsdb /bsd: carp0: state transition: MASTER -> BACKUP
notamos que el cambio al estado original fue casi inmediato. En el host "A", la salida de log cuando volvió al estado de Master fue:
May 4 18:52:10 obsda /bsd: carp0: state transition: BACKUP -> MASTER
2.7.3. Incrementar verbosidad de logs
Como vimos en el punto 2 de este manual con sysctl(8) podemos cambiar el nivel de verbosidad de CARP en el kernel, usando la variable:
net.inet.carp.log
descripción sacada del manual oficial:
"net.inet.carp.log
Cambio de estado, paquetes defectuosos y otros errores. Este valor puede estar entre 0 y 7, correspondiente a las prioridades de syslog(3). El valor predeterminado es 2 (solo mostrar cambios de estado)."[1]
entonces la variable puede ser configurada con los valores de 0 a 7, valores que tienen la siguiente correspondencia como vemos en el man de syslog(3):
The level (ORed with the facility) is selected from the
following list, ordered by decreasing importance:
LOG_EMERG : A panic condition. This is normally broadcast
to all users.
LOG_ALERT : A condition that should be corrected
immediately, such as a corrupted system database.
LOG_CRIT : Critical conditions, e.g., hard device
errors.
LOG_ERR : Errors.
LOG_WARNING : Warning messages.
LOG_NOTICE : Conditions that are not error conditions,
but should possibly be handled specially.
LOG_INFO : Informational messages.
LOG_DEBUG : Messages that contain information normally
of use only when debugging a program.
si consultamos:
/usr/include/syslog.h
vemos más claros los valores:
#define LOG_EMERG 0 /* system is unusable */
#define LOG_ALERT 1 /* action must be taken immediately */
#define LOG_CRIT 2 /* critical conditions */
#define LOG_ERR 3 /* error conditions */
#define LOG_WARNING 4 /* warning conditions */
#define LOG_NOTICE 5 /* normal but significant condition */
#define LOG_INFO 6 /* informational */
#define LOG_DEBUG 7 /* debug-level messages */
entonces como expresa el manual, el valor por defecto de esta variable es 2 y como nosotros aún no lo hemos cambiado, ese es el valor actual en ambos hosts:
Host A:
obsda ~ root # sysctl net.inet.carp.log
net.inet.carp.log=2
obsda ~ root #
Host B:
obsdb ~ root # sysctl net.inet.carp.log
net.inet.carp.log=2
obsdb ~ root #
con este valor por defecto, si procedemos a bajar la interface carp0 del host "A":
obsda ~ root # ifconfig carp0 down
en el archivo de log:
/var/log/messages
de este host no veríamos ninguna salida, solo se informará en el host "B", el cual cambia al estado de Master:
May 4 19:03:05 obsdb /bsd: carp0: state transition: BACKUP -> MASTER
ahora volvemos a levantar la interface carp0 en el host "A":
obsda ~ root # ifconfig carp0 up
lo que produce en ambos hosts los siguientes mensajes:
Host A:
May 4 19:05:13 obsda /bsd: carp0: state transition: BACKUP -> MASTER
Host B:
May 4 19:05:13 obsdb /bsd: carp0: state transition: MASTER -> BACKUP
Ahora vamos a configurar la variable:
net.inet.carp.log
con el valor máximo, este valor es 7, para ello ejecutamos en ambos hosts:
Host A:
obsda ~ root # sysctl net.inet.carp.log=7
net.inet.carp.log: 2 -> 7
obsda ~ root #
Host B:
obsdb ~ root # sysctl net.inet.carp.log=7
net.inet.carp.log: 2 -> 7
obsdb ~ root #
ahora al bajar la interface carp0 en el host "A":
obsda ~ root # ifconfig carp0 down
vemos que si aparece una linea informativa en el log del host "A":
May 4 19:07:08 obsda /bsd: carp0: state transition: MASTER -> INIT
la cual antes no aparecia. En el host "B" el log es:
May 4 19:07:08 obsdb /bsd: carp0: state transition: BACKUP -> MASTER
Ahora volvemos a levantar la interface carp0 en el host "A":
obsda ~ root # ifconfig carp0 up
lo que muestra en el log del host "A":
May 4 19:10:13 obsda /bsd: carp0: state transition: INIT -> BACKUP
May 4 19:10:14 obsda /bsd: carp0: state transition: BACKUP -> MASTER
como podemos ver aparece una linea extra de información que avisa que estamos pasando del estado "INIT" al estado "BACKUP", para luego pasar al estado "MASTER". En el host "B" la linea de log es:
May 4 19:10:14 obsdb /bsd: carp0: state transition: MASTER -> BACKUP
Conclusión, como vimos elevando el nivel de log aparecen mensajes en el archivo de log que no aparecerían con el valor por defecto. Existen más mensajes que podrían aparecer en el log pero son relacionados a errores en el protocolo y el tráfico de red, ellos se pueden consultar en el código fuente:
/usr/src/sys/netinet/ip_carp.c
mi concejo es elevar el nivel de log a LOG_DEBUG o sea 7, para estar atentos a cualquier eventualidad, si lo consideramos molesto lo bajamos. Para dejar persistente esta configuración agregamos la siguiente linea al siguiente archivo en ambos hosts:
obsda ~ root # grep net.inet.carp.log /etc/sysctl.conf
net.inet.carp.log=7
obsda ~ root #
2.7.4. Generar conmutación por error usando contador de degradación de CARP
En ejemplos anteriores hemos forzado el cambio del Master bajando la interface carp0 en el host "A", esto hace que el host "B" tome ese lugar. Luego levantamos la interface carp0 para que en base a los valores de advbase y advskew el host "A" vuelva a ser elegido Master del grupo. Si bien es valido generar lo que se denomina failover o conmutación por error bajando una interface de red, existe una forma más acorde de hacerlo en la implementación del protocolo CARP en OpenBSD. Podemos lograr realizar una conmutación por error en un grupo de redundancia CARP usando el argumento "-g" del comando ifconfig(8), copio párrafo del man donde explica el uso:
INTERFACE GROUPS
ifconfig -g group-name [[-]carpdemote [number]]
The following options are available for interface groups:
-g group-name
Specify the group.
carpdemote [number]
Increase carp(4) demotion counter for given interface
group by number. Acceptable values are 0 to 128. If
number is omitted, it is increased by 1. The maximum
value for a demotion counter is 255.
-carpdemote [number]
Decrease carp(4) demotion counter for given interface
group by number. Acceptable values are 0 to 128. If
number is omitted, it is decreased by 1.
Todas las interfaces CARP están por defecto en el grupo "carp", como vemos en la linea:
groups: carp
de la salida del comando:
obsda ~ root # ifconfig carp0
carp0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:00:5e:00:01:56
index 7 priority 15 llprio 3
carp: MASTER carpdev vio0 vhid 86 advbase 1 advskew 10
groups: carp
status: master
inet 192.168.46.254 netmask 0xffffff00 broadcast 192.168.46.255
obsda ~ root #
Podemos consultar el contador de degradación CARP de la interface con el comando:
obsda ~ root # ifconfig -g carp
carp: carp demote count 0
obsda ~ root #
en el host "B" el valor esta también por defecto como en el host "A":
obsdb ~ root # ifconfig -g carp
carp: carp demote count 0
obsdb ~ root #
ahora vamos hacer que el host "B" tome el lugar de Master incrementando el contador de degradación en el host "A", vamos a incrementar el contador en 1 con el siguiente comando:
obsda ~ root # ifconfig -g carp carpdemote 1
obsda ~ root #
obsda ~ root # ifconfig -g carp
carp: carp demote count 1
obsda ~ root #
vemos en logs de los host:
Host A:
May 6 22:19:49 obsda /bsd: carp0: state transition: MASTER -> BACKUP
Host B:
May 6 22:20:52 obsdb /bsd: carp0: state transition: BACKUP -> MASTER
o si consultamos las interfaces:
Host A:
obsda ~ root # ifconfig carp0
carp0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:00:5e:00:01:56
index 7 priority 15 llprio 3
carp: BACKUP carpdev vio0 vhid 86 advbase 1 advskew 10
groups: carp
status: backup
inet 192.168.46.254 netmask 0xffffff00 broadcast 192.168.46.255
obsda ~ root #
Host B:
obsdb ~ root # ifconfig carp0
carp0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:00:5e:00:01:56
index 4 priority 15 llprio 3
carp: MASTER carpdev vio0 vhid 86 advbase 1 advskew 20
groups: carp
status: master
inet 192.168.46.254 netmask 0xffffff00 broadcast 192.168.46.255
obsdb ~ root #
hemos logrado hacer un failover o conmutación por error sin necesidad de bajar una interface. Ahora volvemos a poner en cero el contador en el host "A":
obsda ~ root # ifconfig -g carp -carpdemote 1
obsda ~ root #
obsda ~ root # ifconfig -g carp
carp: carp demote count 0
obsda ~ root #
con esto el host "A" vuelve a ser Master:
Host A:
May 6 22:26:38 obsda /bsd: carp0: state transition: BACKUP -> MASTER
Host B:
May 6 22:27:39 obsdb /bsd: carp0: state transition: MASTER -> BACKUP
3. Que es pfsync
Según explica el manual oficial pfsync es:
"La interfaz de red pfsync(4) expone ciertos cambios realizados en la tabla de estado de pf(4). Al monitorear esta interfaz con tcpdump(8), se pueden observar los cambios en la tabla de estado en tiempo real. Además, la interfaz pfsync puede enviar estos mensajes de cambio de estado a la red para que otros nodos que ejecutan PF puedan fusionar los cambios en sus propias tablas de estado. Asimismo, pfsync también puede escuchar en la red los mensajes entrantes."[1]
Entonces con pfsync podemos compartir entre dos hosts el estado de conexiones que se están manejando con PF. Los "Estados" de PF son como las "Conexiones" de Conntrack de IPTables, recordar que PF es un firewall de "Estado", eso significa que se hace un seguimiento de cada conexión, PF entiende que un paquete es parte de una conexión que ya fue aceptada con anterioridad, entonces las reglas de filtrado ya no avalúan los siguientes paquetes que pertenecen a conexiones aceptadas.
A modo de explicación muy general en PF si un paquete es aceptado queda registrada esa conexión con el estado de "Pass", si un paquete es denegado se bloquea "Block" y no se genera un estado. Esto lo podemos monitorear a nivel de logs con la herramienta tcpdump sobre la interfaz de red pflog0, en esta interfaz se registra solo una vez el paso de una conexión, puesto que las conexiones relacionadas a un estado de Pass ya quedan con el estado impuesto y los siguientes paquetes de la misma ya no se registran en esta interfaz.
3.1. Sobre los programadores de pfsync
El programador inicial del proyecto pfsync es Michael "Mickey" Shalayeff en este caso el mismo del proyecto CARP, ver:
2.1. Sobre los programadores de CARP
3.2. Estados en PF
Si el lector ya conoce sobre el manejo de estados sobre PF puede saltear este punto, si no es recomendable leer y practicar el par de ejercicios, es indispensable entender que son los estados en PF para implementar pfsync.
3.2.1. Ejemplo de seguimiento de un Estado
En este ejemplo veremos el seguimiento de un Estado de "pass" de una conexión generada por un comando ping. En el servidor tendremos una sola regla que acepta todo el tráfico y genera logs de todo, como se muestra en el archivo de configuración de PF:
/etc/pf.conf
tendremos el siguiente contenido:
# $OpenBSD: pf.conf,v 1.55 2017/12/03 20:40:04 sthen Exp $
#
# See pf.conf(5) and /etc/examples/pf.conf
pass log all
la regla anterior ya cargada en el sistema se muestra como:
server ~ root # pfctl -sr
pass log all flags S/SA
server ~ root #
ahora ejecutamos un comando tcpdump sobre la interface pflog0 así:
server ~ root # tcpdump -i pflog0 -n icmp
tcpdump: WARNING: snaplen raised from 116 to 160
tcpdump: listening on pflog0, link-type PFLOG
ahora probamos desde un cliente en la red enviar un ping al servidor:
client ~ root # ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1): 56 data bytes
64 bytes from 10.0.0.1: icmp_seq=0 ttl=255 time=0.296 ms
64 bytes from 10.0.0.1: icmp_seq=1 ttl=255 time=0.294 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=255 time=0.285 ms
...
como se puede ver en la salida del comando tcpdump, solo se registra el primer paquete que es el que genera el estado de "pass", o sea se genera un tipo de conexión que también podemos llamar "establecida":
server ~ root # tcpdump -i pflog0 -n icmp
tcpdump: WARNING: snaplen raised from 116 to 160
tcpdump: listening on pflog0, link-type PFLOG
23:15:52.453781 10.0.0.100 > 10.0.0.1: icmp: echo request
notar que solamente aparece una linea en la salida del comando tcpdump, que es la que genera el nuevo estado, luego de generarlo los paquetes posteriores de esta conexión ya no son analizados por las reglas de PF, estos paquetes ya pasan directo.
Ahora si cortamos el comando ping y lo relanzamos, vemos que se genera un nuevo estado porque aparece en la salida del comando tcpdump una nueva linea:
server ~ root # tcpdump -i pflog0 -n icmp
tcpdump: WARNING: snaplen raised from 116 to 160
tcpdump: listening on pflog0, link-type PFLOG
23:15:52.453781 10.0.0.100 > 10.0.0.1: icmp: echo request
23:26:22.303932 10.0.0.100 > 10.0.0.1: icmp: echo request
También podemos consultar con PF los estados que tenemos con protocolo icmp en el servidor:
server ~ root # pfctl -vss | grep -A1 icmp
all icmp 10.0.0.1:8 <- 10.0.0.100:25440 0:0
age 00:01:38, expires in 00:00:09, 98:98 pkts, 8232:8232 bytes, rule 0
server ~ root #
el estado expuesto para icmp tiene un vencimiento de 10 segundos (fijarse que aparece en 00:00:09), como son conexiones del comando ping que van y vuelven en 1 segundo aproximadamente ese valor de vencimiento esta siempre rotando entre 10 y 9. Ahora si cortamos el comando ping ese valor entra a caer desde los 10 segundos hasta que llega a 0 y entonces el estado desaparece del sistema. Ejemplo, si cortamos el comando ping, lanzamos otro y rápidamente consultamos los estados, veremos dos estados icmp, uno del ping que cortamos y uno del ping nuevo:
server ~ root # pfctl -vss | grep -A1 icmp
all icmp 10.0.0.1:8 <- 10.0.0.100:25440 0:0
age 00:04:02, expires in 00:00:02, 235:235 pkts, 19740:19740 bytes, rule 0
--
all icmp 10.0.0.1:8 <- 10.0.0.100:52322 0:0
age 00:00:06, expires in 00:00:09, 6:6 pkts, 504:504 bytes, rule 0
server ~ root #
Si cortamos todos los comandos ping y esperemos más de 10 segundos veremos que en el sistema no queda rastros de un estado icmp:
server ~ root # pfctl -vss | grep -A1 icmp
server ~ root #
3.2.2. Como impacta los cambios de reglas, flush de estados en conexiones ya establecidas
Tenemos en el archivo de configuración de Packet Filter:
/etc/pf.conf
solo la siguiente rule:
pass log all
que cargada en el sistema se lee como:
server ~ root # pfctl -sr
pass log all flags S/SA
server ~ root #
lo que significa que dejamos pasar todo el tráfico pero registrando el mismo en logs. Ahora ejecutamos el comando tcpdump sobre la interface pflog0 de esta forma:
server ~ root # tcpdump -i pflog0 -n -p icmp
Y probamos ejecutando un ping contra el mismo servidor desde un cliente:
client ~ root # ping -c5 10.0.0.1
PING 10.0.0.1 (10.0.0.1): 56 data bytes
64 bytes from 10.0.0.1: icmp_seq=0 ttl=255 time=18.760 ms
64 bytes from 10.0.0.1: icmp_seq=1 ttl=255 time=0.237 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=255 time=0.248 ms
64 bytes from 10.0.0.1: icmp_seq=3 ttl=255 time=0.387 ms
64 bytes from 10.0.0.1: icmp_seq=4 ttl=255 time=0.530 ms
--- 10.0.0.1 ping statistics ---
5 packets transmitted, 5 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 0.237/4.032/18.760/7.365 ms
client ~ root #
El ping anterior genera un estado que puede verse en el comando tcpdump que ejecutamos anteriormente:
server ~ root # tcpdump -i pflog0 -n -p icmp
10:54:34.203846 10.0.0.100 > 10.0.0.1: icmp: echo request
como puede verse los cinco ping han generado solo una linea en pflog0, esta linea representa el primer paquete, el que crea el nuevo estado, ya con el mismo creado o sea ya cuando la conexión esta establecida, a los siguiente paquetes PF ya los deja pasar sin analizar puesto que ya tienen un estado de Pass.
Ahora que pasaría si en vez de limitar la ejecución a un número fijo de pings como en el ejemplo anterior, los dejamos corriendo indefinidamente y mientras el mismo se está ejecutando levantamos una regla de bloqueo del protocolo icmp, realicemos ese ejercicio:
client ~ root # ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1): 56 data bytes
64 bytes from 10.0.0.1: icmp_seq=0 ttl=255 time=0.281 ms
64 bytes from 10.0.0.1: icmp_seq=1 ttl=255 time=0.294 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=255 time=0.220 ms
...
como ya vimos eso me genera un log de estado en pflog:
11:10:09.543915 10.0.0.100 > 10.0.0.1: icmp: echo request
si queremos consultar el estado podemos ejecutar este comando de PF:
server ~ root # pfctl -vss | grep -A1 icmp
all icmp 10.0.0.1:8 <- 10.0.0.100:8172 0:0
age 00:02:20, expires in 00:00:09, 140:140 pkts, 11760:11760 bytes, rule 0
server ~ root #
también si consultamos las rules con el argumento "-v" vemos que la columna "state" tiene muchos estados, 52 en esta caso, uno de ellos generado por el ping:
server ~ root # pfctl -vsr
pass log all flags S/SA
[ Evaluations: 949 Packets: 1806 Bytes: 668500 States: 52 ]
[ Inserted: uid 0 pid 33621 State Creations: 151 ]
server ~ root #
Ahora en el archivo:
pf.conf
vamos agregar la siguiente regla:
block on vport0 proto icmp
lo que ya cargado se ve en el sistema de esta forma:
server ~ root # pfctl -sr
pass log all flags S/SA
block drop on vport0 proto icmp all
server ~ root #
entonces como vemos tenemos ya cargada una rule que bloquea icmp, sin embargo si consultamos la pantalla donde se esta ejecutando el comando ping desde el host cliente, vemos que ese ping sigue llegando a destino. Esto pasa por lo que dijimos anteriormente, las reglas de Pass y Block se analizan la primera vez, en el caso de Pass generan un estado, en el caso de Block no. En nuestro caso que el ping nació cuando no existía el "Block" pero si el "Pass" este ping tiene un estado y que por más que carguemos una nueva regla ese estado sigue siendo valido, por ese motivo los paquetes icmp del comando ping siguen llegando a destino. Si queremos que se actualicen los estados en base a las nuevas reglas podemos ejecutar el comando:
pfctl -Fs
para hacer un flush de los estados, lo hacemos en la siguiente secuencia de comandos:
server ~ root #
server ~ root # pfctl -vss | grep -A1 icmp
all icmp 10.0.0.1:8 <- 10.0.0.100:8172 0:0
age 00:09:27, expires in 00:00:09, 567:567 pkts, 47628:47628 bytes
server ~ root #
server ~ root # pfctl -Fs
59 states cleared
server ~ root #
server ~ root # pfctl -vss | grep -A1 icmp
server ~ root #
notar que no solo desapareció el estado icmp anterior, sino que no se generó uno nuevo, esto porque como ya sabemos block no genera estados. También logramos que el comando ping que se estaba ejecutando desde el cliente ya no pueda alcanzar el destino vía protocolo icmp.
3.3. Modos de conexión entre hosts pfsync
Existen dos modos de conexión entre hosts pfsync:
- Vía protocolo multicast
- Vía protocolo unicast
vamos a realizar las dos tipos de configuraciones, primeramente usando el comando ifconfig(8), leer la sección ifconfig(8)#PFSYNC, sintaxis:
ifconfig pfsync-interface [[-]defer] [maxupd n] [[-]syncdev iface] [[-]syncpeer peer_address]
3.3.1. Conexión con protocolo multicast
Vamos a levantar en ambos host una conexión pfsync con protocolo multicast, esto tiene la ventaja que los nodos se auto descubren en la red, pero existe un inconveniente con este tipo de configuración y es que el protocolo pfsync viaja en texto plano. Para asegurarlo en el modo multicast lo podemos hacer a nivel físico, una forma sería si es que tenemos dos hosts es destinar en ambos una placa de red dedicada a este tráfico y unir ambos con un cable cruzado. Otra forma sería usar una VLAN específica para pfsync. Si no podemos segurizar la conexión multicast debemos analizar el modo unicast, que se explica en el siguiente punto.
Lista de comandos para modo multicast:
Host B:
obsdb ~ root #
obsdb ~ root # ifconfig pfsync0 syncdev vio2 up
obsdb ~ root #
obsdb ~ root # ifconfig pfsync0
pfsync0: flags=41<UP,RUNNING> mtu 1500
index 8 priority 0 llprio 3
encap: parent vio2
pfsync: syncdev: vio2 maxupd: 128 defer: off
groups: carp pfsync
obsdb ~ root #
Host A:
obsda ~ root #
obsda ~ root # ifconfig pfsync0 syncdev vio2 up
obsda ~ root #
obsda ~ root # ifconfig pfsync0
pfsync0: flags=41<UP,RUNNING> mtu 1500
index 8 priority 0 llprio 3
encap: parent vio2
pfsync: syncdev: vio2 maxupd: 128 defer: off
groups: carp pfsync
obsda ~ root #
ahora si ejecutamos este comando tcpdump en ambos host sobre la interfaz pfsync0, vemos la salida:
Host A:
obsda ~ root # tcpdump -i pfsync0 -n -p
tcpdump: listening on pfsync0, link-type PFSYNC
22:42:59.694245 PFSYNCv69 len 128
22:43:00.714287 PFSYNCv69 len 128
22:43:01.754351 PFSYNCv69 len 128
22:43:03.094329 PFSYNCv69 len 128
^C
4 packets received by filter
0 packets dropped by kernel
obsda ~ root #
Host B:
obsdb ~ root # tcpdump -i pfsync0 -n -p
tcpdump: listening on pfsync0, link-type PFSYNC
22:43:12.334987 PFSYNCv69 len 128
22:43:13.334885 PFSYNCv69 len 128
22:43:14.354986 PFSYNCv69 len 128
22:43:15.595078 PFSYNCv69 len 128
^C
4 packets received by filter
0 packets dropped by kernel
obsdb ~ root #
vemos que pfsync esta funcionando. Este tráfico al ser multicast podemos mirarlo con algo como:
obsda ~ root # tcpdump -i vio0 -v -n net 224.0.0.0/4
tcpdump: listening on vio0, link-type EN10MB
22:53:06.444251 0.0.0.0 > 224.0.0.240: PFSYNCv6 len 108
act UPD ST COMP count 1
...
(DF) [tos 0x10] (ttl 255, id 17036, len 128)
22:53:07.444075 0.0.0.0 > 224.0.0.240: PFSYNCv6 len 108
act UPD ST COMP count 1
...
(DF) [tos 0x10] (ttl 255, id 1024, len 128)
22:53:08.125512 0.0.0.0 > 224.0.0.240: PFSYNCv6 len 124
act UPD ST COMP count 1
...
como vemos estamos analizando todo el espectro multicast con tcpdump, puesto que pusimos la subred entera:
224.0.0.0/4
Por otro lado vemos que en las lineas que muestra el comando esta la dirección multicast del protocolo pfsync, es la dirección:
224.0.0.240
entonces para que el tráfico a analizar no se mezcle con otro tráfico multicast podemos mejorar el comando ejecutándolo de esta forma:
obsda ~ root # tcpdump -i vio0 -v host 224.0.0.240
tcpdump: listening on vio0, link-type EN10MB
22:58:47.582328 0.0.0.0 > 224.0.0.240: PFSYNCv6 len 108
act UPD ST COMP count 1
...
(DF) [tos 0x10] (ttl 255, id 10673, len 128)
22:58:48.592265 0.0.0.0 > 224.0.0.240: PFSYNCv6 len 108
act UPD ST COMP count 1
...
(DF) [tos 0x10] (ttl 255, id 3103, len 128)
22:58:48.612223 0.0.0.0 > 224.0.0.240: PFSYNCv6 len 288
act INS ST count 1
...
(DF) [tos 0x10] (ttl 255, id 19810, len 308)
22:58:49.802285 0.0.0.0 > 224.0.0.240: PFSYNCv6 len 108
act UPD ST COMP count 1
...
ahora en el siguiente punto vamos hacer el ejercicio de configurar pfsync en modo unicast, pero antes vamos a dar de baja la interfaz pfsync0 que creamos en ambos hosts, ejecutar el siguiente comando en ambos hosts:
ifconfig pfsync0 destroy
3.3.2. Conexión con protocolo unicast
En este punto vamos hacer una configuración unicast de pfsync, similar a como hicimos en el punto anterior, pero ahora usando el argumento "syncpeer" para especificar al otro host con pfsync. Este modo tiene la desventaja que los host no se auto descubren por la red, pero tiene la ventaja que podemos canalizar el tráfico por un túnel VPN, primero veamos un ejemplo sin túnel VPN, quedaría así:
10.1.1.200/24 .---. .---.
vio2| B |<--------------->| A |vio2
'---' pfsync0 '---' 10.1.1.100/24
Recordar leer la página del manual en ifconfig(8)#PFSYNC, sintaxis:
ifconfig pfsync-interface [[-]defer] [maxupd n] [[-]syncdev iface] [[-]syncpeer peer_address]
Los comandos manuales en ambos hosts serían:
Host B:
obsdb ~ root #
obsdb ~ root # ifconfig pfsync0 syncdev vio2 syncpeer 10.1.1.100 up
obsdb ~ root #
obsdb ~ root # ifconfig pfsync0
pfsync0: flags=41<UP,RUNNING> mtu 1500
index 8 priority 0 llprio 3
encap: parent vio2
pfsync: syncdev: vio2 syncpeer: 10.1.1.100 maxupd: 128 defer: off
groups: carp pfsync
obsdb ~ root #
Host A:
obsda ~ root #
obsda ~ root # ifconfig pfsync0 syncdev vio2 syncpeer 10.1.1.200 up
obsda ~ root #
obsda ~ root # ifconfig pfsync0
pfsync0: flags=41<UP,RUNNING> mtu 1500
index 8 priority 0 llprio 3
encap: parent vio2
pfsync: syncdev: vio2 syncpeer: 10.1.1.200 maxupd: 128 defer: off
groups: carp pfsync
obsda ~ root #
fijarse como en estos comandos ahora especificamos el peer en la conexión, el mismo también se lo puede visualizar cuando ejecutamos ifconfig(8) para consultar la configuración de la interfaz. Como dijimos previamente la ventaja de este modo de configuración de pfsync con unicast es que podemos canalizar el tráfico a través de un túnel VPN, la documentación habla de usar IPSEC, en este manual vamos a usar el más moderno Wireguard.
3.4. Segurizar conexión pfsync
Para segurizar la conexión de pfsync con protocolo unicast vamos a utilizar un túnel VPN Wireguard. Como se vio en el punto:
3.3.2. Conexión con protocolo unicast
podemos montar la sincronización de estados de pf con pfsync vía la conexión de dos puntos, especificados con direcciones IPs. Como ya se mencionó el problema es que ese tráfico de sincronización de estados viaja en texto plano.
NOTA: No es la función de este manual realizar una explicación exhaustiva sobre posibles configuraciones de túneles VPN con Wireguard. Si bien los pasos de la configuración propuesta en este ejercicio son sencillos y se los describe uno por uno más adelante, se recomienda leer alguna documentación introductoria sobre Wireguard.
NOTA: Para este ejercicio tenemos que instalar las utilidades Wireguard con el siguiente comando:
pkg_add -mv wireguard-tools
En el ejercicio tenemos dos servidores virtuales que se interconectan a través de las interfaces vio0 que están con IPs de la subred:
10.0.0.0/24
diagrama:
10.0.0.200/24 .---. .---.
vio0| B |<--------------->| A |vio0
'---' '---' 10.0.0.100/24
consultamos esta configuración en el sistema:
Host A:
obsda ~ root # ifconfig vio0
vio0: flags=8b43<UP,BROADCAST,RUNNING,PROMISC,ALLMULTI,SIMPLEX,MULTICAST> mtu 1500
lladdr fe:e1:bb:d1:ae:d9
index 1 priority 0 llprio 3
groups: egress
media: Ethernet autoselect
status: active
inet 10.0.0.100 netmask 0xffffff00 broadcast 10.0.0.255
obsda ~ root #
Host B:
obsdb ~ root # ifconfig vio0
vio0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 12:e4:07:63:53:78
index 1 priority 0 llprio 3
groups: egress
media: Ethernet autoselect
status: active
inet 10.0.0.200 netmask 0xffffff00 broadcast 10.0.0.255
obsdb ~ root #
ahora vamos a proceder a levantar el túnel VPN Wireguard. En ambos servidores vamos a crear las keys necesarias (public y secret) con los siguientes comandos, ejemplo en host "A":
obsda ~ root # cd /etc
obsda /etc root #
obsda /etc root # mkdir wireguard
obsda /etc root #
obsda /etc root # chmod 700 wireguard/
obsda /etc root #
obsda /etc root # cd wireguard/
obsda /etc/wireguard root #
obsda /etc/wireguard root # wg genkey > secret.key
Warning: writing to world accessible file.
Consider setting the umask to 077 and trying again.
obsda /etc/wireguard root #
obsda /etc/wireguard root # chmod 600 secret.key
obsda /etc/wireguard root #
obsda /etc/wireguard root # ls -la secret.key
-rw------- 1 root wheel 45 May 26 09:09 secret.key
obsda /etc/wireguard root #
obsda /etc/wireguard root # wg pubkey < secret.key > public.key
obsda /etc/wireguard root #
obsda /etc/wireguard root # ls -la public.key
-rw-r--r-- 1 root wheel 45 May 26 09:10 public.key
obsda /etc/wireguard root #
repetir los mismos comandos en host "B". Una vez que los dos hosts tienen las keys necesarias pasamos a configurar el túnel vía archivos de hostname.if(5). Vamos a crear el siguiente archivo:
/etc/hostname.wg1
en el host "A".
NOTA: fijarse que vamos a usar la interfaz wg1 para la sincronización de pfsync, esto es para dejar la wg0 libre, imaginando que ya podría estar siendo usada para otra utilidad con Wireguard. Así como usamos la wg1 podríamos usar la wg2 o incluso la wg0 si estaría libre, esto queda a disponibilidad de cada diseño particular de red.
El contenido del archivo será similar a:
wgport 51830 wgkey 'CIqY6QylkTAizY0TXd3tNBLJyPYVNaOCpLGEwFM/oUw='
10.1.1.100/24
# obsdb
wgpeer X+islmYJ263cJjh2h/x+pHGgVCAzthzQDYYmb14YEAw= wgendpoint 10.0.0.200 51830 wgaip 10.1.1.200/32
up
NOTA: fijarse que no hemos usado el puerto estándar de Wireguard que es el 51820, así como no usamos la interfaz wg0 tampoco usamos el puerto estándar por considerar que ya podría estar siendo usado.
Ahora en el host "B" creamos de forma similar el archivo:
obsdb ~ root # cat /etc/hostname.wg1
wgport 51830 wgkey '0LDS09MpEFdw8d+5yyiMDs10LBkkX2j2nx3Gumsap0g='
10.1.1.200/24
# obsda
wgpeer sB2kGR9mFaeR190jZ1lNs5SDUYLP5LFA0VgX1iWsJzs= wgendpoint 10.0.0.100 51830 wgaip 10.1.1.100/32
up
NOTA importante: Recordar poner en los archivos de configuración sus propias keys, tanto la pública como la privada.
Ahora vamos agregar una configuración bastante sencilla y permisiva en PF de ambos host. En el archivo de configuración del mismo:
/etc/pf.conf
pondremos las siguientes lineas:
block log all
set skip on lo
pass on vio0
pass on wg1
y cargamos en el sistema esas reglas con el comando:
pfctl -f /etc/pf.conf
ahora en ambos host cargamos el túnel VPN Wireguard con el comando netstart(8) que ya explicamos anteriormente:
sh /etc/netstart wg1
comprobamos que se haya cargado la configuración:
Host A:
obsda ~ root # ifconfig wg1
wg1: flags=80c3<UP,BROADCAST,RUNNING,NOARP,MULTICAST> mtu 1420
index 6 priority 0 llprio 3
wgport 51830
wgpubkey sB2kGR9mFaeR190jZ1lNs5SDUYLP5LFA0VgX1iWsJzs=
wgpeer X+islmYJ263cJjh2h/x+pHGgVCAzthzQDYYmb14YEAw=
wgendpoint 10.0.0.200 51830
tx: 0, rx: 0
wgaip 10.1.1.200/32
groups: wg
inet 10.1.1.100 netmask 0xffffff00 broadcast 10.1.1.255
obsda ~ root #
Host B:
obsdb ~ root # ifconfig wg1
wg1: flags=80c3<UP,BROADCAST,RUNNING,NOARP,MULTICAST> mtu 1420
index 6 priority 0 llprio 3
wgport 51830
wgpubkey X+islmYJ263cJjh2h/x+pHGgVCAzthzQDYYmb14YEAw=
wgpeer sB2kGR9mFaeR190jZ1lNs5SDUYLP5LFA0VgX1iWsJzs=
wgendpoint 10.0.0.100 51830
tx: 0, rx: 0
wgaip 10.1.1.100/32
groups: wg
inet 10.1.1.200 netmask 0xffffff00 broadcast 10.1.1.255
obsdb ~ root #
probamos un ping desde el host "B" hacia el host "A":
obsdb ~ root # ping -c3 10.1.1.100
PING 10.1.1.100 (10.1.1.100): 56 data bytes
64 bytes from 10.1.1.100: icmp_seq=0 ttl=255 time=0.959 ms
64 bytes from 10.1.1.100: icmp_seq=1 ttl=255 time=1.701 ms
64 bytes from 10.1.1.100: icmp_seq=2 ttl=255 time=1.923 ms
--- 10.1.1.100 ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 0.959/1.528/1.923/0.412 ms
obsdb ~ root #
y desde el host "A" hacia el host "B":
obsda ~ root # ping -c3 10.1.1.200
PING 10.1.1.200 (10.1.1.200): 56 data bytes
64 bytes from 10.1.1.200: icmp_seq=0 ttl=255 time=6.384 ms
64 bytes from 10.1.1.200: icmp_seq=1 ttl=255 time=1.434 ms
64 bytes from 10.1.1.200: icmp_seq=2 ttl=255 time=1.132 ms
--- 10.1.1.200 ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 1.132/2.984/6.384/2.408 ms
obsda ~ root #
como vemos tenemos conectividad a través del túnel.
Ahora vamos a montar pfsync sobre Wireguard o sea la interfaz pfsync0 sobre wg1, comandos:
Host B:
obsdb ~ root # ifconfig pfsync0 syncdev wg1 syncpeer 10.1.1.100 up
obsdb ~ root #
obsdb ~ root # ifconfig pfsync0
pfsync0: flags=41<UP,RUNNING> mtu 1500
index 8 priority 0 llprio 3
encap: parent wg1
pfsync: syncdev: wg1 syncpeer: 10.1.1.100 maxupd: 128 defer: off
groups: carp pfsync
obsdb ~ root #
Host A:
obsda ~ root # ifconfig pfsync0 syncdev wg1 syncpeer 10.1.1.200 up
obsda ~ root #
obsda ~ root # ifconfig pfsync0
pfsync0: flags=41<UP,RUNNING> mtu 1500
index 8 priority 0 llprio 3
encap: parent wg1
pfsync: syncdev: wg1 syncpeer: 10.1.1.200 maxupd: 128 defer: off
groups: carp pfsync
obsda ~ root #
En el siguiente y último punto en donde desarrollaremos un ejemplo más práctico, veremos como dejar persistentes en el sistema estas configuraciones.
4. Ejemplo práctico del uso de CARP y pfsync
Ahora que ya nos ejercitamos con CARP, los estados de PF y levantamos pfsync segurizado con Wireguard, vamos a proponer un ejemplo más real del uso de CARP con pfsync para lograr la tan apreciada HA.
En este ejercicio proponemos levantar la configuración necesaria para obtener un router/firewall en HA que separa dos redes. Estas dos redes LAN pueden cumplir la función que usted necesite en su organización, una puede ser una red LAN de usuarios y la otra de servidores o ambas ser redes de usuarios pero de distintas oficinas por ejemplo. La idea detrás es que tenemos dos redes separadas por un firewall en donde solo dejamos pasar entre ambas el tráfico permitido.
Como en los ejemplos anteriores, vamos a tener dos host cumpliendo la función de firewall en HA, el obsda en estado Master por defecto y el obsdb en estado Backup por defecto. Como son maquinas virtuales no vamos a escatimar en interfaces de red para armar el diseño, el mismo se podría armar con menos interfaces, pero como son gratis en la virtualización mejor tener una por función, para auditar el tráfico más fácilmente en el uso diario.
El diseño en cuanto a interfaces de red para ambos host sería:
obsda
|____vio0.....(10.0.0.100/24) MGMT y montar Wireguard
|____vio1.....Montar el pseudo dispositivo carp0
|____vio2.....Montar el pseudo dispositivo carp1
|____carp0....(192.168.46.254/24 - vio1) LAN1
|____carp1....(192.168.47.254/24 - vio2) LAN2
|____wg1......(10.1.1.100/24 - vio0) túnel VPN Wireguard
|____pfsync0..(wg1) Sincronizar conexiones de PF
obsdb
|____vio0.....(10.0.0.200/24) MGMT y montar Wireguard
|____vio1.....Montar el pseudo dispositivo carp0
|____vio2.....Montar el pseudo dispositivo carp1
|____carp0....(192.168.46.254/24 - vio1) LAN1
|____carp1....(192.168.47.254/24 - vio2) LAN2
|____wg1......(10.1.1.200/24 - vio0) túnel VPN Wireguard
|____pfsync0..(wg1) Sincronizar conexiones de PF
.-------------.
| |
,---------------------| Workstation |<----.
| | MGMT | |
| '-------------' |
| 10.0.0.1/24 |
| |
| |
| .-------------. |
| | | |
| | Workstation |<--------. |
| | LAN 1 | \ |
| '-------------' \ |
| 192.168.46.102/24 \ |
| | |
| v |
| .---. |
| / \ |
| | LAN 1 | |
| \ / |
| '-----' |
| ^ |
| | |
|10.0.0.100/24 | |10.0.0.200/24
'---->vio0 .---. | '-->vio0 .---.
| | v | |
| | 192.168.46.254/24 | |
| |vio1 <-----------------------> vio1| |
| F | carp0 | F |
| I | | I |
| R | | R |
| E | 10.1.1.200/24| E |
| W |wg1 <---------pfsync0---------->wg1| W |
| A |10.1.1.100/24 | A |
| L | | L |
| L | | L |
| | 192.168.47.254/24 | |
| A |vio2 <-----------------------> vio2| B |
'---' carp1 '---'
^
|
|
v
.---.
/ \
| LAN 2 |
\ /
'-----'
^
|
\ 192.168.47.102/24
\ .-------------.
\ | |
'-->| Workstation |
| LAN 2 |
'-------------'
4.1. Configuración inicial de los hosts
Como se dijo anteriormente vamos a necesitar para el ejercicio dos maquinas virtuales para poder albergar los dos host, los mismos serían:
- Host A:
Nombre de host...........................: obsda
Número de interfaces de red virtuales....: 3
Dirección IP en vio0.....................: 10.0.0.100/24
RAM......................................: 1536MB
si estamos virtualizando con VMM de OpenBSD la definición de la VM sería:
switch "uplink_veb0" {
interface veb0
}
vm "obsda" {
memory 1536M
enable
disk /datos/vmm/obsda/obsda.qcow2
interface { switch "uplink_veb0" }
interface { switch "uplink_veb0" }
interface { switch "uplink_veb0" }
}
Recordar cambiar el path del disco a conveniencia. Con realizar una instalación estándar de OpenBSD es suficiente. Una vez instalado OpenBSD y configurado el entorno a nuestro gusto recordar instalar ademas este paquete:
pkg_add -mv wireguard-tools
similarmente la configuración del host "B" sería:
- Host B:
Nombre de host...........................: obsdb
Número de interfaces de red virtuales....: 3
Dirección IP en vio0.....................: 10.0.0.200/24
RAM......................................: 1536MB
una vez que arranquen ambos host el comando:
ifconfig -A
debería mostrar algo similar a:
- Host A:
comando:
obsda ~ root # ifconfig -A
lo0: flags=2008049<UP,LOOPBACK,RUNNING,MULTICAST,LRO> mtu 32768
index 5 priority 0 llprio 3
groups: lo
inet6 ::1 prefixlen 128
inet6 fe80::1%lo0 prefixlen 64 scopeid 0x5
inet 127.0.0.1 netmask 0xff000000
vio0: flags=8b43<UP,BROADCAST,RUNNING,PROMISC,ALLMULTI,SIMPLEX,MULTICAST> mtu 1500
lladdr fe:e1:bb:d1:90:c9
index 1 priority 0 llprio 3
groups: egress
media: Ethernet autoselect
status: active
inet 10.0.0.100 netmask 0xffffff00 broadcast 10.0.0.255
vio1: flags=8802<BROADCAST,SIMPLEX,MULTICAST> mtu 1500
lladdr fe:e1:bb:d2:76:c8
index 2 priority 0 llprio 3
media: Ethernet autoselect
status: no carrier
vio2: flags=8802<BROADCAST,SIMPLEX,MULTICAST> mtu 1500
lladdr fe:e1:bb:d3:27:db
index 3 priority 0 llprio 3
media: Ethernet autoselect
status: no carrier
enc0: flags=0<>
index 4 priority 0 llprio 3
groups: enc
status: active
pflog0: flags=141<UP,RUNNING,PROMISC> mtu 33136
index 6 priority 0 llprio 3
groups: pflog
obsda ~ root #
comando route:
obsda ~ root # route -n show -inet -gateway
Routing tables
Internet:
Destination Gateway Flags Refs Use Mtu Prio Iface
default 10.0.0.1 UGS 5 15 - 8 vio0
224/4 127.0.0.1 URS 0 444 32768 8 lo0
10.0.0/24 10.0.0.100 UCn 1 0 - 4 vio0
10.0.0.255 10.0.0.100 UHb 0 0 - 1 vio0
127/8 127.0.0.1 UGRS 0 0 32768 8 lo0
127.0.0.1 127.0.0.1 UHhl 1 2 32768 1 lo0
obsda ~ root #
- Host B:
comando:
obsdb ~ root # ifconfig -A
lo0: flags=2008049<UP,LOOPBACK,RUNNING,MULTICAST,LRO> mtu 32768
index 5 priority 0 llprio 3
groups: lo
inet6 ::1 prefixlen 128
inet6 fe80::1%lo0 prefixlen 64 scopeid 0x5
inet 127.0.0.1 netmask 0xff000000
vio0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 12:e4:07:63:53:78
index 1 priority 0 llprio 3
groups: egress
media: Ethernet autoselect
status: active
inet 10.0.0.200 netmask 0xffffff00 broadcast 10.0.0.255
vio1: flags=8802<BROADCAST,SIMPLEX,MULTICAST> mtu 1500
lladdr 12:e4:07:63:53:79
index 2 priority 0 llprio 3
media: Ethernet autoselect
status: no carrier
vio2: flags=8802<BROADCAST,SIMPLEX,MULTICAST> mtu 1500
lladdr 12:e4:07:63:53:80
index 3 priority 0 llprio 3
media: Ethernet autoselect
status: no carrier
enc0: flags=0<>
index 4 priority 0 llprio 3
groups: enc
status: active
pflog0: flags=141<UP,RUNNING,PROMISC> mtu 33136
index 6 priority 0 llprio 3
groups: pflog
obsdb ~ root #
comando route:
obsdb ~ root # route -n show -inet -gateway
Routing tables
Internet:
Destination Gateway Flags Refs Use Mtu Prio Iface
default 10.0.0.1 UGS 5 11 - 8 vio0
224/4 127.0.0.1 URS 0 1 32768 8 lo0
10.0.0/24 10.0.0.200 UCn 1 0 - 4 vio0
10.0.0.255 10.0.0.200 UHb 0 0 - 1 vio0
127/8 127.0.0.1 UGRS 0 0 32768 8 lo0
127.0.0.1 127.0.0.1 UHhl 1 2 32768 1 lo0
obsdb ~ root #
4.2. Descripción general de los pasos de configuración
Vamos a realizar las configuraciones progresivamente y de forma persistente en archivos de configuración de OpenBSD. Cuando sea el caso ademas vamos a probar el resultado de las mismas. Al final del ejercicio tenemos que lograr tener conexiones persistentes entre las workstations de ambas redes (LAN1, LAN2) y alternar los modos Master y Backup entre los hosts de firewall sin que las conexiones se corten.
4.2.1. Configuración de variables de estados en el kernel (sysctl)
En la sección de configuración de CARP en este manual ya vimos que hacen las siguientes variables del kernel:
- net.inet.carp.allow
- net.inet.carp.preempt
- net.inet.carp.log
ademas de las anteriores variables, vamos a configurar la siguiente, para que permita el forward entre interfaces de red:
- net.inet.ip.forwarding
podemos configurar estas variables al vuelo de la siguiente forma:
obsda ~ root # sysctl net.inet.carp.allow=1
net.inet.carp.allow: 1 -> 1
obsda ~ root #
obsda ~ root # sysctl net.inet.carp.preempt=1
net.inet.carp.preempt: 1 -> 1
obsda ~ root #
obsda ~ root # sysctl net.inet.carp.log=7
net.inet.carp.log: 7 -> 7
obsda ~ root #
obsda ~ root # sysctl net.inet.ip.forwarding=1
net.inet.ip.forwarding: 1 -> 1
obsda ~ root #
para dejar configuradas estas variables de forma persistente, agregamos las mismas al siguiente archivo:
obsda ~ root # cat /etc/sysctl.conf
net.inet.carp.allow=1
net.inet.carp.preempt=1
net.inet.carp.log=7
net.inet.ip.forwarding=1
obsda ~ root #
ambos hosts tienen que tener la misma configuración. Si necesita mayor información leer las siguientes páginas de man:
4.2.2. Configuración de las interfaces de red de la VM
En este punto vamos a ver la configuración de las interfaces de red de la VM (Virtual Machine o Máquina Virtual), como ya se dijo en el punto:
4.1. Configuración inicial de los hosts
la principal interface que tenemos en este diseño de red es la:
- vio0
puesto que se usará para MGMT, entonces es muy importante asegurarnos el ingreso por la misma. Ademas en esta interface vamos a montar el túnel VPN Wireguard, necesario para segurizar pfsync. La misma tiene una dirección IP configurada, que en cada host es:
Host A:
10.0.0.100/24
Host B:
10.0.0.200/24
entonces los archivos de configuración que tenemos que crear en cada host son:
Host A:
obsda ~ root # cat /etc/hostname.vio0
inet 10.0.0.100 0xffffff00
obsda ~ root #
Host B:
obsdb ~ root # cat /etc/hostname.vio0
inet 10.0.0.200 0xffffff00
obsdb ~ root #
también cada VM tendrá dos interfaces de red extras:
- vio1
- vio2
para montar sobre ellas las interfaces CARP. La configuración de estas es la misma para ambos host, archivo:
/etc/hostname.vio1
con el contenido:
up
archivo:
/etc/hostname.vio2
con el contenido:
up
verificar que una vez que el sistema reinicie estas interfaces esten levantadas:
Host A:
obsda ~ root # ifconfig vio1
vio1: flags=8b43<UP,BROADCAST,RUNNING,PROMISC,ALLMULTI,SIMPLEX,MULTICAST> mtu 1500
lladdr fe:e1:bb:d2:76:c8
index 2 priority 0 llprio 3
media: Ethernet autoselect
status: active
obsda ~ root #
obsda ~ root # ifconfig vio2
vio2: flags=8b43<UP,BROADCAST,RUNNING,PROMISC,ALLMULTI,SIMPLEX,MULTICAST> mtu 1500
lladdr fe:e1:bb:d3:27:db
index 3 priority 0 llprio 3
media: Ethernet autoselect
status: active
obsda ~ root #
Host B:
obsdb ~ root # ifconfig vio1
vio1: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 12:e4:07:63:53:79
index 2 priority 0 llprio 3
media: Ethernet autoselect
status: active
obsdb ~ root #
obsdb ~ root # ifconfig vio2
vio2: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 12:e4:07:63:53:80
index 3 priority 0 llprio 3
media: Ethernet autoselect
status: active
obsdb ~ root #
4.2.3. Configuración de las interfaces del protocolo CARP
Como se vio en el inicio del punto:
4. Ejemplo práctico del uso de CARP y pfsync
en los routers/firewalls en HA vamos a tener dos interfaces para el protocolo CARP, una con una IP de la subred que vive en LAN1 y la otra con una IP de la subred que vive en LAN2. Las direcciones IP serán:
carp0......(192.168.46.254/24 - vio1) Interface hacia LAN1
carp1......(192.168.47.254/24 - vio2) Interface hacia LAN2
Estas direcciones IP asignadas a las interfaces del protocolo CARP, pueden ser usadas en los clientes como puertas de enlace o simplemente como router para encaminar una subred o IP.
Host A:
crear el siguiente archivo:
/etc/hostname.carp0
con el contenido:
inet 192.168.46.254 255.255.255.0 192.168.46.255 vhid 86 advskew 10 carpdev vio0 pass 4PAmAmVfRaRG
y el archivo:
/etc/hostname.carp1
con el contenido:
inet 192.168.47.254 255.255.255.0 192.168.47.255 vhid 87 advskew 10 carpdev vio1 pass PavkTgmtMZU4
Host B:
crear el siguiente archivo:
/etc/hostname.carp0
con el contenido:
inet 192.168.46.254 255.255.255.0 192.168.46.255 vhid 86 advskew 20 carpdev vio0 pass 4PAmAmVfRaRG
y el archivo:
/etc/hostname.carp1
con el contenido:
inet 192.168.47.254 255.255.255.0 192.168.47.255 vhid 87 advskew 20 carpdev vio1 pass PavkTgmtMZU4
lo único en lo que difieren los archivos del host "A" con los del host "B" es en el parámetro advskew, que fue explicado en el punto:
2.5. Que tener en cuenta en la elección del Master
Una vez creados los archivos, reiniciamos y gracias a los mismos se levantaran automáticamente las interfaces CARP, que quedaran funcionando como se explicó en el punto que se trató CARP. El resultado sería el siguiente:
Host A:
obsda ~ root # ifconfig carp0
carp0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:00:5e:00:01:56
index 6 priority 15 llprio 3
carp: MASTER carpdev vio0 vhid 86 advbase 1 advskew 10
groups: carp
status: master
inet 192.168.46.254 netmask 0xffffff00 broadcast 192.168.46.255
obsda ~ root #
obsda ~ root # ifconfig carp1
carp1: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:00:5e:00:01:57
index 7 priority 15 llprio 3
carp: MASTER carpdev vio1 vhid 87 advbase 1 advskew 10
groups: carp
status: master
inet 192.168.47.254 netmask 0xffffff00 broadcast 192.168.47.255
obsda ~ root #
Host B:
obsdb ~ root # ifconfig carp0
carp0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:00:5e:00:01:56
index 6 priority 15 llprio 3
carp: BACKUP carpdev vio0 vhid 86 advbase 1 advskew 20
groups: carp
status: backup
inet 192.168.46.254 netmask 0xffffff00 broadcast 192.168.46.255
obsdb ~ root #
obsdb ~ root # ifconfig carp1
carp1: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:00:5e:00:01:57
index 7 priority 15 llprio 3
carp: BACKUP carpdev vio1 vhid 87 advbase 1 advskew 20
groups: carp
status: backup
inet 192.168.47.254 netmask 0xffffff00 broadcast 192.168.47.255
obsdb ~ root #
4.2.4. Configuración de túnel VPN Wireguard para segurizar pfsync
Anteriormente en el punto:
3.4. Segurizar conexión pfsync
se trató como realizar la configuración de forma persistente para levantar un túnel VPN Wireguard, logrando que se establezca automáticamente al iniciar el host. Todo esto con el fin de que el tráfico pfsync entre los hosts se transfiera encapsulado por dicho túnel. Ahora escribiremos un resumen de lo ya tratado. Recordemos que lo primero que tenemos que hacer es instalar en ambos host el port:
pkg_add -mv wireguard-tools
luego ejecutar en cada host los siguientes comandos para la generación de las llaves pública y privada. Ejemplo en host "A":
obsda ~ root # cd /etc
obsda /etc root #
obsda /etc root # mkdir wireguard
obsda /etc root #
obsda /etc root # chmod 700 wireguard/
obsda /etc root #
obsda /etc root # cd wireguard/
obsda /etc/wireguard root #
obsda /etc/wireguard root # wg genkey > secret.key
Warning: writing to world accessible file.
Consider setting the umask to 077 and trying again.
obsda /etc/wireguard root #
obsda /etc/wireguard root # chmod 600 secret.key
obsda /etc/wireguard root #
obsda /etc/wireguard root # ls -la secret.key
-rw------- 1 root wheel 45 May 26 09:09 secret.key
obsda /etc/wireguard root #
obsda /etc/wireguard root # wg pubkey < secret.key > public.key
obsda /etc/wireguard root #
obsda /etc/wireguard root # ls -la public.key
-rw-r--r-- 1 root wheel 45 May 26 09:10 public.key
obsda /etc/wireguard root #
también debemos crear un archivo de configuración en cada host para que al iniciar levante el túnel VPN automáticamente. Ejemplo de archivo en el host "A":
/etc/hostname.wg1
con el contenido:
wgport 51830 wgkey 'CIqY6QylkTAizY0TXd3tNBLJyPYVNaOCpLGEwFM/oUw='
10.1.1.100/24
# peer obsdb
wgpeer X+islmYJ263cJjh2h/x+pHGgVCAzthzQDYYmb14YEAw= wgendpoint 10.0.0.200 51830 wgaip 10.1.1.200/32
up
y ejemplo de archivo en el host "B":
/etc/hostname.wg1
con el contenido:
wgport 51830 wgkey '0LDS09MpEFdw8d+5yyiMDs10LBkkX2j2nx3Gumsap0g='
10.1.1.200/24
# peer obsda
wgpeer sB2kGR9mFaeR190jZ1lNs5SDUYLP5LFA0VgX1iWsJzs= wgendpoint 10.0.0.100 51830 wgaip 10.1.1.100/32
up
para mayor detalles de las opciones y comandos de configuración consultar el punto:
3.4. Segurizar conexión pfsync
Una vez que hayamos reiniciado para verificar esta configuración, veremos la siguiente interfaz wg1:
Host A:
obsda ~ root # ifconfig wg1
wg1: flags=80c3<UP,BROADCAST,RUNNING,NOARP,MULTICAST> mtu 1420
index 8 priority 0 llprio 3
wgport 51830
wgpubkey sB2kGR9mFaeR190jZ1lNs5SDUYLP5LFA0VgX1iWsJzs=
wgpeer X+islmYJ263cJjh2h/x+pHGgVCAzthzQDYYmb14YEAw=
wgendpoint 10.0.0.200 51830
tx: 436, rx: 380
last handshake: 29 seconds ago
wgaip 10.1.1.200/32
groups: wg
inet 10.1.1.100 netmask 0xffffff00 broadcast 10.1.1.255
obsda ~ root #
Host B:
obsdb ~ root # ifconfig wg1
wg1: flags=80c3<UP,BROADCAST,RUNNING,NOARP,MULTICAST> mtu 1420
index 8 priority 0 llprio 3
wgport 51830
wgpubkey X+islmYJ263cJjh2h/x+pHGgVCAzthzQDYYmb14YEAw=
wgpeer sB2kGR9mFaeR190jZ1lNs5SDUYLP5LFA0VgX1iWsJzs=
wgendpoint 10.0.0.100 51830
tx: 380, rx: 436
last handshake: 46 seconds ago
wgaip 10.1.1.100/32
groups: wg
inet 10.1.1.200 netmask 0xffffff00 broadcast 10.1.1.255
obsdb ~ root #
si ejecutamos un ping por el túnel desde el host "A":
obsda ~ root # ping -c3 10.1.1.200
PING 10.1.1.200 (10.1.1.200): 56 data bytes
64 bytes from 10.1.1.200: icmp_seq=0 ttl=255 time=1.915 ms
64 bytes from 10.1.1.200: icmp_seq=1 ttl=255 time=1.202 ms
64 bytes from 10.1.1.200: icmp_seq=2 ttl=255 time=1.305 ms
--- 10.1.1.200 ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 1.202/1.474/1.915/0.315 ms
obsda ~ root #
comprobamos que responde, ahora ejecutamos desde el host "B":
obsdb ~ root # ping -c3 10.1.1.100
PING 10.1.1.100 (10.1.1.100): 56 data bytes
64 bytes from 10.1.1.100: icmp_seq=0 ttl=255 time=1.226 ms
64 bytes from 10.1.1.100: icmp_seq=1 ttl=255 time=1.221 ms
64 bytes from 10.1.1.100: icmp_seq=2 ttl=255 time=1.637 ms
--- 10.1.1.100 ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/std-dev = 1.221/1.361/1.637/0.195 ms
obsdb ~ root #
hemos comprobado que el túnel esta funcionando correctamente. Ahora podemos levantar pfsync sobre el mismo.
4.2.5. Configuración de la interfaz del protocolo pfsync
En los puntos:
3.3.1. Conexión con protocolo multicast
3.3.2. Conexión con protocolo unicast
hemos configurado la interfaz pfsync(4) usando el comando ifconfig(8), ahora vamos a usar el formato de archivo hostname.if(5) para que dicha configuración quede persistente ante reinicios del sistema. Recordar que para segurizar la conexión de pfsync, vamos a realizar la misma con protocolo unicast vía túnel VPN Wireguard. Los archivos de configuración son:
Host A:
/etc/hostname.pfsync0
contenido:
syncdev wg1
syncpeer 10.1.1.200
up
Host B:
/etc/hostname.pfsync0
contenido:
syncdev wg1
syncpeer 10.1.1.100
up
al reiniciar veremos la interfaz pfsync0 en cada host, por otro lado recordar que en vez de reiniciar también podemos ejecutar el comando netstart(8) de la siguiente forma:
sh /etc/netstart pfsync0
ahora consultamos las interfaces pfsync en ambos hosts:
Host A:
obsda ~ root # ifconfig pfsync0
pfsync0: flags=41<UP,RUNNING> mtu 1500
index 8 priority 0 llprio 3
encap: parent wg1
pfsync: syncdev: wg1 syncpeer: 10.1.1.200 maxupd: 128 defer: off
groups: carp pfsync
obsda ~ root #
Host B:
obsdb ~ root # ifconfig pfsync0
pfsync0: flags=41<UP,RUNNING> mtu 1500
index 8 priority 0 llprio 3
encap: parent wg1
pfsync: syncdev: wg1 syncpeer: 10.1.1.100 maxupd: 128 defer: off
groups: carp pfsync
obsdb ~ root #
si quisiéramos verificar que entre los hosts están compartiendo estados PF, podemos ejecutar por ejemplo en el host "B" el siguiente comando tcpdump, fijarse que lo ejecutamos sobre la interfaz que transporta pfsync que es la wg1 (interface de Wireguard) y que ponemos de filtro que solo muestre el tráfico del source host 10.1.1.100 que es el host "A".
tcpdump -c 1 -i wg1 -n src host 10.1.1.100
ejemplo de salida:
obsdb ~ root # tcpdump -c 1 -i wg1 -n src host 10.1.1.100
tcpdump: listening on wg1, link-type LOOP
22:39:31.172576 10.1.1.100: PFSYNCv6 len 276
act UPD ST COMP count 3
...
(DF) [tos 0x10]
obsdb ~ root #
con esta salida comprobamos que pfsync esta funcionando.
4.2.6. Configuración de PF en función de los estados a sincronizar
Ahora que ya tenemos todas las interfaces listas podemos sincronizar "estados" entre los dos hosts de firewall:
-
obsda
-
obsdb
recordar que para hacerlo, debemos definir explícitamente con reglas de PF que conexiones vamos a dejar pasar generando un estado que luego será sincronizado con el otro peer. Una configuración inicial de PF podría ser:
Host A: /etc/pf.conf
contenido:
# $OpenBSD: pf.conf,v 1.55 2017/12/03 20:40:04 sthen Exp $
#
# See pf.conf(5) and /etc/examples/pf.conf
block log all
set skip on lo
pass out on vio0
pass in on vio0 proto icmp
pass in on vio0 inet proto tcp from any to 10.0.0.100 port 22
pass in on vio0 inet proto udp from 10.0.0.200 port 51830 to 10.0.0.100 port 51830
pass out on vio1
pass in on vio1 proto icmp
pass in on vio1 proto carp
pass in on vio1 inet proto tcp from 192.168.46.0/24 to 192.168.46.254
pass out on vio2
pass in on vio2 proto icmp
pass in on vio2 proto carp
pass in on vio2 inet proto tcp from 192.168.47.0/24 to 192.168.47.254
pass on wg1 proto pfsync
pass on wg1 proto icmp
match in on vio1 inet proto tcp from any to 192.168.46.254 port 28232 rdr-to 192.168.47.102 port 22
match out on vio2 inet proto tcp from any to 192.168.47.102 nat-to 192.168.47.254
Host B: /etc/pf.conf
contenido:
# $OpenBSD: pf.conf,v 1.55 2017/12/03 20:40:04 sthen Exp $
#
# See pf.conf(5) and /etc/examples/pf.conf
block log all
set skip on lo
pass out on vio0
pass in on vio0 proto icmp
pass in on vio0 inet proto tcp from any to 10.0.0.200 port 22
pass in on vio0 inet proto udp from 10.0.0.100 port 51830 to 10.0.0.200 port 51830
pass out on vio1
pass in on vio1 proto icmp
pass in on vio1 proto carp
pass in on vio1 inet proto tcp from 192.168.46.0/24 to 192.168.46.254
pass out on vio2
pass in on vio2 proto icmp
pass in on vio2 proto carp
pass in on vio2 inet proto tcp from 192.168.47.0/24 to 192.168.47.254
pass on wg1 proto pfsync
pass on wg1 proto icmp
match in on vio1 inet proto tcp from any to 192.168.46.254 port 28232 rdr-to 192.168.47.102 port 22
match out on vio2 inet proto tcp from any to 192.168.47.102 nat-to 192.168.47.254
Como podemos ver la configuración de reglas de filtrado es casi idéntica en ambos hosts. Vamos a describir los distintos bloques y que se quiere lograr con cada uno. Tomaremos de ejemplo la configuración del host "A", como se dijo la configuración del host "B" es idéntica excepto que cambian la IPs de los hosts en tres reglas, empezamos por:
block log all
con la regla anterior decimos que bloqueamos y generamos logs de todo el tráfico que ingresa al firewall, terminara pasando solamente lo que coincida con una regla que este por debajo de esta.
set skip on lo
es recomendable no aplicar reglas de filtrado en la interfaz de loopback, por eso realizamos la configuración anterior.
pass in on vio0 proto icmp
pass in on vio0 inet proto tcp from any to 10.0.0.100 port 22
pass in on vio0 inet proto udp from 10.0.0.200 port 51830 to 10.0.0.100 port 51830
las reglas anteriores la aplicamos en la interfaz de MGMT, también usada para montar el túnel VPN Wireguard. En la primera dejamos pasar ida y vuelta (Input/Output) el protocolo ICMP, o sea que nos tiene que responder ping sin problemas. En la segunda regla habilitamos conexiones ssh a la IP 10.0.0.100 puerto 22. En la última y tercer regla dejamos pasar el tráfico UDP de Wireguard.
pass out on vio1
pass in on vio1 proto icmp
pass in on vio1 proto carp
pass in on vio1 inet proto tcp from 192.168.46.0/24 to 192.168.46.254
pass out on vio2
pass in on vio2 proto icmp
pass in on vio2 proto carp
pass in on vio2 inet proto tcp from 192.168.47.0/24 to 192.168.47.254
las reglas para las interfaces vio1 y vio2 son similares puesto que son las destinadas para montar los pseudo dispositivos:
carp0 - 192.168.46.254
y
carp1 - 192.168.47.254
La primer regla de cada interfaz deja salir (output) todo tipo de tráfico. La segunda regla deja entrar protocolo ICMP. La tercer regla deja entrar protocolo CARP. La última deja entrar todo tipo de tráfico desde la subred de confianza que le corresponde, 192.168.46.0/24 para vio1/carp0 y 192.168.47.0/24 para vio2/carp1.
pass on wg1 proto pfsync
pass on wg1 proto icmp
en estas dos reglas dejamos pasar por el túnel VPN Wireguard el tráfico de protocolos ICMP y pfsync.
match in on vio1 inet proto tcp from any to 192.168.46.254 port 28232 rdr-to 192.168.47.102 port 22
match out on vio2 inet proto tcp from any to 192.168.47.102 nat-to 192.168.47.254
las últimas dos reglas vendrían a ser un ejemplo para probar conexiones persistentes como las de SSH que atraviesan el firewall, generando estados a compartir por pfsync. Estas reglas nos permitirán probar el beneficio de contar con HA en el último ejercicio de esta manual.
4.2.7. Probar la configuración de HA en OpenBSD
Para comprobar que la configuración de HA que realizamos en los puntos anteriores funciona, vamos a ejecutar un ejercicio en donde generamos una conexión con el protocolo SSH, esta conexión se realiza desde una Workstation en la LAN 1 hacia otra Workstation en la LAN 2. Estas Workstations pueden tener instalado cualquier sistema operativo que permita tener un cliente y un servidor del protocolo SSH. Entonces la conexión persistente SSH con la cual abriremos una sesión desde el cliente al servidor sería:
Workstation1(client)<---SSH--->Workstation2(server)
Recordar el diagrama de conectividad en donde observamos que ambas Workstations están en subredes diferentes:
Workstation1 IP:192.168.46.102/24 subred:192.168.46.0/24
Workstation2 IP:192.168.47.102/24 subred:192.168.47.0/24
como vimos en el punto anterior las últimas dos reglas de filtrado:
match in on vio1 inet proto tcp from any to 192.168.46.254 port 28232 rdr-to 192.168.47.102 port 22
match out on vio2 inet proto tcp from any to 192.168.47.102 nat-to 192.168.47.254
sirven para que las conexiones con protocolo SSH dirigidas a la IP 192.168.46.254, puerto 28232, sean redirigidas a la IP 192.168.47.102 con el puerto 22. Como se puede apreciar estamos usando NAT para pasar una conexión SSH desde una IP origen de una subred a una IP destino de otra subred. Con rdr-to cambiamos IP y puertos destinos y con nat-to cambiamos IP origen.
NOTA: El ejercicio podría también realizarse haciendo un forwarding entre las IPs de ambas subredes, ejemplo:
pass in on vport1 from 192.168.46.102 to 192.168.47.102
pass in on vport2 from 192.168.47.102 to 192.168.46.102
Sea de una forma u otra la idea es generar conexiones que PF las registre para que las mismas produzcan "estados" que sean replicados con pfsync entre los hosts de firewall.
A continuación vamos a establecer una conexión SSH contra la Workstation 2, vamos a probar alternar el estado de los hosts CARP (obsda, obsdb) entre Master y Backup, ya sea degradando el host "A" o reiniciando el mismo. La sesión SSH no debe ni cortarse ni colgarse, debe seguir como si nada hubiese pasado.
4.2.7.a. Primer test host "A" en estado Master
El primer test va a ser realizado con el firewall en HA en estado óptimo, host "A" como Master y host "B" como Backup. Vamos usar unos comandos en ambos hosts para comprobar como atraviesa la conexión y como se replican los estados de PF, los comandos son:
pfctl -vsr | grep -A 2 192.168.47.102
pfctl -ss | grep 192.168.47.102:22
tcpdump -i vio1 -n -p src host 192.168.46.102 and dst port 28232
tcpdump -i vio2 -n -p src host 192.168.47.254 and dst host 192.168.47.102 and dst port 22
Por supuesto que también debemos ejecutar en la Workstation 1, un comando para abrir una sesión ssh contra la Workstation 2. Si el sistema operativo de la Workstation 1 es un Unix Like como algún *BSD o un Linux ejecutamos algo como:
ssh -p 28232 192.168.46.254
Entonces abrimos dos sesiones en el host "A" y dejamos corriendo los comandos tcpdump, en una tercera sesión vamos a ejecutar con pfctl los comandos correspondientes. Al realizar la conexión por SSH desde la Workstation 1, los comandos tcpdump nos muestran:
obsda ~ root # tcpdump -i vio1 -n -p src host 192.168.46.102 and dst port 28232
tcpdump: listening on vio1, link-type EN10MB
11:01:20.575489 192.168.46.102.38408 > 192.168.46.254.28232: S 3825979887:3825979887(0) win 65535 <mss 1460,sackOK,timestamp 2530648975 0,nop,wscale 10> (DF)
11:01:20.635651 192.168.46.102.38408 > 192.168.46.254.28232: . ack 1083415399 win 86 <nop,nop,timestamp 2530649071 3639759073> (DF)
11:01:20.636425 192.168.46.102.38408 > 192.168.46.254.28232: P 0:22(22) ack 1 win 86 <nop,nop,timestamp 2530649071 3639759073> (DF)
11:01:20.759861 192.168.46.102.38408 > 192.168.46.254.28232: . ack 22 win 86 <nop,nop,timestamp 2530649195 3639759263> (DF)
11:01:20.765525 192.168.46.102.38408 > 192.168.46.254.28232: P 22:1046(1024) ack 22 win 86 <nop,nop,timestamp 2530649199 3639759263> (DF)
11:01:20.785791 192.168.46.102.38408 > 192.168.46.254.28232: P 1046:1094(48) ack 1142 win 88 <nop,nop,timestamp 2530649221 3639759283> (DF)
...
y:
obsda ~ root # tcpdump -i vio2 -n -p src host 192.168.47.254 and dst host 192.168.47.102 and dst port 22
tcpdump: listening on vio2, link-type EN10MB
11:01:20.576045 192.168.47.254.65160 > 192.168.47.102.22: S 3825979887:3825979887(0) win 65535 <mss 1460,sackOK,timestamp 2530648975 0,nop,wscale 10> (DF)
11:01:20.635670 192.168.47.254.65160 > 192.168.47.102.22: . ack 1083415399 win 86 <nop,nop,timestamp 2530649071 3639759073> (DF)
11:01:20.636445 192.168.47.254.65160 > 192.168.47.102.22: P 0:22(22) ack 1 win 86 <nop,nop,timestamp 2530649071 3639759073> (DF)
11:01:20.759882 192.168.47.254.65160 > 192.168.47.102.22: . ack 22 win 86 <nop,nop,timestamp 2530649195 3639759263> (DF)
11:01:20.765548 192.168.47.254.65160 > 192.168.47.102.22: P 22:1046(1024) ack 22 win 86 <nop,nop,timestamp 2530649199 3639759263> (DF)
11:01:20.785810 192.168.47.254.65160 > 192.168.47.102.22: P 1046:1094(48) ack 1142 win 88 <nop,nop,timestamp 2530649221 3639759283> (DF)
...
la salida es la esperada. El primer tcpdump muestra como se recibe en el host "A" la conexión desde la Workstation 1, se puede ver el inicio de la conexión con la flag "S" que es "SYN" o sea inicio de conexión, después vemos el tráfico habitual, fijarse que el puerto destino es el 28232. El segundo tcpdump muestra la conexión desde otra óptica, desde el mismo firewall ya haciendo el source NAT y teniendo ya como destino la Workstation 2, en este caso el puerto destino ya es el 22.
Con el primer comando pfctl observamos:
obsda ~ root # pfctl -vsr | grep -A 2 192.168.47.102
match in on vio1 inet proto tcp from any to 192.168.46.254 port = 28232 rdr-to 192.168.47.102 port 22
[ Evaluations: 38133 Packets: 224 Bytes: 24210 States: 1 ]
[ Inserted: uid 0 pid 42880 State Creations: 0 ]
--
match out on vio2 inet proto tcp from any to 192.168.47.102 nat-to 192.168.47.254
[ Evaluations: 23346 Packets: 817 Bytes: 83896 States: 1 ]
[ Inserted: uid 0 pid 42880 State Creations: 0 ]
obsda ~ root #
que las reglas de destination NAT "rdr-to" y source NAT "nat-to" tuvieron movimiento, notar la cantidad de paquetes y bytes procesados, pero por sobre todo notar el campo "States" que en ambas reglas aparece con un número 1, eso significa que estas reglas generaron un estado, que es lo que queríamos aparte de sus funciones de Destination NAT y Source NAT, ya que estos estados son los que tendrían que estar en este momento sincronizados gracias a pfsync en el host "B". El siguiente comando pfctl muestra:
obsda ~ root # pfctl -ss | grep 192.168.47.102:22
all tcp 192.168.47.102:22 (192.168.46.254:28232) <- 192.168.46.102:38408 ESTABLISHED:ESTABLISHED
all tcp 192.168.47.254:65160 (192.168.46.102:38408) -> 192.168.47.102:22 ESTABLISHED:ESTABLISHED
obsda ~ root #
que en el host "A" tenemos dos estados persistentes que son las dos conexiones SSH que esta administrando el firewall, una desde la Workstation 1 hacia 192.168.46.254 que como ya vimos es redirigida hacia la Workstation 2 con IP 192.168.47.102. Ahora lo más importante de este ejercicio es comprobar que dichos estados fueron sincronizados al host "B", ejecutamos el mismo comando en ese host:
obsdb ~ root # pfctl -ss | grep 192.168.47.102:22
all tcp 192.168.47.102:22 (192.168.46.254:28232) <- 192.168.46.102:38408 ESTABLISHED:ESTABLISHED
all tcp 192.168.47.254:65160 (192.168.46.102:38408) -> 192.168.47.102:22 ESTABLISHED:ESTABLISHED
obsdb ~ root #
como observamos en la salida del comando en el host "B", los estados si fueron sincronizados a este Host.
4.2.7.b. Segundo test degradar host "A" al estado de Backup
Para este test vamos aplicar lo aprendido en el punto:
2.7.4. Generar conmutación por error usando contador de degradación de CARP
en pocas palabras vamos a degradar el host "A" para que cambie al estado de Backup cosa que el host "B" pase al estado Master, todo ello con la conexión ssh en curso. Si todo esta bien configurado la conexión ssh no tiene que cerrarse ni colgarse, no se tiene que notar nada en la misma, tiene que seguir funcionando de forma normal. Por supuesto podemos realizar el mismo ejercicio apagando o reiniciando el host "A". Procedamos abriendo en ambos Host una sesión en donde dejamos corriendo este comando:
tail -f /var/log/messages
ahora en el host "A" ejecutamos el siguiente comando para proceder a degradarlo al estado de Backup:
obsda ~ root # ifconfig -g carp carpdemote 1
vemos en el log del host "A":
Jun 17 12:03:13 obsda /bsd: carp1: state transition: MASTER -> BACKUP
Jun 17 12:03:13 obsda /bsd: carp0: state transition: MASTER -> BACKUP
que pasó al estado de Backup y en el host "B" vemos:
Jun 17 12:02:57 obsdb /bsd: carp1: state transition: BACKUP -> MASTER
Jun 17 12:02:57 obsdb /bsd: carp0: state transition: BACKUP -> MASTER
que pasó al estado de Master. Lo primero que podemos comprobar es que la sesión ssh en la Workstation 1 no se cortó ni se clavó, sigue respondiendo como si nada hubiera cambiado. Lo otro que podemos ver es que los dos comandos tcpdump que ejecutamos en el punto anterior en el host "A", ya no muestran tráfico de red al movernos por la sesión ssh de la Workstation 1. Pero si ejecutamos esos mismos comandos tcpdump en el host "B", veremos que con solo apretar la tecla enter dentro de la sesión ssh de la Workstation 1, ahora el tráfico de red pasa por ese host:
obsdb ~ root # tcpdump -i vio1 -n -p src host 192.168.46.102 and dst port 28232
tcpdump: listening on vio1, link-type EN10MB
12:07:14.285776 192.168.46.102.38408 > 192.168.46.254.28232: P 3825982698:3825982734(36) ack 1083419388 win 93 <nop,nop,timestamp 2534619389 3643703703> (DF)
12:07:14.289078 192.168.46.102.38408 > 192.168.46.254.28232: . ack 37 win 93 <nop,nop,timestamp 2534619417 3643729523> (DF)
12:07:14.289110 192.168.46.102.38408 > 192.168.46.254.28232: . ack 73 win 93 <nop,nop,timestamp 2534619417 3643729523> (DF)
12:07:44.682321 192.168.46.102.38408 > 192.168.46.254.28232: P 36:72(36) ack 73 win 93 <nop,nop,timestamp 2534649783 3643729523> (DF)
...
y:
obsdb ~ root # tcpdump -i vio2 -n -p src host 192.168.47.254 and dst host 192.168.47.102 and dst port 22
tcpdump: listening on vio2, link-type EN10MB
12:07:14.285898 192.168.47.254.65160 > 192.168.47.102.22: P 3825982698:3825982734(36) ack 1083419388 win 93 <nop,nop,timestamp 2534619389 3643703703> (DF)
12:07:14.289284 192.168.47.254.65160 > 192.168.47.102.22: . ack 37 win 93 <nop,nop,timestamp 2534619417 3643729523> (DF)
12:07:14.289287 192.168.47.254.65160 > 192.168.47.102.22: . ack 73 win 93 <nop,nop,timestamp 2534619417 3643729523> (DF)
12:07:44.682433 192.168.47.254.65160 > 192.168.47.102.22: P 36:72(36) ack 73 win 93 <nop,nop,timestamp 2534649783 3643729523> (DF)
...
todo funciona según lo esperado.
4.2.7.c. Tercer test volver host "A" al estado de Master
Ahora vamos a devolver al estado de Master al host "A" con el siguiente comando:
obsda ~ root # ifconfig -g carp -carpdemote 1
vemos en el log del host "A":
Jun 17 12:11:28 obsda /bsd: carp1: state transition: BACKUP -> MASTER
Jun 17 12:11:28 obsda /bsd: carp0: state transition: BACKUP -> MASTER
en el host "B":
Jun 17 12:11:11 obsdb /bsd: carp1: state transition: MASTER -> BACKUP
Jun 17 12:11:11 obsdb /bsd: carp0: state transition: MASTER -> BACKUP
ahora nuevamente comprobamos que la conexión ssh no se cortó ni se clavó y ademas vemos que los comandos tcpdump en el host "A" volvieron a tener tráfico y en cambio los del host "B" ya no.
5. Sincronización de archivos entre hosts
No podíamos dar por concluido este manual sin ofrecer una recomendación para la sincronización de archivos entre los hosts que están en HA. Habitualmente en una configuración real de un router/firewall en HA vamos a tener archivos que cambian regularmente, estos archivos pueden estar relacionados a reglas de firewall, rutas o cualquier archivo con configuraciones que cambien periódicamente. No ganaríamos nada en tener dos instancias de un router/firewall funcionando en la modalidad HA, si una de ellas esta desactualizada en cuanto a archivos de configuración diarios.
Habitualmente estaríamos realizando configuraciones en el Master, que seria el host "A", pero que pasaría si no sincronizamos esos archivos en donde hacemos configuraciones al host "B" en estado de Backup, pasaría que cuando este tome el lugar de Master nos encontraríamos con el firewall, ruteo y otras configuraciones desactualizadas, entonces ese host "B" no nos sería útil como Master.
Tampoco nos serviría una copia o sincronización, ya sea manual o automática, que sea desde una sola dirección. Habitualmente la haríamos desde el host "A" que es el Master default hacia el host "B" que es el de Backup. Tendríamos un problema cuando el host "B" tome el lugar de Master, no podríamos hacer ningún cambio en los archivos de configuración, ya que los mismos no se propagarán automáticamente hacia el host "A" cuando este reviva y tome el lugar de Master nuevamente. En este caso deberíamos recordar que cambios hicimos y pasarlos manualmente.
Por todo lo comentado anteriormente necesitamos algo que haga la sincronización de forma automática cosa de evitar olvidos y que sea bidireccional.
5.1. Unison
NOTA: En este manual veremos como solucionar el problema de la sincronización bidireccional con el programa Unison, pero esto no significa que no haya otras opciones, la elección de Unison y la forma de funcionamiento que elegimos obedece a ser lo menos invasivo posible a la base del sistema OpenBSD, si bien Unison se instala a través de un port, solo se instala uno el cual aparte no tiene dependencias. Otras opciones para evaluar:
- Syncthing
- Rclone
NOTA: Unison puede enterarse de cambios en archivos y carpetas a través de eventos del kernel. Unison usa para ello un programa helper para monitorear los cambios. Este programa helper mira estos cambios a través de la API inotify() en Linux. La compatibilidad con los BSDs se logra usando libinotify-kqueue, que es una capa por arriba de lo nativo de BSD que es kqueue(). Entonces libinotify-kqueue sirve para hacer compatibles programas que usan inotify en BSD. El problema es que el port de Unison en OpenBSD no tiene la opción para construir el helper unison-fsmonitor, desconozco el porque, por lo tanto, en este manual vamos a configurar Unison para evalúe cada cierto tiempo si hay cambios para sincronizar, dicha evaluación se configura para que se haga cada tantos segundos, minutos, etc.
5.1.1. Sobre los programadores de Unison
El principal programador de Unison es Benjamin C. Pierce, su sitio web personal:
https://www.cis.upenn.edu/~bcpierce/
entrada en la wikipedia:
https://en.wikipedia.org/wiki/Benjamin_C._Pierce
en github:
No solo es creador del programa Unison, si no también un prolífico escritor de documentos relacionados a las ciencias de la computación:
https://www.cis.upenn.edu/~bcpierce/papers/index.shtml
5.2. Instalar Unison
Vamos a instalar Unison en OpenBSD en formato paquete binario con el siguiente comando:
pkg_add -mv unison
la instalación la vamos a realizar en ambos host del cluster de HA, sin embargo como veremos en el siguiente punto, la sincronización siempre se va a disparar desde el host Master por defecto que es el A. Instalamos en ambos porque siempre es aconsejable tener los mismos igualados en cuanto al software instalado. Comandos de instalación:
Host A:
obsda ~ root # pkg_add -mv unison
Update candidates: quirks-7.14 -> quirks-7.14
quirks-7.14 signed on 2024-07-10T21:22:44Z
unison-2.53.4: ok
New and changed readme(s):
/usr/local/share/doc/pkg-readmes/unison
obsda ~ root #
Host B:
obsdb ~ root # pkg_add -mv unison
Update candidates: quirks-7.14 -> quirks-7.14
quirks-7.14 signed on 2024-07-10T21:26:59Z
unison-2.53.4: ok
New and changed readme(s):
/usr/local/share/doc/pkg-readmes/unison
obsdb ~ root #
en OpenBSD siempre que instalamos un paquete es recomendable leer el archivo de novedades, en este caso el archivo es:
/usr/local/share/doc/pkg-readmes/unison
5.3. Diseñar esquema de sincronización
Vamos a diseñar un esquema de sincronización lo más sencillo posible, las condiciones serían:
- La sincronización se debe realizar cada un minuto.
- La sincronización la dispara el usuario syncha.
- El comando de sincronización se disparará desde el host Master default que en nuestro caso es el "A".
- La sincronización es de forma bidireccional "Host A <--> Host B". Si hay conflictos se resuelve con preferencia para los archivos más nuevos en su timestamp.
- Independientemente del estado de CARP (Maestro o Backup) en los hosts, la sincronización se realiza de la misma forma establecida en el punto 4.
- Si ambos host están encendidos pero no tienen comunicación entre ellos, una vez que la misma vuelva se realiza la sincronización.
- Si un host se apaga se sincronizará una vez que se encienda.
5.4. Configurar Unison
Necesitamos que el proceso de Unison se levante automáticamente al encender el host "A", que es el Master default y que ademas ejecute cada 60 segundos la sincronización, todo esto se debe realizar con un usuario dedicado a tal fin. A continuación analizamos los pasos para realizar esta configuración.
5.4.1. Usuario para ejecutar la sincronización
Vamos a crear el usuario "syncha" en ambos host para realizar la sincronización, en este manual vamos a proponer un ejemplo de sincronización de archivos de Packet Filter (PF), o sea que son archivos que tienen reglas de filtrado, como es lógico a estos archivos solo puede acceder para leerlos el usuario root y usuarios del grupo wheel, por tal motivo al usuario syncha lo vamos a poner de agregado en ese grupo. Teniendo en cuenta que la sincronización podría llegar a usar algo más de recursos de lo normal, también vamos a agregar al usuario en la clase staff. Procedamos con los pasos de creación del usuario.
5.4.1.a. Creación de usuario
El comando que vamos a usar para la creación del usuario syncha es:
adduser
la primera vez que ejecutamos este comando nos hace unas preguntas para crear un archivo de perfil para la creación de usuarios, dicho archivo es:
/etc/adduser.conf
a continuación un ejemplo de como ejecutar este comando en el host "A":
obsda ~ aali $ doas adduser
Couldn't find /etc/adduser.conf: creating a new adduser configuration file
Reading /etc/shells
Enter your default shell: bash csh git-shell ksh nologin sh
[ksh]:
Your default shell is: ksh -> /bin/ksh
Default login class: authpf bgpd daemon default pbuild staff syncthing unbound xenodm
[default]:
Enter your default HOME partition: [/home]:
Copy dotfiles from: /etc/skel no [/etc/skel]:
Send welcome message?: /path/file default no [no]:
Do not send message(s)
Prompt for passwords by default (y/n) [y]:
Default encryption method for passwords: auto blowfish [auto]:
Use option ''-silent'' if you don't want to see all warnings and questions.
Reading /etc/shells
Check /etc/master.passwd
Check /etc/group
Ok, let's go.
Don't worry about mistakes. There will be a chance later to correct any input.
Enter username []: syncha
Enter full name []: syncha
Enter shell bash csh git-shell ksh nologin sh [ksh]:
Uid [1001]:
Login group syncha [syncha]:
Login group is ''syncha''. Invite syncha into other groups: guest no
[no]: wheel
Login class authpf bgpd daemon default pbuild staff syncthing unbound xenodm
[default]: staff
Enter password []:
Enter password again []:
Name: syncha
Password: ****
Fullname: syncha
Uid: 1001
Gid: 1001 (syncha)
Groups: syncha wheel
Login Class: staff
HOME: /home/syncha
Shell: /bin/ksh
OK? (y/n) [y]: y
Added user ''syncha''
Copy files from /etc/skel to /home/syncha
Add another user? (y/n) [y]: n
Goodbye!
obsda ~ aali $
Como ya dijimos inicialmente en este punto, vamos a crear el usuario syncha en ambos hosts, recordar ejecutar este comando de la misma forma en el host "B". Una vez que hayamos creado el usuario en ambos host probamos el ingreso:
workstation ~ aali $ ssh syncha@10.0.0.100
syncha@10.0.0.100's password:
Last login: Thu Jul 11 22:17:12 2024 from 10.0.0.1
OpenBSD 7.5 (GENERIC) #79: Wed Mar 20 15:33:49 MDT 2024
Welcome to OpenBSD: The proactively secure Unix-like operating system.
Please use the sendbug(1) utility to report bugs in the system.
Before reporting a bug, please try to reproduce it with the latest
version of the code. With bug reports, please try to ensure that
enough information to reproduce the problem is enclosed, and if a
known fix for it exists, include that as well.
obsda$
obsda$ id
uid=1001(syncha) gid=1001(syncha) groups=1001(syncha), 0(wheel)
obsda$
ingresamos perfecto.
5.4.1.b. Generación del par de llaves ssh
Ahora vamos a generar el par de llaves ssh (pública y privada) con el siguiente comando:
obsda$ ssh-keygen
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/syncha/.ssh/id_ed25519):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/syncha/.ssh/id_ed25519
Your public key has been saved in /home/syncha/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:vSDboWRNwjb0AvKGhgK4yAkTN3HmzUm7lViB+cTB2CM syncha@obsda.fortix.com.ar
The key's randomart image is:
+--[ED25519 256]--+
|+.=.+ oB+o |
|=o O BE**. |
|*o+ + %+=. |
|++ . . O.. |
| = S . |
| o = o . |
| o . . |
| |
| |
+----[SHA256]-----+
obsda$
Cuando se nos pregunte por una passphrase, no vamos a cargar ninguna, para pasar por alto esta pregunta solo presionamos la tecla enter. Esto es necesario puesto que si protegeríamos la private key con una passphrase, cada vez que se requiera una validación nos solicitaría la misma, usar de esta forma la validación SSH no es posible con procedimientos automáticos como el que necesitamos configurar. En el momento de escribir este manual se usa el algoritmo de firma digital de curva de Edwards (EdDSA) ed25519. Recordar ejecutar el comando anterior en ambos hosts. Esto genera los archivos:
obsda$ ls -la .ssh/
total 16
drwx------ 2 syncha syncha 512 Jul 11 22:48 .
drwxr-xr-x 3 syncha syncha 512 Jul 11 22:33 ..
-rw------- 1 syncha syncha 0 Jul 11 22:33 authorized_keys
-rw------- 1 syncha syncha 419 Jul 11 22:47 id_ed25519
-rw-r--r-- 1 syncha syncha 108 Jul 11 22:47 id_ed25519.pub
obsda$
5.4.1.c. Método de autenticación ssh por publickey entre ambos hosts
Ahora vamos agregar confianza entre ambos hosts, dejando que el usuario syncha pueda iniciar sesión en ambos sentidos:
hostA(syncha)<--SSH-->(syncha)hostB
El método de autenticación que vamos a usar no será "password" sino "publickey", con esto vamos a lograr que cuando se ejecute la sincronización no nos pida un password de forma interactiva, lo cual sería ineficiente puesto que tendríamos que disparar la sincronización de forma manual en una sesión y no de forma automática con una tarea en el planificador Cron. Para lograr esto debemos copiar la llave pública del host "A" en el host "B" y viceversa, esta copia de la llave pública se ubica en el archivo:
~/.ssh/authorized_keys
podemos realizar esto de varias formas, manualmente es una, por ejemplo si empezamos con el host "A", abrimos una sesión en el mismo, editamos el archivo:
~/.ssh/id_ed25519.pub
y copiamos su contenido, el cual pegamos en otra sesión, en otra pantalla en donde estemos editando en el host "B" el archivo:
authorized_keys
Luego hacemos lo mismo pero desde el host "B" al host "A". Otra forma de realizar esto es instalar el script "ssh-copy-id" con el comando:
obsda ~ root # pkg_add -mv ssh-copy-id
Update candidates: quirks-7.14 -> quirks-7.14
quirks-7.14 signed on 2024-07-12T15:17:11Z
ssh-copy-id-8.8pl1: ok
obsda ~ root #
con este script ya instalado en el sistema, la copia se realiza simplemente ejecutando:
Host A:
obsda$ ssh-copy-id -i .ssh/id_ed25519.pub obsdb.fortix.com.ar
/usr/local/bin/ssh-copy-id: INFO: Source of key(s) to be installed: ".ssh/id_ed25519.pub"
The authenticity of host 'obsdb.fortix.com.ar (10.0.0.200)' can't be established.
ED25519 key fingerprint is SHA256:lE9I56sG7MqG3JDBJspftJqWfFquizrt0uH1IZ5PLYA.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
/usr/local/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/local/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
syncha@obsdb.fortix.com.ar's password:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'obsdb.fortix.com.ar'"
and check to make sure that only the key(s) you wanted were added.
obsda$
Host B:
obsdb$ ssh-copy-id -i .ssh/id_ed25519.pub obsda.fortix.com.ar
/usr/local/bin/ssh-copy-id: INFO: Source of key(s) to be installed: ".ssh/id_ed25519.pub"
The authenticity of host 'obsda.fortix.com.ar (10.0.0.100)' can't be established.
ED25519 key fingerprint is SHA256:ksgShTKABatQvzLMbzuTdLbj4S/gFrrAFlOvYLXMJlU.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
/usr/local/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/local/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
syncha@obsda.fortix.com.ar's password:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'obsda.fortix.com.ar'"
and check to make sure that only the key(s) you wanted were added.
obsdb$
ahora probamos ingresar desde un host al otro.
Desde host "A" hacia el host "B":
obsda$ ssh obsdb.fortix.com.ar
Last login: Sun Jul 14 19:24:33 2024 from 10.0.0.1
OpenBSD 7.5 (GENERIC.MP) #138: Wed Mar 20 19:42:15 MDT 2024
Welcome to OpenBSD: The proactively secure Unix-like operating system.
Please use the sendbug(1) utility to report bugs in the system.
Before reporting a bug, please try to reproduce it with the latest
version of the code. With bug reports, please try to ensure that
enough information to reproduce the problem is enclosed, and if a
known fix for it exists, include that as well.
obsdb$
Desde host "B" hacia el host "A":
obsdb$ ssh obsda.fortix.com.ar
Last login: Sun Jul 14 19:40:55 2024 from 10.0.0.1
OpenBSD 7.5 (GENERIC) #79: Wed Mar 20 15:33:49 MDT 2024
Welcome to OpenBSD: The proactively secure Unix-like operating system.
Please use the sendbug(1) utility to report bugs in the system.
Before reporting a bug, please try to reproduce it with the latest
version of the code. With bug reports, please try to ensure that
enough information to reproduce the problem is enclosed, and if a
known fix for it exists, include that as well.
obsda$
ambos accesos ssh con publickey funcionan.
5.4.2. Configurar en Unison una tarea de sincronización
Podemos ejecutar Unison de dos formas, una es cargar los argumentos necesarios en la linea de comandos, ejemplo:
unison -logfile /var/log/unison.log -prefer newer -auto -batch -repeat 60 /etc/pf/clients/ ssh://obsdb//etc/pf/clients/
o generar un archivo tipo perfil por cada sincronización, para luego en la linea de comandos ejecutar Unison con el perfil que necesitamos usar, ejemplo:
unison syncha
Entonces vamos a crear un archivo de perfil, el mismo se ubica en:
~/.unison/syncha.prf
con el siguiente contenido:
label = syncha
prefer = newer
auto = true
batch = true
repeat = 60
root = /etc/pf/clients/
root = ssh://obsdb//etc/pf/clients/
logfile = /var/log/unison.log
sshargs = -oIdentityFile=/home/syncha/.ssh/id_ed25519
#debug=all
notar que los archivos de perfiles se guardan por defecto en el directorio:
~/.unison/
Ejecutemos la primer sincronización y veamos el resultado:
obsda$ unison syncha
Unison 2.53.4 (ocaml 4.14.1): Contacting server...
Connected [//obsda.fortix.com.ar//etc/pf/clients -> //obsdb.fortix.com.ar//etc/pf/clients]
Looking for changes
Warning: No archive files were found for these roots, whose canonical names are:
/etc/pf/clients
//obsdb.fortix.com.ar//etc/pf/clients
This can happen either
because this is the first time you have synchronized these roots,
or because you have upgraded Unison to a new version with a different
archive format.
Update detection may take a while on this run if the replicas are
large.
Unison will assume that the 'last synchronized state' of both replicas
was completely empty. This means that any files that are different
will be reported as conflicts, and any files that exist only on one
replica will be judged as new and propagated to the other replica.
If the two replicas are identical, then no changes will be reported.
If you see this message repeatedly, it may be because one of your machines
is getting its address from DHCP, which is causing its host name to change
between synchronizations. See the documentation for the UNISONLOCALHOSTNAME
environment variable for advice on how to correct this.
Waiting for changes from server
Reconciling changes
file ----> mysql01.conf
file ----> mysql02.conf
file ----> owncloud01.conf
file ----> proxy01.conf
file ----> proxy02.conf
file ----> proxy03.conf
file ----> webserver01.conf
file ----> webserver02.conf
file ----> webserver03.conf
file ----> webserver04.conf
file ----> webserver05.conf
11 items will be synced, 0 skipped
1.6 KiB to be synced from local to obsdb.fortix.com.ar
0 B to be synced from obsdb.fortix.com.ar to local
Propagating updates
Unison 2.53.4 (ocaml 4.14.1) started propagating changes at 22:25:47.85 on 14 Jul 2024
[BGN] Copying mysql01.conf from /etc/pf/clients to //obsdb.fortix.com.ar//etc/pf/clients
[BGN] Copying mysql02.conf from /etc/pf/clients to //obsdb.fortix.com.ar//etc/pf/clients
[BGN] Copying owncloud01.conf from /etc/pf/clients to //obsdb.fortix.com.ar//etc/pf/clients
[BGN] Copying proxy01.conf from /etc/pf/clients to //obsdb.fortix.com.ar//etc/pf/clients
[BGN] Copying proxy02.conf from /etc/pf/clients to //obsdb.fortix.com.ar//etc/pf/clients
[BGN] Copying proxy03.conf from /etc/pf/clients to //obsdb.fortix.com.ar//etc/pf/clients
[BGN] Copying webserver01.conf from /etc/pf/clients to //obsdb.fortix.com.ar//etc/pf/clients
[BGN] Copying webserver02.conf from /etc/pf/clients to //obsdb.fortix.com.ar//etc/pf/clients
[BGN] Copying webserver03.conf from /etc/pf/clients to //obsdb.fortix.com.ar//etc/pf/clients
[BGN] Copying webserver04.conf from /etc/pf/clients to //obsdb.fortix.com.ar//etc/pf/clients
[BGN] Copying webserver05.conf from /etc/pf/clients to //obsdb.fortix.com.ar//etc/pf/clients
[END] Copying mysql01.conf
[END] Copying mysql02.conf
[END] Copying owncloud01.conf
[END] Copying proxy01.conf
[END] Copying proxy02.conf
[END] Copying proxy03.conf
[END] Copying webserver01.conf
[END] Copying webserver02.conf
[END] Copying webserver03.conf
[END] Copying webserver04.conf
[END] Copying webserver05.conf
Unison 2.53.4 (ocaml 4.14.1) finished propagating changes at 22:25:47.96 on 14 Jul 2024, 0.106 s
Saving synchronizer state
Synchronization complete at 22:25:47 (11 items transferred, 0 skipped, 0 failed)
Sleeping for 60 seconds...
como observamos la sincronización funcionó con éxito. Un detalle a tener en cuenta es que al ser la primera vez que sincronizamos estas carpetas, se nos informa lo siguiente:
Looking for changes
Warning: No archive files were found for these roots, whose canonical names are:
/etc/pf/clients
//obsdb.fortix.com.ar//etc/pf/clients
This can happen either
because this is the first time you have synchronized these roots,
or because you have upgraded Unison to a new version with a different
archive format.
este mensaje aparece porque es la primera vez que sincronizamos estas carpetas, en las próximas sincronizaciones ya no aparecerá. Tener en cuenta que aparte de los perfiles de sincronización, en el directorio "~/.unison/" también se guardan archivos de control de las sincronizaciones, con los cuales Unison puede hacer el seguimiento de las mismas, entonces en el normal funcionamiento nunca deberíamos borrar el directorio ~/.unison/ ni los archivos que el programa genera adentro. Ejemplo de como quedo el directorio en ambos hosts con la primer sincronización:
Host A:
obsda$ cd .unison/
obsda$ ls -la
total 20
drwx------ 2 syncha syncha 512 Jul 14 22:25 .
drwxr-xr-x 5 syncha syncha 512 Jul 14 22:24 ..
-rw------- 1 syncha syncha 1740 Jul 14 22:25 ar5e8abc5af841f5a0ade604c2f0956860
-rw------- 1 syncha syncha 862 Jul 14 22:25 fp5e8abc5af841f5a0ade604c2f0956860
-rw-r--r-- 1 syncha syncha 301 Jul 14 22:15 syncha.prf
obsda$
Host B:
obsdb$ cd .unison/
obsdb$ ls -la
total 16
drwx------ 2 syncha syncha 512 Jul 14 22:27 .
drwxr-xr-x 5 syncha syncha 512 Jul 14 22:03 ..
-rw------- 1 syncha syncha 1718 Jul 14 22:25 ar8723b97164c76ecba6400f55a6e69858
-rw------- 1 syncha syncha 34 Jul 14 22:25 fp8723b97164c76ecba6400f55a6e69858
obsdb$
5.4.3. Script de arranque RC para Unison
Si el lector no esta al tanto del sistema de arranque RC en OpenBSD puede hacer una rápida lectura a:
https://www.fortix.com.ar/sistemadearranquercenopenbsd/
con unos conocimientos básicos ya podemos crear un script RC para que Unison arranque automáticamente en el tiempo de boot. Entonces vamos a crear el script, abrir con el editor el nuevo archivo:
/etc/rc.d/unison
y agregar el siguiente contenido:
#!/bin/ksh
daemon="/usr/local/bin/unison syncha &"
daemon_user="syncha"
. /etc/rc.d/rc.subr
rc_reload=NO
rc_cmd $1
recordar darle los permisos de ejecución con:
chmod 555 /etc/rc.d/unison
y por último en el archivo:
/etc/rc.conf.local
agregar la siguiente linea:
unison=
ahora al reiniciar el sistema, observaríamos en el log de Unison:
/var/log/unison.log
las lineas:
Unison 2.53.4 (ocaml 4.14.1) log started at 2024-07-15 at 13:50:27
Roots:
/etc/pf/clients
ssh://obsdb//etc/pf/clients
y el proceso cargado:
obsda ~ root # ps xau | grep unison
syncha 17180 0.0 0.5 7596 8004 p2 S 2:03PM 0:00.01 /usr/local/bin/unison syncha
syncha 27179 0.0 0.3 1684 4604 p2 Sp 2:03PM 0:00.05 ssh obsdb -e none -oIdentityFile=/home/syncha/.ssh/id_ed25519 unison -server __ne
obsda ~ root #
6. Listas de referencias
[1] https://www.openbsd.org/faq/pf/carp.html
[2] https://man.openbsd.org/carp
[3] https://man.openbsd.org/pfsync.4
[4] https://man.openbsd.org/ifconfig#CARP
[5] https://en.wikipedia.org/wiki/Common_Address_Redundancy_Protocol
[6] https://github.com/jedisct1/UCarp
[7] https://manpages.ubuntu.com/manpages/bionic/man8/ucarp.8.html