Принудительное проксирование ipv6 трафика
Постановка задачи
Есть локальная сеть с IPv6. Есть маршрутизатор/фаервол на Linux и есть squid на отдельной машине, тоже под Linux.
Надо завернуть http трафик для IPv6 через сквид прозрачно для клиентов.
Есть много описаний решения аналогичной задачи, если маршрутизация и проксирование выполняются на одной и той же машине. Например:
Общая идея
Поднимаем отдельный линк между маршрутизатором и прокси. Можно либо физически выделить по сетевому интерфейсу на каждой из машин, либо поднять отдельный VLAN.
Весь http-трафик из локальной сети на маршрутизаторе заворачиваем на прокси. Чтобы не было петли исключаем из этого процесса трафик, идущий со стороны прокси через выделенный линк.
Обратный трафик от http-серверов заворачиваем на прокси через выделенный линк, он проходит через сквид и возвращается на маршрутизатор по тому же выделенному линку. Далее этот трафик направляется клиенту в локальную сеть.
+--------------+ +----------------------+ +-------------------+ | | | | | | | <-----------+ +<----------+ | | Client | | | Router | | | HTTP Server | | +------->+ | | +--------> | | | | | | | | | | | +--------------+ +--|--|----------|--|--+ +-------------------+ | | | | | | | | +--|--|----------|--|--+ | | | | | | | | +<---------+ | | | | Proxy | | | +--------------->+ | | | +----------------------+
Настройки прокси
Настраиваем выделенный линк в сторону маршрутизатора
- /etc/conf.d/net
vlans_eth0="4" vlan4_name="eth0.0004" config_eth0="null" config_eth0_0004="172.17.4.5/24 2001:DB8:0002:0004::5/64" # proxy
Объясняем сквиду, что локальный IPv6 трафик надо отправлять с конкретного адреса, трафик от локальной сети надо маркировать и слушать на порту 3129. Что значит tproxy, смотрим в доке на сквид. :)
- squid.conf
http_port 3129 tproxy acl LAN6 src 2001:DB8:2::/48 tcp_outgoing_mark 0x5 LAN6 tcp_outgoing_address 2001:DB8:2:4::5
Готовим таблицы маршрутизации и политики
#Чистим ip -6 route flush table 53 ip -6 route flush table 64 ip -6 rule del from all fwmark 0x5 ip -6 rule del from all fwmark 0x6 #Это таблица для исходящих пакетов от сквида ip -6 route add default via 2001:DB8:2:4::1 dev eth0.0004 table 53 ip -6 rule add from all fwmark 0x5 lookup 53 #Это таблица для входящих пакетов, которые надо завернуть на сквид ip -6 route add local default dev lo table 64 ip -6 rule add from all fwmark 0x6 lookup 64
Настраиваем iptables
ip6tables -t mangle -N divert ip6tables -t mangle -A divert -j MARK --set-mark 0x6 ip6tables -t mangle -A divert -j ACCEPT ip6tables -t mangle -A PREROUTING -p TCP -m socket -j divert ip6tables -t mangle -A PREROUTING -p TCP -i eth0.0004 \ -j TPROXY --tproxy-mark 0x6 --on-port 3129
Суть происходящего:
- Входящий пакет проверяется на принадлежность к уже открытым сокетам. Если данный пакет принадлежит уже установленному соединению, и сокет уже существует, мы маркируем пакет и прерываем просмотр цепочки PREROUTING. Иначе проверяем, что пакет пришёл от маршрутизатора по выделенному линку. Если это так, мы его маркируем и отправляем на порт 3129.
- На этапе принятия решения о маршрутизации срабатывает правило
from all fwmark 0x6 lookup 64
, и пакет попадает в таблицу 64, в результате чего попадает на сквид. Сквид создаёт своё соединение с HTTP-сервером, для чего отправляет пакет с меткой 5. Через правилоfrom all fwmark 0x5 lookup 53
пакет попадает в таблицу 53 и уходит на маршрутизатор через выделенный линк.
Настройки маршрутизатора
Настраиваем выделенный линк в сторону прокси сервера
- /etc/conf.d/net
vlans_eno2="4" vlan4_name="eno2.0004" config_eno2="null" config_eno2_0004="172.17.4.1/24 2001:DB8:0002:0004::1/64" # выделенный VLAN для прозрачного проксирования
Готовим сет с номерами портов, которые надо проксировать:
ipset -! destroy ip6_proxying_port_list ipset -! create ip6_proxying_port_list bitmap:port range 1-65535 ipset flush ip6_proxying_port_list ipset -! add ip6_proxying_port_list 80 ipset -! add ip6_proxying_port_list 81 ipset -! add ip6_proxying_port_list 85 ipset -! add ip6_proxying_port_list 88 ipset -! add ip6_proxying_port_list 8000 ipset -! add ip6_proxying_port_list 8001 ipset -! add ip6_proxying_port_list 8080 ipset -! add ip6_proxying_port_list 8081 ipset -! add ip6_proxying_port_list 8101 ipset -! add ip6_proxying_port_list 8108 ipset -! add ip6_proxying_port_list 8888 ipset -! add ip6_proxying_port_list 9080
Готовим таблицу маршрутизации и политики
#Чистим ip -6 route flush table 106 ip -6 rule del fwmark 0x6 2> /dev/null #Это таблица перенаправления трафика на сквид ip -6 route add 2000::/3 via 2001:DB8:2:4::5 dev eno2.0004 table 106 ip -6 rule add fwmark 0x6 priority 106 lookup 106
Настраиваем iptables
ip6tables -t mangle -A PREROUTING -p TCP \ ! -i eno2.0004 \ -m set ! --match-set "ip6_all_vlans" dst \ -m set --match-set "ip6_proxying_port_list" dst \ -j MARK --set-mark 0x6 ip6tables -t mangle -A PREROUTING -p TCP \ ! -i eno2.0004 \ -m set ! --match-set "ip6_all_vlans" src \ -m set --match-set "ip6_proxying_port_list" src \ -j MARK --set-mark 0x6
Суть происходящего:
- игнорируем пакеты со стороны прокси
- если пакет идет во внешний мир (не в локальные сети) и его dst-порт подлежит обязательному проксированию ставим метку
- если пакет идет из внешнего мира (не из локальных сетей) и его src-порт подлежит обязательному проксированию ставим метку
- отмеченные пакеты через политику
fwmark 0x6 priority 106 lookup 106
попадают в таблицу 106 и сливаются на прокси
Кроме того, надо настроить FORWARD правила (выдержка, разумеется):
ip6tables -N forward_conntrack_check ip6tables -N forward_tcp_packets echo " forward_conntrack_check" ip6tables -A forward_conntrack_check -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT ip6tables -A forward_conntrack_check -p TCP -m conntrack --ctstate INVALID -m set --match-set "ip6_proxying_port_list" dst -j RETURN ip6tables -A forward_conntrack_check -p TCP -m conntrack --ctstate INVALID -m set --match-set "ip6_proxying_port_list" src -j RETURN ip6tables -A forward_conntrack_check -m conntrack --ctstate INVALID -m limit --limit 3/minute --limit-burst 3 -j LOG --log-prefix "INVALID ctstate: " ip6tables -A forward_conntrack_check -m conntrack --ctstate INVALID -j DROP echo " forward_tcp_packets" # ОТ НАС В ТЫРНЕТ # 1. LAN во внешний мир ip6tables -A forward_tcp_packets -p TCP -m set --match-set "ip6_lan_2_inet_tcp" src,dst -j allowed_tcp_packets # 2. Сервера во внешний мир ip6tables -A forward_tcp_packets -p TCP -m set --match-set "ip6_servers_2_inet_tcp" src,dst -j allowed_tcp_packets ####################################################################### # ## FORWARD. # ####################################################################### #Проверяем чёрный список ip6tables -A FORWARD -p ALL -m set --match-set "ip6_black_list" src -m limit --limit 3/minute --limit-burst 3 -j LOG --log-prefix "SRC black-listed: " ip6tables -A FORWARD -p ALL -m set --match-set "ip6_black_list" src -j DROP ip6tables -A FORWARD -p ALL -m set --match-set "ip6_black_list" dst -m limit --limit 3/minute --limit-burst 3 -j LOG --log-prefix "DST black-listed: " ip6tables -A FORWARD -p ALL -m set --match-set "ip6_black_list" dst -j DROP #Принимаем установленные соединения и чистим мусор ip6tables -A FORWARD -p ALL -j forward_conntrack_check ip6tables -A FORWARD -p TCP -j bad_tcp_packets #Выпускаем всех из белого списка ip6tables -A FORWARD -p ALL -m set --match-set "ip6_white_list_2_inet" src -j ACCEPT #Проверяем и принимаем лигитимный трафик ip6tables -A FORWARD -p TCP -j forward_tcp_packets
Сеты ip6_lan_2_inet_tcp и ip6_servers_2_inet_tcp содержат пары (адрес локальной сети; номер порта). Порты, подлежащие принудительному проксированию, также должны быть указаны и в данных сетах.
Коннекшен трекер ломается на проксируемых соединениях, поэтому мы исключаем их из проверки.