Configurar e-mail de entrada de entrega direta para sites auto-hospedados com Mail-Receiver

Como exatamente desativar o suporte DMARC?

Ou seja, adicionar INCLUDE_DMARC: false à seção env de mail-receiver.yml não parece resolver. Isso parece fazer com que os daemons opendkim e opendmarc não sejam executados (levando a um aviso nos logs), mas a verificação SPF ainda está sendo realizada.

Editado para adicionar:
Acho que consegui desativar as verificações SPF adicionando também a seguinte linha POSTCONF_ à seção env:

env:
  ...
  INCLUDE_DMARC: false
  POSTCONF_smtpd_recipient_restrictions: check_policy_service unix:private/policy
  ...

Consegui isso olhando o commit que introduziu as verificações DMARC, e vendo o que deveria acontecer quando INCLUDE_DMARC é falso.

Sei quase nada sobre como as imagens docker são construídas, mas tenho a impressão de que o sinalizador INCLUDE_DMARC é algo que deve ser definido por outra pessoa, em outro lugar, em outro momento — não algo que possa ser feito em mail-receiver.yml.

2 curtidas

Encontrei a necessidade de abrir a porta 443 no ufw — caso contrário, recebo API Request Preparation Failed nos logs. Pensei que isso deveria ser mencionado porque as instruções de instalação padrão mencionam a ativação do ufw.

A porta 25 é mencionada em mail-receiver.yml e parece contornar o ufw.

1 curtida

O repositório do GitHub deve estar no OP?

3 curtidas

Usuários do mail-receiver, por favor, vejam Remove smtp_should_reject & discourse-smtp-fast-rejection

Vamos remover completamente a rejeição rápida, pois o recurso original estava quebrado e causando problemas aos usuários, especificamente este tipo de coisa:

e também afeta e-mails encaminhados, pois o teste pré-entrega estava verificando o envelope-from e o envelope-to, enquanto o Discourse usa apenas os valores nos cabeçalhos.

1 curtida

Acabei de enviar este PR para remover as aspas desnecessárias em torno do valor de DISCOURSE_BASE_URL no arquivo de exemplo mail-receiver.yml. As aspas estavam quebrando minha configuração. Livrar-se das aspas permite a conclusão bem-sucedida deste documento.

Você pode explicar como? A presença/ausência de aspas ao redor deste valor não produz diferença:

[2] pry(main)=> YAML::load("env:\n  DISCOURSE_BASE_URL: 'https://discourse.example.com'")
=> {"env"=>{"DISCOURSE_BASE_URL"=>"https://discourse.example.com"}}

[3] pry(main)=> YAML::load("env:\n  DISCOURSE_BASE_URL: https://discourse.example.com")
=> {"env"=>{"DISCOURSE_BASE_URL"=>"https://discourse.example.com"}}

Ao acompanhar os logs daquele contêiner e enviar mensagens para ele, eu estava vendo um monte de erros mencionando algo como discourse.example.com não faz parte dos registros MX ou algo assim. Eu removi as aspas, reconstruí o contêiner e começou a funcionar :person_shrugging:

A sequência de eventos também pode importar:

  1. Configurei e iniciei o contêiner mail-receiver
  2. Alguns dias depois, configurei os registros DNS MX
  3. Validei que os registros MX estavam configurados corretamente e então comecei a testar. Não estava funcionando
  4. Removi as aspas, reconstruí o contêiner, começou a funcionar

Então não tenho certeza se a resolução estava relacionada à remoção das aspas, ou à reconstrução do contêiner depois que os registros MX foram criados.

No pior dos casos, o PR deixa o yml com aparência consistente :slight_smile:

1 curtida

Parece haver uma suposição de que o receptor de e-mail será sempre o mesmo domínio do fórum base. Quando isso não acontece, como configuramos o TLS?

Por exemplo:
fórum => forum.domain.tld
receptor-email => mail.domain.tld

Em mail-receiver.yml, o TLS aponta para os certificados do fórum base. Existe uma maneira de fazer com que o receptor de e-mail obtenha seus próprios certificados?

Eu não sei a resposta direta, embora eu suspeite que exigiria opções adicionais no yml para fazer modificações no contêiner durante a compilação.

Mais sobre isso a seguir, mas eu me pergunto qual é o seu motivo para querer executá-lo em um domínio diferente. O mail-receiver é fortemente adaptado e, sem modificações, funciona exclusivamente para receber e-mails para uma instância do Discourse pareada, então é geralmente razoável tê-lo operando no mesmo domínio que essa instância.


