Пользователь
0,0
рейтинг
29 августа 2011 в 23:56

Администрирование → Конфигурирование iptables при помощи ferm

Ferm — это низкоуровневая надстройка над iptables, позволяющая организовывать своего рода циклы по спискам параметров iptables. Это оказывается особенно полезно при настройке сложных правил файрвола, для написания которых с использованием одного только iptables приходится повторять до умопомрачения -t filter -A INPUT -p tcp --state NEW ..., потом делать аналогичное для -p udp, в общем кто плавал — знает.

Ferm прекрасен тем, что, сохраняя всю гибкость iptables, позволяет добиваться тех же эффектов меньшими усилиями. Так, например, разрешить новые соединения на порты ftp, ssh и http и запретить все остальное (кроме связанных), можно написать вот такой конфиг:
chain INPUT {
policy DROP;
mod state state (RELATED ESTABLISHED) ACCEPT;
proto tcp dport (http ftp ssh) ACCEPT;
}


Удивительно дело, поиск дает по запросу ferm только одну статью, которая к собственно ferm никакого отношения не имеет. То ли все уже все знают и всем очевидно, то ли наоборот. Исходя из последнего, я решил написать этот топик.



Зачем же это нужно?


Для простых случаев — не нужно совершенно. Но вот у меня есть, например, софтверный роутер с двумя аплинками. Конфигурация файрвола состоит из 102 правил. Многие из них повторяются с разницей в один параметр. Если, не дай Бог, я захочу что-нибудь в нем поменять — меня ждет боль в глазах и по крайней мере минут 20 попыток вспомнить, что, где и как. Согласитесь, неприятно.

Ferm в данном случае спасает не только более читабельным, но и несколько более компактным синтаксисом. Например, на том же сервере ferm.conf (за вычетом комментариев и пустых строк) он составляет 84 строчки, конфигурируя при этом еще и ip6tables. Каждая индивидуальная сточка при этом короче. 8960 символов iptables против 2998 символов ferm.

Учитывая, что при использовании ferm не налагается практически никаких ограничений на возможности iptables, выигрыш налицо.

Кроме прочего, ferm предлагает достаточно продвинутые возможности для написания скриптовых правил, в т.ч. вызов внешних программ и условные операторы. Но это уже немного выходит за рамки топика.

Как это работает?


Ferm представляет из себя, если смотреть в корень, perl-скрипт, конвертирующий формат ferm-конфига в вызовы iptables (или формат iptables-save/restore). Соответственно, работает оно очень просто: читает конфиг в заранее заданных соглашениях и вызывает iptables с соответствующими параметрами. В общем, никакой особой магии не присутствует.

Формат конфига (вкратце)

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

Допустим, у нас есть правило iptables
iptables --protocol tcp -j ACCEPT
В ferm оно будет транслироваться как
protocol tcp ACCEPT;
Переносы строк здесь не важны, важна только точка с запятой.

Таблица и цепочка в данном случае по умолчанию (-t filter -A INPUT).

Определение таблицы и цепочки производится с помощью ключевых слов table и chain. Например,
table nat chain PREROUTING protocol tcp dport 12345 DNAT to 1.2.3.4;

В общем, правило состоит из места (table, chain), собственно правила (protocol tcp, dport 12345) и действия (DNAT to 1.2.3.4). На само правило налагаются те же ограничения, что и при использовании «чистого» iptables. Здесь table, chain, protocol, dport и DNAT являются ключевыми словами. То, что идет после них — параметры.

Теперь добавим немного магии. В качестве параметров для ключевых слов можно указывать не только собственно значения, но и списки значений (или функции, или переменные, но это опять выходит за рамки топика):
table nat chain PREROUTING protocol (tcp udp) dport (12345 54321) DNAT to 1.2.3.4;

И теперь еще немного магии — поскольку не всего можно добиться списками параметров, можно определить список ключевых слов:
table nat chain PREROUTING { protocol tcp dport 12345 DNAT to 1.2.3.4; protocol udp dport 54321 DNAT to 1.2.3.4; }

С чего начать?


Если мне удалось Вас заинтересовать, то цель топика выполнена. Начать можно с сайта проекта: ferm.foo-projects.org
В Debian, Gentoo и Fedora ferm ставится из репозитариев.

Стоит почитать man ferm, он очень подробный, что является несомненным плюсом.

Стоит посмотреть примеры (у меня они легли в /usr/share/doc/ferm-*/examples, у вас может быть иначе)

Помимо прочего, есть еще две вещи, о которых стоит знать: импорт существующей конфигурации и параметры командной строки.

