Carp + Pfsync no FreeBSD 10.1
E ai galera aqui vamos fazer a configuração do Carp e o Pfsync no FreeBSD 10.1 a ideia principal aqui é disponibilizar um ip compartilhado para os servidores trabalharem com alta disponibilidade o equivalente em Linux seria o Heartbeat e vamos também habilitar o pfsync que vai fazer a replicação da tabela de estados do PF (Packet Filter) no servidor slave.
A grande diferença que eu notei entre a implementação no Linux e no FreeBSD ou OpenBSD foi a replicação da tabela de estados, pois caso um cliente esteja acessando algo que contenha um token por exemplo de autenticação ele não vai precisar abrir uma nova conexão quando um servidor cair e o outro assumir pois a tabela de estados vai ser a mesma em ambos os servidores.
Se estiver trabalhando com VMs para testar não se esqueça de habilitar o modo promiscuous na interface, caso contrário o sistema pode não funcionar corretamente.
Informações sobre o Carp: Introduction to CARP (English)
Informações sobre o Pfsync: Introduction to pfsync (English)
Prepara o seu kernel com o seguinte how-to Preparação do Kernel
O que vamos utilizar:
- FreeBSD 10.1 Master:
- IP LAN: 192.168.1.70 Interface: em0 Função: pfsync
- IP DMZ: 192.168.56.70 Interface: em1 Função: carp IP carp: 192.168.56.100
- IP WAN: 192.168.57.70 Interface: em2 Função: carp IP carp: 192.168.57.100
- FreeBSD 10.1 Slave:
- IP LAN: 192.168.1.80 Interface: em0 Função: pfsync
- IP DMZ: 192.168.56.80 Interface: em1 Função: carp IP carp: 192.168.56.100
- IP WAN: 192.168.57.80 Interface: em2 Função: carp IP carp: 192.168.57.100
Configuração do Servidor Master
Como o carp trabalha enviando pacotes em multicast vamos precisar informar uma senha para que haja a troca de informações entre os nodos.
Caso esteja preocupado com a segurança da transferencia dos dados o carp utilizar a criptografia SHA-1 HMAC para a autenticação da senha.
Vamos gerar uma senha para a interface DMZ
cat /dev/random | hexdump -n 30| cut -d \ -f 2-| head -n 1 | tr -d " " 31adfd738f958d9f24e13a761ada65ba
Vamos gerar uma senha para a interface WAN
cat /dev/random | hexdump -n 30| cut -d \ -f 2-| head -n 1 | tr -d " " 64e537080834c4774f60d920dbc8d1f1
Agora vamos a configuração do carp no servidor Master a configuração é bem simples.
vim /etc/rc.conf [...] ### GW gateway_enable="YES" #Habilita o servidor trabalhar como GW ### PF pf_enable="YES" # Habilita o PF pf_rules="/etc/pf.conf" # Definição de regras para o pf pf_flags="" # Flags adicionais para o pfctl inicializar caso necessário pflog_enable="YES" # Habilita o pflog pflog_logfile="/var/log/pflog" # Local de armazenamento dos logs pflog_flags="" # Flags adicionais para o pflogd inicializar caso necessário ### CARP ifconfig_em1_alias0="vhid 10 advskew 0 pass 31adfd738f958d9f24e13a761ada65ba alias 192.168.56.100/32" ifconfig_em2_alias0="vhid 16 advskew 0 pass 64e537080834c4774f60d920dbc8d1f1 alias 192.168.57.100/32" ### PFSYNC pfsync_enable="YES" pfsync_syncdev="em0"
Explicando configuração do carp:
- vhid: Identificação do host para o carp tem que ser a mesma identificação e mesma interface em ambos os hosts
- advskew: Quando menor o número maior a prioridade para se tornar master
- pass: Especifica uma senha para a comunicação do carp a senha tem que ser a mesma em ambos os servidores.
- Alias: Especifica que estamos atribuindo um ip adicional a interface
Configuração do Servidor Slave
Agora vamos configuração o carp no servidor Slave a configuração é bem parecida o que vai mudar vai ser o advskew que é quem vai determinar quem vai ser o servidor master, como este servidor vai ser um servidor slave ele vai ter um valor maior do que o servidor Master.
vim /etc/rc.conf [...] ### GW gateway_enable="YES" #Habilita o servidor trabalhar como GW ### PF pf_enable="YES" # Habilita o PF pf_rules="/etc/pf.conf" # Definição de regras para o pf pf_flags="" # Flags adicionais para o pfctl inicializar caso necessário pflog_enable="YES" # Habilita o pflog pflog_logfile="/var/log/pflog" # Local de armazenamento dos logs pflog_flags="" # Flags adicionais para o pflogd inicializar caso necessário ### CARP ifconfig_em1_alias0="vhid 10 advskew 128 pass 31adfd738f958d9f24e13a761ada65ba alias 192.168.56.100/32" ifconfig_em2_alias0="vhid 16 advskew 128 pass 64e537080834c4774f60d920dbc8d1f1 alias 192.168.57.100/32" ### PFSYNC pfsync_enable="YES" pfsync_syncdev="em0"
Explicando configuração do carp:
- vhid: Identificação do host para o carp tem que ser a mesma identificação e mesma interface em ambos os hosts
- advskew: Quando menor o número maior a prioridade para se tornar master
- pass: Especifica uma senha para a comunicação do carp a senha tem que ser a mesma em ambos os servidores.
- Alias: Especifica que estamos atribuindo um ip adicional a interface
Configuração mínima para o PF
Configuração mímina para o pf para que ele deixe os protocolos carp e pfsync trabalharem.
Precisamos desta configuração em ambos os servidores.
vim /etc/pf.conf ### Interfaces ext_if="em2" int_if="em1" pf_sync="em0" ### Filtering pass quick on $pf_sync proto pfsync keep state (no-sync) pass quick on $ext_if proto carp keep state (no-sync) pass quick on $int_if proto carp keep state (no-sync)
Agora precisamos so reiniciar os servidores para a alta diponibilidade estar ativa.
Testando
Vamos checar a configuração de rede do servidor Master
ifconfig em0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500 options=9b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM> ether 08:00:27:52:b3:f2 inet 192.168.1.70 netmask 0xffffff00 broadcast 192.168.1.255 nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL> media: Ethernet autoselect (1000baseT <full-duplex>) status: active em1: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> metric 0 mtu 1500 options=9b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM> ether 08:00:27:01:c3:56 inet 192.168.56.70 netmask 0xffffff00 broadcast 192.168.56.255 inet 192.168.56.100 netmask 0xffffffff broadcast 192.168.56.100 vhid 10 nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL> media: Ethernet autoselect (1000baseT <full-duplex>) status: active carp: MASTER vhid 10 advbase 1 advskew 0 em2: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> metric 0 mtu 1500 options=9b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM> ether 08:00:27:2c:ec:3f inet 192.168.57.70 netmask 0xffffff00 broadcast 192.168.57.255 inet 192.168.57.100 netmask 0xffffffff broadcast 192.168.57.100 vhid 16 nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL> media: Ethernet autoselect (1000baseT <full-duplex>) status: active carp: MASTER vhid 16 advbase 1 advskew 0 pflog0: flags=141<UP,RUNNING,PROMISC> metric 0 mtu 33160 pfsync0: flags=41<UP,RUNNING> metric 0 mtu 1500 pfsync: syncdev: em0 syncpeer: 224.0.0.240 maxupd: 128 defer: off lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384 options=600003<RXCSUM,TXCSUM,RXCSUM_IPV6,TXCSUM_IPV6> inet6 ::1 prefixlen 128 inet6 fe80::1%lo0 prefixlen 64 scopeid 0x6 inet 127.0.0.1 netmask 0xff000000 nd6 options=21<PERFORMNUD,AUTO_LINKLOCAL>
Note que nas interaces em1 e em2 temos a ultima linha carp: MASTER informando que o nosso servidor é o master do pool.
Note também que na interface pfsync0 agora temos syncdev em0 que é a nossa interface de replicação do pfsync.
Podemos checar o status das interfaces com o seguinte comando.
systat -ifstat 1 /0 /1 /2 /3 /4 /5 /6 /7 /8 /9 /10 Load Average || Interface Traffic Peak Total pfsync0 in 0.000 KB/s 0.000 KB/s 0.082 KB out 0.000 KB/s 0.000 KB/s 0.199 KB em2 in 0.000 KB/s 0.484 KB/s 1.663 KB out 0.065 KB/s 0.067 KB/s 6.682 KB em1 in 0.000 KB/s 0.484 KB/s 1.663 KB out 0.065 KB/s 0.067 KB/s 6.682 KB em0 in 0.062 KB/s 0.374 KB/s 11.193 KB out 0.186 KB/s 0.733 KB/s 11.887 KB
Podemos checar o trabalho da nossa interface pfsync com o pftop se não estiver instalado podemos instalar ele da seguinte forma
pkg install pftop
Agora podemos chamar ele comando o comando pftop
pftop pfTop: Up State 1-2/2, View: default, Order: none, Cache: 10000 14:26:26 PR DIR SRC DEST STATE AGE EXP PKTS BYTES carp Out 192.168.56.70:0 224.0.0.18:0 SINGLE:NO_TRAFFIC 00:07:37 00:00:30 410 22960 carp Out 192.168.57.70:0 224.0.0.18:0 SINGLE:NO_TRAFFIC 00:07:37 00:00:30 410 22960
Agora vamos testar os nossos ips do carp utilizado na DMZ
ping 192.168.56.100 -c 3 PING 192.168.56.100 (192.168.56.100) 56(84) bytes of data. 64 bytes from 192.168.56.100: icmp_seq=1 ttl=64 time=0.415 ms 64 bytes from 192.168.56.100: icmp_seq=2 ttl=64 time=0.429 ms 64 bytes from 192.168.56.100: icmp_seq=3 ttl=64 time=0.331 ms --- 192.168.56.100 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 1998ms rtt min/avg/max/mdev = 0.331/0.391/0.429/0.048 ms
Agora vamos testar os nossos ips do carp utilizado na WAN
ping 192.168.57.100 -c 3 PING 192.168.57.100 (192.168.57.100) 56(84) bytes of data. 64 bytes from 192.168.57.100: icmp_seq=1 ttl=64 time=0.438 ms 64 bytes from 192.168.57.100: icmp_seq=2 ttl=64 time=0.451 ms 64 bytes from 192.168.57.100: icmp_seq=3 ttl=64 time=0.516 ms --- 192.168.57.100 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 1998ms rtt min/avg/max/mdev = 0.438/0.468/0.516/0.038 ms
Como podemos notar está tudo ok.
Agora vamos checar a configuração do servidor Slave.
Vamos checar as interfaces de rede.
ifconfig em0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500 options=9b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM> ether 08:00:27:93:50:a5 inet 192.168.1.80 netmask 0xffffff00 broadcast 192.168.1.255 nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL> media: Ethernet autoselect (1000baseT <full-duplex>) status: active em1: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> metric 0 mtu 1500 options=9b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM> ether 08:00:27:01:90:a4 inet 192.168.56.80 netmask 0xffffff00 broadcast 192.168.56.255 inet 192.168.56.100 netmask 0xffffffff broadcast 192.168.56.100 vhid 10 nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL> media: Ethernet autoselect (1000baseT <full-duplex>) status: active carp: BACKUP vhid 10 advbase 1 advskew 128 em2: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> metric 0 mtu 1500 options=9b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM> ether 08:00:27:b1:6f:dc inet 192.168.57.80 netmask 0xffffff00 broadcast 192.168.57.255 inet 192.168.57.100 netmask 0xffffffff broadcast 192.168.57.100 vhid 16 nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL> media: Ethernet autoselect (1000baseT <full-duplex>) status: active carp: BACKUP vhid 16 advbase 1 advskew 128 pflog0: flags=141<UP,RUNNING,PROMISC> metric 0 mtu 33160 pfsync0: flags=41<UP,RUNNING> metric 0 mtu 1500 pfsync: syncdev: em0 syncpeer: 224.0.0.240 maxupd: 128 defer: off lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384 options=600003<RXCSUM,TXCSUM,RXCSUM_IPV6,TXCSUM_IPV6> inet6 ::1 prefixlen 128 inet6 fe80::1%lo0 prefixlen 64 scopeid 0x6 inet 127.0.0.1 netmask 0xff000000 nd6 options=21<PERFORMNUD,AUTO_LINKLOCAL>
Assim como o servidor Master a configuração do carp esta ok porem ao invés de MASTER na linha carp temos BACKUP este servidor vai assumir a função de master quando o outros servidor ficar down.
Agora vamos testar o failover.
Vamos deixar pingando no servidor master o ip 192.168.56.100 e vamos baixar a interface em1 para testar
Agora vamos checar no servidor slave se ele agora está com a função de master
ifconfig em1 em1: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> metric 0 mtu 1500 options=9b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM> ether 08:00:27:01:90:a4 inet 192.168.56.80 netmask 0xffffff00 broadcast 192.168.56.255 inet 192.168.56.100 netmask 0xffffffff broadcast 192.168.56.100 vhid 10 nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL> media: Ethernet autoselect (1000baseT <full-duplex>) status: active carp: MASTER vhid 10 advbase 1 advskew 128
Note que agora o nosso servido slave assumiu a função de master para o ip.
Agora vamos analisar os pings.
ping 192.168.56.100 PING 192.168.56.100 (192.168.56.100) 56(84) bytes of data. 64 bytes from 192.168.56.100: icmp_seq=1 ttl=64 time=0.363 ms 64 bytes from 192.168.56.100: icmp_seq=2 ttl=64 time=0.399 ms 64 bytes from 192.168.56.100: icmp_seq=3 ttl=64 time=0.342 ms 64 bytes from 192.168.56.100: icmp_seq=4 ttl=64 time=0.358 ms 64 bytes from 192.168.56.100: icmp_seq=5 ttl=64 time=0.387 ms 64 bytes from 192.168.56.100: icmp_seq=8 ttl=64 time=0.701 ms 64 bytes from 192.168.56.100: icmp_seq=9 ttl=64 time=0.333 ms 64 bytes from 192.168.56.100: icmp_seq=10 ttl=64 time=0.444 ms 64 bytes from 192.168.56.100: icmp_seq=11 ttl=64 time=0.321 ms 64 bytes from 192.168.56.100: icmp_seq=12 ttl=64 time=0.341 ms 64 bytes from 192.168.56.100: icmp_seq=13 ttl=64 time=0.348 ms 64 bytes from 192.168.56.100: icmp_seq=14 ttl=64 time=0.321 ms 64 bytes from 192.168.56.100: icmp_seq=15 ttl=64 time=0.381 ms 64 bytes from 192.168.56.100: icmp_seq=16 ttl=64 time=0.413 ms ^C --- 192.168.56.100 ping statistics --- 16 packets transmitted, 14 received, 12% packet loss, time 14997ms rtt min/avg/max/mdev = 0.321/0.389/0.701/0.094 ms
Como podemos notar tivemos um atraso nos pacotes de 314ms na troca de função entre os servidores.
Agora se a interface em1 do servidor master subir novamente o slave não vai devolver a função para que isso aconteça precisamos habilitar uma flag no kernel.
Está flag deve ser habilitada somente o no servidor master.
vim /etc/sysctl.conf [...] #Habilitando a devolução da função net.inet.carp.preempt=1
Podemos efetuar a mesma configuração temporariamente da seguinte forma
sysctl -w net.inet.carp.preempt=1
Agora já podemos checar o nosso servidor master que a função deve ter retornado para ele.
ifconfig em1 em1: flags=8943<UP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST> metric 0 mtu 1500 options=9b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM> ether 08:00:27:01:c3:56 inet 192.168.56.70 netmask 0xffffff00 broadcast 192.168.56.255 inet 192.168.56.100 netmask 0xffffffff broadcast 192.168.56.100 vhid 10 nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL> media: Ethernet autoselect (1000baseT <full-duplex>) status: active carp: MASTER vhid 10 advbase 1 advskew 0