Exim 4: ACL and others to reject spams

 

Introduction

This article is an explained example of exim 4 ACL configuration. For a larger point of view see «Spam Filtering for Mail Exchangers».

With this configuration we got approximately (for one person) 1 spam per day and we reject 20 per day (see the exim rejectlog file).

The context is a host server with a public IP (82.224.147.80, www.maretmmanu.org), wich is also used as local mail server:

######################################################################
#                    MAIN CONFIGURATION SETTINGS                     #
######################################################################
# If exim is used localy in batch mode (exim4 -bs) then "$host" is empty, the ": :" adds the empty string.
hostlist   own_hosts = 127.0.0.1 : : 192.168.113.114 : 192.168.113.113 : 82.224.147.80
domainlist public_domains = maretmmanu.org

Remote host IP checking

We allow connections from our own hosts and a white-list (Some hosts from big internet providers) with no more check. We refuse connections with some hosts (marketing company etc).

acl_check_host:
        accept
                hosts = +own_hosts : /etc/exim4/filters/host_white.list

        deny
                log_message = match host_reject.list
                hosts = /etc/exim4/filters/host_reject.list
        accept

Remote host IP checking by DNS black-list

Hosts listed by the dns list <a style="color: purple;" href="http://www.spamhaus.org/sbl/" rel="nofollow noopener noreferrer" target="_blank">sbl-xbl.spamhaus.org</a>"" are spammers or relays for spams. Often if you refuse the connection for one of these hosts then a new try is done by another relay some seconds later (see my old reject log). A better solution is to do the rejection when the RCPT is received. Then the spammer does not try again (see the new reject log).

acl_check_rcpt:
 . . .
        drop
                log_message = match sbl-xbl.spamhaus.org
                dnslists = sbl-xbl.spamhaus.org

HELO checking

Often spammers send for the HELO argument the name or the IP of your host. Here my own domain is maretmmanu.org"" and my own IP is 82.224.147.80.

acl_check_helo:
        accept
                hosts = +own_hosts

    # If the HELO pretend to be this host
    deny    condition = ${if or {
                    {eq {${lc:$sender_helo_name}}{maretmmanu.org}}
                    {eq {${lc:$sender_helo_name}}{82.224.147.80}}
                    } {true}{false} }
        # by default we accept
        accept

Sender checking

We refuse some senders, from some marketing companies.

acl_check_sender:
        deny    senders = /etc/exim4/filters/sender_reject.list
        accept

Recipient: emails addresses to catch spams

You can publish a sacrified email address in a web page to trap spammers (some spammers crawl other web pages to get emails). When this email address matches then an error is returned and all the message reception is droped. There are changes that the spammer software will not retry with this recipient removed.

When you write to a suspicious company wich could send you spam or when you write in a newsgroup, you can use a special email, with date (like echant-td-n040531@maretmmanu.org) or with an included identifier (like echant-tr-lemonde@maretmmanu.org). Then if you receive spam for this email you can put it in the drop list (in this example: /etc/exim4/filters/recipients_drop.list).

acl_check_rcpt:
 . . .
        drop
                log_message   = match recipients_drop.list.
                recipients = /etc/exim4/filters/recipients_drop.list

I use this script in cron.daily/ to update my emails with a date incorporated. The letter before the date is used to trace the origine (web, news, email).

#!/bin/bash

# Update my email wich include the today date
set -e
T=$(tempfile)
D=$(date '+%y%m%d')

function mod_file {
    EMAIL="$1"
    LETTRE="$2"
    CONF="$3"
    if [ -f "$CONF" -a -r "$CONF" ]; then
        lockfile-create "$CONF"
        sed "s/${EMAIL}-td-${LETTRE}[0-9]{6}@maretmmanu.org/${EMAIL}-td-${LETTRE}${D}@maretmmanu.org/g" <"$CONF" >"$T"
        cp "$T" "$CONF"
        lockfile-remove "$CONF"
    fi
}

# The first line will replace echant-td-n040625@maretmmanu.org
# with echant-td-n040626@maretmmanu.org
mod_file echant n /home/manu/.kde/share/config/knoderc
mod_file echant e /home/manu/.sylpheed/accountrc
mod_file echant e /home/manu/.initvar
# For apache we should reload but it is done by
# logrotate from time to time.
mod_file echant w /etc/apache-extern/httpd.conf

rm $T

Recipient: no hack

(From /usr/share/doc/exim4-doc-html/html/C043.txt.gz):

Deny if the local part contains @ or % or / or | or !. These are rarely found in genuine local parts, but are often tried by people looking to circumvent relaying restrictions.

Also deny if the local part starts with a dot. Empty components aren't strictly legal in RFC 2822, but Exim allows them because this is common. However, actually starting with a dot may cause trouble if the local part is used as a file name (e.g. for a mailing list).

acl_check_rcpt:
 . . .
        # refuse if the recipient string is a hack,
        # see exim file example C043.txt.gz
        deny
                local_parts = ^.*[@%!/|] : ^\.

Recipient: no relay

I refuse to relay spams:

acl_check_rcpt:
 . . .
        # For the rest, the domain of the recipient address
        # must be my public domain. (no relay)
        require
                log_message = no relay.
                domains     = +public_domains

Recipient: manual redirect by the sender

The idea is to send an automatic reply, using mail"" command in a filter, to inform that an email is blocked and that the user must use an other email address. This can be used to change a user email which receive to much spam or to protect a public email address.

In a filter:

### reply for echant@maretmmanu.org
if $original_local_part is "echant" then
    seen mail from drop@maretmmanu.org subject "Re: $h_subject" file .echant_reponse.txt
    finish
endif

greylist

Greylisting use the fact that most of the time spammers softwares do not take account tempory errors to retry later. It's very effective. You can use a daemon like [urlspan]greylistd[/urlspan].

When exim send a «tempory error»:

  • nether if the host is in a white list of «good» hosts
  • nether if there is no sender: it is a bounce message
  • nether if the HELO argument is well configured and the host name seams owned by the sender.
  • if the greylist daemon want to

The two lines with «set acl_m9» are used to send the request to the daemon and get the result. The /24"" is here because some big MTA can be spreaded on multiple hosts.

