Простая настройка ipfw

В Интернете есть много пространных рассказов про то как настроить межсетевые фильтры, экраны, объединить сети и прочие сложности. Я про это писать не буду. Я напишу несколько простых рецептов и примеров, вполне готовых к использованию и рассчитанных на рядового пользователя, которые помогут ему защититься от внешнего вторжения.

Кроме того, здесь я расскажу об одной полезной для web-разработчика (и не только) дополнительной возможности FireWall.

Лично мне понадобился FireWall только когда я стал работать в большой компании в тесном сотрудничестве со многими людьми и мне пришлось открыть ssh-доступ, http-доступ на мою рабочую машину. Кроме того, моя собственная работа привела к тому, что мне пришлось открыть десятки портов, а слушали эти порты приложения, находящиеся в процессе разработки… Одним словом, мне понадобился FireWall, но я не уверен, что он жизненно необходим вам.
Что такое FireWall (коротко)

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

Это всё теория. Главное для нас то, что FireWall — это не программа, а подсистема ядра, что он может блокировать трафик и что его можно гибко настраивать.
Подготовка

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

Для тестирования, вы можете сделать это «руками» командой:

kldload ipfw

ВНИМАНИЕ! После того, как вы загрузите FireWall вступят в действие настройки по умолчанию и вы будете полностью изолированны от внешнего мира! Поэтому не торопитесь с выполнением этой команды, не прочитав, как настраивать FireWall, или не сохранив инструкции на своём локальном диске.

Чтобы модуль загружался сам при каждой загрузке системы, добавьте строчку:

ipfw_load="YES"

в файл /boot/loader.conf.

Строго говоря, /boot/loader.conf можно и не трогать, модуль подгрузится и если в /etc/rc.conf присутствует firewall_enable=»YES» (см. ниже).
Написание правил

Я не буду здесь пересказывать man 8 ipfw. Он есть в каждой системе, а в сети он есть (хотя и немного устаревший) переведённый на русский язык. Я приведу готовые примеры.

Пример 1: Можно всё

Чтобы разрешить весь трафик, достаточно одного правила

00100 pass all from any to any

Смысл этого правила понятен и без чтения документации: «Пропускай все пакеты от кого угодно к кому угодно». Это правило пригодится вам во время отладки. Неминуемо у вас будут получаться конфигурации в которых вы будете полностью отрезаны от мира. Это правило — идеальное временное решение.

Первое число это номер правила. Каждое правило пронумеровано и проверяются они в порядке возрастания номеров.

Пример 2: Паранойя

Эти правила разрешают только соединения инициированные вами. Снаружи никто даже не может попытаться подключиться. Ваша машина даже не будет «пинговаться», то есть большинство окружающих будут уверены, что она вообще выключена.

Для пользователя эти правила тоже очень жёсткие, они позволяют ему пользоваться далеко не всеми ресурсами сети.

Давайте рассмотрим эти правила по порядку.

00100 allow all from any to any via lo0
00101 deny all from any to 127.0.0.0/8
00102 deny all from 127.0.0.0/8 to any
 
00500 check-state
 
00501 deny all from any to any frag
00502 deny tcp from any to any established
 
00600 allow tcp from any to any  80 out via bge0 setup keep-state
00601 allow tcp from any to any 110 out via bge0 setup keep-state
00602 allow tcp from any to any 143 out via bge0 setup keep-state
00603 allow tcp from any to any  25 out via bge0 setup keep-state
 
00700 allow tcp from any to XX.XX.XX.XX 53 out via bge0 setup keep-state
00701 allow udp from any to XX.XX.XX.XX 53 out via bge0 keep-state
00702 allow tcp from any to YY.YY.YY.YY 53 out via bge0 setup keep-state
00703 allow udp from any to YY.YY.YY.YY 53 out via bge0 keep-state

Правила 100-102 разрешают общение вашего компьютера с самим собой. Может оказаться, что вам эти правила не нужны, но без них могут не заработать некоторые программы.

Правило 500 проверяет виртуальные правила (о них чуть ниже). Если виртуальные правила не сработали, то поиск подходящего правила продолжается.