Импорт

ferm поставляется с утилитой import-ferm, которая берет вывод iptables-save и записывает его вывод в формате ferm. Если этой утилите подать что-нибудь на стандартный ввод, то она интерпретирует это что-нибудь как вывод iptables-save, и не будет ничего вызывать. Если не подавать — то сама вызовет iptables-save.

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

Параметры командной строки

Которые могут очень пригодиться.
--noexec
Не вызывает iptables. Имеет смысл вместе с --lines
--lines
Показывает сгенерированный конфиг iptables (и выполняет его! чтобы просто посмотреть, используйте с --noexec)
--interactive
Применяет конфиг и спрашивает ввода пользователя. При отсутствии ввода в течение 30 секунд, возвращает старые настройки. ОЧЕНЬ полезно при редактировании правил на удаленной машине в три часа ночи :)
--fast / --slow
Первое включает режим работы через iptables-restore, второе — режим работы через вызов iptables. С версии 2.0 по умолчанию используется fast, соответственно до — slow. Есть некоторые оговорки касательно экранируемых символов в iptables-restore, поэтому в редких случаях --slow может быть стабильнее. --slow --lines будет выводить строчку непосредственно перед вызовом, что крайне удобно для отладки.

Ну и обязательным параметром идет путь к конфиг-файлу.

Остальные описаны в мануале.

P.S.



Если тема интересна, я могу углубляться дальше в формат конфига ferm. Однако надо иметь ввиду, что это будет в значительной степени вольный перевод/пересказ man ferm и примеров.
@Livid
карма
9,0
рейтинг 0,0

Самое читаемое Администрирование