Se você der uma olhada em alguns dos modelos para incluir em seu Discourse yml, alguns dos quais já estarão em uso, você deverá conseguir obter algumas dicas sobre como executar comandos e modificar arquivos via yml (durante a compilação do contêiner).

web.onion.template.yml tem alguns exemplos de como substituir strings em arquivos e web.letsencrypt.ssl.template.yml é aquele que adiciona o Let’s Encrypt ao contêiner principal do Discourse.

Eu não sei o quanto disso depende de coisas na imagem base, então potencialmente pode ser mais simples fazer o contêiner principal do Discourse obter um segundo certificado, e então apenas mudar os caminhos do certificado/chave em mail-receiver.yml para corresponder.

Tenha cuidado com esse tipo de alteração se você adotar essa abordagem, certificando-se de saber exatamente qual efeito a alteração terá. Uma alteração errônea nas coisas do Let’s Encrypt pode levar a uma falha silenciosa na renovação de certificados, por exemplo, o que você pode não notar até cerca de 3 meses depois, quando os visitantes começarem a receber erros de certificado expirado.

Caso de Uso do CloudFlare

Estas instruções são para fóruns Discourse auto-hospedados que usam o Proxy do Cloudflare.

Quando você usa o Proxy do Cloudflare, isso impede que todo o tráfego SMTP (Porta 25) chegue ao seu servidor. Isso exige que você configure um subdomínio diferente para o mail-receiver funcionar.

Por exemplo, se o seu domínio for forums.domain.tld, você precisará criar um novo subdomínio, como mail.domain.tld.

Com o Cloudflare, você tem as etapas adicionais abaixo.

  1. Crie o registro A para o novo subdomínio. Ele usará o mesmo endereço de IP que o seu forums.domain.tld.
  2. Crie o registro MX para o novo subdomínio, conforme fornecido nas instruções principais.

Siga os conjuntos de instruções principais com esta pequena alteração. Funcionará perfeitamente com a segurança TLS desativada.

Se você deseja executar a segurança TLS, isso exigirá um trabalho adicional.

Visão Geral da Configuração TLS

Estas instruções instalarão o Certbot e um plugin Certbot do CloudFlare. Os comandos obterão Certificados Let’s Encrypt no Modo Autônomo (Standalone) através do processo de certificação DNS. Assim que os Certificados estiverem disponíveis, eles serão copiados para a área compartilhada do mail-receiver para que o contêiner os utilize. Temos que usar o modelo DNS, já que o Discourse já possui a porta 80.

Desafio DNS

Em vez de comprovar a propriedade do domínio via HTTP, o certbot comprova criando um registro TXT no seu DNS. Como seu DNS é o Cloudflare, isso pode ser totalmente automatizado com um token de API do Cloudflare — sem porta 80, sem necessidade de desligar o servidor web.

Como funciona

Certbot → Cria o registro TXT _acme-challenge.mail.lotuselan.net no Cloudflare
Let's Encrypt → Procura por esse registro TXT → Valida → Emite o certificado
Certbot → Exclui o registro TXT

Tudo isso é feito no seu servidor base, não dentro do contêiner do discourse.

Configuração

1 — Instale o certbot e o plugin certbot do Cloudflare:

bash

apt install certbot python3-certbot-dns-cloudflare -y

2 — Crie um token de API do Cloudflare:

  1. Vá para Cloudflare → Meu Perfil → Tokens de API → Criar Token
  2. Use o modelo “Editar DNS da Zona”
  3. Permissões: Zona → DNS → Editar
  4. Recursos da Zona: Incluir → Zona Específica → lotuselan.net
  5. Restrições de IP: Configure apenas para permitir o endereço de IP do seu servidor
  6. Copie o token

3 — Salve o token em um arquivo de credenciais:

bash

mkdir -p /etc/letsencrypt/cloudflare
nano /etc/letsencrypt/cloudflare/credentials.ini

Cole:

dns_cloudflare_api_token = SEU_TOKEN_DE_API_CLOUDFLARE

Bloqueie o arquivo:

bash

chmod 600 /etc/letsencrypt/cloudflare/credentials.ini

4 — Solicite o certificado:

Atualize o seguinte comando com seu e-mail de administrador e nome de domínio.

bash

certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare/credentials.ini \
  --non-interactive \
  --agree-tos \
  --email seuemail@dominio.tld \
  -d mail.domain.tld

Nos seus resultados, deve haver uma declaração que diz:

Certbot has set up a scheduled task to automatically renew this certificate in the background.