Правила 501 и 502 запрещают все соединения, которые уже созданы.

Правила 600-603 создают виртуальные правила (keep-state), пропускающие (allow) пакеты для соединений, которые инициированы (setup) вами (out via bge0).

Кстати, имя интерфейса bge0 вам следует изменить на имя вашего интерфейса.

Причём правила 600-603 позволяют вам работать только с WWW (порт 80) и e-mail (порты 25, 110, 143). Если вам нужно работать и по другим протоколам, то добавьте правил по аналогии.

Правила 700-703 аналогичны предыдущим, они позволяют вам работать с DNS, причём только с легальными DNS вашего провайдера. Замените XX.XX.XX.XX и YY.YY.YY.YY на адреса ваших DNS. Добавьте или удалите правила по необходимости.

Пакеты, которые не удовлетворяют ни одному правилу, будут блокированы FireWall.

Таким образом мы получили набор правил, который не пропускает входящие пакеты кроме тех, которые пришли в ответ на ваш запрос. Кроме того, вы можете работать только с ограниченным кругом Интернет-сервисов. То есть если у вас заведётся какая-то программа (разве такое возможно под FreeBSD?!), желающая «выйти в интернет» у неё это скорее всего не получится. Кроме того, вы не сможете воспользоваться никакими DNS, кроме тех, которым доверяете. Это сразу резко сужает круг возможных атак.
Пример 3: Разумный компромисс

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

00100 pass all from any to any via lo0
00101 deny all from any to 127.0.0.0/8
00102 deny all from 127.0.0.0/8 to any
00500 check-state
00502 deny all from any to any frag
00501 deny tcp from any to any established
00600 allow tcp from any to any out via bge0 setup keep-state
00601 allow udp from any to any out via bge0 keep-state
# allow in to 80
00700 allow tcp from any to any 80 in via bge0 setup keep-state
# ping
02000 allow icmp from any to any out icmptype 8
02001 allow icmp from any to any in icmptype 0

Здесь разрешён весь трафик инициированный мной (на любые порты).

Правило 700 разрешает всем обращаться к Web-серверу, размещённому на моей машине. Вы можете добавить аналогичные правила для других сервисов. На пример:

00701 allow tcp from AA.AA.AA.AA to any 22 in via bge0 setup keep-state

Это правило разрешит обращаться к вашему sshd с машины с IP-адресом AA.AA.AA.AA.

Наконец, правила 2000 и 2001 разрешают вам «пинговать» других. Вас по-прежнему никто «пинговать» не сможет.

Чтобы ping на вас работал, поправьте эти правила вот так:

02000 allow icmp from any to any icmptype 8
02001 allow icmp from any to any icmptype 0

Или напишите просто:

02000 allow icmp from any to any

Это не опасно.

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

Итак, вы выбрали правило, поправили его под свои конкретные нужды и хотите его загрузить. Для загрузки правил есть программа, которая называется так же, как и модель ядра — ipfw. Существует два способа загрузки.

Первый способ — загрузить правила из файла:

ipfw /полный/путь/к/файлу

Перед загрузкой не забудьте удалить существующие правила:

ipfw -q -f flush

Второй способ более громоздок, но он более общепринятый. Для загрузки пишется скрипт, который загружает правила по одному. Традиционно, скрипт имеет примерно такую организацию:

#!/bin/sh
ipfw -q -f flush       # чистим
# устанавливаем переменные
oif="bge0"             # мой интерфейс
cmd="ipfw -q add "     # для краткости
ks="keep-state"        # для краткости
# теперь загружаем, используя краткие "макросы"
$cmd 00500 check-state
$cmd 00502 deny all from any to any frag
$cmd 00501 deny tcp from any to any established
$cmd 00600 allow tcp from any to any out via $oif setup $ks
$cmd 00601 allow udp from any to any out via $oif $ks

Чтобы при старте системы всё загружалось автоматически, вам надо дописать в файл /etc/rc.conf строчки:

firewall_enable="YES"
firewall_script="/etc/rc.ipfw"

