hoarywolf (hoarywolf) wrote,
hoarywolf
hoarywolf

прикручиваем spf к postfix-у

Уже достаточно давно к postfix-у прикручен у меня грейлистинг sqlgrey. Работает, вроде, исправно, спамеров давит, почту вот только порядком задерживает при доставке. А поскольку заносить ручками блоки адресов "хороших" отправителей в whitelist сильно влом, возникло желание прикрутить проверку spf перед грейлистингом. Чтобы те, кто успешно прошел проверку spf (pass) доставлялись сразу, кто не прошел (fail) шли нафиг, а остальные (те у кого нет записи spf, а также SoftFail и Neutral) мариновались грейлистингом.

С SoftFail и Neutral я жесток, поскольку большинство spf записей оканчиваются на ~all или ?all и если их принимать, то эффективность проверки spf будет стремиться в ноль.

Для проверки spf я буду использовать postfix-policyd-spf-perl, который можно взять отсюда. Для работы ему нужен модуль Mail::SPF, который, поскольку у меня установлен репозитарий rpmforge, устанавливается одним движением:

# yum install perl-Mail-SPF

После чего распаковываем скаченный файл и немножко правим postfix-policyd-spf-perl, что бы его поведение соответствовало желаемому.

В подпрограмме sender_policy_framework находим строки

    # Reject on HELO fail.  Defer on HELO temperror if message would otherwise
    # be accepted.  Use the HELO result and return for null sender.
    if ($helo_result->is_code('fail')) {
        syslog(
            info => "%s: SPF %s: HELO/EHLO: %s",
            $attr->{queue_id}, $helo_result, $attr->{helo_name}
        );
        return "550 $helo_authority_exp";
    }
    elsif ($helo_result->is_code('temperror')) {
        syslog(
            info => "%s: SPF %s: HELO/EHLO: %s",
            $attr->{queue_id}, $helo_result, $attr->{helo_name}
        );
        return "DEFER_IF_PERMIT SPF-Result=$helo_local_exp";
    }
    elsif ($attr->{sender} eq '') {
        syslog(
            info => "%s: SPF %s: HELO/EHLO (Null Sender): %s",
            $attr->{queue_id}, $helo_result, $attr->{helo_name}
        );
        return "PREPEND $helo_spf_header"
            unless $cache->{added_spf_header}++;
    }


и приводим их к следующему виду

    # Reject on HELO fail.  Defer on HELO temperror if message would otherwise
    # be accepted.  Use the HELO result and return for null sender.
    if ($helo_result->is_code('fail')) {
        syslog(
            info => "%s: SPF %s: HELO/EHLO: %s",
            $attr->{queue_id}, $helo_result, $attr->{helo_name}
        );
        return "550 $helo_authority_exp";
    }
    elsif ($helo_result->is_code('temperror')) {
        syslog(
            info => "%s: SPF %s: HELO/EHLO: %s",
            $attr->{queue_id}, $helo_result, $attr->{helo_name}
        );
        return "DEFER_IF_PERMIT SPF-Result=$helo_local_exp";
    }
    elsif ($attr->{sender} eq '' && $helo_result->is_code('pass')) {
        syslog(
            info => "%s: SPF %s: HELO/EHLO (Null Sender): %s",
            $attr->{queue_id}, $helo_result, $attr->{helo_name}
        );
        return 'OK';
    }
    elsif ($attr->{sender} eq '') {
        syslog(
            info => "%s: SPF %s: HELO/EHLO (Null Sender): %s",
            $attr->{queue_id}, $helo_result, $attr->{helo_name}
        );
        return "DUNNO"
    }


Эти строки связаны с spf-проверкой заголовка helo. В оригинальной версии, если не произошло spf-отказа (fail) и все нормально работает, то при пустом отправителе, вне зависимочти от статуса (нет spf-записи, pass, softfail или neutral) в заголовки письма добавляется запись типа Received-SPF: pass (mail.domain.ru: xxx.xxx.xxx.xxx is authorized to use бла-бла-бла бла-бла-бла... и письмо проходит дальше. А дальше стоит грейлистинг...

В поправленом варианте, при пустом отправителе, если helo прошло spf-проверку -- статус OK, письмо на доставку, если не прошло -- мариноваться в грейлистинг.

Далее ищем строки

    # Same approach as HELO....
    syslog(
        info => "%s: SPF %s: Envelope-from: %s",
        $attr->{queue_id}, $mfrom_result, $attr->{sender}
    );
    if ($mfrom_result->is_code('fail')) {
        return "550 $mfrom_authority_exp";
    }
    elsif ($mfrom_result->is_code('temperror')) {
        return "DEFER_IF_PERMIT SPF-Result=$mfrom_local_exp";
    }
    else {
        return "PREPEND $mfrom_spf_header"
            unless $cache->{added_spf_header}++;
    }

    return;


и меняем их на

    # Same approach as HELO....
    syslog(
        info => "%s: SPF %s: Envelope-from: %s",
        $attr->{queue_id}, $mfrom_result, $attr->{sender}
    );
    if ($mfrom_result->is_code('fail')) {
        return "550 $mfrom_authority_exp";
    }
    elsif ($mfrom_result->is_code('temperror')) {
        return "DEFER_IF_PERMIT SPF-Result=$mfrom_local_exp";
    }
    elsif ($mfrom_result->is_code('pass')) {
        return 'OK';
    }

    return;


Тут тоже все просто. Проверка по отправителю. Вместо добавления аналогичного заголовка и отправки в грейлистинг всех, кто не fail, измененная версия прошедшим проверку -- статус OK, письмо на доставку, остальных мариноваться в грейлистинг. Fail изменения не затронули, они все также идут нафиг.

Проверить работу модифицированного postfix-policyd-spf-perl можно запустив его (можно с ключиком -v), скармливая ему кусочками содержимое идущего в комплекте файлика test_cases и наблюдая за реакцией подопытного.

Если все устраивает, копируем файлик postfix-policyd-spf-perl в /usr/local/lib/

- добавляем в файл /etc/postfix/master.cf
        policy  unix  -       n       n       -       0       spawn
            user=nobody argv=/usr/local/lib/postfix-policyd-spf-perl
пользователь nobody тут используется для примера, лучше создать отдельного пользователя

- конфигурируем проверку в /etc/postfix/main.cf
        smtpd_recipient_restrictions =
            ...
            reject_unauth_destination
            check_policy_service unix:private/policy
            check_policy_service inet:127.0.0.1:2501
            ...


- и добавляем в него же policy_time_limit = 3600

- перегружаем postfix
Tags: postfix, spf
Subscribe

  • test

    test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test…

  • CCSP

    Вчера сдал последний экзамен из трека -- 642-533 IPS. Сегодня пришло письмо от Cisco, что я отныне являюсь Cisco Certified Security Professional

  • Раздел, защищенный GPT

    Подключил к виндовой машинке диск от мака. В управлении дисками диск с одним разделом, сабжевой надписью и полным отсутствием возможностей что-либо с…

  • Post a new comment

    Error

    default userpic

    Your reply will be screened

    Your IP address will be recorded 

    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.
  • 2 comments