Как в Linux сделать проброс портов?

Чаще всего проброс трафика используется, если мы находимся в локальной сети и от внешнего мира отделены шлюзом. Для того, чтобы открыть доступ для локальных служб (ssh, web, ftp), нам необходимо пробросить порты. Поскольку в качестве шлюза мы будем использовать сервер на Linux, то осуществлять данные действия будем с помощью iptables.

Порты в Linux

Как мы уже знаем, каждое устройство (компьютер, ноутбук, мобильный телефон, и т.д.) в сети имеет свой собственный IP-адрес. Он уникален для каждого отдельного устройства и дает возможность организовывать сетевые соединения между устройствами. Тем не менее, на отдельном устройстве может быть запущено несколько сетевых приложений одновременно. Порты предоставляют возможность идентифицировать такие сетевые приложения на отдельно взятом компьютере.

Порт — это числовой идентификатор программы или процесса, которые обслуживают сетевые соединения на заданном сетевом адресе (IP-адресе).

Например, некоторые Интернет-сервера имеют несколько одновременно работающих сетевых приложений, таких как веб-сервер (для организации доступа к веб-сайтам на этом сервере), почтовый сервер (для организации получения и отправки сообщений электронной почты) и FTP-сервер (для передачи файлов). Самому физическому серверу в данном случае назначен его уникальный IP-адрес, но если мы попытаемся создать соединение с сервером только по его IP, возникнет проблема неоднозначности — мне не знаем с каким именно приложением мы должны соединиться для обмена данными.

Проброс портов в Linux

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

Читайте также:  Команда Grep в Linux (поиск текста в файлах)

iptables -t nat -A PREROUTING --dst $EXT_IP -p tcp --dport $SRV_PORT -j DNAT --to-destination $LAN_IP

Перенаправление портов в Linux

Перенаправление портов нужно в том случае, если мы хотим «замаскировать» внутреннюю службу, обеспечив к ней доступ извне не по стандартному, а совсем по другому порту. Пусть $FAKE_PORT — обманный порт на внешнем интерфейсе шлюза, подключившись к которому мы должны попасть на адрес $LAN_IP и порт $SRV_PORT. Набор правил для iptables будет отличаться несущественно, поэтому приведу сразу пример итогового скрипта для ленивых.

#!/bin/bash
EXT_IP="xxx.xxx.xxx.xxx" # внешний, реальный IP-адрес шлюза;
INT_IP="xxx.xxx.xxx.xxx" # См. выше.
EXT_IF=eth0 # Внешний и внутренний интерфейсы.
INT_IF=eth1 # Для шлюза они вряд ли изменятся, поэтому можно прописать вручную.
FAKE_PORT=$1 # Вначале передаём скрипту "неправильный" порт на внешнем интерфейсе,
LAN_IP=$2 # затем - локальный адрес сервера
SRV_PORT=$3 # и в конце - реальный порт для подключения к серверу
# Здесь опять надо сделать проверку ввода данных, потому что операции всё ещё серьёзные.
iptables -t nat -A PREROUTING -d $EXT_IP -p tcp -m tcp --dport $FAKE_PORT -j DNAT --to-destination $LAN_IP:$SRV_PORT
iptables -t nat -A POSTROUTING -d $LAN_IP -p tcp -m tcp --dport $SRV_PORT -j SNAT --to-source $INT_IP
iptables -t nat -A OUTPUT -d $EXT_IP -p tcp -m tcp --dport $SRV_PORT -j DNAT --to-destination $LAN_IP
iptables -I FORWARD 1 -i $EXT_IF -o $INT_IF -d $LAN_IP -p tcp -m tcp --dport $SRV_PORT -j ACCEPT

Предположим, у вас есть веб-сервер в локальной сети, который должен быть доступным из дикого интернета только по https (443/tcp). Причин не использовать VPN или отдельный IP для отдельного сервера может быть много. Например, архитектура сети, отсутствие свободных IP, веб-сервер — гостевая виртуальная машина, а наш шлюз — хост )) Ну, не знаю, сами придумайте ситуацию.

Читайте также:  Вышла версия Vim 8.0 — это первое значительное обновление с 2006 года