######################################################################
#                    MAIN CONFIGURATION SETTINGS                     #
######################################################################
# Mandatory to use  "verify = helo"
helo_try_verify_hosts = !+own_hosts
 . . .
######################################################################
#                          ACL CONFIGURATION                         #
######################################################################
# ACL "subroutine" used by acl_check_rcpt below. Used to detect
# hosts wich have not their own registered domain-name (probably spammer).
# Return ok if the HELO argument correspond to the connected HOST and
# if the argument does not contain an IP in decimal or hexa.
# I have created this ACL subroutine because we can't do a list of "or"
# in ACL (it's a list of "and"), so I use a negation of "and":
# no (no A and no B) = A or B.
acl_clean_helo:
        accept
                verify     = helo
                condition  = ${if match{$sender_helo_name}{N(d{1,3}[.-]d{1,3}[.-]d{1,3}[.-]d{1,3})|([0-9a-f]{8})|([0-9A-F]{8})N}{false}{true}}

acl_check_rcpt:
 . . . 

        # Greylisting, if the HELO argument seems bad or
        # a dialin name (with IP included in the name). Some hosts from big
        # providers are in a white list to avoid testing. When there is no
        # sender then it is a bounce message, so no greylist.
        defer
                message = Please try later.
                !hosts      = /etc/exim4/filters/host_white.list
                !senders    = :
                !acl        = acl_clean_helo
                log_message = greylisted.
                set acl_m9  = ${mask:$sender_host_address/24} $sender_address $local_part@$domain
                set acl_m9  = ${readsocket{/var/run/greylistd/socket}{$acl_m9}{5s}{}{}}
                condition   = ${if eq {$acl_m9}{grey}{true}{false}}

anti-virus: windows executables in attachment

It's a very basic anti-virus: every emails with a windows executable as attachment is rejected.

acl_check_data:
 . . .
        deny    message = This message contains an attachment of a type which we do not accept (.$found_extension)
                demime = bat:btm:cmd:com:cpl:dll:exe:lnk:msi:pif:prf:reg:scr:vbs:url

anti-virus:[urlspan]clamav[/urlspan]

I keep this chapter but I have removed clamav from my computer since there are some vulnerabilities announced times to times and because, with other spam filters, it seems not very usefull.

We used an anti-virus not to avoid virus (we have just linux hosts) but to remove unwanted emails.

######################################################################
#                    MAIN CONFIGURATION SETTINGS                     #
######################################################################
av_scanner = clamd:/var/run/clamd.ctl
 . . .
######################################################################
#                          ACL CONFIGURATION                         #
######################################################################
acl_check_data:
 . . .
        deny    message = This message contains a virus or other harmful content ($malware_name)
                demime = *
                malware = *

anti-spam external detector: [urlspan]spamassassin[/urlspan]

