Usando Mandos com OpenVPN para decriptar Linux com LUKS
mandos-client_1.7.15-1_amd64.debVocê quer proteger o HD de seu computador ou servidor Linux totalmente com criptografia. Para isso, você faz uma FDE (Full Disk Encryption/Encriptação Completa de Disco) do tipo LUKS (Linux Unified Key Setup). Então toda vez que este computador for desligado e reiniciado você precisará digitar pelo teclado a senha para decriptar o HD.
Isso é um problema. E se este computador for um servidor? E se você não tiver sempre próximo para digitar a senha todas as vezes que o computador desligar ou reiniciar?
Pra isso existe um programa chamado Mandos. O Mandos são 2 softwares:
- Mandos Client - para ser instalado no computador encriptado
- Mandos Server - para ser instalado num servidor (que precisa estar sempre ligado) que enviará a senha para decriptar os Mandos Clients
Quando você encripta um computador Linux usando o LUKS, ao ligá-lo ele inicia um mini Linux chamado initramfs. Esse initramfs cria a tela para digitarmos a senha e decriptar o HD. Ao instalar o Mandos Client normalmente no seu Linux (via apt install mandos-client) ele também será instalado no initramfs. No momento em que o computador for reiniciado, ele procurará um Mandos Server na rede e pedir para ele a senha para decriptar o HD.
Em meu caso, não tenho certeza de como é a infraestrutura do lugar onde o computador com Mandos Client será instalado. Para driblar isso preciso que o OpenVPN inicie junto com Mandos. O Mandos por si só não é suficiente. O envio da chave entre o Mandos Client e o Mandos Server é encriptado, porém
Este tutorial foi feito em um Ubuntu Server 18.04 x64 (mas também deve funcionar normalmente em x32)
Instalando e configurando o Mandos Client
Antes de instalar o Mandos Client é interessante instalar um pacote gerador de números randômicos, isso agilizará a instalação Mandos Client. A que sugiro é a haveged, ela é leve e razoavelmente randômica para computadores sem hardware especial para gerar números randômicos. O sugerido por Boyeau é a rng-tools, é uma biblioteca muito pesada, ocupa aproximadamente 120mb de disco e é o software padrão do Linux para gerar números randômicos gerados por hardware (ou seja, você precisa de uma placa/usb geradora de números randômicos para usar a rng-tools apropriadamente).
[code lang="bash"]# Para instalar o rng-tools: #sudo apt-get install rng-tools #sudo rngd -r /dev/urandom # Para instalar o haveged sudo apt install haveged -y [/code]
Instalando o Mandos Client
[code lang="bash"]#No Ubuntu 16.04 não funciona, instalar o pacote manualmente #sudo apt install mandos-client sudo dpkg -i mandos-client_1.7.15-1_amd64.deb #Para corrigir erros de dependência sudo apt -f install [/code]
O pacote presente no repositório do Ubuntu 16.04 está com problemas, então o autor do blog Boyeau compilou uma versão mais atualizada e funcional que fé este aqui: mandos-client_1.7.15-1_amd64.deb
Agora é preciso gerar o certificado do cliente. Esse certificado é gerado pelo Mandos Client mas deverá ser transferido ao Mandos Server. Esse certificado faz com que o Server confie no Client e envie a senha para ele com segurança.
[code lang="bash"]sudo mandos-keygen --password --type RSA --force [/code]
Usando o Mandos Client para iniciar uma conexão com a internet usando DHCP (caso queira definir o IP do servidor manualmente)
Isso é importante porque para nos conectarmos à rede OpenVPN precisamos de conexão com a internet. O seguinte script faz o computador se conectar a rede automaticamente para nós.
*** Pelos testes que fiz, o DNS não funciona direito! ***
[code lang="bash"]sudo nano /etc/mandos/network-hooks.d/ethernet-interface-hook [/code]
Nele coloque o seguinte script:
[code lang="bash"]#!/bin/sh# # This is an example of a Mandos client network hook. This hook # brings up an interface as specified in a separate # configuration file. To be used, this file and any needed # configuration file(s) should be copied into the # /etc/mandos/network-hooks.d directory. # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without any warranty. # Exit immediately if a command exits with a non-zero status set -e do_start(){ ip link set enp1s0 up sleep 10 ipconfig -c dhcp -d enp1s0 sleep 10 } do_stop(){ ip link set enp1s0 down } case "${MODE:-$1}" in start|stop) do_"${MODE:-$1}" ;; esac[/code]
Esse script utiliza o software ipconfig para fazer a conexão com DHCP (quem disse que o Linux não tem ipconfig?). Acredito que a documentação é esta aqui. E possivelmente o código fonte é este aqui.
Veja abaixo como configurar um IP por DHCP ou estático
[code language="bash"]#Modelo da configuração de IP estático no ipconfig ipconfig endereco_ip:dns_server:gateway:netmask:interface:proto #Exemplo da configuração com IP estático ipconfig -d enp0s3 192.168.1.200:8.8.8.8:192.168.1.1:255.255.255.0:enp0s3:none #Exemplo da configuração com IP DHCP ipconfig -c dhcp -d enp0s3 [/code]
Usando o Mandos Client para inserir o OpenVPN no initramfs
Até poderíamos inserir o OpenVPN por fora do Mandos no initramfs, mas pode ser um pouco complexo. Por sorte, o Mandos Client têm um facilitador, ele também nos deixa definir hooks de instalação no initramfs. Se usarmos o Mandos para isso, também teremos controle da ordem em que os scripts serão iniciados, o que é bom pois evita erros.
Descobri o script na documentação que é instalada junto com o Mandos. Há alguns outros exemplos de scripts na pasta. Caso tenha interesse de criar o seu ou fazer modificações, é só abrir:
[code language="bash"]cd /usr/share/doc/mandos-client/examples/network-hooks.d[/code]
Voltando à configuração, crie um gancho de rede (network hook) chamado openvpn:
[code lang="bash"]sudo nano /etc/mandos/network-hooks.d/openvpn[/code]
Coloque o seguinte script:
[code lang="bash"]#!/bin/sh # # This is an example of a Mandos client network hook. This hook # brings up an OpenVPN interface as specified in a separate # configuration file. To be used, this file and any needed # configuration file(s) should be copied into the # /etc/mandos/network-hooks.d directory. # # Copyright © 2012-2018 Teddy Hogeborn # Copyright © 2012-2018 Björn Påhlsson # # Copying and distribution of this file, with or without modification, # are permitted in any medium without royalty provided the copyright # notice and this notice are preserved. This file is offered as-is, # without any warranty. set -e CONFIG="openvpn.conf" # Extract the "dev" setting from the config file VPNDEVICE=`sed -n -e 's/[[:space:]]#.*//' \ -e 's/^[[:space:]]*dev[[:space:]]\+//p' \ "$MANDOSNETHOOKDIR/$CONFIG"` echo "$MANDOSNETWORKHOOKDIR" echo "$CONFIG" PIDFILE=/run/openvpn-mandos.pid # Exit if no device set in config if [ -z "$VPNDEVICE" ]; then exit fi # Exit if DEVICE is set and it doesn't match the VPN interface if [ -n "$DEVICE" ]; then case "$DEVICE" in *,"$VPNDEVICE"*|"$VPNDEVICE"*) :;; *) exit;; esac fi openvpn=/usr/sbin/openvpn do_start(){ echo "Starting OpenVPN" "$openvpn" --cd "$MANDOSNETHOOKDIR" --daemon 'OpenVPN(Mandos)' --writepid "$PIDFILE" --config "$CONFIG" sleep "$DELAY" } do_stop(){ PID="`cat \"$PIDFILE\"`" if [ "$PID" -gt 0 ]; then echo "Killing OpenVPN process" kill "$PID" echo "OpenVPN process killed" fi } case "${MODE:-$1}" in start|stop) do_"${MODE:-$1}" ;; files) echo "$openvpn" ;; modules) echo tun ;; esac [/code]
Coloque o arquivo de configuração da conexão do OpenVPN:
[code lang="bash"]sudo mv seu-arquivo-vpn.conf /etc/mandos/network-hooks.d/openvpn.conf[/code]
Mude a permissão do arquivo openvpn, permitindo que ele possa ser executado:
[code lang="bash"]sudo chmod 660 /etc/mandos/network-hooks.d/openvpn[/code]
Atenção! Prefira colocar o IP de seu servidor VPN ao invés do DNS. Nos testes que fiz, o DNS não funciona no initramfs do Ubuntu 16.
Inserindo o IP no initramfs para permitir que o OpenVPN funcione
O OpenVPN usa o comando IP para criar e destruir interfaces de rede. Este comando não vem por padrão dentro do initramfs, por isso, é preciso colocá-lo à mão. Para isso, basta criar um "hook" do initramfs, que nada mais é um script em bash que é executando quando a imagem do initramfs é gerada pelo comando update-initramfs -u. A pasta /etc/initramfs-tools/hooks é onde o Ubuntu guarda por padrão os scripts do usuário que serão executados na hora de montar a imagem initramfs. Crie um novo hook chamado ip-hook digitando:
[code lang="bash"]sudo nano /etc/initramfs-tools/hooks/ip-hook[/code]
Com esse comando você criará um arquivo chamado ip-hook. Coloque nele o seguinte script bash:
[code lang="bash"]#!/bin/bash -e # Copy the compatibility symlinks until initramfs-tools will be converted # to use the ip program. if [ "$1" = "prereqs" ]; then exit 0; fi . /usr/share/initramfs-tools/hook-functions copy_exec /sbin/ip [/code]
Agora mude a permissão do arquivo e permita que ele seja executado pelo root ou pelo seu grupo:
[code language="bash"]sudo chmod 550 /etc/initramfs-tools/hooks/ip-hook [/code]
Agora você já poderia atualizar a imagem do initramfs. Mas como ainda serão feitas mudanças no initramfs, vale a pena esperar mais um pouco, vamos às configurações do Mandos Client.
Configurando o Mandos Client
O mandos tem 2 arquivos de configuração.
O plugin-runner.conf para definir os parâmetros de inicialização do Mandos quando estiver em initramfs, é principalmente usado para definir um IP estático de um Mandos Server.
Para editá-lo:
[code language="bash"]sudo nano /etc/mandos/plugin-runner.conf[/code]
O plugin-runner.conf te permite escolher os parâmetros de inicialização do mandos client. A lista completa destes parâmetros está aqui. Vamos ao conteúdo do arquivo:
[code language="bash"]#Caso queira que mensagens de debug #apareçam no initramfs #--options-for=mandos-client:--debug #Caso queira que o mandos se conecte #num servidor com IPV6 estático --options-for=mandos-client:--connect=bddd::1234:5555 #Onde o bddd::1234 é o IPV6 do servidor #E o 5555 é a porta onde o Mandos Server está escutando [/code]
O arquivo de configuração do Mandos Client.
[code language="bash"]sudo nano /etc/mandos/mandos.conf[/code]
Atualizando a initramfs
Atualize a imagem do initramfs digitando:
[code lang="bash"]sudo update-initramfs -u[/code]
Observe a saída do comando. Pode demorar um pouco, a imagem estará sendo montada. Se não ocorrer erros, tudo jóia. Se ocorrer alguma mensagem de erro, pela experiência que tive, os casos podem ser o seguinte:
- Erro de programação do script
- Binários usados nos scripts não existem na imagem do initramfs
- Dependências dos binários não encontradas
Pronto, toda vez que o computador for reiniciado, o initramfs vai iniciar, criar uma conexão DHCP, iniciar o cliente OpenVPN e pedir a senha para o Mandos Server.
Instalando e configurando o Mandos Server
Instalando o Mandos Server
[code language="bash"]sudo apt install mandos[/code]
Configurando o Mandos Server
Usando o Mandos Server
Verificando o estado das conexões
Verificando o estado atual das conexões e quanto tempo falta para elas serem desativadas
[code lang="bash"]sudo mandos-monitor[/code]
Verificando as senhas dos computadores que o Mandos Server está controlando
[code lang="bash"]sudo mandos-ctl[/code]
Habilitando um computador que foi desativado por timeout
[code lang="bash"]sudo mandos-ctl -e nome_do_computador[/code]
Testando conectividade entre o Mandos Client e o Mandos Server
Quando o computador com encriptação LUKS é ligado, ele executa um programa que faz solicitação de senhas ao(s) servidor(es) Mandos. Você pode simular esta solicitação para verificar e solucionar erros de conexão de rede do Mandos Client com o Mandos Server.
Se o seu servidor está configurado para zeroconf
basta usar o seguinte comando:
[code language="bash"]#O retorno desse comando poderá ser: # a senha de decriptação, caso tudo ocorra bem # ficar em branco por algum tempo, enquanto o programa tenta localizar os servidores # um erro, indicando qual o motivo de não ter conseguido se conectar com os servidores mandos sudo /usr/lib/x86_64-linux-gnu/mandos/plugins.d/mandos-client --pubkey=/etc/keys/mandos/pubkey.txt --seckey=/etc/keys/mandos/seckey.txt; echo #Caso queira mais informações sobre os erros, você pode informar na chamada o --debug #com isso, várias mensagens serão mostradas na tela, mostrando cada passo das tentativas de conexão sudo /usr/lib/x86_64-linux-gnu/mandos/plugins.d/mandos-client --pubkey=/etc/keys/mandos/pubkey.txt --seckey=/etc/keys/mandos/seckey.txt --debug; echo [/code]
Se o seu servidor está configurado para IPV6 direto com link global unicast
basta usar o mesmo comando (do zeroconf) adicionado do IPV6 pelo argumento --connect=IPV6_DO_MANDOS_SERVER:PORTA_DO _MANDOS_SERVER
[code language="bash"]#Exemplo de comando genérico para IPV6 com link global sudo /usr/lib/x86_64-linux-gnu/mandos/plugins.d/mandos-client --pubkey=/etc/keys/mandos/pubkey.txt --seckey=/etc/keys/mandos/seckey.txt --connect=[ServerIP]:[ServerPort]; echo #Exemplo de comando com IPV6 com link global sudo /usr/lib/x86_64-linux-gnu/mandos/plugins.d/mandos-client --pubkey=/etc/keys/mandos/pubkey.txt --seckey=/etc/keys/mandos/seckey.txt --connect=fdaa::1234:1234; echo #Exemplo de comando com IPV6 e porta de exemplo e debug sudo /usr/lib/x86_64-linux-gnu/mandos/plugins.d/mandos-client --pubkey=/etc/keys/mandos/pubkey.txt --seckey=/etc/keys/mandos/seckey.txt --connect=fdaa::1234:1234 --debug; echo [/code]
Se o seu servidor está configurado para IPV6 direto com link local
basta usar o mesmo comando (do link global unicast), informando também a interface de rede pelo argumento --interface=INTERFACE
[code language="bash"]#Exemplo de comando genérico para IPV6 com link local sudo /usr/lib/x86_64-linux-gnu/mandos/plugins.d/mandos-client --pubkey=/etc/keys/mandos/pubkey.txt --seckey=/etc/keys/mandos/seckey.txt --connect=[ServerIP]:[ServerPort] --interface[Interface]; echo #Exemplo de comando para IPV6 com link local sudo /usr/lib/x86_64-linux-gnu/mandos/plugins.d/mandos-client --pubkey=/etc/keys/mandos/pubkey.txt --seckey=/etc/keys/mandos/seckey.txt --connect=fe80::1234:1234 --interface=enp0s3; echo #Exemplo de comando para IPV6 com link local e debug sudo /usr/lib/x86_64-linux-gnu/mandos/plugins.d/mandos-client --pubkey=/etc/keys/mandos/pubkey.txt --seckey=/etc/keys/mandos/seckey.txt --connect=fe80::1234:1234 --interface=enp0s3 --debug; echo [/code]
Quais os riscos do Mandos?
Confs Específicas
Mandos Client
Exemplo de script configurador automático para o Mandos Client.
Este script pode ser executado como usuário normal, pois todos os comandos estão com sudo.
Configurações do script:
- Configura o Mandos Client para pedir a senha ao Mandos Server no IPV6 fdbb:bb na porta 4545
- Configura o initramfs para inicia a interface enp1s0 com DHCP client
- Salva o arquivo de permissão de envio de chave (para ser enviado ao Mandos Server e inserido no /etc/mandos/clients.conf de lá) em ~/nome-do-host.mandos-client-pass
- Move e renomeia o arquivo de configuração do OpenVPN que será executado no initramfs ~/initram-client.conf para a pasta adequada (então salve o arquivo do OpenVPN do initramfs com este nome!).
- Move e renomeia arquivo de configuração do OpenVPN que será executado no sistema normal em ~/client.conf para a pasta /etc/openvpn/client.conf (então salve o arquivo do OpenVPN do sistema normal com este nome!).
[code language="bash"]#Instalando OpenVPN sudo apt install openvpn #Configurando o OpenVPN # sudo echo '## This is the configuration file for plugin-runner(8mandos). This ## file should be installed as "/etc/mandos/plugin-runner.conf", and ## will be copied to "/conf/conf.d/mandos/plugin-runner.conf" in the ## initrd.img file. ## ## After editing this file, the initrd image file must be updated for ## the changes to take effect! Example: --options-for=mandos-client:--connect=fdbb::bb:4545' > /etc/mandos/plugin-runner.conf [/code]
Mandos Server
Exemplo de configurador automático para o Mandos Server.
Este script pode ser executado sem ser super usuário, pois todos os comandos já estão chamando sudo.
Antes de executar o script, coloque o arquivo de configuração do cliente do OpenVPN na pasta home do usuário com o nome client.conf. Por exemplo: ~/client.conf
Este script fará o Mandos Server escutar pacotes IPV6 na porta 4545 sem usar zeroconf (que é o padrão do Mandos).
Ele primeiramente pergunta se você quer instalar alguns pacotes. Responda Y.
Depois ele começa a gerar chaves randômicas. Essa etapa pode demorar um pouco.
Na sequência ele pergunta a senha da encriptação. Escreva a senha pelo teclado.
[code language="bash"]#Installing the OpenVPN apt install openvpn && #Instalando the DNS fix for OpenVPN in Linux apt install resolvconf && #Setting file permissions chown root:root ~/client.conf && chmod 550 ~/client.conf && mv ~/client.conf /etc/openvpn && #Appending the DNS configurations fix in the OpenVPN .conf client file echo '#Isso permite que os scripts up e down possam ser executados script-security 2 #Segundo a documentação: 2 -- Allow calling of built-in executables and user-defined scripts #Scripts que arrumam o DNS up /etc/openvpn/update-resolv-conf down /etc/openvpn/update-resolv-conf' >> /etc/openvpn/client.conf && #Installing Mandos Server apt install mandos && #Installing fping for allowing Mandos Server to check client connectivity apt install fping && #Creating a service that restarts OpenVPN connection if the server is not connected properly #Creating the bash echo '#!/bin/bash OPENVPN_STATUS=$(sudo systemctl status [email protected] | grep "Status: ") && case "$OPENVPN_STATUS" in *"Pre-connection initialization successful"*) echo "The service is in the Pre-connection state." && echo "Restarting OpenVPN connection in client.conf." && systemctl restart [email protected] && echo "Connection restarted." ;; *) echo "The service state is alright." ;; esac' > /etc/systemd/system/openvpn-connection-checker.sh && chown root:root /etc/systemd/system/openvpn-connection-checker.sh && chmod 751 /etc/systemd/system/openvpn-connection-checker.sh && #Creating the service echo '# /etc/systemd/system/openvpn-connection-checker.service [Unit] Description=Checks periodically if the client.conf defined OpenVPN client has access to 172.22.0.1 IP Wants=openvpn-connection-checker.timer After=network-online.target [Service] ExecStart=/bin/bash /etc/systemd/system/openvpn-connection-checker.sh [Install] WantedBy=multi-user.target' > /etc/systemd/system/openvpn-connection-checker.service && chown root:root /etc/systemd/system/openvpn-connection-checker.service && chmod 751 /etc/systemd/system/openvpn-connection-checker.service && #Creating the timer echo '# /etc/systemd/system/openvpn-connection-checker.timer [Unit] Description=Run openvpn-connection-checker.sh every 1 minute Requires=openvpn-connection-checker.service [Timer] Unit=openvpn-connection-checker.service OnUnitInactiveSec=1m RandomizedDelaySec=5s AccuracySec=1s [Install] WantedBy=timers.target' > /etc/systemd/system/openvpn-connection-checker.timer && chown root:root /etc/systemd/system/openvpn-connection-checker.timer && chmod 751 /etc/systemd/system/openvpn-connection-checker.timer && systemctl daemon-reload && systemctl enable openvpn-connection-checker.timer && # Creating the Mandos Server config file (mandos.conf) that # listens for IPV6 mandos client requests on port 4545 # and do not use zeroconf echo '# This file must have exactly one section named "DEFAULT". [DEFAULT] # These are the default values for the server, uncomment and change # them if needed. # If "interface" is set, the server will only listen to a specific # network interface. ;interface = # If "address" is set, the server will only listen to a specific # address. This must currently be an IPv6 address; an IPv4 address # can be specified using the "::FFFF:192.0.2.3" syntax. Also, if this # is a link-local address, an interface should be set above. ;address = # If "port" is set, the server to bind to that port. By default, the # server will listen to an arbitrary port. port = 4545 # If "debug" is true, the server will run in the foreground and print # a lot of debugging information. ;debug = False # GnuTLS priority for the TLS handshake. See gnutls_priority_init(3). ;priority = SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:!RSA:+SIGN-DSA-SHA256 # Zeroconf service name. You need to change this if you for some # reason want to run more than one server on the same *host*. # If there are name collisions on the same *network*, the server will # rename itself to "Mandos #2", etc. servicename = Mandos # Whether to provide a D-Bus system bus interface or not ;use_dbus = True # Whether to use IPv6. (Changing this is NOT recommended.) ;use_ipv6 = False # Whether to restore saved state on startup restore = True # The directory where state is saved ;statedir = /var/lib/mandos # Whether to run in the foreground ;foreground = False # File descriptor number to use for network socket ;socket = # Whether to use ZeroConf; if false, requires port or socket zeroconf = False ' > /etc/mandos/mandos.conf && chown root:root /etc/mandos/mandos.conf && chmod 644 /etc/mandos/mandos.conf [/code]
A partir daqui já pode-se fazer configurações manuais, especialmente no arquivo clients.conf em /etc/mandos/clients.conf
Referências
Site oficial do Mandos https://wiki.recompile.se/wiki/Mandos
Documentação do Mandos Server 1.7.19-1 no Ubuntu 18.04 Bionic http://manpages.ubuntu.com/manpages/bionic/man8/mandos.8.html
Documentação do Mandos Client 1.7.19-1 no Ubuntu 18.04 Bionic http://manpages.ubuntu.com/manpages/bionic/man8/mandos-client.8mandos.html
Melhor tutorial de instalação do Mandos https://blog.boyeau.com/booting-an-unattended-full-disk-encrypted-server-ubuntu-server-16-04-setup-with-mandos/
Bug de DNS no initramfs do Ubuntu 16 https://bugs.launchpad.net/ubuntu/+source/initramfs-tools/+bug/1714308
Criando entropia no gerador de números randômicos do Linux com Haveged https://www.digitalocean.com/community/tutorials/how-to-setup-additional-entropy-for-cloud-servers-using-haveged