0,0
рейтинг
22 декабря 2012 в 17:29

Администрирование → Реализация Exim + OpenDKIM для массовых рассылок

Все было готово к запуску нового проекта, осталась одна проблема – письма подтверждения регистрации Gmail отправлял в спам. Ознакомившись со справкой сервиса Gmail, я понял, что основной проблемой является отсутствие реализации DKIM на сервере. В моем случае, отправкой писем занимался Exim. Необходимо было реализовать связку Exim+DKIM. Увы, из-за смены утилиты реализации DKIM на FreeBSD все инструкции устарели. Мне пришлось вслепую настраивать Exim+DKIM несколько дней. В этой статье я опишу все этапы реализации Exim+DKIM.

DKIM необходим для массовых рассылок и отправки автоматических писем с сайта. Без наличия этой технологии спам фильтры Gmail не пропускают письма, отправленные скриптами. Для того чтобы Exim оправлял письма, подписанные ключем DKIM, необходимо поставить дополнительную утилиту OpenDKIM. Ранее использовалась утилита dkim-filter, разработчики прекратили ее поддержку.

Советую ознакомиться со статьей. В ней доходчиво написано, как добиться, чтобы ваши письма не попадали в спам. Статья устарела для реализации связки Exim+DKIM, все остальное – актуально.

Установка реализовывалась в 5 этапов.



1. Установка OpenDKIM

cd /usr/ports/mail/opendkim
make install clean

2. Генерируем ключи и настраиваем OpenDKIM и DNS записи

Для начала нужно сгенерировать пару ключей — внешний и внутренний. Внешний будет храниться в DNS записях, а внутренний – на сервере.

Итак, генерируем ключи:

opendkim-genkey -D /var/db/opendkim -d <domain.name> -s

/var/db/opendkim — директория, где будут лежать ключи
<domain.name> - доменное имя (example.com)
selector — указываем селектор, (например, mail). Селектор будет использоваться в DNS записи и указываться в настройке транспортов exim`а.

В нашем случае пишем следующее:

opendkim-genkey -D /var/db/opendkim -d example.com -s mail


Итак, создается два файла — mail.txt (mail — выбранный нами селектор) и mail.private. Посмотрим их содержимое:

cat /var/db/opendkim/mail.txt
mail._domainkey IN TXT ( "v=DKIM1; k=rsa; t=s; " "p=MIGfMA0GCSqGSIb3DQEBAQUAA4GN … mpwIDAQAB" ) ; ----- DKIM key mail for example.com.

Содержимое файла mail.txt добавим в DNS зону example.com, причем вот в таком виде (скобки и кавычки нужно удалить):

mail._domainkey IN TXT "v=DKIM1; k=rsa; t=s; p=MIGfMA0GCSqGS … wIDAQAB"

Советуют еще добавить вот такую DNS запись:

_adsp._domainkey.example.com IN TXT "dkim=unknown"

Дальше на файл mail.private устанавливаем права, чтобы его мог прочитать exim и только он. В нашем случае вот так:

chown mailnull:mail /var/db/opendkim/mail.private
chmod 600 /var/db/opendkim/mail.private

Добавим в config файле opendkim путь к mail.private. Покажу пример моего config:

vim (or something else) /usr/local/etc/opendkim.conf

# This is a simple config file for signing and verifying

LogWhy yes
Syslog yes
SyslogSuccess yes

Canonicalization relaxed/simple

Domain example.com
Selector mail
KeyFile /var/db/opendkim/mail.private

Socket inet:8891@localhost

ReportAddress support@example.com
SendReports yes

## Hosts to sign email for - 127.0.0.1 is default
## See the OPERATION section of opendkim(8) for more information
#
# InternalHosts 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12

## For secondary mailservers - indicates not to sign or verify messages
## from these hosts
#
# PeerList X.X.X.X

# PidFile /var/run/opendkim/opendkim.pid


Так как мы настраивали подпись только к одному домену, то сложностей не возникло.

C opendkim`ом закончили. Перейдем к настройке Exim.

3. Настройка Exim

Поправим секцию transports:

vim /usr/local/etc/exim/configure

Находим begin transports и сразу же ниже добавляем:

DKIM_DOMAIN = ${lc:${domain:$h_from:}}
DKIM_FILE = /var/db/opendkim/mail.private #- важно!!!! Пишем путь к приватному ключу
DKIM_PRIVATE_KEY = ${if exists{DKIM_FILE}{DKIM_FILE}{0}}

Также заменяем:

remote_smtp:
driver = smtp

На:

remote_smtp:
driver = smtp
dkim_domain = DKIM_DOMAIN
dkim_selector = mail # - выбранный нами селектор.
dkim_private_key = DKIM_PRIVATE_KEY

4. Дальше нужно запустить milter_opendkim

/usr/local/etc/rc.d/milter-opendkim start

5. Перезапустить Exim

/usr/local/etc/rc.d/exim reload

Exim запустился, делаем пробную отправку письма.

После успешного запуска DKIM в оригинале письма появится следующая запись:

DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d= example.com; s=mail;
h=Date:Message-Id:From:Content-type:MIME-Version:Subject:To; bh=RUN…0qKg=;
b=EEla1LMRm…Q6CYJM/VHg=;

Received: from example by xxx.freehost.com.ua with local (Exim 4.80.1 (FreeBSD))

Если запись не появилась – DKIM не запустился.

Напоследок пару слов о сервисе, для которого я это делал. С середины лета все свободное время работал над проектом "умного" подбора фильмов. Сайт перешел с альфа тестирования в бета. Буду благодарен за полезные советы и замечания по работе сервиса. Вот ссылка.
Владимир Бондаренко @coolweb
карма
12,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Администрирование

Комментарии (6)

  • 0
    1. Как это реализовать на CentOS 5?
    2. Через что отправляете письма? PHP — mail()?
    • 0
      1. Размещение конфигов будет в других директориях и еще opendkim нужно поставить (yum например).
      2. Вся почта идет через Exim, не только mail().
  • 0
    А зачем opendkim нужно запускать? Я ключи еще dkim-filter генерировал, а сейчас использую только exim4 без каких-либо dkim пакетов, и работает.
  • +3
    Если доменов много, то делаю так (у меня на Ubuntu 10.04 стоит dk-milter и OpenDKIM):
    /etc/default/opendkim
    # Command-line options specified here will override the contents of
    # /etc/opendkim.conf. See opendkim(8) for a complete list of options.
    #DAEMON_OPTS=""
    #
    # Uncomment to specify an alternate socket
    # Note that setting this will override any Socket value in opendkim.conf
    SOCKET="inet:10035@localhost" # listen on loopback on port 8891 - Ubuntu default
    


    /etc/opendkim.conf
    # This is a basic configuration that can easily be adapted to suit a standard
    # installation. For more advanced options, see opendkim.conf(5) and/or
    # /usr/share/doc/opendkim/examples/opendkim.conf.sample.
    
    ##
    ## opendkim.conf -- configuration file for OpenDKIM filter
    ##
    ADSPAction              Continue
    ADSPNoSuchDomain        Yes
    AutoRestart             Yes
    AutoRestartRate         10/1h
    Canonicalization        simple/simple
    ExternalIgnoreList      refile:/etc/mail/opendkim/trusted-hosts
    InternalHosts           refile:/etc/mail/opendkim/trusted-hosts
    KeyTable                refile:/etc/mail/opendkim/keyTable
    LogWhy                  Yes
    On-Default              accept
    On-BadSignature         accept
    On-DNSError             tempfail
    On-InternalError        accept
    On-NoSignature          accept
    #On-Security             tempfail
    On-Security             accept
    PidFile                 /var/run/opendkim/dkim-milter.pid
    SignatureAlgorithm      rsa-sha1
    SigningTable            refile:/etc/mail/opendkim/signingTable
    Socket                  inet:10035@127.0.0.1
    Syslog                  Yes
    SyslogSuccess           Yes
    TemporaryDirectory      /tmp
    UMask                    000
    UserID                  opendkim:postfix
    #X-Header                Yes
    


    /etc/default/dk-filter
    USER="postfix"
    GROUP="mail"
    SOCKET="inet:10034@127.0.0.1"
    
    SIGNING_DOMAIN="/etc/mail/domainkeys/domains"
    KEYFILE="/etc/mail/domainkeys/keys"
    SELECTOR_NAME="mail"
    SIGNER=yes
    VERIFIER=yes
    CANON=simple
    #REJECTION="bad=r,dns=t,int=t,no=a,miss=r"
    REJECTION="bad=r,dns=t,int=a,no=a,miss=r"
    HOSTLIST="/etc/mail/domainkeys/trusted-hosts"
    EXTRA_ARGS="-A -H -k -l -D -i ${HOSTLIST} -I ${HOSTLIST}"
    #========================================================
    MODES=
    if [ x${SIGNER:0:1} == "xy" -o x${SIGNER:0:1} == "xY" ]; then
        MODES=${MODES}s
    fi
    #if [ x${VERIFIER:0:1} == "xy" -o x${VERIFIER:0:1} == "xY" ]; then
    #    MODES=${MODES}v
    #fi
    [ ! -z "$MODES" ] && MODES="-b $MODES"
    #========================================================
    DAEMON_OPTS="-d ${SIGNING_DOMAIN} -s ${KEYFILE} -S ${SELECTOR_NAME} ${MODES} -c ${CANON} -C ${REJECTION} ${EXTRA_ARGS}"
    


    Ну, и генерация ключиков
    #!/bin/bash
    
    if [ -z $1 ]; then
        echo "The domainname is not specified"
        exit 1
    fi
    
    PREFIX=/etc/mail
    #PREFIX=/opt/postfixadmin-hooks/mail
    
    # Location of OpenSSL binary (whereis -b openssl)
    OPENSSL=/usr/bin/openssl
    DOMAIN=$1
    USE_DKIM=1
    USE_DOMAINKEYS=1
    SELECTOR=mail
    MAILTMPFFILE=/tmp/mail-`date "+%s"`
    POSTMASTER="postmaster@example.com"
    ADMINMAIL="tech@example.com"
    SENDMAIL="/usr/sbin/sendmail"
    
    # Create the directories for keys
    if [ -d ${PREFIX}/.privatekeys/${DOMAIN} ]; then
        echo "Directory for private keys for the domain ${DOMAIN} exists."
        exit 1
    else
        mkdir --mode=755 -p ${PREFIX}/.privatekeys/${DOMAIN}
    fi
    
    if [ -d ${PREFIX}/.publickeys/${DOMAIN} ]; then
        echo "Directory for private keys for the domain ${DOMAIN} exists."
        exit 1
    else
        mkdir --mode=755 -p ${PREFIX}/.publickeys/${DOMAIN}
    fi
    
    if [[ ${USE_DKIM} -eq "1" || ${USE_DOMAINKEYS} -eq "1" ]]; then
        echo "Generating the private key for ${DOMAIN}"
        ${OPENSSL} genrsa -out ${PREFIX}/.privatekeys/${DOMAIN}/${SELECTOR} 1024 > /dev/null
        echo "Generating the public key for ${DOMAIN}"
        ${OPENSSL} rsa -in ${PREFIX}/.privatekeys/${DOMAIN}/${SELECTOR} -out ${PREFIX}/.publickeys/${DOMAIN}/${SELECTOR}.public.key -pubout -outform PEM > /dev/null
        # Prepare the key
        keydata=$(grep -v '^-' ${PREFIX}/.publickeys/${DOMAIN}/${SELECTOR}.public.key)
        pubkey=$(echo ${keydata} | sed 's/ //g')
    fi
    
    # Process dk-milter
    if [ ${USE_DKIM} -eq "1" ]; then
        if [ -d ${PREFIX}/opendkim/keys/${DOMAIN} ]; then
    	echo "DKIM directory for private keys for the domain ${DOMAIN} exists."
    	exit 1
        else
    	mkdir --mode=755 -p ${PREFIX}/opendkim/keys/${DOMAIN}
    	install --owner=opendkim --group=opendkim --mode=400 ${PREFIX}/.privatekeys/${DOMAIN}/${SELECTOR} ${PREFIX}/opendkim/keys/${DOMAIN}/${SELECTOR}
    	echo "${SELECTOR}._domainkey.${DOMAIN} ${DOMAIN}:${SELECTOR}:${PREFIX}/opendkim/keys/${DOMAIN}/${SELECTOR}" >> ${PREFIX}/opendkim/keyTable
    	echo "*@${DOMAIN} ${SELECTOR}._domainkey.${DOMAIN}" >> ${PREFIX}/opendkim/signingTable
    	/usr/sbin/service opendkim restart
        fi
    else
        echo "DKIM signature is not used. Skipping."
    fi
    
    # Process Domainkeys
    if [ ${USE_DOMAINKEYS} -eq "1" ]; then
        if [ -d ${PREFIX}/domainkeys/privatekeys/${DOMAIN} ]; then
    	echo "Domainkeys directory for private keys for the domain ${DOMAIN} exists."
    	exit 1
        else
    	mkdir --mode=755 -p ${PREFIX}/domainkeys/privatekeys/${DOMAIN}
    	install --owner=postfix --group=root --mode=400 ${PREFIX}/.privatekeys/${DOMAIN}/${SELECTOR} ${PREFIX}/domainkeys/privatekeys/${DOMAIN}/${SELECTOR}
    	echo "*@${DOMAIN}:${PREFIX}/domainkeys/privatekeys/${DOMAIN}/${SELECTOR}" >> ${PREFIX}/domainkeys/keys
    	echo "${DOMAIN}" >> ${PREFIX}/domainkeys/domains
    	/usr/sbin/service dk-filter restart
        fi
    else
        echo "Domainkeys signature is not used. Skipping."
    fi
    
    echo "From: Postfix Admin <${POSTMASTER}>" >> ${MAILTMPFFILE}
    echo "To: ${ADMINMAIL}" >> ${MAILTMPFFILE}
    echo "Subject: Domain ${DOMAIN} was added, DNS intervention is needed" >> ${MAILTMPFFILE}
    echo "MIME-Version: 1.0" >> ${MAILTMPFFILE}
    echo "Content-Type: text/plain; charset=us-ascii" >> ${MAILTMPFFILE}
    echo "X-Priority: 1" >> ${MAILTMPFFILE}
    echo "X-MSMail-Priority: High" >> ${MAILTMPFFILE}
    echo "" >> ${MAILTMPFFILE}
    echo "Publish these NS records:" >> ${MAILTMPFFILE}
    echo "_domainkey.${DOMAIN}              IN      TXT \"o=-\"" >> ${MAILTMPFFILE}
    echo "_adsp._domainkey.${DOMAIN}        IN      TXT \"dkim=all\"" >> ${MAILTMPFFILE}
    echo "${SELECTOR}._domainkey.${DOMAIN}  IN      TXT \"v=DKIM1; k=rsa; p=${pubkey}\"" >> ${MAILTMPFFILE}
    echo "" >> ${MAILTMPFFILE}
    echo "" >> ${MAILTMPFFILE}
    echo "" >> ${MAILTMPFFILE}
    echo "--" >> ${MAILTMPFFILE}
    echo "Thanks," >> ${MAILTMPFFILE}
    echo "Postfix Administrator                         mailto:${POSTMASTER}" >> ${MAILTMPFFILE}
    
    cat ${MAILTMPFFILE} | ${SENDMAIL} -f ${POSTMASTER} -t
    unlink ${MAILTMPFFILE}
    


    Связка работает с постфиксом, но двумя взмахами напильника можно приловчить на Exim тоже.
    Да, кстате… Товарищи, если шлёте почту на буржуев, то не забывайте про Feedback Loops:
    mail.live.com/mail/services.aspx#JMRPP
    feedbackloop.yahoo.net/
    fbl.hostedemail.com/
    postmaster.aol.com/SupportRequest.php
    feedback.comcast.net/

    После этого к вам начнут поступать отчёты в т.н. формате ARF (вложения типа message/feedback-report).
    Также немаловажно переваривать bounce-messages (т.е. отлупы о том, что письма не доставлены адресату по тем или иным причинам (не верно указан адрес получателя, на получателе закончилось место и т.д. — можно заюзать тот же перловский Mail::DeliveryStatus::BounceParser ).

    И сообщите им же, что вы хотите слать МНОГО почты:
    postmaster.aol.com/cgi-bin/whitelist/whitelist_guides.pl
    help.yahoo.com/l/us/yahoo/mail/postmaster/bulkv2.html
    • 0
      Порадовало, комментарий может потягаться с самой статьёй.
  • +2
    У вас exim сам подписывает сообщения, и никакой ему opendkim не нужен. В вашем случае, opendkim понадобился только для генерации ключей (хоть и можно было обойтись без него) Вот тут написано:
    As of version 4.70, Exim has native support for DKIM.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.