Aprendendo a enviar e-mails autênticos

Hoje em dia o e-mail é algo tão presente no nosso dia a dia que poucas vezes paramos para pensar em toda a complexidade que está envolvida por trás do funcionamento deste serviço. É muito semelhante a outros serviços básicos, como energia elétrica, por exemplo, alguém já parou pra pensar sobre os meios geração, produção, transporte e distribuição de energia elétrica? E de e-mails? Às vezes parece que funciona por mágica, a intenção é que os usuários realmente pensem assim, mas a coisa não é tão simples assim na prática.

E não é difícil imaginar que haja muita magia – de verdade – envolvida na configuração de um servidor de e-mail, especialmente quando você precisa configurar um deles para enviar e-mails autênticos. São muitas variáveis envolvidas e algumas formas de trabalho diferentes entre cada tipo de servidor de e-mail, o que dificulta um pouco as coisas. Mas alguns conceitos importantes nós devemos conhecer a fundo pois serão úteis em qualquer ambiente e com qualquer aplicação de servidor de e-mail.

Apenas uma observação: eu não sou especialista em administração de servidores, muito menos em servidores de e-mails, as dicas a seguir são fruto de muito estudo, tentativas, erros e acertos até chegar numa configuração e conhecimento que estão me atendendo.

O host, hostname e domain

Você sabe (sem consultar) qual o FQDN (Fully Qualified Domain Name) da sua máquina? E dos servidores que você usa e/ou administra? É comum que você não saiba, principalmente se não forem servidores de e-mail ou web.

O FQDN nada mais é do que o nome do host (hostname) junto com o nome do domínio (DNS). Na maioria dos servidores, especialmente os de desenvolvimento, a maior importância do hostname é a possibilidade de dar nomes personalizados para as máquinas, mas em produção isso tem uma importância bem grande.

A configuração e validação por SPF

SPF – Sender Policy Framework – é uma tentativa para controlar a quantidade de e-mails forjados que circulam na internet. Como encontramos no FAQ do SPF: “SPF não é anti-spam da mesma forma que a farinha não é comida, é parte da solução”. O SPF é um das configurações mais importantes para a maioria dos seus e-mails serem entregues com sucesso, especialmente se você quiser entregar e-mail para os servidores do gmail.

Citando um pouco o FAQ do SPF: “O SPF é um protocolo desenvolvido por um grupo de voluntários, motivados e unidos por um desejo em comum de melhorar o funcionamento da internet. Não é um produto comercial oferecido por uma empresa com fins lucrativos”. O protocolo SPF está sendo adotado por um número crescente de servidores de hospedagem e ISPs – Internet Service Providers, tornando-se cada vez mais importante e essencial para o funcionamento correto de um servidor de e-mails.

Na configuração do DNS do domínio o SPF aparece como uma entrada TXT comum, com esta aparência:

[cc lang=’text’ ]v=spf1 a mx …[/cc]

Para consultar as entradas TXT de um domínio, use: [cci lang=’text’ ]dig txt dominio.com[/cci]. Um exemplo completo de configuração de SPF:

[cc lang=’text’ ]v=spf1 a mx ip4:192.168.1.2 include:aspmx.googlemail.com include:google.com include:_spf.google.com -all[/cc]

Nesta configuração estou dizendo que, para o domínio em questão (imaginando o jeveaux.com) o IP 192.168.1.2 é um IP válido para enviar e-mails utilizando o domínio jeveaux.com. Além disso estou também incluindo os domínios de envio do Google para que possam também enviar e-mails utilizando o meu domínio (neste caso através do google apps).

É importante reparar um detalhe no final: [cci land=’text’]-all[/cci]. Esta cláusula está informando que qualquer outro IP (all) deverá ser negado (-), mas pode ser configurado de outras maneiras se você precisar, usando o [cci land=’text’]-all[/cci] ou diretamente um IP:

  • -all – Fail – Recusa qualquer e-mail partindo de outros IPs que não estiverem na configuração do SPF
  • +all – Pass – Significa que todo IPs (todo mundo) está autorizado a enviar e-mails em nome do seu domínio
  • ~all – SoftFail – Intermediário entre o Fail (-) e o Pass (+). Geralmente usado em transições de domínios ou servidores de domínios.
  • ?all – Neutral – O dono do domínio não tem como ou não quer definir quem está autorizado a enviar e-mails em nome do seu domínio