O Certbot configurará um cron para verificar a expiração do certificado duas vezes por dia. Ele renovará os certificados quando estiverem a menos de 30 dias de expirar. Você pode validar isso por:

# Verifique se o temporizador systemd está ativo (a maioria dos sistemas Ubuntu modernos)
systemctl status certbot.timer

# Ou verifique se um job cron foi adicionado
cat /etc/cron.d/certbot

Agora você tem os certificados TLS no seu servidor para o novo nome de domínio do mail-receiver. Eles não estão em um local que possa ser usado.

5 — Configure um script de implantação para mover os arquivos
Como o certbot se renova automaticamente, você só precisa que seu script lide com as partes específicas do Discourse — copiando os certificados renovados e reconstruindo o mail-receiver. Você pode simplificar significativamente o script usando o gancho de implantação (deploy hook) integrado do certbot, que é executado automaticamente após uma renovação bem-sucedida.

Crie um arquivo de gancho de implantação:

bash

nano /etc/letsencrypt/renewal-hooks/deploy/mail-receiver-deploy.sh
chmod +x /etc/letsencrypt/renewal-hooks/deploy/mail-receiver-deploy.sh

Cole isto:

bash

#!/bin/bash
DOMAIN="mail.domain.tld"
DISCOURSE_DIR="/var/discourse"
CERT_SRC="/etc/letsencrypt/live/${DOMAIN}"
CERT_DEST_1="${DISCOURSE_DIR}/shared/mail-receiver/letsencrypt/${DOMAIN}"
CERT_DEST_2="${DISCOURSE_DIR}/shared/mail-receiver/letsencrypt/${DOMAIN}_ecc"
ADMIN_EMAIL="endereço de email do administrador"
LOG_FILE="/var/log/mail-cert-renewal.log"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

log "=== Gancho de implantação do Certbot acionado para ${DOMAIN} ==="

# Copia os certificados (use -L para resolver links simbólicos)
for DEST in "$CERT_DEST_1" "$CERT_DEST_2"; do
    mkdir -p "$DEST"
    cp -L "${CERT_SRC}/fullchain.pem" "${DEST}/fullchain.pem"
    cp -L "${CERT_SRC}/privkey.pem"   "${DEST}/privkey.pem"
    cp -L "${CERT_SRC}/cert.pem"      "${DEST}/cert.pem"
    cp -L "${CERT_SRC}/chain.pem"     "${DEST}/chain.pem"
    chmod 644 "${DEST}/fullchain.pem" "${DEST}/cert.pem" "${DEST}/chain.pem"
    chmod 600 "${DEST}/privkey.pem"
    log "Certificados copiados para ${DEST}"
done

# Reconstrói o mail-receiver
cd "$DISCOURSE_DIR" || { echo "Não foi possível cd para ${DISCOURSE_DIR}" | mail -s "[FALHA] Falha no gancho de implantação do certificado de email" "$ADMIN_EMAIL"; exit 1; }
log "Reconstruindo o mail-receiver..."
if ./launcher rebuild mail-receiver >> "$LOG_FILE" 2>&1; then
    log "mail-receiver reconstruído com sucesso"
else
    log "ERRO: falha na reconstrução"
    echo "A reconstrução do mail-receiver falhou após a renovação do certificado. Verifique ${LOG_FILE}" | \
        mail -s "[FALHA] Falha no gancho de implantação do certificado de email" "$ADMIN_EMAIL"
    exit 1
fi

log "=== Gancho de implantação concluído com sucesso ==="

Nenhum cron job manual é necessário — o certbot orquestra todo o processo. O gancho de implantação só é acionado quando uma renovação realmente acontece, então seu mail-receiver não terá reconstruções desnecessárias em dias em que o certbot verifica, mas não renova.

Para testar o gancho de renovação, execute o seguinte:

bash

bash /etc/letsencrypt/renewal-hooks/deploy/mail-receiver-deploy.sh

Se tudo foi configurado corretamente, isso irá
→ copiar os certificados para os diretórios do Discourse
→ reconstruir o mail-receiver
→ logar tudo

6 — Configure TLS no mail-receiver.yml

O principal problema aqui parece ser que o registro A para forum.domain.tld está mascarado pelo proxy, em vez de desejar explicitamente o servidor de e-mail em um domínio separado.

