У сервера 3 iface, 2 внутренних (eth1 / 2) в разных публичных сетях, одно внешнее (eth0).
Существует служба (openvpn), которая не может привязываться к некоторым IP / iface, только ко всем или одному, но мне нужно, чтобы она принимала соединения (UDP) только на внутренних iface. По умолчанию gw - через внешний.
У меня есть рабочая настройка с двумя экземплярами службы, привязанными к 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.
Можно ли эффективно установить метку для всех исходящих пакетов DNATed "сеанса" (UDP), чтобы я мог использовать fwmark в правиле IP? Есть ли другое решение основной проблемы?
Нашел решение. Это решение должно работать для любой службы Linux, которая не может прослушивать определенные интерфейсы, но только на всех (0.0.0.0) или на одном конкретном, например MySQL, OpenVPN и многих других. Поэтому мы заставляем службу прослушивать один iface и добавляем правила netfilter / iproute2 для перенаправления всех запросов для того же протокола и порта на другой iface в нашу службу на первом iface.
"Сеанс" (несмотря на то, что он является UDP в случае OpenVPN) фактически поддерживается 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
исходный (предварительно DNAT) 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
Чтобы все это работало, необходимо ослабить захват фильтра обратного тракта на втором внутреннем экране.
# 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