Упомянутый здесь файл /etc/rc.ipfw может называться и по другому (это имя выбрал я сам). Это просто файл-скрипт, загружающий правила.
Диагностика

Для диагностики есть две команды:

ipfw list

показывает все правила, а

ipfw show

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

Не удивляйтесь, в списках правил всегда будет присутствовать правило, которое вы явно не загружали:

65535 deny ip from any to any

Это правило по умолчанию, оно присутствует всегда и удалить его нельзя. Оно обрабатывается последним в цепочке правил и останавливает все пакеты, которые не совпали ни с одним правилом.
Ограничение трафика

Наконец, ipfw предоставляет возможность, которая никак не связана с безопасностью, но может быть очень полезной при отладке web-приложений. Ipfw может эмитировать торможение сети.

Не вдаваясь в детали (которые всегда можно прочитать в man), приведу сразу пример правил. Загрузить их можно вот такими тремя командами:

ipfw pipe 1 config bw 50Kbit/s queue 20
ipfw queue 1 config pipe 1 weight 50 queue 20
ipfw 00099 add queue 1 tcp from any 9090 to any

Первой командой мы создали трубу (pipe) с скоростью пропускания 50Kbit/s и длиной очереди 20 пакетов. Второй командой мы создали очередь (queue), которая работает с нашей трубой. И третьей командой мы создали правило, которое отправляет в нашу очередь все пакеты, которые идут с порта 9090. То есть даже если вы будете соединяться сами с собой через порт 9090, то ширина канала будет ограничена 50Kbit/s.

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

Я лишь добавлю, что очерёдность выполнения этих трёх команд не важна. А чтобы удалить трубы и очереди, есть две специальные команды:

ipfw -f pipe flush
ipfw -f queue flush

Не забывайте удалять текущие настройки перед загрузкой новых.

Обработка текстов в *nix

Очень часто администраторам *nix-систем приходится выполнять различные выборки, сортировки, группировки неструктурированных данных. Благо система имеет уйму утилит для этого. Рассмотрим в примерах выборку определённых полей файла. Надо отобрать из файла данные размещённые в первой, пятой и шестой колонках файла /etc/passwd.

Существует множество решений, ниже приведены лишь несколько из них:

   1. cat /etc/passwd | awk -F: '{print $1" "$5" "$6}'
 
      ...
      avahi Avahi mDNS daemon,,, /var/run/avahi-daemon
      haldaemon Hardware abstraction layer,,, /home/haldaemon
      ...
 
   2. cat /etc/passwd | awk -F: '{print $1":"$5":"$6}'
 
      ...
      avahi:Avahi mDNS daemon,,,:/var/run/avahi-daemon
      haldaemon:Hardware abstraction layer,,,:/home/haldaemon
      ...
 
   3. cat /etc/passwd | cut -d":" -f1,5,6
 
      ...
      avahi:Avahi mDNS daemon,,,:/var/run/avahi-daemon
      haldaemon:Hardware abstraction layer,,,:/home/haldaemon
      ...
 
   4. cut -d":" -f1,5,6 /etc/passwd
 
      ...
      avahi:Avahi mDNS daemon,,,:/var/run/avahi-daemon
      haldaemon:Hardware abstraction layer,,,:/home/haldaemon
      ...

Чем отличаются друг от друга эти варианты?

Первые два варианта: 1 и 2, — используют cat и awk. Первая утилита выводит содержимое файла /etc/passwd, вторая отбирает необходимые поля. «-F:» указывает на то, что разделителем полей в потоке служит двоеточие. Обратный слэш () перед двоеточием предписывает читать двоеточие, как двоеточие и не пытаться обработать его, как спецсимвол. В общем-то, обратный слэш используется в данном случае для перестраховки. Обратите внимание, что в первом случае мы вывели поля (нумеруются начиная с 1), разделённые пробелом, а во втором — двоеточием. Если вам необходимо модифицировать вывод, используйте эту связку, если вам нужно просто вывести требуемые поля пользуйтесь вариантами три или четыре.