Комментарии (18)

  • 0
    Занятно. Тема интересна, было приятно увидеть углубление в формат конфига.
    • +2
      # -*- shell-script -*-
      #
      # Configuration file for ferm(1).
      #

      # Interfaces
      @def $WAN_IF = eth0;
      @def $LO_IF = lo;

      # My IPs
      @def $L_NET = 0.0.0.0/20;
      @def $M_IP = 0.0.0.0;
      @def $LO_IP = 127.0.0.1;

      # SIP proviers
      @def $S_IP = 0.0.0.0;
      @def $M_IP = sip.sip.sip;
      @def $A_IP = 0.0.0.0;

      @def $PROVS_IPS = ($S_IP $M_IP $A_IP);

      # Users
      @def $SIP_USERS = ( 0.0.0.0 0.0.0.0 0.0.0.0 0.0.0.0 );

      @def $ADM_IPS = ($A_IP $M_IP);

      table filter {
      chain INPUT {
      policy ACCEPT;

      # allow local packages
      interface $LO_IP ACCEPT;

      saddr (
      $L_NET
      $PROVS_IPS
      $ADM_IPS
      $SIP_USERS
      ) proto udp dport 5060 ACCEPT;
      proto udp dport 5060 DROP;

      # connection tracking
      mod state state INVALID DROP;
      mod state state (ESTABLISHED RELATED) ACCEPT;

      # respond ping
      proto icmp ACCEPT;

      # allow SSH connections
      proto tcp dport ssh mod state state NEW mod recent {
      update seconds 180 hitcount 5 rsource name NOSSHBRUTE DROP;
      set rsource name NOSSHBRUTE ACCEPT;
      }

      saddr ( $ADM_IPS ) proto tcp dport 5038 ACCEPT;
      proto tcp dport 5038 DROP;
      }

      chain OUTPUT {
      policy ACCEPT;
      }

      chain FORWARD {
      policy DROP;
      }
      }

      Для примера сойдёт?
      • 0
        Да, спасибо большое.
      • НЛО прилетело и опубликовало эту надпись здесь
  • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      Перевод мана на русский — это задача богоугодная, однако явно не для публикации на хабре. Принципиально она выполнима, с той оговоркой, что мануал, вообще говоря, большой. Не так. БОЛЬШОЙ :) Поэтому займет оно не один вечер.
      Плюс, в общем случае мануал не заменяет выжимки из мануала — иначе как объяснить дикую популярность различных хауту?

      Ну да, над переводом мана я подумаю, может и сделаю в свободное время.
      • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    Очень интересно. Хотел бы увидеть продолжение рассказа про ferm.
  • +1
    Ferm — гениальная вещь. В свое время просмотрел все утилиты для управления iptables, ни одна из них не давала такой гибкости, которую может дать ferm. Основной плюс ferm (может для кого-то минус) — это просто небольшая надстройка над iptables, позволяющая использовать всю мощь iptables в более читаемом виде. Поэтому если не знаешь iptables, смысла использовать ferm нет, а если знаешь, то разобраться в ferm не составит труда.
    • 0
      ИМХО, чтобы строить грамотно правила, знать iptables нужно в любом случае. Можно не знать отдельных названий совпадений/действий/модулей, но для этого man'ы и придуманы. А понимать, как оно работает, к добру ли, нет ли, приходится. В таких условиях человеческая обертка воспринимается как манна небесная.
      • 0
        Многие используют какой-нибудь firehol, вообще не задумываясь о внутреннем устройстве iptables. Это удобно на домашнем сервере, быстро открыть-закрыть несколько портов не вникая в iptables.
  • 0
    # import-ferm >/dev/null 
    option 'nfmask' in line 25 not understood
    # grep nfmask /etc/iptables 
    -A OUTPUT -j CONNMARK --restore-mark --nfmask 0xffffffff --ctmask 0xffffffff
    

    Общие проблемы всех надстроек — всё-таки они не все фичи понимают/поддерживают. :(
    • 0
      В данном случае проблема решается не просто, а очень просто :)
      --- /usr/sbin/ferm 2011-08-30 09:49:03.857287358 +0400
      +++ ferm 2011-08-30 09:49:29.384287034 +0400
      @@ -285,7 +285,7 @@
      add_target_def 'BALANCE', qw(to-destination to:=to-destination);
      add_target_def 'CLASSIFY', qw(set-class);
      add_target_def 'CLUSTERIP', qw(new*0 hashmode clustermac total-nodes local-node hash-init);
      -add_target_def 'CONNMARK', qw(set-mark save-mark*0 restore-mark*0 mask);
      +add_target_def 'CONNMARK', qw(set-mark save-mark*0 restore-mark*0 mask nfmask ctmask);
      add_target_def 'CONNSECMARK', qw(save*0 restore*0);
      add_target_def 'DNAT', qw(to-destination=m to:=to-destination random*0);
      add_target_def 'DSCP', qw(set-dscp set-dscp-class);


      Это добавит поддержку и в import-ferm тоже.
      • 0
        Не всё так просто. С этими изменениями import-ferm не заработал. После прописывания этих двух ключевых слов и в import-ferm, он съел это правило и навернулся на следующем:
        # import-ferm >/dev/null 
        option 'mark' in line 26 cannot be pre-negated
        # awk 'NR==26{print}' /etc/iptables 
        -A OUTPUT -m mark ! --mark 0x0 -j ACCEPT
        
        В общем, типичное допиливание надстроек. Всё решаемо, само собой, но нафига это нужно…
        • 0
          Что-то у вас старая версия по-моему. У меня с mark все в порядке, и import подгружает таблицу из ferm.

          # ferm --version
          ferm 2.1
          • 0
            Да, у меня 1.3.3 — текущая «стабильная» в портаж Gentoo. Поставил 2.1 — она даже ещё раньше споткнулась, не доходя до 25-26 строчек:
            # import-ferm >/dev/null 
            option 'set-xmark' in line 19 not understood
            # awk 'NR==19{print}' /etc/iptables 
            -A INPUT -i ppp0 -j CONNMARK --set-xmark 0x1/0xffffffff
            

            И, сразу отвечая на Ваш второй ответ ниже — под «нафига» имелось в виду не «нафига ferm» — на этот вопрос Вы действительно ответили в топике, а «нафига дополнительные проблемы из-за использования надстроек, типичные для всех надстроек». Все тулзы такого типа как-бэ облегчают жизнь пользователю, но на практике это мины замедленного действия. Кому-то повезёт, и он на мине не подорвётся, кому-то не повезёт… И проблема не только в том, что нет 100% совместимости, но и в том, что появляется дополнительная прослойка, в которой тоже (как и в любой программе) есть баги. А отладка ситуации, когда из вроде-бы корректного конфига ferm будут генерироваться не совсем корректные правила iptables, может занять немало времени (большая часть которого уйдёт на то, чтобы прийти к мысли что нужно искать проблему именно там).
        • 0
          Ну и да, нафига это нужно написано в первом разделе топика после введения. Ограничения, налагаемые ferm'ом минимальны и практически все легко устранимы, а выигрыш вполне ощутим (имхо). Если аргументы не выглядят весомыми, ну так я не настаиваю, вкусы бывают разные. Обращать в религию я никого не собирался в общем-то.
  • 0
    Ничего себе. Разумнее использовать циклы на шелле.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.