acme.sh, ZeroSSL & Let’s Encrypt

O fato é que o Let’s Encrypt conquistou o mercado revolucionando o acesso aos certificados gratuitos. A EFF – Electronic Frontier Foundation fez um trabalho sensacional democratizando o acesso e permitindo que a internet fosse um lugar mais seguro de forma acessível para todos. Portanto fica o registro e meus mais honestos agradecimentos.

Mas diversidade é bom e todos gostam.

A Let’s Encrypt trabalha com dois servidores separados: um para testes e um de produção e não é incomum esquecermos disso. O servidor de produção tem um limite de tentativas que quando atingido te bloqueia por uma uma semana.

O objetivo é evitar sobrecarregar o servidor de produção e eles estão certíssimos, mas na “hora da agonia” isso pode ser um desastre. A urgência em gerar ou renovar um certificado te leva a fazer 10 tentativas e o Let’s Encrypt bloqueia seu acesso. Como ficar uma semana sem certificado?

A necessidade faz o homem é um provérbio antigo e um dos mais reais também. Eu cai nessa pegadinha e antes de ser consumido pelo desespero, me lembrei da dica do amigo Fábio Schmidt que tinha me falado sobre o acme.sh e o ZeroSSL.

A busca foi rápida e achei que o acme.sh já tem suporte ao Let’s Encrypt e seria um substituto muito interessante ao certbot, mas só isso não me ajudaria. O problema não era o certbot, mas o fato do servidor de produção do Let’s Encrypt ter me bloqueado.

O que eu queria era gerar o certificado da ZeroSSL e instalar ele no Zimbra.

Não achei nenhum material nas buscas, então aceitei o desafio e meti a mão no script =)

O maior desafio foi achar o CA Root certo e a ordem de concatenação das chaves para que o Zimbra aceitasse o certificado.

O certificado gratuito da ZeroSSL também tem duração de 90 dias igual ao Let’s Encrypt.

Segue abaixo o caminho das pedras. Espero que gostem.

A partir de agora, podemos escolher usar Let’s Encrypt ou ZeroSSL para nossos certificados. Ter opção de escolha é tudo de bom. Segue o tutorial.

Instalando o acme.sh

A instalação do acme.sh é super simples e tem uma única dependência: socat. Então instale ele antes. Os comandos abaixo são para Ubuntu.

apt -y install socat
cd /root
wget -O - https://get.acme.sh | sh
cd /root/.acme.sh

Gerando o certificado

O acme.sh funciona na mesma lógica do certbot, subindo um servidor web local com as portas 80 e 443, portanto o Zimbra tem que estar parado para o acme.sh funcionar em modo standalone. Se quiser usar outros meios como validação por DNS consulte o github do projeto.

Será necessário, também, registrar um e-mail que ficará vinculado ao certificado gerado.

Vamos aos comandos:

su - zimbra -c "zmcontrol stop"
./acme.sh --register-account -m [email protected]
./acme.sh --issue --standalone --keylength 2048 -d mail.exemplo.com.br -d webmail.exemplo.com.br

Perceba que da mesma forma que fazemos no certbot a sequência de domínios é passada usando um “-d” para cada domínio.

Quando o acme.sh gerar o certificado, os arquivos estarão em um diretório com o nome do primeiro domínio da lista, que no exemplo acima é mail.exemplo.com.br

Copiando os arquivos necessários para /tmp

O usuário zimbra não vai conseguir ler os arquivos do /root, portanto vamos fazer uma cópia para /tmp, assim:

mkdir -p /tmp/zero.exemplo.com.br
cd mail.exemplo.com.br
cp * /tmp/zero.exemplo.com.br
cd /tmp/zero.exemplo.com.br

Baixando e concatenando o CA Root

Em seguida vamos baixar o CA Root correspondente ao ZeroSSL e concatenar a cadeia de certificados da forma correta para que o gerenciador de certificados do Zimbra funcione como esperado.

Esta parte foi a mais desafiadora, porque a ZeroSSL não compartilha essa informação. Por isso mesmo eu copiei o CA root para meu blog e vamos fazer o download de lá. Seguem os comandos:

cd /tmp/zero.exemplo.com.br
wget --no-check-certificate http://www.anahuac.eu/USERTrustRSACertificationAuthority.crt
cat USERTrustRSACertificationAuthority.crt fullchain.cer > zimbra_ca.pem

Corrigindo permissões

Todos os arquivos que estão no /tmp/zero.exemplo.com.br tem que estar com permissões para o usuário zimbra.

chown zimbra: /tmp/zero.exemplo.com.br -R

Testando o certificado

Certificado gerado, CA Root no lugar, permissões corrigidas…. hora de testar se esta tudo perfeito. Execute:

su - zimbra -c"cd /tmp/zero.exemplo.com.br ; /opt/zimbra/bin/zmcertmgr verifycrt comm mail.exemplo.com.br.key mail.exemplo.com.br.cer zimbra_ca.pem"

Se tudo deu certo, podemos seguir adiante e instalar o certificado da ZeroSSL

Instalando o certificado

Últimos passos:

cp mail.exemplo.com.br.key /opt/zimbra/ssl/zimbra/commercial/commercial.key -rf
chown zimbra: /opt/zimbra/ssl/zimbra/commercial/commercial.key
su - zimbra -c"cd /tmp/zero.exemplo.com.br ; /opt/zimbra/bin/zmcertmgr deploycrt comm mail.exemplo.com.br.cer zimbra_ca.pem"

Finalmente inicie o Zimbra:

su - zimbra -c "zmcontrol start"

Script para agilizar

Quero compartilhar com vocês o script que fiz para realizar as tarefas acima e mais: ele também gera e instala certificados com Let’s Encrypt.

Então esse é um script multi função que usa acme.sh em vez de certbot para instalar certificados gerados pelo Let’s Encrypt ou ZeroSSL a sua escolha. Basta configurar as variáveis no inicio do script.

#! /bin/bash 

domain="exemplo.com.br"
certs_dom="mail.exemplo.com.br"
dom_list="-d mail.exemplo.com.br -d webmail.exemplo.com.br"
cert_path="/root/.acme.sh/mail.exemplo.com.br"
zerossl="yes"
letsencrypt="no"

if [ "$domain" = "" ] ; then
        echo "Defina as variáveis no início do script"
        exit
fi

echo -n "Tem que parar o Zimbra: [Y/n] "
read rest
if [ "$rest" = "" ]  || [ "$rest" = "Y" ] ; then
        su - zimbra -c"zmcontrol stop"
fi

apt -y install socat
cd /root
wget -O - https://get.acme.sh | sh
cd /root/.acme.sh

rm $cert_path -rf

if [ "$zerossl" = "yes" ] && [ "$letsencrypt" = "yes" ] ; then
        echo "Pick one: zerossl or letsencrypt!"
        exit
fi
if [ "$zerossl" = "no" ] && [ "$letsencrypt" = "no" ] ; then
        echo "Pick one: zerossl or letsencrypt!"
        exit
fi

if [ "$zerossl" = "yes" ] ; then
        ./acme.sh --register-account -m zerossl@$domain
        ./acme.sh --issue --standalone --keylength 2048 $dom_list
        if [ "$?" != "0" ] ; then
                echo ""
                echo "Deu erro ao gerar o certificado para o dominio $domain"
                exit 1
        fi
fi

if [ "$letsencrypt" = "yes" ] ; then
        # Testa a geração do certificado
        ./acme.sh --set-default-ca --server letsencrypt_test 
        ./acme.sh --issue --standalone --preferred-chain "ISRG Root X1" --keylength 2048 $dom_list 
        if [ "$?" != "0" ] ; then
                echo ""
                echo "Erro na no certificado para o dominio $domain"
                echo ""
                exit 1
        else
                rm $cert_path -rf
        fi


        # Gera o certificado
        ./acme.sh --set-default-ca --server letsencrypt
        ./acme.sh --issue --standalone --preferred-chain "ISRG Root X1" --keylength 2048 $dom_list 
        # Registrar se deu erro e abortar esse procedimento
        if [ "$?" != "0" ] ; then
                echo ""
                echo "Deu erro ao gerar o certificado para o dominio $domain"
                exit 1
        fi
fi

# Entrar no dir certo, que é o primeiro dominio
cd $cert_path

if [ "$letsencrypt" = "yes" ] ; then
        # Copiando para permitir acesso ao usuário zimbra
        mkdir -p /tmp/lets.$domain
        rm /tmp/lets.$domain/* -rf
        cp * /tmp/lets.$domain
        cd /tmp/lets.$domain
        tmp_path="lets"

        wget --no-check-certificate -O ISRG-X1.pem https://letsencrypt.org/certs/isrgrootx1.pem.txt
        cat fullchain.cer ISRG-X1.pem > zimbra_ca.pem
fi

if [ "$zerossl" = "yes" ] ; then
        # Copiando para permitir acesso ao usuário zimbra
        mkdir -p /tmp/zero.$domain
        rm /tmp/zero.$domain/* -rf
        cp * /tmp/zero.$domain
        cd /tmp/zero.$domain
        tmp_path="zero"

        wget --no-check-certificate http://www.tbs-x509.com/USERTrustRSACertificationAuthority.crt
        cat USERTrustRSACertificationAuthority.crt fullchain.cer > zimbra_ca.pem
fi

chown zimbra: /tmp/$tmp_path.$domain -R
su - zimbra -c"cd /tmp/$tmp_path.$domain ; /opt/zimbra/bin/zmcertmgr verifycrt comm $certs_dom.key $certs_dom.cer zimbra_ca.pem"
if [ "$?" != "0" ] ; then
        echo ""
        echo "Deu erro aotestr o certificado para o dominio $domain"
        exit 1
fi 

cd /tmp/$tmp_path.$domain
cp $certs_dom.key /opt/zimbra/ssl/zimbra/commercial/commercial.key -rf
chown zimbra: /opt/zimbra/ssl/zimbra/commercial/commercial.key

su - zimbra -c"cd /tmp/$tmp_path.$domain ; /opt/zimbra/bin/zmcertmgr deploycrt comm $certs_dom.cer zimbra_ca.pem"

echo -n "Tem que iniciar o Zimbra: [Y/n] "
read rest
if [ "$rest" = "" ]  || [ "$rest" = "Y" ] ; then
        su - zimbra -c"zmcontrol start"
fi