В третьем варианте используется связка cat и cut. Первый выводит, второй режет 🙂 Эту связку можно можифицировать так, как указано в варианте 4, так как cut может принимать на вход и поток (вариант три), и файл (вариант четыре). -d»:» — указывает считать разделителем полей двоеточие. Здесь разделитель указан в кавычках, поэтому использовать обратный слэш () нет необходимости. -f1,5,6 говорит о том, что нужно отобразить 1-ое, 5-ое и 6-ое поля. Имейте ввиду, что для cut порядок полей, пречисленных после -f, не имеет значения. Поля будут выводится так, как они расположены в файле. Другими словами, вы просто указываете какие поля вас интересуют, а не порядок вывода полей. Если вам необходимо изменить порядок полей, воспользуйтесь awk.Например, так:

cat /etc/passwd | awk -F: '{print $6" "$5" "$1}'
 
...
/var/run/avahi-daemon Avahi mDNS daemon,,, avahi
/home/haldaemon Hardware abstraction layer,,, haldaemon
...

Кроме того, awk также умеет получать данные не только из потока, но и напрямую из файла. Т.е. последний пример мы можем переписать так:

awk -F: '{print $6" "$5" "$1}' /etc/passwd
 
...
/var/run/avahi-daemon Avahi mDNS daemon,,, avahi
/home/haldaemon Hardware abstraction layer,,, haldaemon
...

Почему я чаще пользуюсь конструкцией «cat | awk» или «cat | cut»? Всё просто. Когда я пытаюсь отобрать поля, я не всегда помню их точное положение в файле. Тогда я делаю: cat /etc/passwd

Получаю полный список. После чего, уточнив нужные поля, нажимаю стрелочку вверх (в bash это вызов предыдущей команды) и дописываю | awk……..

Поиск файла по его временным атрибутам

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

Например

  • Найти все папки измененные с конца обеда вчерашнего дня
$> find . -newerct 'yesterday 14:00' -and -type d -print
  • Найти и удалить все файлы старше двух недель
$> find . -not -newerBt '2 week ago' -and -type f -unlink
  • Нужно найти все файлы, которые были созданы с трех часов ночи позавчерашнего до обеда вчерашнего дня
$> find . -newerBt '2 day ago 03:00' -and -not -newerBt 'yesterday 13:00' -and -type f -print

Список ключей, ответственных за фильтрацию по временным атрибутам

-Bmin n
Истина если разница между временем создания файла и временем начала поиска, округленная до минуты в большую сторону, составляет n минут.

-Bnewer file
Смотрите описание -newerBm.

-Btime n[smhdw]
Если не определены никакие единицы времени, этот ключ вычисляется как истина если разница между временем создания файла и временем начала поиска, округленная до 24-часового периода в большую сторону, составляет n 24-часовых периодов.
Если определены единицы времени, этот ключ вычисляется как истина если разница между временем последнего изменения информации о файле и временем начала поиска составляет n единиц времени. Пожалуйста обратитесь к описанию ключа -atime для получения информации о поддерживаемых единицах времени.

-amin n
Истина если разница между временем последнего доступа к файлу и временем начала поиска, округленная до минуты в большую сторону, составляет n минут.

-anewer file
Смотрите описание -neweram.

-atime n[smhdw]
Если не определены никакие единицы времени, этот ключ вычисляется как истина если разница между временем последнего доступа к файлу и временем начала поиска, округленная до 24-часового периода в большую сторону, составляет n 24-часовых периодов.
Если определены единицы времени, этот ключ вычисляется как истина если разница между временем последнего доступа к файлу и временем начала поиска составляет точно n единиц времени. Возможные единицы времени:

  • s секунда
  • m минута (60 секунд)
  • h час (60 минут)
  • d день (24 часов)
  • w неделя (7 дней)

В аргументе ключа единицы времени можно комбинировать в произвольном порядке. Для примера «-atime -1h30m». Единицы времени используются только совместно с модификаторами «+» или «-».

-cmin n
Истина если разница между временем последнего изменения информации о файле и временем начала поиска, округленная до минуты в большую сторону, составляет n минут.

