dnl dnl Проверка соответствия адреса хоста отправителя SPF записи домена отправителя dnl dnl NO - отключить поддержку SPF dnl YES - включить поддержку SPF dnl DEFER_OK - включить поддержку SPF и игнорировать невозможность dnl проверки соответствия адреса хоста отправителя SPF записи домена отправителя dnl define(`confSPF2', `NO')dnl dnl dnl варианты действий при несоответствии рилея отправителя SPF записи домена отправителя dnl описаны в файле CONFDIR/access-spf2 в виде: dnl dnl sender.domain.tld|result : действие : сообщение клиенту : сообщение в файл протокола dnl dnl где sender.dom.tld - домен отправителя, допускаются wildcards dnl result - результат проверки соответствия рилея отправителя SPF записи dnl домена отправителя (pass, fail, softfail, none, neutral, dnl permerror, temperror dnl dnl в качестве "действия" могут выступать: dnl ok - принимать сообщения dnl warn - выдача предупреждения в лог файл и в заголовки письма dnl и добавление в заголовки сообщения поле X-Warn-Mail-MX dnl текст сообщения об ошибке может быть указан через двоеточие dnl deny или reject - отказ в приеме сообщения dnl drop - отказ в приеме сообщения с обрывом соединения dnl discard - прием письма без доставки получателю dnl defer - возврат клиенту временной ошибки 4xx dnl quarantine - принять письмо с сохранением в карантин без доставки получателям dnl greylist=xx или greylisting=xx - добавление xx баллов к счетчику опционального greylisting'а dnl reject=yy или deny=yy - добавление yy баллов к счетчику опционального reject'а dnl delay=zz или pause=zz - задержка на zz секунд перед продолжением обработки сообщения dnl submit_mysql - занесение записи о хосте в базу MySQL dnl submit_sqlite - занесение записи о хосте в базу SQLite dnl submit_rbl - занесение записи о хосте в DNSBL dnl dnl сообщения, возвращаемое клиенту и выводимое в файл протокола, могут отсуствовать dnl dnl пример: dnl *.aol.com|fail : deny : AOL sender, but not from AOL-approved relay dnl *.aol.com|neutral : warn : AOL sender, but not from AOL-approved relay dnl aol.com|fail : deny : AOL sender, but not from AOL-approved relay dnl aol.com|neutral : deny : AOL sender, but not from AOL-approved relay dnl dnl bigmir.net|softfail : defer dnl bigmir.net|temperror: defer dnl gala.net|softfail : deny : You can not send messages with gala.net as sender domain dnl gala.net|fail : deny : You can not send messages with gala.net as sender domain dnl rambler.ru|softfail : deny : You can not send messages with rambler.ru as sender domain dnl rambler.ru|fail : deny : You can not send messages with rambler.ru as sender domain dnl dnl *|fail : deny dnl *|softfail : defer dnl *|temperror : defer dnl *|neutral : warn dnl dnl исключения из проверки SPF записей указываются в файле CONFDIR/access-spf2 в виде: dnl адрес_отправителя : skip : список сетей dnl dnl пример: dnl *@hotmail.com : skip : 194.183.162.130 dnl dnl необходимо внести в виде исключений адреса backup MX'ов: dnl * : skip : backup_MX_1 : backup_MX_2 dnl dnl backend для проверки соответствия адреса хоста отправителя SPF записи домена отправителя dnl LIBSPF2 - использовать libspf2 dnl SPFD - использовать spfd из состава Mail::SPF::Query dnl define(`confSPF2_BACKEND', `LIBSPF2')dnl dnl dnl путь к файлу UNIX domain сокета при использовании spfd в качестве backend для dnl проверки соответствия адреса хоста отправителя SPF записи домена отправителя dnl define(`confSPF2_SPFD_SOCKET', `/tmp/spfd')dnl dnl dnl SPF_RESULT_INVALID "(invalid)" "invalid" dnl SPF_RESULT_NEUTRAL "neutral" "neutral" dnl SPF_RESULT_PASS "pass" "pass" dnl SPF_RESULT_FAIL "fail" "fail" dnl SPF_RESULT_SOFTFAIL "softfail" "softfail" dnl SPF_RESULT_NONE "none" "none" dnl SPF_RESULT_TEMPERROR "error (temporary)" "temperror" dnl SPF_RESULT_PERMERROR "unknown (permanent error)" "permerror" dnl "(error: unknown result)" dnl ifelse(SECTION, `ACLS_ADDITIONAL', `dnl acl_spf_pass: accept condition = ${if eq{$acl_m_spf_pass}{}{no}{yes}} ifelse_strstr(confSPF2_BACKEND, `LIBSPF2', ` warn condition = ${if eq{$acl_m_spf_result}{}{yes}{no}} spf = pass warn condition = ${if eq{$acl_m_spf_result}{}{yes}{no}} set acl_m_spf_result = $spf_result ')dnl ifelse_strstr(confSPF2_BACKEND, `LIBSPF2', `') ifelse_strstr(confSPF2_BACKEND, `SPFD', ` warn condition = ${if eq{$acl_m_spf_result}{}{yes}{no}} set acl_m_spf = \ ${readsocket{confSPF2_SPFD_SOCKET}{\ ip=$sender_host_address\n\ helo=${if def:sender_helo_name{$sender_helo_name}{NOHELO}}\n\ sender=$sender_address\n\ result\n\ quit\n\ }{20s}{\n}{socket failure}} condition = ${if eq{$acl_m_spf_result}{}{yes}{no}} condition = ${if eq{$acl_m_spf}{socket failure}{no}{yes}} set acl_m_spf_result = ${extract{result}{$acl_m_spf}} ')dnl ifelse_strstr(confSPF2, `DEFER_OK', `dnl', `') # accept condition = ${if eq{$acl_m_spf_result}{}{yes}{no}} accept set acl_m_spf_pass = no condition = ${if eq{$acl_m_spf_result}{pass}{yes}{no}} set acl_m_spf_pass = yes accept ')dnl ifelse(SECTION, `ACLS_ADDITIONAL', `') ifelse(SECTION, `ACL_CHECK_RCPT', `dnl # Проверка соответствия адреса хоста отправителя SPF записи домена отправителя warn set acl_m0 = skip condition = ${if eq{$sender_address}{}{no}{yes}} set acl_m0 = set acl_m2 = ${lookup{$sender_address}wildlsearch{CONFDIR/access-spf2}} condition = ${if eq{$acl_m2}{}{no}{yes}} acl = acl_normalize_action "${extract{1}{:}{$acl_m2}}" condition = ${if eq{$acl_m_normalize_action_result}{skip}{yes}{no}} set acl_m2 = ${sg{$acl_m2}{\N^\s*\S+\s*(:\s*)?\N}{}} set acl_m2 = ${if eq{$acl_m2}{}{*}{$acl_m2}} hosts = $acl_m2 set acl_m0 = skip ifelse_strstr(confSPF2_BACKEND, `LIBSPF2', ` warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} spf = fail warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} set acl_m_spf_result = $spf_result dnl ifelse_strstr(confSPF2, `DEFER_OK', `', `dnl dnl defer condition = ${if eq{$acl_m0}{skip}{no}{yes}} dnl condition = ${if or{\ dnl {eq{$acl_m_spf_result}{(invalid)}}\ dnl # {eq{$acl_m_spf_result}{error (temporary)}}\ dnl # {eq{$acl_m_spf_result}{unknown (permanent error)}}\ dnl }{yes}{no}} dnl message = Unable to check SPF record of sender address domain $sender_address_domain dnl log_message = Unable to check SPF record of sender address domain $sender_address_domain (SPF check result is $acl_m_spf_result) dnl ')dnl ifelse_strstr(confSPF2, `DEFER_OK', `dnl', `') warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} condition = ${if eq{$spf_result}{}{no}{yes}} logwrite = SPF check result is "$spf_result" for sender host address $sender_host_address and sender address domain $sender_address_domain add_header = ${if eq{$spf_received}{}{}{:at_start:$spf_received}} ifelse_strstr(confAUTH_RESULTS_ADD, `NO', `', `dnl set acl_m_auth_results = ${acl_m_auth_results};\n\tspf=\ ${if eq{$spf_result}{none}{none}{\ ${if eq{$spf_result}{(invalid)}{invalid}{\ ${sg{$spf_received}{\N^\S+:\s*(.+\(.+\)).*$\N}{\$1}}}} smtp.mail=$sender_address\ }} warn condition = ${if eq{$acl_m0}{skip}{yes}{no}} set acl_m_auth_results = ${acl_m_auth_results};\n\tspf=skip ') dnl ifelse_strstr(confAUTH_RESULTS_ADD, `NO', `', `') warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} set acl_m_spf_result = $spf_result warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} condition = ${if eq{$acl_m_spf_result}{(invalid)}{yes}{no}} set acl_m_spf_result = invalid warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} condition = ${if eq{$acl_m_spf_result}{error (temporary)}{yes}{no}} set acl_m_spf_result = temperror warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} condition = ${if eq{$acl_m_spf_result}{unknown (permanent error)}{yes}{no}} set acl_m_spf_result = permerror warn set acl_m1 = condition = ${if eq{$acl_m0}{skip}{no}{yes}} condition = ${if eq{$acl_m_spf_result}{}{no}{yes}} set acl_m0 = ${lookup{$sender_address_domain|$acl_m_spf_result}wildlsearch{CONFDIR/access-spf2}} condition = ${if eq{$acl_m0}{}{no}{yes}} acl = acl_normalize_action "${extract{1}{:}{$acl_m0}}" set acl_m1 = ${sg{$acl_m_normalize_action_result }{\N\b([^=\s\d]+)(\s)\N}{\$1=00\$2}} # message = $acl_m2 # log_message = $acl_m0 set acl_m2 = ${sg{${extract{2}{:}{$acl_m0}}}{\N^\s+\N}{}} set acl_m2 = ${if eq{$acl_m2}{}{${if eq{$spf_header_comment}{}{}{$spf_header_comment\n}}\ ${sg{\ ${sg{$spf_smtp_comment}{Please%see%}{Please see }}\ }{\N\s*: Reason: mechanism$\N}{}}\ }{$acl_m2}} set acl_m0 = ${sg{${extract{3}{:}{$acl_m0}}}{\N^\s+\N}{}} set acl_m_check_spf_msg = ${if eq{$acl_m0}{}{}{: $acl_m0}} set acl_m0 = ${if eq{$acl_m0}{}{${if eq{$spf_header_comment}{}{}{$spf_header_comment }}(SPF check result is $spf_result)}{$acl_m0}} ')dnl ifelse_strstr(confSPF2_BACKEND, `LIBSPF2', `') ifelse_strstr(confSPF2_BACKEND, `SPFD', ` warn set acl_m_spf = \ ${readsocket{confSPF2_SPFD_SOCKET}{\ ip=$sender_host_address\n\ helo=${if def:sender_helo_name{$sender_helo_name}{NOHELO}}\n\ sender=$sender_address\n\ result\n\ quit\n\ }{20s}{\n}{socket failure}} warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} set acl_m_spf_result = condition = ${if eq{$acl_m_spf}{socket failure}{no}{yes}} set acl_m_spf_result = ${extract{result}{$acl_m_spf}} warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} condition = ${if eq{$acl_m_spf_result}{}{no}{yes}} logwrite = SPF check result is "$acl_m_spf_result" for sender host address $sender_host_address and sender address domain $sender_address_domain dnl ifelse_strstr(confSPF2, `DEFER_OK', `', `dnl dnl defer condition = ${if eq{$acl_m0}{skip}{no}{yes}} dnl condition = ${if or{\ dnl {eq{$acl_m_spf_result}{(invalid)}}\ dnl # {eq{$acl_m_spf_result}{error (temporary)}}\ dnl # {eq{$acl_m_spf_result}{unknown (permanent error)}}\ dnl }{yes}{no}} dnl message = Unable to check SPF record of sender address domain $sender_address_domain dnl log_message = Unable to check SPF record of sender address domain $sender_address_domain (SPF check result is "$acl_m_spf_result") dnl defer condition = ${if eq{$acl_m0}{skip}{no}{yes}} dnl condition = ${if eq{$acl_m_spf}{socket failure}{yes}{no}} dnl message = Temporary local problem - please try later dnl log_message = Could not connect to SPFD socket confSPF2_SPFD_SOCKET dnl ', ` dnl warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} dnl condition = ${if eq{$acl_m_spf}{socket failure}{yes}{no}} dnl set acl_m_spf = dnl ')dnl ifelse_strstr(confSPF2, `DEFER_OK', `dnl', `') # warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} # condition = ${if eq{$acl_m_spf_result}{fail}{yes}{no}} # add_header = Received-SPF: $acl_m_spf_result ($domain: domain of $sender_address_domain does not designate $sender_address as permitted sender) client-ip=$sender_address; envelope-from=$sender_address; helo=$sender_helo_name; # warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} # condition = ${if eq{$acl_m_spf_result}{pass}{yes}{no}} # add_header = Received-SPF: $acl_m_spf_result ($domain: domain of $sender_address_domain designates $sender_address as permitted sender) client-ip=$sender_address; envelope-from=$sender_address; helo=$sender_helo_name; # warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} # condition = ${if eq{$acl_m_spf_result}{error (temporary)}{yes}{no}} # add_header = Received-SPF: $acl_m_spf_result ($domain: encountered temporary error during SPF processing of domain of $sender_address_domain) client-ip=$sender_address; envelope-from=$sender_address; helo=$sender_helo_name; # warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} # condition = ${if eq{$acl_m_spf_result}{neutral}{yes}{no}} # add_header = Received-SPF: $acl_m_spf_result ($domain: $sender_address is neither permitted nor denied by domain of $sender_address_domain) client-ip=$sender_address; envelope-from=$sender_address; helo=$sender_helo_name; # warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} # condition = ${if eq{$acl_m_spf_result}{softfail}{yes}{no}} # add_header = Received-SPF: $acl_m_spf_result ($domain: transitioning domain of $sender_address_domain does not designate $sender_address as permitted sender) client-ip=$sender_address; envelope-from=$sender_address; helo=$sender_helo_name; warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} # set acl_m_spf_header_comment = ${extract{header_comment}{$acl_m_spf}} set acl_m_spf_header_comment = ${if match{$acl_m_spf}{\N(?m)^header_comment=(.+)\n\N}{$1}{}} condition = ${if eq{$acl_m_spf_header_comment}{}{no}{yes}} condition = ${if eq{$acl_m_spf_result}{none}{no}{yes}} add_header = :at_start:Received-SPF: $acl_m_spf_result ($acl_m_spf_header_comment) client-ip=$sender_host_address; envelope-from=$sender_address; helo=$sender_helo_name; ifelse_strstr(confAUTH_RESULTS_ADD, `NO', `', `dnl set acl_m_auth_results = ${acl_m_auth_results};\n\tspf=$acl_m_spf_result ($acl_m_spf_header_comment) smtp.mail=$sender_address ') dnl ifelse_strstr(confAUTH_RESULTS_ADD, `NO', `', `') warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} # set acl_m_spf_smtp_comment = ${extract{smtp_comment}{$acl_m_spf}} set acl_m_spf_smtp_comment = ${if match{$acl_m_spf}{\N(?m)^smtp_comment=(.+)\n\N}{$1}{}} warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} condition = ${if eq{$acl_m_spf_result}{(invalid)}{yes}{no}} set acl_m_spf_result = invalid warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} condition = ${if eq{$acl_m_spf_result}{error (temporary)}{yes}{no}} set acl_m_spf_result = temperror warn condition = ${if eq{$acl_m0}{skip}{no}{yes}} condition = ${if eq{$acl_m_spf_result}{unknown (permanent error)}{yes}{no}} set acl_m_spf_result = permerror warn set acl_m1 = condition = ${if eq{$acl_m0}{skip}{no}{yes}} condition = ${if eq{$acl_m_spf_result}{}{no}{yes}} set acl_m0 = ${lookup{$sender_address_domain|$acl_m_spf_result}wildlsearch{CONFDIR/access-spf2}} condition = ${if eq{$acl_m0}{}{no}{yes}} acl = acl_normalize_action "${extract{1}{:}{$acl_m0}}" set acl_m1 = ${sg{$acl_m_normalize_action_result }{\N\b([^=\s\d]+)(\s)\N}{\$1=00\$2}} # message = $acl_m2 # log_message = $acl_m0 set acl_m2 = ${sg{${extract{2}{:}{$acl_m0}}}{\N^\s+\N}{}} set acl_m2 = ${if eq{$acl_m2}{}{$acl_m_spf_header_comment\n\ ${sg{\ ${sg{$acl_m_spf_smtp_comment}{Please%see%}{Please see }}\ }{\N\s*: Reason: mechanism$\N}{}}\ }{$acl_m2}} set acl_m0 = ${sg{${extract{3}{:}{$acl_m0}}}{\N^\s+\N}{}} set acl_m_check_spf_msg = ${if eq{$acl_m0}{}{}{: $acl_m0}} set acl_m0 = ${if eq{$acl_m0}{}{$acl_m_spf_header_comment (SPF check result is ${extract{result}{$acl_m_spf}})}{$acl_m0}} ')dnl ifelse_strstr(confSPF2_BACKEND, `SPFD', `')dnl ifdef(`confENTERPRISE_USER', `dnl warn condition = ${if match{$acl_m1}{submit_mysql}{yes}{no}} ENTERPRISE(`mysql', `submit', `spf', `blacklisted', `$sender_address', `0') warn condition = ${if match{$acl_m1}{submit_sqlite}{yes}{no}} ENTERPRISE(`sqlite', `submit', `spf', `blacklisted', `$sender_address', `0') warn condition = ${if match{$acl_m1}{submit_rbl}{yes}{no}} dnl ENTERPRISE(`rbl', `update', `mx.org.ua', `spf.rbl.mx.org.ua', `blacklisted', `$sender_address') ENTERPRISE(`rbl', `submit', `spf', `blacklisted', `$sender_address') ')dnl ifdef(`confENTERPRISE_USER', `') warn condition = ${if match{$acl_m1}{warn}{yes}{no}} log_message = $acl_m0${if eq{${extract{pause}{$acl_m1}}}{}{}{: message delayed for ${extract{pause}{$acl_m1}}s}} add_header = X-Warn-SPF: $acl_m0 warn condition = ${if eq{${extract{pause}{$acl_m1}}}{}{no}{yes}} delay = ${extract{pause}{$acl_m1}}s set acl_m_spam_action = ${acl_m_spam_action}\t\ delay=${extract{pause}{$acl_m1}}s\t\t\ $acl_m0\n # quarantine and !reject warn condition = ${if eq{${extract{quarantine}{$acl_m1}}}{00}{yes}{no}} condition = ${if eq{${extract{reject}{$acl_m1}}}{00}{no}{yes}} add_header = X-Quarantine-SPF: $acl_m0 log_message = message will be quarantined: $acl_m0 set acl_m_add_x_orig_rcpt = yes set acl_m_quarantined = $acl_m_quarantined envelope accept condition = ${if eq{${extract{quarantine}{$acl_m1}}}{00}{yes}{no}} condition = ${if eq{${extract{reject}{$acl_m1}}}{00}{no}{yes}} # quarantine and reject accept condition = ${if eq{${extract{quarantine}{$acl_m1}}}{00}{yes}{no}} condition = ${if eq{${extract{reject}{$acl_m1}}}{00}{yes}{no}} log_message = message will be quarantined: $acl_m0 set acl_m_fakereject = \ message will be quarantined: $acl_m0\ |X-Quarantine-SPF: $acl_m0\ |${expand:$acl_m2} set acl_m_add_x_orig_rcpt = yes set acl_m_quarantined = $acl_m_quarantined envelope # !quarantine and reject deny condition = ${if eq{${extract{reject}{$acl_m1}}}{00}{yes}{no}} condition = ${if eq{${extract{quarantine}{$acl_m1}}}{00}{no}{yes}} log_message = $acl_m0 message = ${expand:$acl_m2} defer condition = ${if match{$acl_m1}{defer}{yes}{no}} log_message = $acl_m0 message = $acl_m2 drop condition = ${if match{$acl_m1}{drop}{yes}{no}} log_message = $acl_m0 message = $acl_m2 discard condition = ${if match{$acl_m1}{discard}{yes}{no}} log_message = $acl_m0 ifelse(confGREYLIST, `OPTIONAL', `dnl warn condition = ${if eq{${extract{greylist}{$acl_m1}}}{}{no}{yes}} set acl_m_optional_greylist = \ scores=${eval:${extract{scores}{$acl_m_optional_greylist}}+${extract{greylist}{$acl_m1}}} \ log_message="${extract{log_message}{$acl_m_optional_greylist}} $acl_m0;" set acl_m_spam_action = ${acl_m_spam_action}\t\ greylist scores=${extract{greylist}{$acl_m1}}\t\ $acl_m0\n ') dnl ifelse(confGREYLIST, `OPTIONAL', `') ifdef(`confOPTIONAL_REJECT', `ifelse(confOPTIONAL_REJECT, `NO', `dnl', `dnl warn condition = ${if eq{${extract{reject}{$acl_m1}}}{}{no}{yes}} condition = ${if eq{${extract{reject}{$acl_m1}}}{00}{no}{yes}} set acl_m_optional_reject = \ scores=${eval:${extract{scores}{$acl_m_optional_reject}}+${extract{reject}{$acl_m1}}} \ log_message="${extract{log_message}{$acl_m_optional_reject}} $acl_m0;" set acl_m_spam_action = ${acl_m_spam_action}\t\ reject scores=${extract{reject}{$acl_m1}}\t\t\ $acl_m0\n ')') dnl ifdef(`confOPTIONAL_REJECT', `ifelse(confOPTIONAL_REJECT, `NO', `', `')') ')dnl ifelse(SECTION, `ACL_CHECK_RCPT', `')