Mais informações e detalhes sobre o SPF aqui no Antispam.br.

MX ou A records?

Esta é uma dúvida que eu já tive diversas vezes e sempre precisei pensar e repensar a mesma coisa várias e várias vezes. Mas é muito mais simples do que parece, vejamos por necessidade:

  • O servidor de e-mails em questão funciona apenas como SMTP (envio) ou também é utilizado como POP/IMAP (recebimento)?

Se a resposta for SIM, o seu servidor de e-mails apenas envia e-mails, esqueça a configuração dos MX Records pois elas não lhe serão úteis, preocupe-se apenas com que exista um registro do tipo A no seu domínio contendo o nome (o hostname visto acima) do seu servidor associado com o IP do mesmo.

Se a resposta for NÃO, o seu servidor de e-mails também receberá e-mails, então lembre-se também de configurar corretamente os registros MX no seu domínio. Mas como neste artigo estou focando apenas no envio não entrarei em detalhes na configuração do MX, para mais informações siga por aqui.

DNS Reverso

DNS o que!? Reverso!? Pois é, DNS reverso. Pense num domínio: é um nome que encaminha você até um determinado servidor – IP. Agora pense de maneira reversa, ao contrário: a partir de um determinado servidor – ou IP – como chego ao domínio? Pois é isso que faz a configuração do DNS reverso, ela permite que outros servidores verifiquem a identidade do seu servidor comparando se um determinado IP bate com o IP informado no servidor de DNS.

Exemplo: Verificando o IP de um domínio através da resolução de DNS.

[cc lang=’bash’ line_numbers=’false’]jeveaux@valakas ~  $ host giran.com.br

giran.com.br has address 109.74.206.147[/cc]

E agora fazendo a resolução reversa, de um IP para o domínio.

[cc lang=’bash’ line_numbers=’false’]jeveaux@valakas ~  $ host 109.74.206.147

147.206.74.109.in-addr.arpa domain name pointer giran.com.br.[/cc]

Na maioria dos casos essa configuração será até fácil, basta pedir ao responsável pelo seu servidor de DNS que o faça. Exceto se você for o responsável também pelo servidor de DNS, então você terá que fazer a configuração do DNS reverso também. Se você precisar configurar manualmente o seu DNS reverso precisará saber um pouco sobre o BIND, recomendo este tutorial do Dicas-L.

Resumindo o que é importante lembrar

Se você quiser enviar e entregar e-mails corretamente para todos os servidores – ou o máximo possível – lembre-se de configurar corretamente:

  1. O hostname e domínio do seu servidor de e-mail
  2. O DNS do domínio com uma entrada válida no SPF para o IP do servidor de e-mails
  3. O DNS reverso no servidor de DNS da rede do servidor para o nome configurado no registro A (próximo passo)
  4. O DNS do domínio com um registro do A apontando para o IP do seu servidor de e-mails.

Alguns servidores de e-mail farão apenas a verificação por SPF ou DNS, por isso, configure sempre os dois, não custa nada e vai poupar algumas dores de cabeça e muitas falhas na entrega dos e-mails.

Um cenário fictício baseado no meu cenário real

Basicamente omiti apenas os nomes e os endereços IP dos hosts por outros que não são válidos, mas que são suficientes para exemplificar. Vamos ao exemplo:

O domínio jeveaux.com.br, registrado no registro.br está usando os servidores de DNS da Linode. Imaginando uma situação onde temos uma infra-estrutura grande e que uma determinada aplicação precise enviar muitos e-mails, o site ficará num servidor ([cci lang=’text’ ]srv1[/cci]) e o servidor de e-mails ficará em outro servidor ([cci lang=’text’ ]srv2[/cci]), logo, teremos dois IPs diferentes.

  • [cci lang=’text’ ]srv1[/cci]: apenas servidor web (o site, apache), IP 192.168.1.1
  • [cci lang=’text’ ]srv2[/cci]: apenas servidor de e-mail (postfix, sendmail, etc), IP 192.168.1.2

