Настройка PPPoE с шейпингом трафика для небольшой сети
Однажды возникла задача настроить раздачу интернета на пару десятков компьютеров (офисные и домашние). Коробочные решения оказались либо платными, либо достаточно сложно настраиваемыми, поэтому было принято решение использовать собственное на базе Debian Linux. Опытом его поднятия я хочу поделиться с вами в этом посте, но приведенные здесь версии могли немного устареть с момента написания мануала "для себя", так что нужно проявить некоторую внимательность. Также, нужно учитывать, что приведенное решение далеко от идеального и профессионального. Оно поможет скорее тем, кому нужно быстро поднять у себя сервер, раздающий интернет. В конце у нас будет раздача интернета через PPPoE с назначением внутренних IP клиентам, шейпинг трафика, DNS сервер и простой мониторинг текущих сессий из консоли.
Подготовка системы
aptitude update
aptitude install openssh-server mc mtr tracert nano tcpdump bind9 pppoe freeradius radiusclient1 rcconf php5
Настройка DNS-сервера
Выполняем
nano /etc/bind/named.conf.options
Добавляем в конец файла:
allow-transfer { none; };
allow-query { 10.128.0.0/16; localhost; }; //подсеть наших пользователей
allow-recursion { 10.128.0.0/16; localhost; }; //рекурсия только локальным
version "My ISP DNS"; //скрыли информацию о версии сервера
Начнем настраивать chroot-окружение для нашего сервера DNS. Создадим каталоги корневой файловой системы сервера DNS - root-dns:
mkdir -p /root-dns/etc
mkdir /root-dns/dev
mkdir -p /root-dns/var/cache/bind
mkdir -p /root-dns/var/run/bind/run
/etc/init.d/bind9 stop
nano /etc/default/bind9
Нашли строчку "OPTIONS=" и заменили:
OPTIONS="-u bind -t /root-dns"
mv /etc/bind /root-dns/etc
ln -s /root-dns/etc/bind /etc/bind
nano /etc/init.d/bind9
Нашли строчку "OPTIONS=" и заменили:
OPTIONS="-u bind -t /root-dns"
Запускаем:
nano /etc/init.d/rsyslogd
Нашли "RSYSLOGD_OPTIONS" и заменили:
RSYSLOGD_OPTIONS="-c3 -a /root-dns/dev/log"
Защитим от редактирования и удаления файл конфигурации named.conf:
chattr +i /root-dns/etc/bind/named.conf
Примечание. Не забудьте снять атрибут "i" перед редактированием файла конфигурации (chattr -i ...)
mknod /root-dns/dev/null c 1 3
mknod /root-dns/dev/random c 1 8
chmod 666 /root-dns/dev/null /root-dns/dev/random
chown -R bind:bind /root-dns/var/*
chown -R bind:bind /root-dns/etc/bind
/etc/init.d/bind9 start
Сервер PPPoE
cd /tmp
apt-get build-dep pppoe
apt-get source pppoe
cd rp-pppoe-3.8/src
./configure
Запускаем:
nano config.h
Теперь в только что созданном файле config.h нужно заменить строчку:
/* #undef HAVE_LINUX_KERNEL_PPPOE */
на строчку:
#define HAVE_LINUX_KERNEL_PPPOE 1
Выполняем:
cd ..
./debian/rules PLUGIN_PATH=/usr/lib/pppd/2.4.4/rp-pppoe.so
./debian/rules binary
cd ..
dpkg -i pppoe_3.8-3_i386.deb
Заморозим пакет в системе, чтобы предотвратить его автоматическое обновление:
echo pppoe hold | dpkg -set-selections
С этого момента нужно как минимум при каждом обновлении системы просматривать список обновлений. Если для pppoe будет выпущено обновление, закрывающее уязвимость, необходимо будет собрать пакет из свежих исходников заново, снять фиксацию пакета в системе с помощью команды:
echo pppoe install | dpkg --set-selections
Теперь в системе установлен пакет с PPPoE-сервером, позволяющим поддерживать PPPoE-соединения на уровне ядра. Для запуска сервера в этом режиме, к команде описанной в предыдущем разделе нужно добавить опцию -k: pppoe-server -I eth1 -L 192.168.0.1 -k
Запускаем:
nano /etc/ppp/pppoe-server-options
Вставляем:
logfile /var/log/pppoe.log
debug
mtu 1472
mru 1472
auth
require-pap
#require-chap
default-asyncmap
ktune
lcp-echo-interval 20
lcp-echo-failure 2
ms-dns 10.128.0.1
plugin radius.so
plugin radattr.so
10.128.0.1:
nobsdcomp
noccp
noendpoint
noipdefault
noipx
novj
receive-all
Запускаем:
nano /etc/ppp/options
В конфиге /etc/ppp/options: убираем всё, ставим:
lock
Запускаем:
nano /etc/init.d/pppoe-server
Скрипт:
#!/bin/bash
# init file for rp-pppoe server
#
# description: PPPOE kernel mode server
#
# processname: pppoe-server
# chkconfig: - 45 45
# source function library
#. /etc/rc.status
case "$1" in
start)
echo -n "Starting PPPOE server: "
/usr/sbin/pppoe-server -I eth0 -L 10.128.0.1 -R 10.128.1.1 -k
#Здесь eth0 - интерфейс, который смотрит в локальную сеть
#10.128.0.1 - IP PPPoE сервера
#10.128.1.1 - Первый IP адрес клиента
#touch /var/lock/subsys/pppoed
#rc_status -v
;;
stop)
echo -n "Shutting down PPPOE server: "
pkill pppoe-server
#rm -f /var/lock/subsys/pppoed
#rc_status -v
;;
restart)
$0 stop
$0 start
;;
status)
status pppoe-server
;;
*)
echo "Usage: pppoed {start|stop|restart|status}"
exit 1
esac
exit 0
Запускаем:
rcconf
Отключаем bind. Потом туда опять заходим и видим что он вообще исчез из списка. Внизу он таки есть, а рядом наш pppoe-server. Включили и то и то. Вышли.
cd /root-dns/etc/bind
rndc-confgen -r /dev/urandom -a
chgrp bind rndc.key
chmod +r rndc.key
nano named.conf.options
В конце файла добавляем:
controls {
inet 127.0.0.1 allow { localhost; } keys { rndc-key; };
};
include "/etc/bind/rndc.key";
Теперь ещё нужно включить форвардинг в ядре и сделать подхват правил iptables. Конфигурация пока будет тестовой.
Параметры sysctl.conf, iptables, настройки сетевых интерфейсов
Запускаем:
nano /etc/sysctl.conf
Раскомментировали, добавили чего небыло:
net.ipv4.conf.default.rp_filter=1
net.ipv4.conf.all.rp_filter=1
net.ipv4.tcp_syncookies=1
net.ipv4.ip_forward=1
kernel.panic = 1
kernel.panic_on_oops = 1
kernel.panic_on_io_nmi = 1
kernel.panic_on_unrecovered_nmi = 1
Последние 4 строчки - это перезагрузка при различных падениях системы, чтобы не ездить ради перезагрузки куда-то в другой город. Сохранили, далее делаем тестовый конфиг для iptables:
iptables -t nat -F
iptables-save > /etc/firewall.conf
nano rc.local
Теперь в rc.local у нас будет:
ifconfig eth0 up
iptables-restore </etc/firewall.conf
Далее, конфигурируем /etc/network/interfaces - тут eth0 получился локальным, для него ничего вообще не пишем, разве что auto eth0. А по поводу eth1 - если это интернет - я бы задумался о статическом внешнем айпи, в противном случае нельзя использовать SNAT, а только MASQUERADE, а он сильнее грузит систему.
Запускаем:
nano /etc/network/interfaces
Конфиг:
auto lo
iface lo inet loopback
auto eth0
auto eth1
allow-hotplug eth0 eth1
iface eth1 inet dhcp
В данном листинге eth1 - интернет через NAT и DHCP конфиг.
Пробный запуск сервера PPPoE
Далее надо протестировать PPPoE без радиуса. Для этого комментируем 2 строчки плагинов в /etc/ppp/pppoe-server-options.
Запускаем:
nano /etc/ppp/pppoe-server-options
Конфиг:
#plugin radius.so
#plugin radattr.so
Запускаем:
nano /etc/ppp/pap-secrets
Дописываем в конец либо заменяем полностью содержимое:
test * test 10.128.2.10
Запускаем pppoe-server:
/etc/init.d/pppoe-server start
Перезагрузили сервер, проверили PPPoE и инет. На любом клиенте создаем PPPoE соединение и подключаемся, используя логин и пароль test. Для того чтобы инет работал у клиента, нужно включить маскарадинг:
iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE
Работает? Хорошо, вернули как было:
iptables -t nat -F
FreeRadius
Запускаем:
nano /etc/radiusclient/servers
Заменяем содержимое файла:
127.0.0.1 my-isp-radius
Запускаем:
nano /etc/radiusclient/radiusclient.conf
Заменяем содержимое файла:
auth_order radius,local
login_tries 4
login_timeout 60
nologin /etc/nologin
issue /etc/radiusclient/issue
# RADIUS settings
authserver localhost:1812
acctserver localhost:1812
servers /etc/radiusclient/servers
dictionary /etc/radiusclient/dictionary
login_radius /usr/sbin/login.radius
# RADIUS server
seqfile /var/run/radius.seq
mapfile /etc/radiusclient/port-id-map
default_realm
radius_timeout 10
radius_retries 3
login_local /bin/login
Запускаем:
nano /etc/freeradius/radiusd.conf
Заменяем содержимое файла:
prefix = /usr
exec_prefix = /usr
sysconfdir = /etc
localstatedir = /var
sbindir = ${exec_prefix}/sbin
logdir = /var/log/freeradius
raddbdir = /etc/freeradius
radacctdir = ${logdir}/radacct
# Месторасположение конфигурационных и лог файлов.
confdir = ${raddbdir}
run_dir = ${localstatedir}/run/freeradius
# Каталог с подгружаемыми модулями.
libdir = ${exec_prefix}/lib
# Месторасположение pid-файла. Содержащего идентификатор процесса.
pidfile = ${run_dir}/freeradius.pid
# Имя пользователя и группа от которых запускается FreeRADIUS
user = freerad
group = freerad
# Максимальное время (в секундах) используемое для обработки запроса.
max_request_time = 30
# Удалить запросы которые обрабатываются более чем max_request_time
delete_blocked_requests = no
# Время ожидания (в секундах) перед очисткой reply запроса отправленного NAS.
cleanup_delay = 5
# Максимальное количество запросов хранимых сервером. Это число должно быть равно
# количеству клиентов помноженному на 256.
# К примеру для четырех клиентов оно будет 1024.
max_requests = 5120
# Закрепить за ip адресом. По умолчанию RADIUS сервер при старте принимает
# запросы со всех ip адресов.
bind_address = 127.0.0.1
# Закрепить за FreeRADIUS конкретный port. Если указан ноль,
# то значение берется из /etc/services
port = 0
# Запретить/разрешить ip адреса в dns имена.
# Включение этой опции может сильно снизить производительность.
hostname_lookups = no
# Создавать/несоздавать отладочные файлы при падении сервера.
allow_core_dumps = no
# Разрешить использование регулярных выражений.
regular_expressions = yes
extended_expressions = yes
# Записывать полный User-Name аттрибут если найден в запросе.
log_stripped_names = no
# Записывать в лог попытки авторизации.
log_auth = yes
# Записывать в логи пароли при авторизации.
# log_auth_badpass - не корректные пароли
# log_auth_goodpass - корректные пароли
log_auth_badpass = yes
log_auth_goodpass = no
# Включить/выключить коллизию пользователей.
usercollide = no
# конвертировать логин и/или пароль до или после авторизации.
lower_user = no
lower_pass = no
# удалить пробелы в логине и/или пароле.
nospace_user = no
nospace_pass = no
# настройки безопасности от возможных DoS аттак.
security {
# Максимальное допустимое количество аттрибутов в RADIUS пакете.
max_attributes = 200
# Задержка (в секундах) перед отправкой Access-Reject пакета.
reject_delay = 1
# Не отвечать на запросы Status-Server
status_server = no
}
# Конфигрурация клиентов RADIUS сервера.
# Описывается в отдельном файле.
$INCLUDE ${confdir}/clients.conf
# Отключить snmp поддержку.
snmp=no
# Настрока пула процессов.
thread pool {
# количество первоначально запущенных процессов.
start_servers = 5
# Максимально возможное количество процессов.
max_servers = 32
# Динамическая регулировка количества процессов.
min_spare_servers = 3
max_spare_servers = 10
# Количество принимаемых запросов процессом. МОжет помочь при утечках памяти в
# RADIUS сервере. Если выставить 300, процессы будут периодически перегружаться
# для уборки мусора.
max_requests_per_server = 0
}
# Секция конфигурации динамических модулей.
modules {
# Модуль PAP авторизации.
# Необходим для обработки запросов с PAP авторизацией.
# encryption_scheme указывает в каком виде хранятся пароли.
# clear - подразумевает в открытом виде.
pap {
encryption_scheme = clear
}
# Модуль CHAP авторизации.
# Необходим для обработки запросов с CHAP авторизацией.
# authtype подразумевает обработку запросов только с аттрибутом Auth-Type=CHAP
chap {
authtype = CHAP
}
# Модуль преобработки запросов.
# Т.е. перед авторизацией пакета.
preprocess {
# huntgroups - хинт группы см. файл huntgoups.
# hints - хинты.
huntgroups = ${confdir}/huntgroups
hints = ${confdir}/hints
# Обработка Cisco VSA.
with_cisco_vsa_hack = no
}
# Модуль Microsoft CHAP авторизации.
# Поддерживает так же еще и Microsoft CHAP v2
# authtype подразумевает обработку запросов только с аттрибутом Auth-Type=MS-CHAP
# use_mppe = no указывает на отсутствие компресии VPN туннеля.
mschap {
authtype = MS-CHAP
use_mppe = no
}
# Модуль записей Livingston RADIUS типа.
# usersfile содержит авторизационные записи пользователей.
# Рекомендуется использовать только для тестов и выставления значений по умолчанию.
# acctusersfile содержит пользователей подлежащих учету (аккаунтингу).
# compat - совместимость. При использовании файлов только FreeRADIUS можно отключить.
files {
usersfile = ${confdir}/users
compat = no
}
# Запись детального лога аккаунтинговых пакетов.
detail {
detailfile = ${radacctdir}/%{Client-IP-Address}/detail-%Y%m%d
detailperm = 0600
}
# Запись детального лога пакетов авторизации.
detail auth_log {
detailfile = ${radacctdir}/%{Client-IP-Address}/auth-detail-%Y%m%d
detailperm = 0600
}
# Запись детального лога reply пакетов.
detail reply_log {
detailfile = ${radacctdir}/%{Client-IP-Address}/reply-detail-%Y%m%d
detailperm = 0600
}
# Создать уникальный ключ для аккаунтинг сессии.
# Многие NAS повторно используют Acct-Session-ID.
# key перечисление аттрибутов для генерации Acct-Session-ID
acct_unique {
key = "User-Name, Acct-Session-Id, NAS-IP-Address, Client-IP-Address, NAS-Port-Id"
}
# Конфигурация авторизации и аккаунтинга посредством СУБД
# содержится в отдельном файле cakesql.conf
#$INCLUDE ${confdir}/sql.conf
#$INCLUDE ${confdir}/sql/mysql/counter.conf
}
# Авторизация
# сначала идет пакет передается в preprocess
# где может быть модифицирован.
# Далее chap mschap обрабатывают chap и mschap авторизацию.
authorize {
preprocess
chap
mschap
# Не ведем логи пакетов авторизации.
# auth_log
files
#sql
}
# Аунтификация
# Секция содержит модули доступные, для аунтификации.
authenticate {
Auth-Type PAP {
pap
}
Auth-Type CHAP {
chap
}
Auth-Type MS-CHAP {
mschap
}
}
# Преобразование аккаунтинговых пакетов.
preacct {
preprocess
}
# Секция ведения аккаунтинга.
accounting {
# Создание Acct-Session-Id если ваш NAS генрит их вполне корректно можете убрать.
acct_unique
# Не создаем detail лог.
detail
# Помещать аккаунтинговые пакеты в СУБД
#sql
}
# Секция ведения логов reply-пакетов.
post-auth {
# Не ведем детальный лог репли пакетов.
reply_log
}
log {
destination = files
file = ${logdir}/radius.log
syslog_facility = daemon
stripped_names = no
auth = no
auth_badpass = no
auth_goodpass = no
}
Запускаем:
nano /etc/freeradius/clients.conf
Заменяем содержимое файла:
client localhost {
ipaddr = 127.0.0.1
secret = my-isp-radius
require_message_authenticator = no
nastype = other
}
Запускаем:
nano /etc/freeradius/users
Конфиг:
steve Cleartext-Password := "testing"
Service-Type = Framed-User,
Framed-Protocol = PPP,
Framed-IP-Address = 10.128.13.3,
Framed-IP-Netmask = 255.255.0.0,
Framed-Routing = Broadcast-Listen,
Framed-Filter-Id = "std.ppp",
Framed-MTU = 1500,
Framed-Compression = Van-Jacobsen-TCP-IP
# тут остальные юзеры по такому же принципу
DEFAULT Framed-Protocol == PPP
Framed-Protocol = PPP,
Framed-Compression = Van-Jacobson-TCP-IP
DEFAULT Hint == "CSLIP"
Framed-Protocol = SLIP,
Framed-Compression = Van-Jacobson-TCP-IP
DEFAULT Hint == "SLIP"
Framed-Protocol = SLIP
Вообще в будущем вам нужно будет настроить связку с MySQL, но на данном этапе это не критично. При добавлении пользователя нужно перезапустить Freeradius:
/etc/init.d/freeradius restart
Теперь нужно разобраться с шейпингом трафика. Идея такова, что скорость должна регулироваться индивидуально для каждого пользователя и передаваться в виде атрибута от Radius-сервера серверу NAT. Зачем? Для того чтобы в последствии было возможно расширить структуру и распределить нагрузку между несколькими NAS.
Шейпинг трафика
Итак, подготовка. Установим Wondershaper:
aptitude install wondershaper
Этот пакет представляет собой один скрипт, работающий с CBQ/HTB приоритезацией и имеющий вменяемый формат вызова. При желании можно протестировать его работу до внедрения:
wondershaper ppp0 512 512
Естественно, ppp0 должен существовать и быть результатом соединения клиента с нашим сервером. После того как команда выполнена, можно веб-браузером клиента зайти на любой сайт, измеряющий скорость, типа
speedtest.net Speedcheck.org - и убедиться что она таки режется шейпером. Для сброса настроек без рассоединения с клиентом (кика то есть), делаем
wondershaper clear ppp0
и убеждаемся в том, что скорость опять нормальная (то есть равна скорости, которую мы берем от "старшего брата" на WAN интерфейсе. Добавление ограничений скорости нужно автоматизировать, используя простой парсер файлов /var/run/radattr.ppp*, которые появляются при успешном ppp соединении и содержат все атрибуты, передаваемые Radius-сервером серверу PPPoE. Займемся атрибутами. Дополняем содержимое файла дополнительных атрибутов /etc/freeradius/dictionary вот этим:
Запускаем:
nano /etc/freeradius/dictionary
Конфиг:
# Limit session traffic
ATTRIBUTE Session-Octets-Limit 227 integer
# What to assume as limit - 0 in+out, 1 in, 2 out, 3 max(in,out)
ATTRIBUTE Octets-Direction 228 integer
# Connection Speed Limit
ATTRIBUTE PPPD-Upstream-Speed-Limit 230 integer
ATTRIBUTE PPPD-Downstream-Speed-Limit 231 integer
ATTRIBUTE PPPD-Upstream-Speed-Limit-1 232 integer
ATTRIBUTE PPPD-Downstream-Speed-Limit-1 233 integer
ATTRIBUTE PPPD-Upstream-Speed-Limit-2 234 integer
ATTRIBUTE PPPD-Downstream-Speed-Limit-2 235 integer
ATTRIBUTE PPPD-Upstream-Speed-Limit-3 236 integer
ATTRIBUTE PPPD-Downstream-Speed-Limit-3 237 integer
ATTRIBUTE Acct-Interim-Interval 85 integer
Далее, эти же атрибуты нужно добавить в словарь Raduisclient-а. Редактируем /etc/radiusclient/dictionary, в конце добавляем то же самое:
Запускаем:
nano /etc/radiusclient/dictionary
Конфиг:
# Limit session traffic
ATTRIBUTE Session-Octets-Limit 227 integer
# What to assume as limit - 0 in+out, 1 in, 2 out, 3 max(in,out)
ATTRIBUTE Octets-Direction 228 integer
# Connection Speed Limit
ATTRIBUTE PPPD-Upstream-Speed-Limit 230 integer
ATTRIBUTE PPPD-Downstream-Speed-Limit 231 integer
ATTRIBUTE PPPD-Upstream-Speed-Limit-1 232 integer
ATTRIBUTE PPPD-Downstream-Speed-Limit-1 233 integer
ATTRIBUTE PPPD-Upstream-Speed-Limit-2 234 integer
ATTRIBUTE PPPD-Downstream-Speed-Limit-2 235 integer
ATTRIBUTE PPPD-Upstream-Speed-Limit-3 236 integer
ATTRIBUTE PPPD-Downstream-Speed-Limit-3 237 integer
ATTRIBUTE Acct-Interim-Interval 85 integer
Всё отлично, теперь они (FreeRadius и RadiusClient) друг друга поймут. Соответственно, в настройках пользователей freeradius-а появятся дополнительные строчки с указанием лимита входящей и исходящей скорости (но не обязательные). Пример /etc/freeradius/users с учетом изменений:
Запускаем:
nano /etc/freeradius/users
Конфиг:
steve Cleartext-Password := "testing"
Service-Type = Framed-User,
Framed-Protocol = PPP,
Framed-IP-Address = 10.128.13.3,
Framed-IP-Netmask = 255.255.0.0,
Framed-Routing = Broadcast-Listen,
Framed-Filter-Id = "std.ppp",
Framed-MTU = 1500,
Framed-Compression = Van-Jacobsen-TCP-IP
PPPD-Downstream-Speed-Limit = 1024,
PPPD-Upstream-Speed-Limit = 1024
# тут остальные юзеры по такому же принципу
DEFAULT Framed-Protocol == PPP
Framed-Protocol = PPP,
Framed-Compression = Van-Jacobson-TCP-IP
DEFAULT Hint == "CSLIP"
Framed-Protocol = SLIP,
Framed-Compression = Van-Jacobson-TCP-IP
DEFAULT Hint == "SLIP"
Framed-Protocol = SLIP
Теперь NAS получает сведения о скорости, но не умеет их обрабатывать. Делаем два скрипта, выполняющих парсинг переданных атрибутов при установлении соединения:
Запускаем:
nano /etc/ppp/ip-up.d/0001shaper
Скрипт:
#!/bin/sh
/root-scripts/shape-on $1 $5
exit 0
Запускаем:
nano /etc/ppp/ip-down.d/0001shaper
Скрипт:
#!/bin/sh
/root-scripts/shape-off $1 $5
exit 0
Как видно из этих листингов, вызывается некий скрипт, применяющий и отменяющий шейпинг. С этого момента - поподробнее. $1 - это переданное имя интерфейса, то есть ppp0 к примеру, $5 - это Framed-IP, то есть IP клиента PPP. Создадим заветный каталог и положим в него два файла:
mkdir /root-scripts
nano /root-scripts/shape-on
Скрипт:
#!/bin/sh
if [ -f /var/run/radattr.$1 ]
then
DOWNSPEED=`awk '/PPPD-Downstream-Speed-Limit/ {print $2}' /var/run/radattr.$1`
UPSPEED=`awk '/PPPD-Upstream-Speed-Limit/ {print $2}' /var/run/radattr.$1`
wondershaper $1 $DOWNSPEED $UPSPEED
fi
iptables -A FORWARD -s $2 -j ACCEPT
iptables -A INPUT -s $2 -j ACCEPT
iptables -t nat -A POSTROUTING -s $2 -o eth1 -j MASQUERADE
Запускаем:
nano /root-scripts/shape-off
Скрипт:
#!/bin/sh
iptables -D FORWARD -s $2 -j ACCEPT
iptables -D INPUT -s $2 -j ACCEPT
iptables -t nat -D POSTROUTING -s $2 -o eth1 -j MASQUERADE
Запускаем:
chmod +x /root-scripts/*
В листингах eth1 - это всё ещё WAN интерфейс. Итак, что мы получаем в результате: Клиент соединяется с сервером PPP, получает IP адрес. Автоматически вызывается скрипт, настраивающий шейпинг на клиентском PPP интерфейсе и включающий для него маскарадинг. При рассоединении правило маскарадинга удаляется также автоматически. Вроде как wondershaper не нужно принудительно отцеплять от pppx, во всяком случае мануалы об этом молчат и всё нормально работает на практике.
Простая защита на основе iptables
Теперь уделим немножко внимания iptables и безопасности сервера. Заменим /etc/firewall.conf:
Запускаем:
nano /etc/firewall.conf
Скрипт:
#!/bin/bash
#reset all
iptables -F
iptables -Z
iptables -t nat -F
iptables -t nat -Z
#iface config
INET_IFACE="eth1"
LO_IFACE="lo"
LO_IP="127.0.0.1"
#kernel modules
modprobe ip_tables
modprobe ip_conntrack
modprobe iptable_filter
modprobe iptable_mangle
modprobe iptable_nat
modprobe ipt_LOG
modprobe ipt_limit
modprobe ipt_state
modprobe ipt_MASQUERADE
modprobe ip_conntrack_ftp
modprobe ip_conntrack_irc
modprobe ip_nat_ftp
modprobe ip_nat_irc
#default policy
iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -P FORWARD DROP
#user chains
iptables -N bad_tcp_packets
iptables -N allowed
iptables -N tcp_packets
iptables -N udp_packets
iptables -N icmp_packets
iptables -N blocksshd
blocksshd --start
###bad_tcp_packets chain
iptables -A bad_tcp_packets -p tcp --tcp-flags SYN,ACK SYN,ACK -m state --state NEW -j REJECT --reject-with tcp-reset
#iptables -A bad_tcp_packets -p tcp ! --syn -m state --state NEW -j LOG --log-prefix "New not syn:"
iptables -A bad_tcp_packets -p tcp ! --syn -m state --state NEW -j DROP
###allowed chain
iptables -A allowed -p TCP --syn -j ACCEPT
iptables -A allowed -p TCP -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A allowed -p TCP -j DROP
###tcp_packets chain
iptables -A tcp_packets -p TCP -s 0/0 --dport 22 -j blocksshd
iptables -A tcp_packets -p TCP -s 0/0 --dport 22 -j allowed
iptables -A tcp_packets -p TCP -s 0/0 --dport 25 -j allowed
#iptables -A tcp_packets -p TCP -s 0/0 --dport 80 -j allowed
###udp_packets chain
iptables -A udp_packets -p UDP -s 0/0 --destination-port 53 -j ACCEPT
###icmp chain
iptables -A icmp_packets -p ICMP -s 0/0 --icmp-type 8 -j ACCEPT
iptables -A icmp_packets -p ICMP -s 0/0 --icmp-type 11 -j ACCEPT
###input chain
iptables -A INPUT -p TCP -j bad_tcp_packets
iptables -A INPUT -p ALL -i $LO_IFACE -j ACCEPT
iptables -A INPUT -p ALL -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p TCP -i $INET_IFACE -j tcp_packets
iptables -A INPUT -p UDP -i $INET_IFACE -j udp_packets
iptables -A INPUT -p ICMP -i $INET_IFACE -j icmp_packets
#iptables -A INPUT -m limit --limit 3/minute --limit-burst 3 -j LOG --log-level DEBUG --log-prefix "IPT INPUT packet died: "
###forward chain
iptables -A FORWARD -p tcp -j bad_tcp_packets
#custom rules
#iptables -A FORWARD -s 10.128.0.10/32 -d 192.168.1.1/32 -p tcp -j ACCEPT
iptables -A FORWARD -s 10.128.0.0/16 -d 192.168.1.1/32 -p tcp -j DROP
#/custom rules
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
#iptables -A FORWARD -m limit --limit 3/minute --limit-burst 3 -j LOG --log-level DEBUG --log-prefix "IPT FORWARD packet died: "
###OUTPUT chain
iptables -A OUTPUT -p tcp -j bad_tcp_packets
iptables -A OUTPUT -p ALL -j ACCEPT
#iptables -A OUTPUT -m limit --limit 3/minute --limit-burst 3 -j LOG --log-level DEBUG --log-prefix "IPT OUTPUT packet died: "
###mangle
iptables -t mangle -A POSTROUTING -o $INET_IFACE -j TTL --ttl-set 64
Что мы тут видим? Во-первых, выделенное правило. Оно будет отличаться в реальной конфигурации или просто отсутствовать за ненадобностью, но смысл таков: если шлюзом для сервера является ADSL модем или подобный девайс с веб-админкой, нам не нужно выдумывать хитрые пароли для входа туда. Вместо этого, мы запрещаем к ним обращаться из клиентских туннелей, но одному таки разрешаем - это будет сервисная учетная запись, которой можно на него ломиться. Само собой, она не для клиентов. Далее. Добавлены команды, включающие защиту от брутфорсинга SSH сервера. Для их работы нужно настроить blocksshd.
BlockSSHd
Для начала его нужно скачать с
blocksshd.sourceforge.net/ и разархивировать файлы куда-нибудь. Я использовал для закачки wget, но путь - понятное дело - вырезал со странички SourceForge, то есть он будет скорее всего другим:
cd /tmp
wget downloads.sourceforge.net/project/blocksshd/blocksshd/BlockSSHD%201.3/blocksshd-1.3.tar.gz?use_mirror=kent
Этот скрипт написан на Perl и требует некоторых дополнительных модулей для работы. Итак, займемся модулями:
cpan
upgrade
install Sys::Syslog
install Sys::Hostname
install File::Tail
install Tie::File
install Net::DNS
install Net::Subnets
install Getopt::Long
exit
Обращаю внимание на то, что всё это одним махом не нужно копировать и вставлять в терминал. Это потому что каждая операция требует большого количества времени для выполнения и в процессе всей установки Cpan будет задавать вопросы. На все вопросы можно просто нажимать Enter, там по умолчанию всё правильно, как ни странно.
Теперь нужно распаковать blocksshd и установить его:
cd /tmp
tar -xvzf ./blocksshd-1.3.tar.gz
cd blocksshd-1.3
make install
Ну и естественно, версия на момент написания мануала была именно эта, а может быть любая, так что нужно внимательно всё сделать а не копипастить в консоль. Осталось отредактировать конфигурацию blocksshd:
Запускаем:
nano /etc/blocksshd.conf
Конфиг:
$cfg = {
os => 'linux', # Target OS - either linux or bsd
chain => 'blocksshd', # Name of iptables or pf chain
logfile => '/var/log/secure', # Log file to monitor
logcheck => '10', # How often to check the log file
max_attempts => '4', # Max number of failures
timeout => '360', # Reset IP count if no activity after time out in seconds
unblock => '1', # Enable unblocking
unblock_timeout => '43200', # Time in seconds after which to unblock a blocked IP address
restore_blocked => '0', # Turn on checking for previously blocked IPs
log_ips => '/etc/blocksshd.list', # Log file for blocked IPs
pid_file => '/var/run/blocksshd.pid', # Location of PID file
send_email => '0', # Enable the sending of email notifications
email => 'root', # Email address to send notifications
mail => '/bin/mail', # Location of mail binary
email_whois_lookup => '1', # enable whois lookup of the blocked ip addres in the sent email
whois => '/usr/bin/whois', # location of the whois binary
sed => '/bin/sed', # location of the sed binary
iptables => '/sbin/iptables', # Location of iptables binary - only for Linux
pfctl => '/sbin/pfctl', # Location of pfctl binary - only for BSD
whitelist => [qw{
127.0.0.1/32
}], # whitelist - list of IPs that will never be blocked - IPs must be specified in the form ad$
};
#leave 1; here!
1;
В этом листинге нужно обратить внимание на выделенные строчки. Первая - максимальное число попыток входа с одного адреса, вторая - нужно ли разблокировать забаненный айпи по истечении времени в секундах, указанного в третьей строчке. Четвертая - оповещать ли по email о попытках взлома.
Теперь можно перезагрузить сервер и тестировать то что получилось.
Простейший мониторинг
Для удобства мониторинга активных сессий можно использовать скрипт, написанный "на коленке". Создадим его и настроим симлинк для простого запуска, ну и проверим конечно:
Запускаем:
nano /root-scripts/clients
Скрипт:
#!/usr/bin/php
<?
function time_since($original) {
$chunks = array(
array(60 * 60 * 24 , 'D '),
array(60 * 60 , 'h:'),
array(60 , 'min'),
);
$today = time();
$since = $today - $original;
for ($i = 0, $j = count($chunks); $i < $j; $i++) {
$seconds = $chunks[$i][0];
$name = $chunks[$i][1];
if (($count = floor($since / $seconds)) != 0) {
break;
}
}
$print = "$count{$name}";
if ($i + 1 < $j) {
$seconds2 = $chunks[$i + 1][0];
$name2 = $chunks[$i + 1][1];
if (($count2 = floor(($since - ($seconds * $count)) / $seconds2)) != 0) {
$print .= "$count2{$name2}";
}
}
return $print;
}
function execShellCmdRaw($cmd) {
ob_start();
passthru($cmd);
$buffer = ob_get_contents();
ob_end_clean();
return $buffer;
}
function rawToArray($buffer) {
return empty($buffer)?array():preg_split('/[\r\n]+/', $buffer, -1, PREG_SPLIT_NO_EMPTY);
}
function execShellCmd($cmd) {
return rawToArray(execShellCmdRaw($cmd));
}
function getNATSources() {
$nat = execShellCmd('iptables -t nat -L | grep MASQUERADE');
$nats = array();
foreach ($nat as $s) {
$pos_start = strpos($s, '--') + 2;
$pos_end = strpos($s, 'anywhere');
$n = trim(substr($s, $pos_start, $pos_end - $pos_start));
$nats[$n] = $n;
}
return $nats;
}
function getTunnelMacAddr($pid) {
$pppdc = execShellCmd('ps -p '.$pid.' -f | grep pppd');
$pppdc = $pppdc[0];
$pos_start = strpos($pppdc, '-e ') + 3;
$pos_end = strpos($pppdc, '-S \'\'');
return trim(substr($pppdc, $pos_start, $pos_end - $pos_start));
}
function getPPPTunnels() {
$ifc = execShellCmd('ifconfig -a');
$tunnels = array();
$nat = getNATSources();
foreach ($ifc as $i => $s) {
if (substr($s, 0, 3) == 'ppp') {
$ppp = array();
$ppp['interface'] = substr($s, 0, 4);
$ppp['framed_ip'] = trim(substr($ifc[$i+1], strpos($ifc[$i+1], 'P-t-P:') + 6, 12));
$RXpos_start = strpos($ifc[$i+6], '(') + 1;
$RXpos_end = strpos($ifc[$i+6], ')');
$ppp['tx'] = substr($ifc[$i+6], $RXpos_start, $RXpos_end - $RXpos_start); //TX(user) = RX(server)
$tx_sub = substr($ifc[$i+6], $RXpos_end + 1);
$TXpos_start = strpos($tx_sub, '(') + 1;
$TXpos_end = strpos($tx_sub, ')');
$ppp['rx'] = substr($tx_sub, $TXpos_start, $TXpos_end - $TXpos_start);
$ppp['nat'] = array_key_exists($ppp['framed_ip'], $nat)?'on':'off';
$pidf = '/var/run/'.$ppp['interface'].'.pid';
if (file_exists($pidf)) {
$ppp['uptime'] = time_since(filemtime($pidf));
$ppp['pid'] = trim(file_get_contents($pidf));
$ppp['mac_addr'] = getTunnelMacAddr($ppp['pid']);
$ppp['username'] = 'detectedusername';
$tunnels[] = $ppp;
}
}
}
return $tunnels;
}
function clientTable($tunnels) {
printf("\n");
printf(" %15s | ","IP Address");
printf("%20s | ","MAC Address ");
printf("%3s | ","NAT");
printf("%16s | ","User name ");
printf("%9s | ","TX (up)");
printf("%9s | ","RX (down)");
printf("%11s","Uptime ");
printf("\n ");
for ($i = 0; $i < 101; $i++) printf("-");
printf("\n");
foreach($tunnels as $tun) {
printf(" %15s | ",$tun['framed_ip']);
printf("%20s | ",$tun['mac_addr']);
printf("%3s | ",$tun['nat']);
printf("%16s | ",$tun['username']);
printf("%9s | ",$tun['tx']);
printf("%9s | ",$tun['rx']);
printf("%11s",$tun['uptime']);
printf("\n");
}
printf("\n");
}
function addRadParams(&$tunnels = NULL) {
$radusers = rawToArray(file_get_contents('/etc/freeradius/users'));
$users = array();
foreach ($radusers as $i => $s) {
if (strpos($s, 'Cleartext-Password') !== FALSE) {
$user = array();
$user['username'] = trim(substr($s, 0, strpos($s, 'Cleartext-Password')));
$si = $i + 1;
while (strpos($radusers[$si], 'Framed-IP-Address') == FALSE) $si++;
$ip_raw = explode('=', $radusers[$si]);
$ip_raw = $ip_raw[1];
$users[trim($ip_raw, ' ,')] = $user;
}
}
foreach ($tunnels as $i => $tun) {
$tunnels[$i]['username'] = $users[$tun['framed_ip']]['username'];
}
return $tunnels;
}
$t = getPPPTunnels();
addRadParams($t);
clientTable($t);
Запускаем:
chmod +x /root-scripts/clients
ln -s /root-scripts/clients /usr/bin/clients
clients
Получим что-то типа:
IP Address | MAC Address | NAT | User name | TX (up) | RX (down) | Uptime
-----------------------------------------------------------------------------------------------------
10.128.1.7 | 1:00:19:66:df:39:26 | on | room56 | 507.4 MiB | 445.5 MiB | 16h:31min
10.128.1.3 | 23:00:13:8f:70:30:03 | on | room47 | 8.2 MiB | 137.8 MiB | 1h:14min
10.128.1.5 | 3:00:a1:b0:11:74:cf | on | room50 | 19.2 MiB | 500.2 MiB | 16h:30min
Вот и всё, наш сервер готов к использованию. По своим наблюдениям могу сказать, что простого системника с 1 гб DDR1 и 2 ГГц процессором хватает для раздачи 16-мегабитного канала для 30 пользователей (больше просто не нужно было) и ощутимой нагрузки система не испытывает.
Удачи начинающим провайдерам!
Автор: kest,
Оригинал статьи