Данная заметка повествует о действии REDIRECT в iptables, его ограничениях и области применения.

Iptables и REDIRECT


Действие REDIRECT предназначено для перенаправления пакетов с одного набора портов на другой внутри одной системы, не выходя за пределы хоста.

Работает REDIRECT только в цепочках PREROUTING и OUTPUT таблицы nat. Таким образом, область применения сводится только к перенаправлению с одного порта на другой. Чаще всего это используется для прозрачного прокси, когда клиент из локальной сети коннектится на 80 порт, а шлюз редиректит пакеты на локальный порт прокси:

iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 3128

Кейс


Допустим, надо сменить порт приложения только перенаправлением при помощи iptables, не трогая настроек демона. Пусть новый порт будет 5555, а порт приложения 22. Таким образом, надо сделать редирект с порта 5555 на 22.

REDIRECT и удаленный клиент


Первый шаг очевиден и будет таким же, что и в примере выше:

iptables -t nat -A PREROUTING -p tcp --dport 5555 -j REDIRECT --to-port 22

Однако, правило будет работать только для внешних клиентов и только при открытом порте приложения.

REDIRECT и локальный клиент


Предыдущее правило для самого хоста с iptables не сработает, т.к. пакеты с localhost не попадают в таблицу nat. Чтобы кейс сработал на локальной машине — надо добавить редирект в цепочку OUTPUT таблицы nat:

iptables -t nat -A OUTPUT -p tcp -s 127.0.0.1 --dport 5555 -j REDIRECT --to-ports 22

Теперь локальный клиент тоже может подключиться по 5555 порту.

REDIRECT и закрытый порт


Смысл кейса в том, чтобы использовать левый порт, а порт приложения держать закрытым, но если выполнить DROP правило в INPUT цепочке по 22 порту, то 5555 тоже перестанет отвечать. Собственно, хитрость в том, чтобы открыть порт приложения в INPUT цепочке, а дропать его в mangle:

iptables -t mangle -A PREROUTING -p tcp --dport 22 -j DROP

Полный набор правил


Редирект с сетевым и локальным доступом при закрытом порте приложения:

iptables -t nat -A PREROUTING -p tcp --dport 5555 -j REDIRECT --to-port 22
iptables -t nat -A OUTPUT -p tcp -s 127.0.0.1 --dport 5555 -j REDIRECT --to-ports 22
iptables -A INPUT -p tcp --dport 5555 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -t mangle -A PREROUTING -p tcp --dport 22 -j DROP
iptables -P INPUT DROP
Поделиться с друзьями
-->

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


  1. EvilMan
    19.03.2017 21:03
    +5

    Технически REDIRECT является подвидом DNAT, с той лишь разницей, что адрес назначения (destination) переписывается на primary-адрес интерфейса, с которого получен пакет (вот так это реализовано для IPv4 и для IPv6). Локальные же пакеты будут перенаправляться на порт на адресе 127.0.0.1. Отсюда следует одна неочевидная особенность — если вы перенаправляете пакеты на какой-то порт, но ваше приложение не слушает порт на интерфейсе, на который пришёл пакет, то ничего не заработает — счётчики на правиле будут увеличиваться, но в ответ клиенту будут улетать tcp-reset'ы или icmp-port-unreachable (если там UDP). Точно так же, например, не получится перенаправлять входящие извне пакеты приложению, которое слушает только 127.0.0.1. Если же на интерфейсе нет адреса вообще (например, если вы используете unnumbered-интерфейс), то пакеты будут отброшены. И так как это завязано на коннтраке, то не забывайте очищать таблицу коннтрака после изменения правил; а так же учтите, что под это правило попадают только пакеты соединений с состоянием NEW, то есть только первый пакет соединения, которого ещё нет в таблице коннтрака. Отловить же эти пакеты в других цепочках можно через конструкцию "-m conntrack --ctstate DNAT". Вот как-то так.


    1. mureevms
      20.03.2017 07:28

      Хотел написать про интерфейсы, но пропустил этот момент, уже не буду исправлять. Спасибо за ценное дополнение!