Servidores configurados, tudo funciona. Então vem o primeiro problema: Meus e-mails não estão nem caindo como spam, eles sequer são entregues, e agora?

A primeira coisa a se verificar é a configuração do domínio. Como está o SPF? O domínio jeveaux.com.br está dizendo explicitamente que o IP do [cci lang=’text’ ]srv2[/cci] pode enviar e-mails por este domínio?

Configure o SPF, algumas mensagens começarão a ser entregues, mas não são todas, e agora? Será comum encontrar no log estas mensagens de erro:

[cc lang=’text’ line_numbers=’false’]550 5.7.1 Client host rejected: cannot find your hostname, [xxx.xxx.xxx.xx]

450 5.7.1 Client host rejected: cannot find your reverse hostname, [xxx.xxx.xxx.xx][/cc]

Vamos ao mais fácil: depois de configurar o SPF, vamos configurar o DNS reverso, você pode solicitar ao seu datacenter, ou no caso do Linode fazer diretamente no painel de controle. Com o DNS reverso configurado agora quase todas as mensagens já são entregues, mas nem todas. O que ainda está faltando?

Configurar um apontamento válido no domínio para o endereço do seu servidor [cci lang=’text’ ]srv2[/cci], uma entrada do tipo A na configuração do domínio será suficiente. Mas qual o hostname – não falei que seria importante!? – completo do seu servidor?

No meu servidor [cci lang=’text’ ]srv2[/cci] eu vou executar o comando hostname -f e ver o retorno:

[cc lang=’text’ line_numbers=’false’]user@srv2 ~  $ hostname -f

mail.jeveaux.com.br[/cc]

O que isso quer dizer? Quer dizer que esse nome é o nome que é utilizado para iniciar uma conversão entre o seu servidor de e-mails e o servidor de e-mails que você pretende entregar uma mensagem. É (literalmente) assim:

[cc lang=’text’ line_numbers=’false’]Server: 220 mail.jeveaux.com.br ESMTP Postfix

Client: HELO mail.jeveaux.com.br[/cc]

Ocorre que, quando o cliente (o servidor que você quer entregar um e-mail) for verificar o IP de mail.jeveaux.com.br ele não encontrará nada, ou talvez até encontre o IP do [cci lang=’text’ ]srv1[/cci], dependendo de como estiver a configuração do seu domínio, levando o cliente a concluir que este remetente não é válido. Isso pode fazer seus e-mails irem para a caixa de spam ou diretamente recusados.

Crie então uma entrada do tipo A com o nome mail dentro do domínio jeveaux.com.br apontando para o IP do [cci lang=’text’ ]srv2[/cci].

Com estas configurações você terá as credenciais suficientes para entregar e-mails com autenticidade em qualquer servidor de e-mails. É claro que isso não lhe concederá garantias que o e-mail passará como não sendo spam com qualquer conteúdo que você enviar, isso é completamente diferente. Mas ao menos você preencheu os requisitos essenciais para identificar o seu servidor e valida-lo como um servidor autorizado dentro do seu domínio.

Existem muitas outras maneiras de fazer essa configuração, seja através de ajustes no seu servidor de e-mail, no DNS, etc. As que eu expliquei aqui são as que eu faço e utilizo e que consegui compreender com clareza até hoje.

Trabalhando com mais de um JDK no Ubuntu

Hoje em dia ter o Java5 e Java6 instalados na máquina de um desenvolvedor é praticamente uma obrigação. Java2 1.4 e Java7 também figuram bastante, ao menos aqui por estas bandas.

No meu macbook, que uso para trabalhar, eu controle tudo via a variável de ambiente $JAVA_HOME, é bem tranquilo. Quando quero compilar ou rodar alguma coisa com outro JDK/JVM é só mudar o $JAVA_HOME e pimba!