Ao negociar TLS, o nome comum do certificado é comparado com o nome de host do registro MX, ou seja, o nome de host ao qual o cliente (que pode ser outro servidor de e-mail) está tentando se conectar, em vez do registro A ao qual ele faz referência. Isso significa que você pode criar seu registro A mail.domain.tld definido como Somente Modo DNS, depois criar um registro MX para forum.domain.tld referenciando mail.domain.tld e nenhuma etapa especial adicional é necessária nesse arranjo.

[quote=“Simon_Manning, post:542, topic:49487”]Um conjunto de registros definido como Somente Modo DNS, em seguida, crie um registro MX para forum.domain.tld referenciando mail.domain.tld e nenhuma etapa especial adicional é necessária nesse arranjo.
[/quote]

Sim, você pode usar apenas o modo DNS para o registro A do seu fórum principal. Usar essa abordagem significa que você perde os recursos de proxy global reverso do CloudFlare. (Esta não foi uma opção para minha instalação do Discourse.)

É por isso que a primeira linha que define esta solução é para sites que usam o Proxy CloudFlare.

Eu estava me referindo ao registro A de mail.domain.tld definido como Apenas Modo DNS, em vez do registro A de forum.domain.tld, no entanto, percebi que estava interpretando mal como os clientes SMTP autenticam certificados TLS.

O comportamento que eu estava vendo era um artefato do método oportunista padrão que não valida o nome do host, então minha afirmação de que ele valida o nome do host do registro MX em vez do alvo do registro MX estava incorreta. Funcionaria na maioria dos casos, mas não se DANE ou MTA-STS forem usados para impor a autenticação de identidade TLS.

Ter o registro A como proxy e o registro MX como Apenas DNS não funciona. A documentação do CloudFlare afirma que qualquer domínio que tenha o registro A com proxy ativado tem todo o tráfego SMTP bloqueado.

Eu valido isso com várias rodadas de testes. No minuto em que você desativa o proxy do registro A, os dados SMTP fluem. Ative o Proxy, os dados SMTP nunca fluem. (Os testes foram realizados usando TELNET na porta 25.)

Então, se você quiser:

  • Que seu fórum Discourse use os serviços de Proxy do CloudFlare
  • Que seu Receptor de E-mail SMTP aceite e-mail

Você precisa ter domínios diferentes para seus e-mails de entrada.

Se você quiser TLS para seu receptor de e-mail SMTP:

  • Você precisa configurar um LetsEncrypt via Verificação de DNS

As instruções parecem assustadoras, mas levaram mais tempo para serem escritas do que para realmente implementar a solução.

Não foi isso que eu quis dizer, especificamente o que eu estava sugerindo eram três registros DNS:
A: forum.domain.tld → IP do host (proxy ativado)
A: mail.domain.tld → IP do host (Apenas Modo DNS)
MX: forum.domain.tldmail.domain.tld

No entanto, como mencionado, mais tarde percebi que isso só funcionaria no modo TLS oportunista padrão, não funcionará se você (alguém) também quiser habilitar DANE ou MTA-STS para forçar a autenticação de identidade (garantir que o servidor correto esteja sendo conectado em vez de apenas criptografar o tráfego).

Elas parecem muito boas, fáceis de seguir e fazem tudo fora do container, então não há risco de quebrar com as atualizações do Discourse. Eu particularmente gosto do uso de um hook de renovação do certbot que eu não conhecia antes.

1 curtida

Note que isso expõe o endereço de IP do seu fórum e torna possível que as pessoas ignorem os mecanismos de proteção do Cloudflare, como proteção DDoS e WAF. É melhor executar o receptor de e-mail em um servidor separado.

Minha primeira intenção era executar o mail-receiver em um servidor diferente por esse motivo. Toda vez que tentei executar o aplicativo launcher para iniciar o mail-receiver, ele queria instalar o sistema discourse completo. Existe uma maneira simples de apenas iniciar e executar o mail-receiver em um servidor Docker autônomo?

Não estou muito familiarizado com o aplicativo launcher, já que desenvolvemos nossa própria infraestrutura, mas minha ideia seria não usar o aplicativo launcher e iniciá-lo como qualquer outro contêiner. As variáveis de ambiente que você precisa estão no readme.

@Simon_Manning & @RGJ - O artigo da Cloudflare foi atualizado para apresentar as 3 principais opções e suas compensações. Esperamos que isso resolva as várias questões que vocês levantaram sobre as primeiras opções apresentadas.

@kelv Você pode considerar adicionar uma nota de rodapé na descrição principal do artigo da Cloudflare. Isso economizará tempo para as pessoas às quais se aplica. Configure direct-delivery incoming email for self-hosted sites with Mail-Receiver - #541 by LotusJeff

2 curtidas