Настройка iptables от простого к сложному. Часть 3.

Давайте посмотрим, как работать с тем, что называют iptables routing, или перенаправлением пакетов при помощи iptables.
В первой и второй частях мы рассматривали настройку iptables, касающуюся блокировки пакетов, а теперь посмотрим, как не блокировать их, а направлять туда, куда нам нужно.

Маскарадинг (или маскирование)

Маскарадинг — это метод обработки пакетов, при котором пакеты передаются через некоторую машину, работающую как шлюз. Эта машина при пересылке пакетов помечает их, чтобы знать, какой машине в сети вернуть полученный ответ. Таким образом, несколько машин из внутренней сети могут обращаться к внешней сети, а извне это будет выглядеть так, как будто обращения идут от той самой машины, являющейся шлюзом. Маскарадинг связан в первую очередь с NAT (Network Address Translation), пакеты при трансляции адресов маскируются, чтобы ответ вернулся именно к источнику запроса.
Например, у нас есть некоторая локальная сеть с адресами 192.168.0.0/24, в этой сети есть шлюз с адресом 192.168.0.1, имеющий два сетевых интерфейса, eth0 и eth1. eth0 — внешний, подключенный к провайдеру, например, с адресом 192.168.100.25, eth1 — внутренний, подключенный к локальной сети, тот самый, на котором адрес 192.168.0.1. Необходимо обеспечить работу всех клиентов из локальной сети в сети Интернет таким образом, чтобы это было для них прозрачно.
В таком случае в первую очередь необходимо включить форвардинг пакетов между сетевыми интерфейсами шлюза, чтобы пропускать трафик из внутренней сети наружу. Есть два варианта, как это можно сделать. Первый — раскомментировать в файле /etc/sysctl.conf строчку

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

Этот способ заработает без перезагрузки. Можно использовать оба, а можно в скрипте, например, использовать при загрузке правил iptables только второй.
После этого мы можем задать правило для адресной трансляции:

Это правило после обработки пакетов осуществит маскирование, если пакеты из внутренней сети направлены куда-то в другую подсеть. Если нам нужно маскировать пакеты для конкретной подсети, к примеру, из одной локальной подсети (192.168.2.0/24) в другую (192.168.0.0/24), то мы можем создать следующее правило:

Перенаправление пакетов на другой порт

Перенаправление портов можно использовать для самых разных задач. Например, перенаправление пользователей из разных сетей на разные экземпляры веб-сервера, перенаправление порта на другой, если изменился порт какого-то сервиса, настройка прозрачного проксирования и так далее. Общая идея в том, что пакеты с определенного порта перенаправляются на другой порт на том же сетевом интерфейсе той же самой машины, либо на loopback’е.
Для перенаправления порта, например, 80, с внешнего интерфейса на порт 80 на loopback-интерфейсе мы можем использовать правило

Это правило позволит перенаправить пакеты на loopback, изменив назначение пакета путем трансляции адреса (Destination NAT), и после этого можно будет их отфильтровать в цепочке, которая будет задана для loopback-интерфейса. Естественно, нужно будет сделать и обратную трансляцию:

Таким образом, происходит обратный процесс, при этом изменяется Source NAT, то есть, транслируется адрес источника соединения.

Форвардинг портов на другую машину

По сути форвардинг портов на другую машину не отличается от форварда портов в пределах одной машины, но по существу это не одно и то же, поскольку пакеты будут передаваться не в пределах одной машины, как в случае с loopback-интерфейсом, когда фактически пакеты транслируются в пределах сетевого стека. Обычно форвардинг портов производится с определенного порта внешнего интерфейса на определенный порт машины во внутренней сети, поэтому между сетевыми интерфейсами должен быть настроен форвардинг. Например, проброс порта 3389 для работы удаленного рабочего стола (RDP) с внешнего сетевого интерфейса (192.168.100.25) на порт 3389 на одну из машин во внутренней сети (192.168.0.15):

Таблица форвардинга