Причем мы хитрые и хотим, чтобы наш веб сервер из интернета не был виден на 443 порту, а был доступен, скажем, на 1293 порту (примерно вот так: https://1.2.3.4:1293). Итак, мы хотим перенаправить входящие из интернет на порт 1293 на порт 443 сервера в локальной сети.

IPT="/sbin/iptables"

IF_EXT="eth0" # Внешний сетевой адаптер
IF_INT="eth1" # Внутренний сетевой адаптер

IP_EXT="1.2.3.4" # Внешний IP
IP_INT="192.168.1.1" # Внутренний IP

FAKE_PORT="1293" # Фейковый порт, доступен из интернет

LOCAL_SRV="192.168.1.28" # Web сервер в LAN
SRV_PORT="443" # Настоящий порт

# NAT
$IPT -t nat -A PREROUTING -i $IF_EXT -p tcp -d $IP_EXT --dport $FAKE_PORT -j DNAT --to $LOCAL_SRV:$SRV_PORT

# FORWARD
$IPT -A FORWARD -i $IF_EXT -o $IF_INT -d $LOCAL_SRV -p tcp --dport $SRV_PORT -j ACCEPT

Перенаправление с одного ip на другой в Linux

Не всегда перенаправление совершается из/в локальную сеть. Бывает, что необходимо сделать проброс соединения на какой-то сервер, используя промежуточный. Это может быть необходимо для дополнительной фильтрации подключаемых клиентов, для разделения нагрузки и других задач. Есть хост с публичным IP-адресом (1.1.1.1), ему может быть сопоставлено доменное имя, для которого могут быть выпущены SSL-сертификаты и прочее.

Нужно, чтобы клиенты, обращающиеся на 1.1.1.1:25, направлялись бы на 2.2.2.2:25, с 1.1.1.1:443 — на 3.3.3.3:443, а с ip 4.4.4.4 обращения на порт 3535 шли бы на 3.3.3.3:22. 1.1.1.1, 2.2.2.2, 3.3.3.3 — это публичные IP-адреса (а могут быть и внутренними, не важно, могут быть от разных ISP и вообще, в разных местах). Подключающимся клиентам это знать незачем, не придется ничего перенастраивать в почтовых программах и т.п.

#!/bin/sh

IF_EXT="eth0"
IPT="/sbin/iptables"

# flush
$IPT --flush
$IPT -t nat --flush
$IPT -t mangle --flush
$IPT -X

# loopback
$IPT -A INPUT -i lo -j ACCEPT
$IPT -A OUTPUT -o lo -j ACCEPT

# default
$IPT -P INPUT DROP
$IPT -P OUTPUT DROP
$IPT -P FORWARD DROP

# allow forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward

# NAT
$IPT -t nat -A PREROUTING -i $IF_EXT -p tcp --dport 25 -j DNAT --to 2.2.2.2:25
$IPT -t nat -A PREROUTING -i $IF_EXT -p tcp --dport 443 -j DNAT --to 3.3.3.3:443
$IPT -t nat -A PREROUTING -i $IF_EXT -p tcp -s 4.4.4.4 --dport 3535 -j DNAT --to 3.3.3.3:22

$IPT -t nat -A POSTROUTING -o $IF_EXT -j MASQUERADE

# FORWARD
$IPT -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT

$IPT -A FORWARD -i $IF_EXT -o $IF_EXT -s 4.4.4.4 -d 3.3.3.3 -j ACCEPT
$IPT -A FORWARD -i $IF_EXT -o $IF_EXT -d 2.2.2.2 -j ACCEPT
$IPT -A FORWARD -i $IF_EXT -o $IF_EXT -d 3.3.3.3 -j ACCEPT

# log
#$IPT -A FORWARD -j LOG --log-prefix " ---FORWARD-LOG--- "

# INPUT
$IPT -A INPUT -p tcp ! --syn -m state --state NEW -j DROP
$IPT -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# ssh
$IPT -A INPUT -p tcp --dport 22 -j ACCEPT

# OUTPUT
$IPT -A OUTPUT -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT

Если что-то не так, вы должны понимать, что пакет все же пришел на 1.1.1.1:25. А вот потом не ушел. Он не должен попасть никуда, кроме FORWARD. Поэтому для отлова отбрасываемого трафика в цепочке FORWARD раскомментируйте правило лога (есть в примере) и просматривайте события в реальном времени (например, «tail -f /var/log/messages»). В соответствии с тем, что вы увидите в логе, испрвьте ваши правила iptables. Все должно получиться. Когда это произойдет, не забудьте выключить логирование (иначе размер лог-файла станет огромным).

Читайте также:  Как настроить аутентификацию SSH на основе ключей для безопасных соединений