-cnewer file
Смотрите описание -newercm.

-ctime n[smhdw]
Если не определены никакие единицы времени, этот ключ вычисляется как истина если разница между временем последнего изменения информации о файле и временем начала поиска, округленная до 24-часового периода в большую сторону, составляет n 24-часовых периодов.
Если определены единицы времени, этот ключ вычисляется как истина если разница между временем последнего изменения информации о файле и временем начала поиска составляет n единиц времени. Пожалуйста обратитесь к описанию ключа -atime для получения информации о поддерживаемых единицах времени.

-mmin n
Истина если разница между временем последнего изменения файла и временем начала поиска, округленная до минуты в большую сторону, составляет n минут.

-mtime n[smhdw]
Если не определены никакие единицы времени, этот ключ вычисляется как истина если разница между временем последнего изменения файла и временем начала поиска, округленная до 24-часового периода в большую сторону, составляет n 24-часовых периодов.
Если определены единицы времени, этот ключ вычисляется как истина если разница между временем последнего изменения файла и временем начала поиска составляет n единиц времени. Пожалуйста обратитесь к описанию ключа -atime для получения информации о поддерживаемых единицах времени.

-newer file
-mnewer file

Истина если у текущего файла более позднее время изменения чем у указанного файла.

-newerXY file
Истина если у текущего файла более позднее время доступа (X=a), время создания (X=B) или время изменения (X=m) чем время доступа (Y=a), время создания (Y=B) или время изменения (Y=m) указанного файла (file). В дополнение, если Y=t то file трактуется как время, заданное в одном из описанных в cvs(1) форматов. Отметьте, что -newermm эквивалентен -newer.

Выдержка из man для cvs(1)
Поддерживается множество вариантов форматов для описания даты, в частности ISO и Интернет. В случае, если временная зона в дате не указана явным образом, она трактуется в местной временной зоне. Примеры допустимых форматов:

  • 1 month ago
  • 2 hours ago
  • 400000 seconds ago
  • last year
  • last Monday
  • yesterday
  • a fortnight ago
  • 3/31/92 10:00:07 PST
  • January 23, 1987 10:05pm
  • 22:00 GMT

Массовое переименование файлов с помощью утилиты find

Иногда бывает жизненно необходимо переименовать кучу файлов. Например заменить пробелы в названии файлов и директорий на нижний знак подчеркивания.
Сохраняем приведенный ниже скрипт в файл с именем mass-replace.sh

#!/bin/sh
 
# Определяем глубину вложенности директорий
depth=$((`find "$1" -type d -print | 
      sed -E 's/[^/]+//g' | 
      sort | tail -n 1 | 
      wc -c`+1));
# Макрос, переименовывающий файлы и директории
replacer='new="`dirname "{}"`/`basename "{}" | sed "s/ /_/g"`"; if [ "$new" != "{}" ]; then mv "{}" "$new"; fi;';
# Обходим все уровни вложенности
while [ $depth -ge 0 ];
do
    # Находим и заменяем
    find "$1" -depth $depth -and -name '* *' -print -exec sh -c "$replacer" ;
    # Меняем уровень вложенности
    depth=$(($depth-1));
done;

Сделаем файл исполняемым

$ chmod +x mass-replace.sh

В качестве аргумента скрипту передается путь до обрабатываемой директории

$ ./mass-replace.sh ~/Warez/Music

Восстановление root-пароля в MySQL

Сменить утерянный пароль достаточно просто и требуется проделать следующие шаги:
1. Остановить mysql-сервер

$> /usr/local/etc/rc.d/mysql-server stop

2. Запустить mysqld с опцией —skip-grant-tables

$> mysqld_safe --skip-grant-tables &

3. Соединиться с mysql-сервером

$> mysql -u root

4. Установить пароль root

mysql> USE mysql;
mysql> UPDATE USER SET password=PASSWORD('root_password') WHERE USER='root' AND host='localhost';
mysql> FLUSH privileges;
mysql> quit

5. Остановить сервер

$> killall mysqld_safe

6. Запустить сервер и проверить новый пароль.