Quando eu usava Ubuntu sempre tive problemas para instalar mais de um JDK através do apt-get. A instalação era uma maravilha, como sempre no apt-get, mas por algum motivo o JDK e JVM padrões sempre ficavam com a versão mais alta. Eu tentava ‘corrigir’ usando a solução do $JAVA_HOME e incluindo o $JAVA_HOME/bin no $PATH, mas isso só funcionava enquanto eu estivesse no bash ou em aplicações que não fizessem referência direta ao /usr/bin/java.

Recentemente assumimos alguns servidores aqui na Giran e estamos migrando todos para Ubuntu Server. Não vou entrar no mérito dos porquês desta escolha para não criar uma guerra santa. Mas o que importa é que nestes servidores nós queríamos usar o apt-get para gerenciar todos os pacotes, afinal não temos paciência tempo para cuidar de tantos detalhes pequenos em tantos servidores.

Ao instalar um JDK, qualquer versão, através do apt-get, vários comandos estarão disponíveis no $PATH, dentre eles o java, javac, javap, jar, etc. Estes comandos estão sempre /usr/bin, mas são um link simbólico para os comandos em /etc/alternatives, que por sua vez são um link simbólico para o arquivo executável de verdade. Após instalar mais um JDK aí a coisa complica, qual deles será o padrão!? A cadeia de links simbólicos continua a mesma e mudar todos (mais de 15) manualmente não é muito indolor.

As instalações ficam sempre em /usr/lib/jvm/java-$versão, que é exatamente onde vão bater os links simbólicos de /etc/alternatives. No meu caso, por exemplo, tenho estas duas instalações: /usr/lib/jvm/java-6-sun e /usr/lib/jvm/java-1.5.0-sun. Se eu quiser alternar entre elas como padrão para todo o sistema, posso simplesmente usar o comando “update-alternatives” ao invés de sofrer reconfigurando um monte de link simbólico.

Então vamos lá:

spock@vulcan:~$ sudo update-alternatives --config java
There are 2 alternatives which provide `java'.
Selection    Alternative
-----------------------------------------------
+        1    /usr/lib/jvm/java-6-sun/jre/bin/java
*        2    /usr/lib/jvm/java-1.5.0-sun/jre/bin/java

Press enter to keep the default[*], or type selection number: 2
Using '/usr/lib/jvm/java-1.5.0-sun/jre/bin/java' to provide 'java'.

E pronto, simples assim. O item com o sinal de + (mais) é a opção default e o item com o sinal de * (asterisco) é a opção atual.

Ativando Syntax Highlighting no VIM

Recentemente participei de algumas discussões nas listas que participo que abordaram ambiente de desenvolvimento e, consequentemente, editores e IDEs. A escolha do editor ou da IDE é algo completamente particular e extremamente pessoal, e depois que alguém adota alguma ferramenta, não adianta alfinetar ou empalar com uma lança, as opiniões dificilmente mudarão, não importa quais ou quantos argumentos forem usados. Até mesmo por isso não quero falar sobre melhor ou pior aqui.

Numa dessas discussões, inevitavelmente começaram a falar sobre o VIM e num determinado momento alguém exclamou que o VIM era tão ruim que nem Syntax Highlighting fazia. Mas oras, é claro que faz. Uma dica rápida.

1) Ativando Syntax Highlighting manualmente

Durante a edição de um arquivo, você pode ativar ou desativar a Syntax Highlighting quando quiser. Para ativar:

:syntax on

E para desativar:

:syntax off

2) Ativando Syntax Highlighting automaticamente

Se você quiser, pode deixar que o VIM faça isso automaticamente sempre que possível. Pra isso edite o arquivo vimrc e adicione o trecho abaixo. No Linux esse arquivo geralmente fica em /etc/vim/vimrc, enquanto no Mac OS X você o encontrará em /usr/share/vim/vimrc.

if has("syntax")
  syntax on
endif

E é isso. Feche e abra o próprio arquivo vimrc e veja as diferenças. No meu caso ele ficou assim:

picture-2

Os arquivos de syntax ficam no diretório syntax dentro do diretório de instalação do vim, pro caso de você querer mudar alguma coisa.

Apache2 e Tomcat com mod_jk

Nesses últimos dias trabalhei bastante na administração e configuração de servidores *nix na Giran, revivendo algumas experiências antigas e aprendendo muitas outras novas e estou aproveitando para escrever um pouco sobre elas.

Configurando um servidor de desenvolvimento da Giran as novidades não foram grandes, a maioria das aplicações, serviços e preocupações foram as mesmas de um ambiente de desenvolvimento local. Já as experiências com a configuração do servidor de produção foram bem mais legais e algumas inéditas. Oracle, MySQL, SVN, Gitorious, Ruby Enterprise Edition + Passenger e claro, Apache 2 HTPP Server e Apache Tomcat.

Por hora vou escrever apenas sobre o mod_jk, que é a integração entre o Apache 2 HTPP Server e o Apache Tomcat. Eu já tive experiências anteriores com o mod_jk em ambientes de produção, em ambientes com redudância, com tomcat, com jboss e alguns mais, mas ainda não havia passado por uma situação onde eu iniciasse do zero e todas as responsabilidades estivessem comigo, e isso foi ótimo.

Um resumo do ambiente:

  • Ubuntu Server 8.04
  • Apache 2 HTTP Server 2.2.8
  • Apache Tomcat 6.0.18
  • JDK 1.6.0_13

O Apache

O Apache e o mod_jk foram instalados usando o próprio apt-get, então esta tarefa foi realmente muito fácil:

jeveaux@baium ~ $ sudo apt-get install apache2 libapache2-mod-jk

Uma série de pacotes e dependências virão junto com os dois pacotes acima, pode confirmar que tudo vai dar certo.

Esta instalação deixará o Apache em /etc/apache2, onde nós teremos (os principais arquivos):

  • httpd.conf – Configuração geral do apache.
  • conf.d – Configurações diversas, todos arquivos que estiverem nesse diretório serão carregados como configuração.
  • mods-available – Arquivos de configuração e ativação dos módulos.
  • mods-enabled – Módulos que estão ativados no apache, são links simbólicos para os arquivos do diretório mods-available.
  • sites-available – Arquivos de configuração dos sites (VirtualHost).
  • sites-enabled – Sites que estão ativados, são links simbólicos para os arquivos do diretório sites-available.

No httpd.conf poucas coisas precisam de intervenção, pessoalmente eu gosto muito deste esquema de organização e divisão de configurações utilizada pelo apache. Por exemplo, tudo que estiver no diretório APACHE2_HOME/mods-enabled será carregado automaticamente, primeiro todos os arquivo .load, que geralmente contém o LoadModule, e depois todos os arquivos .conf, que contém as configurações específicas do módulo, desta forma temos vários pares load+conf, um para cada módulo.

O JDK e o Tomcat

Apesar do servidor ser Ubuntu, desta vez eu não usei o apt-get. Eu sempre preferi instalar o JDK e algumas outras ferramentas de forma manual, não sei exatamente porque tenho essa mania, mas não consigo fugir.

O que importa é que o JAVA_HOME e o PATH estejam ajustados, se isso estiver correto tanto faz se você instalar usando o apt-get ou não. De qualquer forma, se você optar por usar o apt-get, basta seguir o comando abaixo:

jeveaux@baium ~ $ sudo apt-get install sun-java6-jdk tomcat5.5

Se não, se você for paranóico como eu, certifique-se de ter configurado o JAVA_HOME e o PATH manualmente no seu .bashrc:

export JAVA_HOME=/development/jdk1.6.0_13
export PATH=$JAVA_HOME/bin:$PATH

O mod_jk

O mod_jk já foi instalado anteriormente, então só precisamos certificar de que ele esteja ativado.

Caso você queira ativar ou desativar um módulo, existem duas maneiras: 1) usar os comandos a2enmod <mod>a2dismod <mod> ou simplesmente criar ou remover os links simbólicos em APACHE2_HOME/mods-enabled.

1) Configurar os workers

A instalação foi tão simples que somente um arquivo nos interessa por enquanto: /etc/libapache2-mod-jk/workers.properties. Abaixo apenas as configurações mais importantes e algumas que precisaremos alterar:

workers.tomcat_home=/development/apache-tomcat-6.0.18
workers.java_home=/development/jdk1.6.0_13
worker.list=ajp13_worker
worker.ajp13_worker.port=8009
worker.ajp13_worker.host=localhost
worker.ajp13_worker.type=ajp13
worker.ajp13_worker.lbfactor=1

No workers.properties temos o mapeamento do tomcat (workers.tomcat_home) e do JDK (workers.java_home). Há outra propriedade muito importante que é a worker.list, nela definimos todos “workers” que teremos. Para um único servidor teremos apenas um worker, mas em ambientes de cluster teremos vários. E temos para cada worker as suas configurações particulares: port, host e type, além de uma em particular, muito importante em ambiente de cluster e load balancer, a lbfactor, que indica a quantidade de trabalho do worker no conjunto, quanto menor o valor, menor o esforço do worker, ou seja, menos requisições serão despachadas para este worker.

2) Iniciar (ou montar) o JK

Mais uma vez temos dois caminhos a seguir aqui. Iniciar o JK no site principal ou em algum VirtualHost (sub-domínio) específico. O que vai mudar é onde você vai inserir o código a seguir.

Caso queira colocar o JK no seu site principal, você poderá inserir o código abaixo no seu httpd.conf – o que eu não recomendo – ou criar um arquivo jk.conf em APACHE2_HOME/mods-available, depois criar o link simbólico para este arquivo em APACHE2_HOME/mods-enabled.

Mas se você quiser ou precisar usar o JK somente em algum site e/ou sub-domínio específico, insira o código abaixo direto no arquivo do site em APACHE2_HOME/sites-enabled.

JkWorkersFile   /etc/libapache2-mod-jk/workers.properties
JkLogFile       /var/log/apache2/mod_jk.log
JkLogLevel      info
JkMount /*.jsp ajp13_worker
JkMount /teste/* ajp13_worker

Com essas configurações estamos escolhendo qual arquivo de workers vamos usar (JkWorkerFile), ou seja, qual o tomcat e JDK. Também definimos o arquivo de log e qual o tipo de log será gravado e, o ponto chave, quando o JK será usado. O JkMount pode ser repetido quantas vezes for preciso e é nele que definiremos todos os padrões de URL quanto forem precisos para que o JK seja usado.

É neste momento, configurando o JkMount, que podemos dividir o processamento de recursos dinâmicos (jsp, servlet) para o Tomcat e recursos estáticos para o Apache. Não vou entrar nesse ponto neste artigo, mas fica a dica.

Com o trecho acima estamos encaminhando para o tomcat – através do JK – tudo que terminar com .jsp ou tudo que estiver após /teste.

O Deployment

Aqui tudo correrá como qualquer aplicação Java, sem nenhuma diferença. Chamaremos nossa aplicação Java de “teste”. Após o deploy podemos acessa-la como de costume em http://localhost:8080/teste, mas agora com o JK podemos acessar também através da porta 80 (apache) em http://localhost/teste.

Maven ‘mvn release:prepare’ falhando com SVN

Um problema com a geração de releases com o maven tem incomodado muita gente nas últimas semanas. Trata-se de alguma incompatibilidade entre o maven e os clientes de SVN de versão superior a 1.5.x. O problema ocorre na preparação da release (mvn release:prepare) e diz que o pom.xml já existe no diretório da tag que nem foi criada ainda.

Existe uma solução bem simples que deverá funcionar de primeira caso seu ambiente de desenvolvimento seja *nix que é basicamente executar um update na sua working copy antes de preparar a release:

svn up -r head
mvn release:prepare

Entretanto, se você não tem vergonha na cara e desenvolve no windows o procedimento é um pouco mais chatinho, vejamos:

mvn release:prepare
## vai dar erro, continue
svn up -r head
mvn release:prepare -Dresume

E se você tiver o TortoiseSVN instalado mate o processo TSVNCache.exe antes de executar os passos acima.

Há esperanças de que a versão 1.5.5 do SVN Client dê um fim nesse problema, mas enquanto ela não chega temos que nos contentar com essa gambiarra terrível. Provavelmente este procedimento deverá ser executado no release:perform também.