[freebsd] policy based routing & stateful firewall

tokuchu

Есть тут отцы файрволов FreeBSD?
Задача такая: есть локальная сеть с сервером, 2 подключения в интернет, в сети адреса локальные и сервер это маскарадит наружу.
Хочется stateful firewall и так же, чтобы сервер отвечал в то же направление, с которого к нему пришёл пакет.
Вот какие есть неработающие варианты:
1) фильтровать и натить с помощью ipfilter, policy routing им же с помощью to. Не работает, т.к. входящий пакет создаёт состояние и остальные файрволом уже не обрабатываются.
2) фильтровать и натить ipfilter, роутить ipfw, forward. Тоже не работает, т.к. ipfilter в состояние включает исходящий интерфейс, а его он устанавливает по default route и не выпускает это с другого интерфейса.
3) фильтровать, натить и роутить ipfw. Сильно не исследовал, т.к. там это извратно делается в сочетании с натом, хотя, возможно если покопать, то с этим можно разобраться.
4) Натить ipfilter, а файрволить ipfw не предлагать, т.к. ipfilter и ipfw вызываются в одном порядке для входящих и исходящих пакетов, и получается, что когда пакет идёт из локальной сети, то ipfw видит его как локальный ip, а когда он идёт обратно, то он видит его как внешний, которым натится (или наоборот - не принципиально). Таким образом файрвола с состоянием не получится
Ещё для усложнения задачи: внутри сети есть сервис и его надо сделать доступным извне на обоих интефейсах, на определённом порту или, например, целый ip-адрес отобразить на внутренний. Чтобы он отправлялся обратно с того же интерфейса, на который пришёл. В ipfilter, например, пакет, приходящий из инета, поподает после ipnat и уже становится неизвестно с какого интерфейса он пришёл и наоборот - для входящего из инета неизвестно с какого интерфейса он пойдёт.
Или лучше сразу поискать гуру в рассылках того же ipfilter или FreeBSD?

Marinavo_0507

Непонятно осталось, по какому каналу выпускать соединения из локалки.

tokuchu

А как это должно повлиять на основную проблему?
Если для тебя это важно, то по default route.

Marinavo_0507

у нас было такое на ipfw и natd, кроме port forwarding
после перехода на linux конфиги сильно упростились

tokuchu

после перехода на linux конфиги сильно упростились
Я в курсе, что в Linux это простая задача.

sergey_m

Хочется stateful firewall и так же, чтобы сервер отвечал в то же направление, с которого к нему пришёл пакет.
Вот и переведи с русского на ipfw.
3) фильтровать, натить и роутить ipfw. Сильно не исследовал, т.к. там это извратно делается в сочетании с натом, хотя, возможно если покопать, то с этим можно разобраться.
Вот и разберись.
4) Натить ipfilter, а файрволить ipfw не предлагать, т.к. ipfilter и ipfw вызываются в одном порядке для входящих и исходящих пакетов, и получается, что когда пакет идёт из локальной сети, то ipfw видит его как локальный ip, а когда он идёт обратно, то он видит его как внешний, которым натится (или наоборот - не принципиально). Таким образом файрвола с состоянием не получится
Наверное у тебя FreeBSD 4.

pupsik77

3) фильтровать, натить и роутить ipfw. Сильно не исследовал, т.к. там это извратно делается в сочетании с натом, хотя, возможно если покопать, то с этим можно разобраться.
Глеб прав.
копай сюда.

sergey_m

Еще можно это сделать с помощью pf. Но тут я не советчик.

tokuchu

Вот и переведи с русского на ipfw.
Вот и разберись.
Ты по делу можешь ответить? Я спрашивал какие есть хорошие решения для этого. Или из этих слов мне сразу должно стать понятно, что ничего лучше, чем ipfw мне не светит? То так бы и стоило написать.
Наверное у тебя FreeBSD 4.
Нет, у меня 5-STABLE.

sergey_m

Ты по делу можешь ответить? Я спрашивал какие есть хорошие решения для этого. Или из этих слов мне сразу должно стать понятно, что ничего лучше, чем ipfw мне не светит? То так бы и стоило написать.
Я ответил по делу. Ты наверное хотел, чтобы я тебе набросал как это примерно будет выглядеть? Этого не будет.
Нет, у меня 5-STABLE.
Тогда очень странно, что прохождение ipfilter и ipfw не симметрично. Ты это проверил или ты опираешься на старый опыт из RELENG_4?