При настройке iptables мы уже использовали таблицу форвардинга, но единственное, что мы делали — это очищали правила командой

В этой таблице не рекомендуется фильтровать трафик, рекомендуется ее использовать только для перенаправления трафика. Фильтрацию необходимо выполнять либо до форвардинга, либо уже после. В таблице FORWARD вы определяете, куда должны форвардиться пакеты, а куда нет. Например:
— разрешить форвардинг между eth0 и eth1
— разрешить форвардинг между eth0 и eth2
— запретить форвардинг между eth1 и eth2
Правила в таком случае могут выглядеть так:

либо еще короче:

Разница между DROP и REJECT

При сбросе пакетов используют обычно две цели — DROP и REJECT, при этом нужно понимать, почему вы используете именно этот вариант. Основное различие состоит в том, что при использовании DROP не будет отправлен ICMP-ответ, по которому можно будет определить, что в соединении отказано. Оно просто не пройдет. Когда вы используете REJECT, то вы явно получите ответ, что в соединении отказано. Поэтому при составлении таблиц правил iptables лучше использовать REJECT, а когда вы окончательно определитесь с конфигурацией, можно изменить REJECT на DROP.


Понравилась статья?

Подпишитесь на новости сайта и получайте новые статьи на свой почтовый ящик (один раз в неделю).