We add a X-SA-Score:"" in the header of all emails, a X-SA-Report:"" for all email with spam score >0, we consider it a spam if score >5 (adding X-SA-Status: Yes"" and we don't accept the email if score >7.

Because of the accept"" we must put this acl block at the end of the acl_check_data.

######################################################################
#                    MAIN CONFIGURATION SETTINGS                     #
######################################################################
spamd_address = 127.0.0.1 783
 . . .
######################################################################
#                          ACL CONFIGURATION                         #
######################################################################
acl_check_data:
 . . .
        ## spamassassin, spams are never big and spamassassin can die on big emails, so we
        ## limit its use under 500ko.
        accept  condition = ${if >={$message_size}{500k}{yes}{no}}
        warn    message = X-SA-Score: $spam_score
                spam = nobody:true
        warn    message = X-SA-Report: $spam_report
                spam = nobody:true
                condition = ${if >{$spam_score_int}{0}{true}{false}}
        warn    message = X-SA-Status: Yes
                spam = nobody:true
                condition = ${if >{$spam_score_int}{50}{true}{false}}
        deny    message = This message scored $spam_score spam points.
                spam = nobody:true
                condition = ${if >{$spam_score_int}{70}{true}{false}}

In your ~/.forward"" you can redirect spams (5< score ≤7) in a special inbox:

#   Exim filter   <<== do not edit or remove this line!
if $h_X-SA-Status: matches "^Yes" then
     save $home/.Mailboxes/incoming/spam
     finish
endif

Checking source of email associated with your domain in whois

If you have an email published in a whois database (spammers scan these databases) but want emails just from your registrar, you can add this in your ~/.forward"" filter:

#   Exim filter   <<== do not edit or remove this line!
if $original_local_part is "echant-tr-myregistrar"
  then
  if $sender_address_domain is "myregistrar.net" then
    deliver marcel
  else
    save $home/.Mailboxes/incoming/spam
    finish
  endif
endif

All in one

######################################################################
#                    MAIN CONFIGURATION SETTINGS                     #
######################################################################

hostlist   own_hosts = 127.0.0.1 : 192.168.109.24 : 192.168.109.23 : 82.224.147.80
domainlist public_domains = maretmmanu.org
 . . .
# Mandatory to use  "verify = helo"
helo_try_verify_hosts = !+own_hosts

av_scanner = clamd:/var/run/clamd.ctl
spamd_address = 127.0.0.1 783

acl_smtp_rcpt = acl_check_rcpt
acl_smtp_mail = acl_check_sender
acl_smtp_connect = acl_check_host
acl_smtp_data = acl_check_data
acl_smtp_helo = acl_check_helo

 . . .
######################################################################
#                          ACL CONFIGURATION                         #
######################################################################
acl_check_host:
        accept
                hosts = +own_hosts : /etc/exim4/filters/host_white.list

        deny
                log_message = match host_reject.list
                hosts = /etc/exim4/filters/host_reject.list

        accept

acl_check_helo:
        accept  hosts = +own_hosts

    # If the HELO pretend to be this host
    deny    condition = ${if or {
                    {eq {${lc:$sender_helo_name}}{maretmmanu.org}}
                    {eq {${lc:$sender_helo_name}}{82.224.147.80}}
                    } {true}{false} }

        # by default we accept
        accept

acl_check_sender:
        deny    senders = /etc/exim4/filters/sender_reject.list
        accept

# ACL "subroutine" used by acl_check_rcpt below.
# Return ok if the HELO argument correspond to the connected HOST and
# if the HELO argument does not contain an IP in decimal or hexa.
# I have created this ACL subroutine because we can't do a list of "or"
# in ACL (it's a list of "and"), so I use a negation of "and":
# no (no A and no B) = A or B.
acl_clean_helo:
        accept
                verify     = helo
                condition  = ${if match{$sender_helo_name}{N(d{1,3}[.-]d{1,3}[.-]d{1,3}[.-]d{1,3})|([0-9a-f]{8})|([0-9A-F]{8})N}{false}{true}}

acl_check_rcpt:
        # refuse if the recipient string is a hack,
        # see exim file example C043.txt.gz
        deny
                local_parts = ^.*[@%!/|] : ^\.

        # Relaying with no more check for my own hosts.
        accept
                hosts = +own_hosts

        # For the rest, the domain of the recipient address
        # must be my public domain. (no relay)
        require
                log_message = no relay.
                domains     = +public_domains

        # Reffuse all the message if the recipient is only used by spammers.
        drop
                log_message   = match recipients_drop.list.
                recipients = /etc/exim4/filters/recipients_drop.list

        drop
                log_message = match sbl-xbl.spamhaus.org
                dnslists = [urlspan]sbl-xbl.spamhaus.org[/urlspan]

        # Greylisting, if the HELO argument seems bad or
        # a dialin name (with IP included in the name). Some hosts from big
        # providers are in a white list to avoid testing. When there is no
        # sender then it is a bounce message, so no greylist.
        defer
                message = Please try later.
                !hosts      = /etc/exim4/filters/host_white.list
                !senders    = :
                !acl        = acl_clean_helo
                log_message = greylisted.
                set acl_m9  = ${mask:$sender_host_address/24} $sender_address $local_part@$domain
                set acl_m9  = ${readsocket{/var/run/greylistd/socket}{$acl_m9}{5s}{}{}}
                condition   = ${if eq {$acl_m9}{grey}{true}{false}}

        # Default rule: accept except if recipient address is unrouteable.
        accept
                message = unrouteable address
                verify = recipient

acl_check_data:

        accept  hosts = +own_hosts

        # if there is a windows executable as attachment then we reject
        deny    message = This message contains an attachment of a type which we do not accept (.$found_extension)
                demime = bat:btm:cmd:com:cpl:dll:exe:lnk:msi:pif:prf:reg:scr:vbs:url

        # clamav
        deny    message = This message contains a virus or other harmful content ($malware_name)
                demime = *
                malware = *

        ## spamassassin, spams are never big and spamassassin can die on big emails, so we
        ## limit its use under 500ko.
        accept  condition = ${if >={$message_size}{500k}{yes}{no}}
        warn    message = X-SA-Score: $spam_score
                spam = nobody:true
        warn    message = X-SA-Report: $spam_report
                spam = nobody:true
                condition = ${if >{$spam_score_int}{0}{true}{false}}
        warn    message = X-SA-Status: Yes
                spam = nobody:true
                condition = ${if >{$spam_score_int}{50}{true}{false}}
        deny    message = This message scored $spam_score spam points.
                spam = nobody:true
                condition = ${if >{$spam_score_int}{70}{true}{false}}
        # accept by default
        accept

IPerf — утилита командной строки, предназначенная для тестирования пропускной способности канала связи. Она включает в себя генератор TCP и UDP трафика и используется для определения скорости прохождения трафика между двумя узлами в сети. IPerf (для Windows) представляет из себя небольшой исполняемый файл размером около 100кб, в котором содержатся клиентская и серверная части. Он не требует установки, достаточно просто скопировать ее на оба компьютера и запустить сначала серверную часть программы, а затем клиентскую.

 

В самом простом варианте запуск IPerf происходит следующим образом:

iperf -s — на сервере.

iperf5

 

iperf -c 192.168.1.56 — на клиенте, где 192.168.1.56 — IP-адрес сервера.

iperf4

 

По умолчанию используется TCP порт 5001, тестирование проходит в течение 10 секунд. Этого вполне достаточно для быстрой оценки скорости соединения, однако возможности IPerf этим не ограничиваются. Например:

iperf -s -i10 -p80 — сервер прослушивает 80 порт и выдает результат с интервалом в 10 секунд.

iperf2

 

iperf -c 192.168.1.60 -p80 -t120 — клиент соединяется с 80 портом сервера, тестирование в течение 120 секунд.

iperf1

 

Приведу полный список опций программы:

  • -f -  в каком формате показывать скорость (Kbits, Mbits, KBytes, Mbytes);
  • -i -  с какими интервалами отображать промежуточные результаты;
  • -l — размер буфера (по умолчанию 8 KB);
  • -m —  показывать максимальный размер TCP сегмента (MSS);
  • -p — указать порт, по которому будет происходить соединение (по умолчанию 5001);
  • -u — использовать UDP вместо TCP;
  • -w — размер окна TCP;
  • -B — указание для сервера, на каком интерфейсе принимать трафик;
  • -C — режим совместимости со старыми версиями;
  • -M —  позволяет изменить максимальный размер TCP сегмента (MSS);
  • -N — меняет некоторые опции TCP (отключение алгоритма Нагла);
  • -V —  использовать IPV6;
  • -h — вывод справки.

Опции для сервера:

  • -s —  запустить как сервер и отображать всю информацию на экран;
  • -D — запустить как сервис (в фоновом режиме) и не отображать информацию.

Опции клиента:

  • -b — используемая полоса для UDP (по умолчанию 1Mbit/sec);
  • -c — запустить как клиент и соединиться с сервером;
  • -d — тестировать линию в обе стороны;
  • -n — установить размер передаваемого трафика (нельзя использовать c ключом -t);
  • -r — не делать двунаправленное тестирование;
  • -t - указать время тестирования (по умолчанию 10 сек);
  • -F — не генерировать трафик, а передавать готовый файл;
  • -I — ввод данных, передаваемых из STDIN (стандартный поток ввода);
  • -L — порт, на котором клиент будет принимать двунаправленный трафик;
  • -P —  запуск нескольких потоков параллельно;
  • -T - время жизни пакета для групповой рассылки (по умолчанию 1).

Программа IPerf очень проста и удобна в использовании, однако для эстетов тех, кто не любит работать в командной строке, есть графический интерфейс для IPerf, написанный на JavaJPerf. Установки он также не требует, но для его работы необходима установленная платформа Java.

iperf3

 

В общем, IPerf  — удобный инструмент для измерения скорости и качества сетевого соединения, который стоит всегда иметь под рукой. Программа абсолютно бесплатна, загрузить ее можно с сайта [urlspan]SourceForge[/urlspan].

I. Списки

      Списки позволяют определять множества, имеющие общие признаки в пределах правила — такие как IP адреса, номера портов и т.д. При загрузке наборов правило для списка «раскладывается» на отдельные правила для каждого элемента.

Синтаксис: {элементы_списка}

Замечание: запятая при перечислении элементов необязательна, достаточно пробела.

II. Макросы

Макросы — определяемые пользователем переменные, которые могут представлять собой IP адреса, номера портов, имена интерфейсов, и т.д.

      Имена макросов должны начаться с символа и могут содержать символы,цифры, и символы подчеркивания. Названия макросов не могут носить имена зарезервированных слов, типа pass, out, или queue.

Синтаксис:  <имя_макроса>="<значение>" # определение макроса
            $<имя_макроса>             # вызов макроса

Замечание:

           <значением> могут быть также списки и другие макросы, возможна рекурсия:
           <имя_макроса_списка>="{" <имя_макроса1><имя_макроса2> "}"

 

III. Таблицы

      Таблицы используются для хранения группы адресов IPv6 и/или IPv4.

Синтаксис:  table <имя_таблицы> { список_адресов }
            table <имя_таблицы> file <имя_файла>

Замечание: Поиски в таблице занимают гораздо меньше времени и потребляют меньше ресурсов, чем списки. По этой причине, таблица идеальна чтобы хранить большую группу адресов.

При определении таблиц могут использоваться следующие атрибуты:

const — содержание таблицы не может быть изменено после ее создания. Без этого атрибута pfctl ( 8 ) может использоваться для добавления или удаления элементов таблицы в любое время, при выполнении с securelevel (7), равным двум и выше.

persist — заставляет ядро сохранять таблицу в памяти, даже когда никакие правила к ней не обращаются. Без этого атрибута, ядро автоматически удалит таблицу, когда последнее правило, ссылающееся на нее будет отработано.

Управление таблицами также может осуществляться на лету, используя pfctl ( 8 ).


Синтаксис:
   pfctl -t <имя_таблицы> -T add    # добавить в таблицу
   pfctl -t <имя_таблицы> -T delete # удалить
   pfctl -t <имя_таблицы> -T show   # вывести на экран ip адреса из таблицы Это
                                    # также откроет таблицу, если она не существует.

IV. Правила фильтрации

Синтаксис: {pass,block}{in,out}[log][quick]on interface[proto{tcp,udp,...}]from SRC_IP[port SRC_PORT] to DST_IP[port DST_PORT][tcp_flags][state]

pass — разрешить
block — запретить
in/out — направление
log — указатель необходимости регистрации пакета через pflog ( 8 ) (регистрируется лишь первый пакет соединения, в противном случае log all)
quick — указатель на то, что список правил далее не должен просматривается и к пакету применяется данное правило
interface — интерфейс
proto — протокол
SRC_IP — источник
SRC_PORT — порт источника
DST_IP — адресат
DST_PORT — порт адресата
tcp_flags — Определяет флаги, которые должны быть установлены в заголовке TCP при использовании proto tcp. Флаги определяются как flags check/mask.
state — Определяет, сохраняется ли информация о состоянии на пакетах, соответствующих этому правилу.

варианты:
keep state — работает с TCP, UDP, и ICMP
modulate state — работы только с TCP. PF генерирует Initial Sequence Numbers (ISNs) для пакетов, соответствующих этому правилу.
synproxy state — проксирует входящие TCP соединения. Эта опция включает функциональные возможности keep state и modulate state.

V. Инициализация управления очередями ALTQ

      Поддержка ALTQ может быть включена только путем компилирования ядра FreeBSD с соответствующими параметрами.

Замечание: ALTQ поддерживается не всеми существующими драйверами сетевых карт. Для просмотра списка поддерживаемых устройств в вашем релизе FreeBSD обратитесь к стравеб сайта PF для FreeBSDнице справочника altq (4).

Следующие параметры включат ALTQ и добавят дополнительную функциональность:

options         ALTQ
options         ALTQ_CBQ        # Class Bases Queuing (CBQ)
options         ALTQ_RED        # Random Early Detection (RED)
options         ALTQ_RIO        # RED In/Out
options         ALTQ_HFSC       # Hierarchical Packet Scheduler (HFSC)
options         ALTQ_PRIQ       # Priority Queuing (PRIQ)
options         ALTQ_NOPCC      # Required for SMP build

options ALTQ — включает подсистему ALTQ
options ALTQ_CBQ — включает Class Based Queuing (CBQ). CBQ позволяет распределять пропускную способность соединений по классам или очередям для выставления приоритетов трафика на основе правил фильтрации.
options ALTQ_RED — включает Random Early Detection ( RED ). RED используется для предотвращения перегрузки сети. RED вычисляет длину очереди и сравнивает ее с минимальной и максимальной границей очереди. Если очередь превышает максимум, все новые пакеты отбрасываются. В соответствии со своим названием, RED отбрасывает пакеты из различные соединений в произвольном порядке.
options ALTQ_RIO — включает Random Early Detection In and Out.
options ALTQ_HFSC — включает Hierarchical Fair Service Curve Packet Scheduler. (Дополнительная информация http://www-2.cs.cmu.edu/~hzhang/HFSC/main.html)
options ALTQ_PRIQ — включает Priority Queuing ( PRIQ ). PRIQ всегда пропускает трафик из более высокой очереди первым.
options ALTQ_NOPCC — включает поддержку SMP для ALTQ . Эта опция необходима для SMP систем.

VI. Правила ALTQ

      Правила для очередей также задаются в конфигурационном файле (/etc/pf.conf)

Синтаксис: altq on interface scheduler bandwidth bw qlimit qlim tbrsize size queue {queue_list}

interface — сетевой интерфейс, на котором активируется очередь.
scheduler — какой планировщик будет использован. Доступные значения cbq и priq. На интерфейсе в один момент времени можно установить только один планировщик.
bw — общая пропускная способность, доступная планировщику. Может содержать аббревиатуры b, Kb, Mb, Gb для обозначения bits, kilobits, megabits, и gigabits в секунду, конкретное значение или процент от общей пропускной способности.
qlim — максимальное число пакетов в очереди. Необязательный параметр. По умолчанию — 50
size — размер token bucket regulator в байтах. Если не определен, то устанавливается на основе ширины полосы пропускания.
queue_list — список дочерних очередей, открываемых из родительской очереди.

Очередь определяется следующим образом

Синтаксис: queue name [on interface] bandwidth bw [priority pri][qlimit qlim] scheduler (sched_options) {queue_list}

name — имя очереди. Эта запись должна быть идентична определенной в директиве altq опцией queue_list. Для cbq это также может быть запись имени очереди в предыдущей директиве queue параметре queue_list. Имя не должно быть длиннее 15 символов.
interface — сетевой интерфейс, на котором запущена очередь. Это значение опциональное и если не определено, то очередь будет работать применительно ко всем интерфейсам. bw — общая пропускная способность, доступная планировщику. Может содержать аббревиатуры b, Kb, Mb, Gb для обозначения bits, kilobits, megabits, и gigabits в секунду, конкретное значение или процент от общей пропускной способности. Указывается только при использовании планировщика cbq.
pri — приоритет очереди. Для cbq приоритет изменяется от 0 до 7, для priq диапазон от 0 до 15. Приоритет 0 считается самым низким. Если этот параметр не определен, ему назначается 1. qlim — максимальное число пакетов в очереди. Необязательный параметр. По умолчанию — 50 scheduler — используемый планировщик — cbq или priq. Должен быть таким же, как и родительская очередь.
sched_options — дополнительные опции для управления планировщиком:
default — определить очередью по умолчанию, куда будут включаться все пакеты не подходящие под остальные очереди. Может быть только одна.
red — включить Random Early Detection (RED) для этой очереди.
rio — включить RED с IN/OUT. В этом режиме RED поддерживает очереди различной длины и различные пороговые значения, по одному на каждый уровень IP Quality of Service.
ecn — включить Explicit Congestion Notification (ECN) для этой очереди. Ecn работает совместно с red.
borrow — эта очередь может заимствовать пропускную способность у других очередей. Может быть определено только при использовании cbq.

pfctl -d выключить пакет-фильтр

pfctl -e включить пакет-фильтр
pfctl -f /etc/pf.conf загрузить конфиг из /etc/pf.conf

pfctl -nf /etc/pf.conf проверить на ошибки /etc/pf.conf, но не загружать его
pfctl -R -f /etc/pf.conf загрузить только правила фитрации
pfctl -N -f /etc/pf.conf загрузить только правила NAT
pfctl -O -f /etc/pf.conf load загрузить только опции

pfctl -F all сбросить все
pfctl -F rules сбросить только правила фильтрации
pfctl -F queue сбросить только очереди
pfctl -F nat сбросить правила NAT
pfctl -F info сбросить всю статистику
pfctl -z clear очистить все счетчики

pfctl -s rules показать текущие правила фильтрации
pfctl -v -s rules show показать информацию по каждому правилу фильтрации
pfctl -vvsr показать текущие правила фильтрации с добавлением номера правила
pfctl -v -s nat показать текущие правила NAT отдельно по каждому правилу
pfctl -s nat -i xl1 показать правила NAT для интерфейса xl1
pfctl -s queue показать очереди
pfctl -s state показать текущее состояние таблиц
pfctl -s info показать статистику правил и состояние счетчиков
pfctl -s all показать все что можно

pfctl -t testtable -T show просмотр содержимого таблицы
pfctl -t testtable -T show -v более подробной статистики по каждому адресу
pfctl -t testtable -T add 192.168.1.1 добавление адресов в таблицу
pfctl -t testtable -T delete 192.168.1.1 удаление адресов из таблицы

Недавно столкнулся с проблемой, настройка двух каналов в интернет на ОС FreeBSD
Ничего абсолютно сложного не предполагалось, но все же пришлось не много почитать документацию.

Собственно задача:

1. создать шлюз с двумя выходами в интернет, один основной, другой резервный.
2. минимизировать участие человека в смене на бек канал.

Инструменты:

ОС FreeBSD 6.x, PF, perl


Решение:

FreeBSD был поставлен с минимальной установкой, единственное изменение, которое нужно сделать
это добавить в ядро модуль PF. Делается это все не сложно.

cp /usr/src/sys/i386/conf/GENERIC /usr/src/sys/i386/conf/PF
ee /usr/src/sys/i386/conf/PF

добавляем строчки:
# PF

device pf
device pflog
device pfsync

# ALTQ

options ALTQ
options ALTQ_CBQ
options ALTQ_RED
options ALTQ_RIO
options ALTQ_HFSC
options ALTQ_PRIQ
options ALTQ_NOPCC

Далее нужно сохранить и пересобрать ядро:

cd /usr/src
make buildkernel KERNCONF=PF
make installkernel KERNKONF=PF
reboot

После загрузки системы нужно подправить /etc/rc.conf
Добавить следующее:

gateway_enable=«YES»
pf_enable=«YES» # Включить PF (загрузить модуль если необходимо)
pf_rules="/etc/pf.conf" # определение правил для pf
pf_flags="« # дополнительные флаги для запуска pfctl
pflog_enable=«YES» # запустить pflogd (8)
pflog_logfile=»/var/log/pflog" # где pflogd должен сохранять протокол
pflog_flags="" # дополнительные флаги для запуска pflogd

сохраняем.

далее нужно прописать правила для PF:

ee /etc/pf.conf

# Main Setting

ext_if_1=«rl0» # IPS_1 — интерфейс первого канала
ext_if_2=«rl1» # IPS_2 — интерфейс второго канал
int_if=«ae0» # lan — интерфейс внутренний сети
lo=«lo0» # loopback

int_net=«172.21.0.0/16» # LAN NETWORK
ext_addr_1=«222.0.0.2» # IPS_1 wan
ext_addr_2=«223.0.0.2» # IPS_2 wan
int_addr=«172.21.0.1» # LAN IP

gw_1=«222.0.0.1» # IPS_1 gw
gw_2=«223.0.0.1» # IPS_2 gw

# services

tcp_svc=«ssh» — разрешенные порты на внешних интерфейсах.

#log — логирование пакетов

set loginterface ae0
set loginterface rl0
set loginterface rl1

# skip iface — не фильтровать loopback,
# многие сервисы используют данный интерфейс под системные вещи.

set skip on lo0

# scrub
scrub in all

# NAT
# Натируем внешние интерфейсы

nat on $ext_if_1 inet from !(self) -> ($ext_if_1:0) # IPS_1 nat
nat on $ext_if_2 inet from !(self) -> ($ext_if_2:0) # IPS_2 nat

# BLOCK ALL
# первоночально необходимо заблокировать весь входящий трафик

block in

# antispoof
antispoof quick for $int_if

# ICMP
# разрешаем icmp на внешних интерфейсах и маршрутизирем их по свои шлюзам
# чтобы не возникло ситуации пингуем один внешний адрес, а ответ идет по второму шлюзу.

# IPS_1
pass in on $ext_if_1 reply-to ($ext_if_1 $gw_1) inet proto icmp to ($ext_if_1) tag EXT_IF_A icmp-type echoreq code 0
pass in on $ext_if_1 inet proto icmp from ($ext_if_1:network) to ($ext_if_1) icmp-type echoreq code 0

# IPS_2
pass in on $ext_if_2 reply-to ($ext_if_2 $gw_2) inet proto icmp to ($ext_if_2) tag EXT_IF_B icmp-type echoreq code 0
pass in on $ext_if_2 inet proto icmp from ($ext_if_2:network) to ($ext_if_2) icmp-type echoreq code 0

# allow tcp ports
# разрешаем на внешних интерфейсах сервисы и маршрутизирем их, выше у нас разрешен только ssh
# для udp аналогичная запись за изменением только proto tcp на proto udp

# IPS_1
pass in on $ext_if_1 reply-to ($ext_if_1 $gw_1) inet proto tcp to ($ext_if_1) port { $tcp_svc }
pass in on $ext_if_1 inet proto tcp from ($ext_if_1:network) to ($ext_if_1) port { $tcp_svc }

# IPS_2
pass in on $ext_if_2 reply-to ($ext_if_2 $gw_2) inet proto tcp to ($ext_if_2) port { $tcp_svc }
pass in on $ext_if_2 inet proto tcp from ($ext_if_2:network) to ($ext_if_2) port { $tcp_svc }

# INCOMING ROUTE
# маршрутизирем весь входящий трафик, под условием, если пришел на тот то интерфейс,
# то отправить необходимо ответ с того-то шлюза
# плюс проставляем теги. Теги помогут нам корректно пробрасывать порты,
# допустим у нас есть терминал сервер, пробросить можно следующим образом
# rdr on $ext_if_2 proto tcp from any to $ext_addr_2 port 3389 tag EXT_IF_B -> 172.21.0.1 port 3389
# rdr on $ext_if_1 proto tcp from any to $ext_addr_1 port 3389 tag EXT_IF_A -> 172.21.0.1 port 3389

# IPS_1
pass in quick from ($ext_if_1:network) tagged EXT_IF_A keep state
pass in quick reply-to ($ext_if_1 $gw_1) tagged EXT_IF_A keep state

# IPS_2
pass in quick from ($ext_if_2:network) tagged EXT_IF_B keep state
pass in quick reply-to ($ext_if_2 $gw_2) tagged EXT_IF_B keep state

# FIREWALL
# разрешаем все во внутреннем пространстве шлюза
pass out inet from (self:network)
pass in inet proto icmp to (self:network)

# LOCAL NETWORK
# Разрешаем весь трафик на выход из локальной сети
pass quick on $int_if

# OUTGOING ROUTE
# Маршрутизирем исходящий трафик

pass out route-to ($ext_if_1 $gw_1) inet from ($ext_if_1) keep state
pass out route-to ($ext_if_2 $gw_2) inet from ($ext_if_2) keep state

pass out inet from { $ext_if_1 $ext_if_2 } to (self:network)

Этих правил достаточно чтобы обеспечить хождение по двум каналам, с правильной маршрутизацией.
В подробности впадать не буду, если захотите посмотреть, что означает каждое правило, то почитайте документацию к PF.

Далее, необходимо в rc.conf добавить основной шлюз:

ee /etc/rc.conf

defaultrouter=«222.0.0.1»

сохраняем.

Далее нужно поставить пакетик для перла NET_PING:

cd /usr/ports/net/p5-Net-Ping
make install

Далле пишем сам скрипт для переключения каналов в случае падение основного, и обратно в случае поднятия.

Сохраняем скрипт, делаем его исполняемым, и закидываем в крон на исполнение раз в минуту.

Все. Перезагружаемся и у Вас все должно работать.

#!/usr/bin/perl -w

use strict;
use warnings;
use Net::Ping;

# 1 - автоматический режим переключение канала
# 2 - принудительное переключение на второй канал.

my $action = 1;
my $p = Net::Ping->new("icmp");
my $host_gw = "222.0.0.1"; # default gw
my $gw = "223.0.0.1";
my $now = localtime time;

if($action == 1){
my $command = `netstat -rn | grep default`;
my @b = split('s+',$command,3);
if ($p->ping($host_gw,0.05)){
print "host $host_gw is okn";
if($b[1] ne $host_gw){
if($b[1] eq ""){
`route add default 222.0.0.1`;
}else{
`route change default 222.0.0.1`;
open(LOG,">>/change_route.log");
print LOG "[!] $now Route change to 222.0.0.1n";
close(LOG);
}
}
}else{
print "host $host_gw is bad.n";
if($b[1] ne $gw){
`route change default 223.0.0.1`;
open(LOG,">>/change_route.log");
print LOG "[!] $now Route change to 223.0.0.1n";
close(LOG);
}
}
$p->close();
}

if($action == 2){
my $command = `netstat -rn | grep default`;
my @b = split('s+',$command,3);
if($b[1] ne $gw){
if($b[1] eq ""){
`route add default 223.0.0.1`;
}else{
`route change default 223.0.0.1`;
open(LOG,">>/change_route.log");
print LOG "[!] $now Route change to 223.0.0.1n";
close(LOG);
}
}
}

 

 

http://habrahabr.ru/post/66851/

Итак, о задаче: есть два канала интернет, шлюз на FreeBSD

gate# uname -a
FreeBSD gate 9.0-RELEASE FreeBSD 9.0-RELEASE #0: Thu Nov  1 06:48:52 OMST 2012     root@gate:/usr/obj/usr/src/sys/GATE  amd64

Не то, чтобы необходимость, но желание создать гибкую систему с балансировкой трафика по каналам и желание получить премию от руководства.
Канал №1: безлимитка, скорость 7 Мб, реальный ip-адрес
Канал №2: безлимитка, скорость до 60Мб, реальный ip-адрес.
Со стороны провайдера были установлены шлюзы, через которые реализую DMZ на «ловушки» для хакеров, поэтому настройки PF и SQUID минимальны

Описание поднятия балансировки

Все манипуляции от пользователя root. (Подключаемся к хосту любым пользователем, затем su, пароль root).

1. Опции ядра для включения PF:
Если не считать трафик средствами PF, то второй пункт можно отключить

cd /sys/amd64/conf
cp GENERIC GATE
ee GATE

device          pf
device          pflog
options ALTQ
options ALTQ_CBQ
options ALTQ_RED
options ALTQ_RIO
options ALTQ_HFSC
options ALTQ_PRIQ
options ALTQ_NOPCC

2. сборка ядра

make kernel KERNCONF=GATE

3. В шлюз установлены 2 сетевых карты, одна интегрированная в материнскую плату:

re0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=389b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,WOL_UCAST,WOL_MCAST,WOL_MAGIC>
        inet 192.168.1.2 netmask 0xffffff00 broadcast 192.168.1.255
re1: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=389b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,WOL_UCAST,WOL_MCAST,WOL_MAGIC>
        inet 192.168.63.26 netmask 0xfffffff8 broadcast 192.168.63.31
nfe0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=82008<VLAN_MTU,WOL_MAGIC,LINKSTATE>
        inet 10.1.1.30 netmask 0xffffff00 broadcast 10.1.1.255 

re0 провайдер №1, re1 — провайдер №2. Внутренний интерфейс nfe0 (назначение ip адресов и масок сложилось исторически, я работаю с тем, что есть). И конечно же подняты vlan на внутреннем интерфейсе, раскидываю по ним систему ip видеонаблюдения и различные отделы компании.

4. Настройка rc.conf

ee /etc/rc.conf
pf_enable=«YES»
pf_rules=«/etc/pf.conf»
squid_enable=«YES»
reboot

То есть при загрузке выполняются правила из файла /etc/pf.conf.

5. Листинг pf.conf.

cat pf.conf
#Задаем переменные, где int_if - внутренний интерфейс, ext_if - внешние.
int_if=«nfe0»
ext_if=«re1»
ext_if2=«re0»
int_net=«10.1.1.0/24»
freeBSD=«10.1.1.30»
icmp_types=«{ echoreq, unreach}»
http=«80»
https=«443»
ssh=«22»

#описываем шлюзы для каждого из провайдеров.
gw1=«192.168.63.25»
gw2=«192.168.1.1»

#Для тех, кто должен идти с конкретного провайдера вводим принудительное направление траффика в обход балансировки.
#Например, клиент-банки или тендерные площадки чувствительны к смене ip-адреса при балансировке.
to_ertel=«{10.1.1.42, 10.1.1.33, 10.1.1.4, 10.1.1.12, 10.1.1.5, 10.1.1.48, 10.1.1.25, 10.1.1.3, 10.1.1.243, 10.1.1.5 }»
to_trans=«{ 10.1.1.27, 10.1.1.2, 10.1.1.181, 10.1.1.46, 10.1.1.39, 10.1.1.27, 10.1.1.31, 10.1.1.113 }»

#настройки политик файрвола - всех из вне посылать,
set block-policy drop
>set skip on lo
#собирать все пакеты перед их отправкой.
scrub in all

#Нат на первый интернет-вход
nat on $ext_if inet from any to any -> ($ext_if)
#Нат на второй интернет-вход
nat on $ext_if2 inet from any to any -> ($ext_if2)
#Проброс на SQUID
rdr on $int_if inet proto tcp from any to any port www -> 127.0.0.1 port 3128

block in from any to any
block out from any to any

#Антиспуфинг
antispoof quick for $int_if inet

#  пропускаем все исходящие пакеты на внутреннем интерфейсе
pass out on $int_if from any to $int_net

#проброс без балансировки (согласно списку адресов)
pass in quick on $int_if route-to ($ext_if2 $gw2) from $to_trans to !$int_net keep state
pass in quick on $int_if route-to ($ext_if $gw1) from $to_ertel to !$int_net keep state

#  пропускаем (quick) пакеты предназначенные самому шлюзу
pass in quick on $int_if from $int_net to $int_if
pass in quick on $int_if route-to { ($ext_if $gw1), ($ext_if2 $gw2)} round-robin sticky-address proto tcp from $int_net to any flags S/SA keep state
#Добавил sticky-address по дельному совету RicoX

#  балансировка исходящего icmp и udp трафика идущего из внутренней сети
pass in on $int_if route-to { ($ext_if $gw1), ($ext_if2 $gw2) } round-robin proto { udp, icmp } from $int_net to any keep state

#  основные "выпускаюшие" правила на внешнем интерфейсе
pass out on $ext_if proto tcp from any to any flags S/SA modulate state
pass out on $ext_if proto { udp, icmp } from any to any keep state
pass out on $ext_if2 proto tcp from any to any flags S/SA modulate state
pass out on $ext_if2 proto { udp, icmp } from any to any keep state
#  маршрутизация пакетов идущих с любого IP на $ext_if1 через $ext_gw1 и
#  пакетов идущих  на $ext_if2 через $ext_gw2
pass out on $ext_if route-to ($ext_if2 $gw2) from $ext_if2 to any
pass out on $ext_if2 route-to ($ext_if $gw1) from $ext_if to any

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

Недостаток данного пути в том, что иногда, когда что-то случается со стороны одного из провайдеров, балансировка нагрузки работать перестает.
Для решения данной проблемы были созданы конфигурационные файлы: pf.conf.ertelecom и pf.conf.trans, которые отличаются от основного файла настроек только тем, что строка:

 #pass in on $int_if route-to { ($ext_if $gw1), ($ext_if2 $gw2) } round-robin proto { udp, icmp } from $int_net to any keep state 

из них выкинута. Вместо нее появляются две строки:

pass out route-to ($ext_if $gw1) from $int_if to any
pass out route-to ($ext_if2 $gw2) from $int_if to any

одна из которых работает, в зависимости от того, какой шлюз по умолчанию установлен.

Скрипты переключения выглядит так:

cat er_conn.sh 
#!/bin/sh

GW1=«192.168.63.25»
if1=«192.168.63.26»
#отключаем файрволл
/sbin/pfctl -d
#и включаем файрволл с правилами провайдера №2
/sbin/pfctl -e -f /etc/pf.conf.dom
#Убиваем шлюз по-умолчанию
/sbin/route del default
#Включаем шлюз по-умолчанию нашего провайдера №2
/sbin/route add default $GW1

Аналогично этому скрипту работают скрипты управления включением провайдера №1 и балансировки нагрузки. Меняется только адрес шлюза и файл правил настройки pf.
В cron подключаем скрипт, который каждые несколько (на выбор) минут проверяет на ответ от шлюза:
ping -S <ip адрес шлюза провайдера> <наш реальный ip-адрес>.
Если ответа нет, автоматически подгружаем правила живого канала.

Что касается настройки SQUID, то там я сделал вообще все примитивно. Собрал squid 3 из портов, не меняя ничего, кроме настройки transparent, то есть прозрачный прокси. Привожу его конфигурационный файл:

cat squid.conf
http_port 127.0.0.1:3128 transparent
icp_port 0
hierarchy_stoplist cgi-bin ?
acl QUERY urlpath_regex cgi-bin ?
no_cache deny QUERY
cache_mem 256 MB
maximum_object_size 8092 KB
maximum_object_size_in_memory 512 KB
cache_dir ufs /bkp/var/squid/cache 2048 64 256
cache_access_log /bkp/var/squid/access.log
cache_log /bkp/var/squid/cache.log
cache_store_log /bkp/var/squid/store.log
cache_mgr root@xxx.ru
cache_effective_user squid
cache_effective_group squid
visible_hostname gate
coredump_dir /bkp/var/squid/cache
pid_filename /var/run/squid/squid.pid
acl our_networks src 10.1.1.0/24
http_access allow our_networks

Вот с этим и работаем уже около двух лет, практически ничего не вылетает, шлюз перезагружается только по прихотям электриков, пользователи забыли, что такое проблемы с доступом интернет.
Трафик считаю спиртом (cnupm), с последующей выгрузкой его логов в базу MySQL.

Небольшой бонус: когда я принял хозяйство, все IP были выставлены вручную для машин. Кроме того, к этим IP (а не именам машин) была привязка некоторых сервисов локальной сети. Поэтому родился следующий скрипт для быстрого трансфера настроек IP в формат DHCP конфигурационного файла сервера, а именно той части, которая присваивает IP по МАК-адресу.
Привожу листинг:

  gate# cat do_dhcp 
#!/bin/sh

arp -an | grep <Ваш локальный сетевой интерфейс> > /usr/arp.txt
#В IFS указан пробел в кавычках
IFS=« »
cat /usr/arp.txt | while read line
do
    set -- $line;
    echo -e «host $1 {
                    hardware ethernet $4;
                    fixed-address $2;
                }n»
done

Вывод происходит на экран терминала, но если в echo добавить после последней кавычки > /usr/dhcp.txt, то вывод будет в файл. Далее произошел copy-paste вывода в соответствующий раздел файла dhcpd.conf. Можно так:

cat dhcpd.txt >> /usr/local/etc/dhcpd.conf

http://habrahabr.ru/post/177767/

Задача: нужно в максимально короткий срок узнать, зашел ли кто под пользователем root на сервер, от какого пользователя была запущена команда su и какой IP посмел такое сделать.

Проверка каждого пользователя

По сути, все просто, если у вас работает Postfix или любой другой почтовик. Все, что вам нужно, это добавить в конец файла

/root/.bash_profile

следующую строку:

echo 'ALERT - Root Shell Access on:' `date` `who` | mail -s "Alert: Root Access from `who | cut -d"(" -f2 | cut -d")" -f1`" it@adminunix.ru
для FreeBSD
echo 'ALERT - Root Shell Access on:' `date` `who` | mail -s "Alert: Root Access from `hostname -f` `who | cut  -f2 | cut  -f1`" it@adminunix.ru

Советую заранее протестировать эту строчку, просто выполнив её. На почту должно прийти сообщение примерно такого вида:

72.22.22.1462
ALERT - Root Shell Access on: Sun Dec 11 00:58:17 MSK 2011 user pts/0 2011-12-10 21:08 (72.22.22.1462) user pts/2 2011-12-10 21:17 (72.22.22.1462)

Эту же строчку можно добавить всем пользователям, которые имеют доступ на сервер по SSH

Проверка для всех пользователей

Этот вариант стоит использовать, если важно получать сообщения о каждом зарегистрировавшемся пользователе. Для этого строчку

echo 'ALERT - Root Shell Access on:' `date` `who` | mail -s "Alert: Root Access from `who | cut -d"(" -f2 | cut -d")" -f1`" it@adminunix.ru
для FreeBSD
echo 'ALERT - Root Shell Access on:' `date` `who` | mail -s "Alert: Root Access from `hostname -f` `who | cut  -f2 | cut  -f1`" it@adminunix.ru

мы добавляем в конец файла /etc/profile. Теперь каждый раз, как какой-либо пользователь пройдет аутентификацию на сервере, вы сможете получить об этом сообщение.