tokuchu

Хотя, наверное, я немного не прав... мне пост пришлось "переписывать", и я нечаянно стёр часть последнего вопроса, в котором как раз и уточнял про "красивость". Только сейчас это заметил.

tokuchu

Ты наверное хотел, чтобы я тебе набросал как это примерно будет выглядеть? Этого не будет.
ггг... Именно потому, что я представляю как это примерно будет выглядеть, я создал эту тему.
Тогда очень странно, что прохождение ipfilter и ipfw не симметрично. Ты это проверил или ты опираешься на старый опыт из RELENG_4?
У меня не было старого опыта. Я не исключаю, что у меня чего-то не вышло, т.к. я уже успел забыть что я тогда делал.
Ты уверен в том, что оно там симметрично или просто считаешь, что это было бы более логично?

tokuchu

... что ничего лучше, чем ipfw мне не светит? То так бы и стоило написать.
... Ты наверное хотел, чтобы я тебе набросал как это примерно будет выглядеть? ...
Я думал, что вполне ясно написал чего я хотел услышать.

sergey_m

Ну короче смысл в том, что различные фильтры вызываются в симметричном порядке в FreeBSD 5.3 и старше. Поэтому вполне можно совмещать ipfilter и ipfw. Но я бы этого делать всё равно не стал, т.к. не красиво. Я точно знаю, что задачу можно решить только с помощью ipfw (и natd конечно). Очень вероятно, что задачу можно решить с помощью pf.
Что же касается ipfilter, то я бы не рекомендовал им пользоваться. Автор пишет и тестирует его под Solaris, и потом добивается компилябельности и базовой работоспособности под другие ОС. Если во времена Giant-locked сетевого стека такой подход прокатывал, то сейчас нет. В ipfilter довольно много проблем при параллелизации сетевого стека. Можно ознакомиться если посмотреть какие PR принадлежат автору.

sergey_m

Ты уверен в том, что оно там симметрично или просто считаешь, что это было бы более логично?
Мамой клясться не буду и тыщу баксов тоже не поставлю. Если оно у тебя несимметрично, то это серьёзный баг, в который вообще с трудом верится. Потому что одним из фокусов 5.3-RELEASE была именно симметричность фильтров.
code reference: src/sys/net/pfil.c, pfil_list_add.

tokuchu

Сейчас поэкспериментирую с симметричностью тогда.
Ещё такой вопрос - порядок вызова для файрволов постоянен или может зависеть от порядка загрузки модулей (и вкомпилированности в ядро)?
Если я правильно тебя понял, то корявость ipfilter может и в ipnat отразиться, т.к. оно его часть или речь шла только про файрвольную составляющую?

sergey_m

Ещё такой вопрос - порядок вызова для файрволов постоянен или может зависеть от порядка загрузки модулей (и вкомпилированности в ядро)?
Да, зависит от порядка загрузки модулей. В случае статического ядра не помню какой там выходит порядок. Это конечно безпонтово, и все это признают. Хорошим решением была бы userland utility, с помощью которой этот порядок можно менять в runtime. Но т.к. этой утилиты нет, то скорее всего никто из девелоперов не использует две фильтра одновременно.
Если я правильно тебя понял, то корявость ipfilter может и в ipnat отразиться, т.к. оно его часть или речь шла только про файрвольную составляющую?
Да, совершенно верно. Всё конечно не так ужасно. У многих людей ipfilter/ipnat работает в production. Но лично меня отпугивает кол-во PR и молчание автора.

tokuchu

Написал монстра, начал его тестировать и выяснилось вот что:
100 check-state
200 skipto 400 src-ip 10.1.0.2 proto icmp keep-state
300 skipto 400 dst-ip 10.1.0.2 proto icmp keep-state
400 allow in
500 fwd 10.1.0.1 src-ip 10.1.0.0/16

Топология такая: сервер двумя интерфейсами 10.0.0.2/16 и 10.1.0.2/16 подключен к внешней сети (в данном случае 10.3.0.0/16) через шлюзы 10.0.0.1 и 10.1.0.1 соответственно. Маршрут по умолчанию через 10.0.0.1.
Если пингать сервер с машины 10.3.0.2 (расположена за шлюзами):
# ping -c 1 10.1.0.2