Настройка iptables от простого к сложному. Часть 3.: 14 комментариев

  1. An

    Думаю, что второе правило неправильное

    должно быть, наверное, так -s 192.168.0.15/32

  2. Александр

    Здравствуйте. Помогите пожалуйста, готову сломал с этими роутингами . Есть компутер с виртуал босом и виртуальой машиной. На хост машине поднят виртуальный интерфейс vboxnet0 с ип 192.168.56.1 и виртуалка, котрая лежит в этой подсети.

    делаю правила на хост машине

    iptables -t nat -A PREROUTING -d 192.168.56.1/32 -p tcp -m tcp —dport 8081 -j DNAT —to-destination 127.0.0.1:8081

    iptables -t nat -A POSTROUTING -s 127.0.0.1/32 -p tcp -m tcp —dport 8081 -j SNAT —to-source 192.168.56.1

    Но при попытке соединитьс на 192.168.56.1:8081 соедененние не устанавливается ни в какую.

    1. Maxim Norin Автор записи

      Здравствуйте. Честно говоря, не очень понятно, что вы хотите сделать.
      Из тех правил, что вы написали, вы пытаетесь перенаправить соединение с 192.168.56.1:8081 на 127.0.0.1 (loopback).
      Правильно ли я понимаю, что вы хотите при открытии в браузере http://192.168.56.1:8081 попадать на виртуальную машину с адресом в подсети 192.168.56.0/24?

  3. Сергей

    Здравствуйте, Максим!
    Тоже к вам за помощью :) Буду очень признателен за любой пинок в нужном направлении…
    Задача стоит следующая:
    Две машины (master, slave) соединены по протоколу Modbus tcp (порт 502). Master может писать и читать в/из slave, slave умеет только отвечать.
    Вклиниваюсь третей машиной (arper) между ними. Делаю что-то типа ARP spoofing. Пытаюсь настроить arper таким образом, чтобы не пропускать только пакеты на запись от master в slave. Пакеты на запись удается идентифицировать при помощи -m string —hex-string ‘|XX|’.
    Если создаю правило: iptables -A FORWARD -p tcp —dport 502 -m string —algo bm —from 30 —to 31 —hex-string ‘|06|’ -j DROP, то запись не проходит, но master рвет соединение после неудачной попытки записать.
    Если пытаюсь перенаправить пакет на другой slave: iptables -t nat A PREROUTING -p tcp —dport 502 -m string —algo bm —from 30 —to 31 —hex-string ‘|06|’ -j DNAT —to-destination fakeslave, то тут не работает -m string.
    Заранее спасибо!!!

    1. Maxim Norin Автор записи

      Здравствуйте, Сергей.
      Насколько помню, до пакета, в котором содержится —hex-string, при использовании -t nat, не дойдёт, потому что в таблицу NAT попадет только первый пакет.
      А в первом случае соединение рвется, скорее всего, по причине того, что мастер получает ACK/RST и считает, что слейв больше недоступен.
      К сожалению, тяжело что-то посоветовать, когда нет возможности посмотреть. Могу пару вариантов предложить:
      1) Попробовать -j REJECT. В этом случае мастер хотя бы получит ответ, можно посмотреть, как он себя поведет
      2) Посмотреть документацию по haproxy, там вроде можно было смотреть содержимое пакетов.

  4. Сергей

    Здравствуйте, Максим.
    Спасибо за очень дельную статью.
    Тоже проблема. Сервер с Ubuntu. На нем 2 интерфейса: lo и eth0 (внешний). На lo слушается порт 2755. На eth0 на порт 2754 прилетают SYN пакеты (вижу их tcpdumpом). На lo не пробрасываются.
    Правила:
    iptables -A FORWARD -i eth0 -o lo -j ACCEPT
    iptables -A FORWARD -i lo -o eth0 -j ACCEPT

    iptables -t nat -A PREROUTING -d 172.31.35.125/32 -p tcp -m tcp —dport 2754 -j DNAT —to-destination 127.0.0.1:2755
    iptables -t nat -A POSTROUTING -s 127.0.0.1/32 -p tcp -m tcp —dport 2755 -j SNAT —to-source 172.31.35.125

    iptables -A FORWARD -i eth0 -o lo -p tcp —syn —dport 2754 -m conntrack —ctstate NEW -j ACCEPT
    iptables -A FORWARD -i eth0 -o lo -m conntrack —ctstate ESTABLISHED,RELATED -j ACCEPT
    iptables -A FORWARD -i lo -o eth0 -m conntrack —ctstate ESTABLISHED,RELATED -j ACCEPT

    Форвардинг включен. Пробовал убирать 3 последние правила и действовать строго по статье. Ничего толкового. (((

    1. Maxim Norin Автор записи

      Добрый день.
      На самом деле редирект портов на loopback — это не нормальная ситуация, лучше так не делать, не уверен, что в более-менее новых версиях ядра получится даже при использовании

      sysctl -w net.ipv4.conf.all.accept_local=1

      и

      sysctl -w net.ipv4.conf.all.route_local=1

      Лучше поднять контейнер (Docker, например) и использовать стандартные средства для анонсирования порта наружу.
      Но если очень надо, попробуйте проверить сначала перенаправление порта куда-то еще, на другой компьютер, а потом попробовать настройки sysctl с «local».
      И да, как-то многовато правил :)

      1. Сергей

        Полностью согласен. Связывать между собой внутренний и внешний интерфейсы — не лучшая идея. Но дело в том, что на локальном интерфейсе порт слушается сервером sshd. И порт этот — вход в обратный ssh-туннель, который имеет выход уже на мой рабочий компьютер. Есть ли возможность запускать в контейнере демоны? И не будет ли это «из пушки по воробьям»?
        Ведь, собственно, задача довольно проста. Сделать так, чтобы клиентская часть приложения, которая стучится на сервер в определенный порт,
        попадала через обратный ssh-туннель на рабочий компьютер (это сделано для того, чтобы пройти за NAT и серый адрес провайдера).

        «…и использовать стандартные средства для анонсирования порта наружу…». Какие именно средства?

        1. Maxim Norin Автор записи

          Демоны в контейнере — это скорее нет, но можно запустить sshd с опцией -D, чтобы он работал не как демон.
          В данном случае это не «из пушки по воробьям», наоборот, это позволит один раз собрать контейнер и потом его переиспользовать столько раз, сколько надо.
          Стандартные средства — это EXPOSE port-number в Dockerfile’е.
          Ну или как вариант сконфигурировать sshd так, чтобы он слушал порт на всех интерфейсах.
          Опять же, если используется reverse port forwarding, то ему можно указать, на каком интерфейсе слушать порт.
          Если напишете конфигурацию sshd и какой командой делаете проброс порта, можно более предметно посмотреть.
          Может быть проблему можно будет как-то проще решить.

  5. Сергей

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

    Создавать туннель приходится через Putty. И, хотя команды идентичны ssh Linux, что-то у них не доработано.
    Putty (вкладка ssh>tunnels):
    Port forwarding:
    v Local ports accept connections from other hosts
    v Remote ports do the same
    Forwarded ports:
    4Rhost2404.zapto.org:2754 localhost:2755

    Логи подключения:
    2018-05-26 07:13:11 Access granted
    2018-05-26 07:13:11 Opening session as main channel
    2018-05-26 07:13:11 Opened main channel
    2018-05-26 07:13:11 Requesting remote port host2404.zapto.org:2754 forward to localhost:2755
    2018-05-26 07:13:11 Remote port forwarding from host2404.zapto.org:2754 enabled (это elastic IP — внешний адрес сервера)
    2018-05-26 07:13:11 Allocated pty (ospeed 38400bps, ispeed 38400bps)
    2018-05-26 07:13:11 Started a shell/command

    Но на сервере:
    lsof -nPE -i | grep 2754
    sshd 4897 ubuntu 9u IPv6 28224 0t0 TCP [::1]:2754 (LISTEN)
    sshd 4897 ubuntu 10u IPv4 28225 0t0 TCP 127.0.0.1:2754 (LISTEN)
    (то есть слушает все равно на локальном интерфейсе)

    Конфигурация sshd:

    # Package generated configuration file
    # See the sshd_config(5) manpage for details

    # What ports, IPs and protocols we listen for
    Port 22
    # Use these options to restrict which interfaces/protocols sshd will bind to
    #ListenAddress ::
    #ListenAddress 0.0.0.0
    Protocol 2

    #Privilege Separation is turned on for security
    UsePrivilegeSeparation yes

    # Lifetime and size of ephemeral version 1 server key
    KeyRegenerationInterval 3600

    sshd уже запускается почему-то с ключом -D.

    1. Maxim Norin Автор записи

      Ну, это смотря в каком часовом поясе.
      Еще один вопрос: у SSH сервера есть опция GatewayPorts (в файле /etc/ssh/sshd_config).
      Она как раз разрешает биндить порты на внешнем интерфейсе, при reverse tunnel, например. Какое у нее значение, yes или no?
      Если no, то надо будет поменять на yes и перезагрузить/перезапустить сервис sshd.
      И можно указать в настройках PuTTY 0.0.0.0:2754:localhost:2755 и посмотреть, прокинется ли порт на внешний интерфейс.

      1. Сергей

        Спасибо, Максим. Сердечная благодарность, всё получилось.
        Но как???!!!!! В /etc/ssh/sshd_config вообще не было опции GatewayPorts. Сервак-то амазоновский. Вписал её сам. И почему при пробросе порта надо указывать для Putty 0.0.0.0, а не внешний адрес сервера? Я, кстати, во время неудачных попыток создания обратного туннеля пробовал прописать не адрес, а DNS. Putty его съела и нормально резольвила.
        Контейнеры пока делаю с помощью LXC. Docker показался мне трудноватым для освоения: очень много документации, от которой могут начаться тошнотики, хоть у меня и свободное владение английским.

        1. Maxim Norin Автор записи

          Всегда пожалуйста.
          0.0.0.0 нужно для того, чтобы проще было проверить, что на внешнем интерфейсе порт слушается, исключив при этом обращение к dns.
          По поводу LXC и Docker прекрасно вас понимаю, сам так же начинал. Да, LXC проще, порог вхождения для использования ниже, но Docker больше подходит для масштабирования, в том числе в облаке Amazon, например. И да, у него есть свои минусы тоже. Давно собираюсь по этому поводу статью написать, но как-то руки не доходят.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.