Ответ на тот же интерфейс, что и входящий с IP-адрес DNAT
Сервер имеет 3 iface, 2 внутренних (eth1/2) в разных публичных сетях, один внешний (eth0).
Существует служба (openvpn), которая не может связываться с некоторыми IP-адресами /ifaces, только со всеми или с одним, но мне нужно, чтобы она принимала соединения (UDP) только для внутренних iface-ов. По умолчанию gw через внешний.
У меня есть рабочая установка с 2 экземплярами службы, привязанными к IP-адресам каждого внутреннего iface и маршрутизации, установленным с помощью iproute2 (ip route add xxx table x
, ip rule add from <IP> table x
).
Можно ли DNAT входящего соединения на втором внутреннем iface (eth2) с IP первого внутреннего iface (eth1) и заставить его отвечать через тот же интерфейс (eth2)? В этом случае нет необходимости запускать второй экземпляр службы и поддерживать 2 идентичных конфига с единственным отличием IP для прослушивания.
Проблема заключается в том, что если я меняю (с DNAT) IP-адрес входящего соединения на eth2 на IP-адрес eth1, тогда правило ip основано на from <IP>
не сработает Или, лучше сказать, служба ответит через eth1, а не eth2, используя gw по умолчанию для eth1.
Можно ли эффективно установить отметку для всех исходящих пакетов DNAT-сеанса (UDP), чтобы я мог использовать fwmark в правиле ip? Любое другое решение для главной проблемы?
1 ответ
Нашел решение. Это решение должно работать для любой службы Linux, которая не может прослушивать определенные интерфейсы, но только для всех (0.0.0.0) или одного конкретного, например MySQL, OpenVPN и многих других. Поэтому мы заставляем сервис прослушивать один iface и добавляем правила netfilter/iproute2, чтобы перенаправлять все запросы на тот же протокол и порт другого iface в наш сервис на первом iface.
"Сеанс" (несмотря на то, что в случае OpenVPN он является UDP) фактически поддерживается netfilter, и существует модуль conntrack, который позволяет ссылаться на пакеты из определенного сеанса. В этом случае я добавил правило для OUTPUT в таблицу mangle, чтобы пометить все пакеты из сеансов DNAT с пометкой. И затем я использую эту метку для маршрутизации пакетов.
Итак, команды:
Определите переменные
iface_int2=eth2 # the second internal iface
ip_int2=xx.xx.xx.xx # the IP of the second internal iface
proto=udp # the protocol of the connection
service_port=1194 # the incoming service port
ip_int1=yy.yy.yy.yy # the IP of the first internal iface
ip_gw2=xx.xx.xx.1 # the IP of the default gateway for the second internal iface
Эта команда инструктирует netfilter перезаписывать IP-адрес назначения входящих соединений на нашем втором iface.
iptables -t nat -A PREROUTING -i $iface_int2 -d $ip_int2 -p $proto --dport \
$service_port -j DNAT --to $ip_int1
Эта команда указывает netfilter установить отметку для исходящих пакетов (ответ службы) перезаписанного (DNATed) входящего соединения. --ctorigdst
исходный (предварительно DNATed) IP-адрес назначения входящего соединения
iptables -t mangle -A OUTPUT -p $proto --sport $service_port -m conntrack \
--ctstate DNAT --ctorigdst $ip_int2 -j MARK --set-mark 0x75
Эта команда инструктирует iproute2 маршрутизировать помеченные набором пакеты с помощью определений маршрутов в таблице 100. Prio необходим для установки наивысшего приоритета для этого правила, поскольку он очень специфичен и не будет мешать другим правилам. Если prio не указан, правила маршрутизации для первого внутреннего iface могут получить более высокий приоритет.
ip rule add prio 10 fwmark 0x75 table 100
Эта команда добавляет шлюз по умолчанию к таблице 100
ip route add default via $ip_gw2 table 100
Чтобы все это работало, необходимо уменьшить сцепление фильтра обратного пути на втором внутреннем iface.
# rp_filter - INTEGER
# 0 - No source validation.
# 1 - Strict mode as defined in RFC3704 Strict Reverse Path
# Each incoming packet is tested against the FIB and if the interface
# is not the best reverse path the packet check will fail.
# By default failed packets are discarded.
# 2 - Loose mode as defined in RFC3704 Loose Reverse Path
# Each incoming packet's source address is also tested against the FIB
# and if the source address is not reachable via any interface
# the packet check will fail.
echo 2 > /proc/sys/net/ipv4/conf/$iface_int2/rp_filter
# -OR-
sysctl -w "net.ipv4.conf.$iface_int2.rp_filter=2"
# -OR-
echo "net.ipv4.conf.$iface_int2.rp_filter=2" >> /etc/sysctl.conf
sysctl -p /etc/sysctl.conf