то имеем до:
00100 0 0 check-state
00200 0 0 skipto 400 ip from any to any src-ip 10.1.0.2 proto icmp keep-state
00300 0 0 skipto 400 ip from any to any dst-ip 10.1.0.2 proto icmp keep-state
00400 0 0 allow ip from any to any in
00500 0 0 fwd 10.1.0.1 ip from any to any src-ip 10.1.0.0/16
65535 0 0 allow ip from any to any

после:
00100 0   0 check-state
00200 0 0 skipto 400 ip from any to any src-ip 10.1.0.2 proto icmp keep-state
00300 2 168 skipto 400 ip from any to any dst-ip 10.1.0.2 proto icmp keep-state
00400 1 84 allow ip from any to any in
00500 1 84 fwd 10.1.0.1 ip from any to any src-ip 10.1.0.0/16
65535 0 0 allow ip from any to any

То есть правило fwd отрабатывает, но на шлюзе видно, что обратный пакет уходит через другой интерфейс. Если пингать внешнюю машину с сервера:
# ping -c 1 -S 10.1.0.2 10.3.0.2

то счётчики аналогичные и уходит куда надо:
00100 0   0 check-state
00200 2 168 skipto 400 ip from any to any src-ip 10.1.0.2 proto icmp keep-state
00300 0 0 skipto 400 ip from any to any dst-ip 10.1.0.2 proto icmp keep-state
00400 1 84 allow ip from any to any in
00500 1 84 fwd 10.1.0.1 ip from any to any src-ip 10.1.0.0/16
65535 0 0 allow ip from any to any

И если оставить только fdw, то тоже работает. Я чего-то не вижу в чём мог накосячить. Ядро 5.4-STABLE, собиралось с IPFIREWALL_FORWARD и IPFIREWALL_FORWARD_EXTENDED.

tokuchu

Наверное, придётся до кучи и с pf ещё поковыряться.

sergey_m

То есть правило fwd отрабатывает, но на шлюзе видно, что обратный пакет уходит через другой интерфейс.
Тут всё понятно. fwd в ipfw не есть полноценный policy routing, то есть он никаким образом не связан с обычным роутингом. То есть сначала пакет проходит по обычному роутингу, и выбирается один интерфейс. В качестве src адреса берётся адрес этого интерфейса. Потом, когда он уже посылается наружу, то попадает в ipfw, там ты делаешь fwd, и пакет уходит с другого интерфейса, но с прежним адресом. На удалённом хосте соотвестенно reply шлётся таким образом, что приходит к тебе в первый интерфейс.
Насколько мне известно states в ipfw не знают про интерфейсы, держат только квадруплет адресов и портов, поэтому всё должно работать.
Всё что ты показал я могу объяснить, но не понимаю что тебе не нравится.

tokuchu

Вообще-то нет. src у него какой нужно - 10.1.0.2, но он уходит на 10.0.0.1. Ведь к тому же он должен по идее отвечать с того же ip-адреса, на который запрос пришёл. Даже больше... чего-то я торможу - правило отрабатывает-то только для этого src и оно отрабатывает, судя по счётчику.
К тому же, как я уже сказал, если в файрволе оставить только строчку с fwd, то будет уходить куда надо. А вот в той обвязке не работает.
PS. Поботал pf, думаю, что с ним будет полегче замутить это всё.

sergey_m

К тому же, как я уже сказал, если в файрволе оставить только строчку с fwd, то будет уходить куда надо. А вот в той обвязке не работает.
Логично. Потому что allow мачтчит и пакет идёт по обычному роутингу.

tokuchu

Чего-то я тебя не понимаю... или ты меня.
allow матчит входящие пакеты и как видно один входящий он заматчил. Если fwd не матчит исходящий, то кто же его тогда матчит и кого матчит fwd?

sergey_m

Да, ты прав. Я не заметил, что у тебя в allow правиле еще есть in. Мне тяжело разобраться в сложном файрволле через общение в форуме. Если ты подозреваешь баг, то вышли мне по почте reproduce recipe, я постараюсь разобраться.
Оставить комментарий
Имя или ник:
Комментарий: