Работа VLESS через промежуточный сервер в России

Доброго времени суток.

Столкнулся с проблемой описаной в https://github.com/net4people/bbs/issues/490 . Перестал работать сервер VLESS с мобильный операторов. Решил попробовать создать промежуточный сервер в России с прозрачным прокси к удалённому серверу с VLESS.

В результате накидал небольшой тестовый скрипт для накатки на промежуточный сервер.

Скрипт
#!/bin/bash

validate_ports() {
	local ports="$1"
	local -n port_array_ref="$2"

	echo "$ports"

	port_array_ref=()

	if [ -z "$ports" ]; then
		echo "Ошибка: строка с портами пуста" >&2
		return 1
	fi

	old_IFS=$IFS
	IFS=',' read -ra port_array <<< "$ports"
	IFS=$old_IFS

	for port in "${port_array[@]}"; do
		port=$(echo "$port" | xargs)

		if ! [[ "${port%%/*}" =~ ^[0-9]+$ ]]; then
			echo "Ошибка: '${port%%/*}' не является числом" >&2
			return 1
		fi

		if (("${port%%/*}" < 1 || "${port%%/*} > 65535")); then
			echo "Ошибка: порт $port вне допустимого диапазона (1-65535)" >&2
			return 1
		fi

		port_array_ref+=("$port")
	done

	return 0
}

if [ "$(id -u)" -ne 0 ]; then
	echo "Этот скрипт должен запускаться от root!" >&2
	exit 1
fi

SERVER_IP=$(hostname -I | awk '{print $1}')

# Запрос параметров
read -p "Введите ip прокси сервера: " DEST_IP
read -p "Введите 'номер порта:протокол' через запятую, которые нужно прокидывать до прокси сервера (e.g. 443/TCP,443/UDP ...): " DEST_PORTS

declare -a ports_list

if ! validate_ports "$DEST_PORTS" "ports_list"; then
	echo "Некорректный ввод портов" >&2
	exit 1
fi

if [ "${#ports_list[@]}" -eq 0 ]; then
	echo "Нет портов для прокидывания" >&2
	exit 1
fi

read -p "Введите имя сетевого интерфейса (default: auto): " INTERFACE

if [ -z "$INTERFACE" ]; then
	INTERFACE=$(ip route show default | awk '/default/ {print $5}' | head -n 1)

	if [ -z "$INTERFACE" ]; then
		echo "Не удалось автоматически определить интерфейс для выхода в сеть!"
		exit 1
	else
		echo "Сетевой интерфейс для выхода в интернет: $INTERFACE"
	fi
fi

echo "Включаем IP-форвардинг..."
sysctl -w net.ipv4.ip_forward=1
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf

read -p "Введите режим работы (DNAT/Transparent  default: DNAT): " MODE

MODE=${MODE:-DNAT}

if [[ "$MODE" != "DNAT" && "$MODE" != "Transparent" ]]; then
    echo "Ошибка: допустимые значения - DNAT или Transparent"
    exit 1
fi

echo "Выбран режим работы: $MODE"

read -p "Reset iptables? (default: no): " IS_REMOVE_OLD_RULES
if [ -n "$IS_REMOVE_OLD_RULES" ]; then
	echo "Очищаем старые правила iptables..."
	iptables -t nat -F
	iptables -t nat -X
	iptables -F
	iptables -X
	iptables -Z
	iptables -t mangle -F
	iptables -t mangle -X
	sudo iptables -F INPUT
	sudo iptables -F FORWARD
	sudo iptables -F OUTPUT
	sudo iptables -P INPUT ACCEPT
	sudo iptables -P FORWARD ACCEPT
	sudo iptables -P OUTPUT ACCEPT
fi

case "$MODE" in
	"DNAT")
		echo "Настраиваем DNAT"
		for port in "${ports_list[@]}"; do
			echo "Настраиваем DNAT для портов ${DEST_PORTS}"
			iptables -t nat -A PREROUTING -d "$SERVER_IP" -p "${port#*/}" --dport "${port%%/*}" -j DNAT --to-destination "$DEST_IP:${port%%/*}"

			echo "Разрешаем переадресацию в FORWARD..."
			iptables -A FORWARD -p "${port#*/}" -d "$DEST_IP" --dport "${port%%/*}" -j ACCEPT
		done

		echo "Настраиваем MASQUERADE для интерфейса $INTERFACE..."
		iptables -t nat -A POSTROUTING -o "$INTERFACE" -j MASQUERADE
		;;

	"Transparent")
		echo "Настраиваем прозрачное проксирование (REDIRECT/TPROXY)..."
		for port in "${ports_list[@]}"; do
			proto="${port#*/}"
			port_num="${port%%/*}"

			if [ "$proto" = "TCP" ]; then
				iptables -t mangle -A PREROUTING -p tcp --dport "$port_num" -j MARK --set-mark 0x1
				iptables -t mangle -A PREROUTING -p tcp --dport "$port_num" -j TPROXY --on-port "$port_num" --tproxy-mark 0x1/0x1
			elif [ "$proto" = "UDP" ]; then
				iptables -t mangle -A PREROUTING -p udp --dport "$port_num" -j MARK --set-mark 0x1
				iptables -t mangle -A PREROUTING -p udp --dport "$port_num" -j TPROXY --on-port "$port_num" --tproxy-mark 0x1/0x1
		        else
				echo "Неподдерживаемый протокол: $proto" >&2
				exit 1
			fi
		done

		ip rule add fwmark 0x1 lookup 100
		ip route add local 0.0.0.0/0 dev lo table 100
		;;

	*)
		echo "Неизвестный режим: $MODE (допустимые варианты: dnat, transparent)" >&2
		exit 1
		;;
esac

if command -v iptables-save &> /dev/null; then
  iptables-save > /etc/iptables/rules.v4
fi

echo "Готово! Правила применены:"
echo "- Весь трафик на $SERVER_IP:${DEST_PORTS} перенаправляется на $DEST_IP:$DEST_PORT"
echo "- Переадресация включена (net.ipv4.ip_forward=1)"

echo -e "\nТекущие правила NAT:"
iptables -t nat -L -n -v

echo -e "\nТекущие правила FILTER:"
iptables -L -n -v

echo -e "\nТекущие правила MANGLE (TPROXY):"
iptables -t mangle -L -n -v

Накатка работает в 2-х режимах - DNAT или Transparent. С DNAT всё хорошо. Трафик ходит нормально, на мобильных операторах vless ожил. Но хотелось бы настроить именно прозрачное прокси, чтобы на vless сервере сохранялись исходные ip адреса (сервером пользуюсь не я один). И вот в этом режиме прокси не работает.

Что делаю:

  • настроил VLESS: sniffing: ON , domain strategy: UseIp, TProxy: TProxy
  • Запускаю свою накатку в режиме Transparent, которая выставляет на промежуточном сервере

iptables -t mangle -A PREROUTING -p tcp --dport $port_num -j MARK --set-mark 0x1
iptables -t mangle -A PREROUTING -p tcp --dport $port_num -j TPROXY --on-port
ip rule add fwmark 0x1 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100

Должен ли я что-то ещё делать на промежуточном сервере или сервере с vless. К сожалению я не являюсь сетевым специалистом и не понимаю в полной мере взаимодействие vless и tsproxy на стыке.

Буду благодарен за любую помощь и советы.

На промежуточном сервере можно поднять обратный прокси на nginx, который будет форвардить запросы на целевой сервер. Погугли как делать reverse proxy on nginx, в моем случае я форваржу вебсокетовый трафик через ру сервер.

это убирает возможность использовать любые другие протоколы кроме websocket, чтобы видеть на основном сервере оригинальные айпи то нужно еще добавлять header x-forwarded-for

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

на счёт tproxy ничего сказать не могу, но можно поднять wireguard между впсками:

на входной впс (ру впс) конфиг такой

[Interface]
Address = 10.5.0.2/24
PrivateKey = yKXgHJTLcbNR/1Ffdj0Q8bytWfKqvhhT6wgdOAF0UkA=
MTU=1440

PostUp=sysctl -w net.ipv4.ip_forward=1

#подстройка MSS из-за различий MTU
PostUp=iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
PostDown=iptables -t mangle -D FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

#проброс порта на европу впс через туннель без masquerade
PostUp=iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to-destination 10.5.0.1:80
PostDown=iptables -t nat -D PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to-destination 10.5.0.1:80

[Peer]
PublicKey = Dl3TiG4ZZY7eEczFvu5Vc1dxtboGzyqy5UZZ6EnN2CQ=
AllowedIPs = 10.5.0.0/24
Endpoint = eu_vps:51820
PersistentKeepalive=15

eth0 заменить на свой wan интерфейс (из ip a). tcp port 80 для примера, протестируйте сначала на веб сервере и смотрите его логи, если будут проблемы то можно снизить mtu до 1280

на выходной впс (европа впс) конфиг такой

[Interface]
Address = 10.5.0.1/24
ListenPort = 51820
PrivateKey = uHTaXqdCUktrlFUlP2GC4BiscMlFP3V4DyLgCvs9Tnk=
MTU=1440

#используем вторичную таблицу маршрутов где 0.0.0.0/0 (шлюз) будет ру впс
Table=111

PostUp=sysctl -w net.ipv4.ip_forward=1

#правило чтобы ответные пакеты от сервера не улетали в wan, а в таблицу 111
PostUp=ip rule add from 10.5.0.1 table 111
PostDown=ip rule del from 10.5.0.1 table 111

[Peer] #yKXgHJTLcbNR/1Ffdj0Q8bytWfKqvhhT6wgdOAF0UkA=
PublicKey = 7Oe80+rD8Xz0nz/RsshRc7gqBspU12FBSMWazuqw7xc=
AllowedIPs = 0.0.0.0/0 #пиру разрешен весь ipv4, т.е. он становится шлюзом в таблице 111

.

А если через nginx для вебсокета, то пример конфига тут Xray-examples/VLESS-WSS-Nginx/nginx.conf at main · XTLS/Xray-examples · GitHub (на другом конце должен быть чистый вебсокет без tls)

Спасибо большое за рекомендации и за конфиги. Как будет время, опробую это на практике.
Насчёт wireguard есть вопрос. Я так понимаю, идея его использования в том, чтобы отказаться от правил masquerade? Но не будет ли проблем с самим wireguard без обфускаций и его блокировками в РФ?

Насчёт TProxy - идея была в гибкости подобного решения. Помимо TCP от vless, было бы здорово иметь на перспективу возможность гонять UDP протоколы по такой же схеме. С TProxy теоретически возможно сохранять исходное IP клиента и в одном и в другом случае. Положим, я захочу гнать по такому же маршруту трафик от amneziaWG или чего-то похожего, можно ли будет это организовать так же через wireguard малой кровью, или нужно будет в любом случае разбираться с HAProxy+ PROXY Protocol или тем же TProxy?

Да

Нужно будет убедиться что нет проблем с мту (mtu wireguard это “wan mtu”-60 для ipv4), в примере у меня мту 1440, значит в вг/авг (который будет через через вг) нужно ставить мту 1380.

Без понятия.

Про haproxy я не в курсе

Ещё раз большое спасибо.

Аналогично вам, не сетевой специалист. Но использую tproxy чтоб весь трафик из amnezia wireguard попал в локальный sing-box по адресу 127.0.0.1:1337

      postUp = ''
        ${pkgs.iproute2}/bin/ip rule add fwmark 1 lookup 100
        ${pkgs.iproute2}/bin/ip route add local 0.0.0.0/0 dev lo table 100

        ${pkgs.iptables}/bin/iptables -t mangle -A PREROUTING -i awg0 -p tcp -j TPROXY --tproxy-mark 1 --on-port 1337 --on-ip 127.0.0.1
        ${pkgs.iptables}/bin/iptables -t mangle -A PREROUTING -i awg0 -p udp -j TPROXY --tproxy-mark 1 --on-port 1337 --on-ip 127.0.0.1

        ${pkgs.iptables}/bin/iptables -I nixos-fw -i awg0 -j nixos-fw-accept
      '';

В таком виде оно у меня работает.
Как выше посоветовали, стоит попробовать поднять туннель между серверами и отправлять через него, что-то вроде:

postUp = ''
  ${pkgs.iproute2}/bin/ip rule add fwmark 1 lookup 200
  ${pkgs.iproute2}/bin/ip route add default dev wg1 via 10.0.0.2 table 200

  # TPROXY to remote server through tunnel wg1, assuming sing-box is 10.0.0.2:1337
  ${pkgs.iptables}/bin/iptables -t mangle -A PREROUTING -i awg0 -p tcp -j TPROXY --tproxy-mark 1 --on-port 1337 --on-ip 10.0.0.2
  ${pkgs.iptables}/bin/iptables -t mangle -A PREROUTING -i awg0 -p udp -j TPROXY --tproxy-mark 1 --on-port 1337 --on-ip 10.0.0.2

  ${pkgs.iptables}/bin/iptables -I nixos-fw -i awg0 -j nixos-fw-accept
'';

Правда могут быть сложности с тем, каким маршрутом удаленный сервер будет посылать ответ, если оригинальные IP клиентов сохраняются с промежуточного сервера…

Возможно вся эта информация будет для вас бесполезна, но на всякий решил что-то написать. Если получится настроить - покажите пример конфигурации, интересно посмотреть :slightly_smiling_face:

Если входной узел будет в РФ, по идее проблем быть не должно. Главное чтобы был доступ с входного узла к Европе, если влесс будет там.

Я себе поднял Амнезию и влесс через РФ сервер, на удивление с входной машины есть доступ до европы с чистым вг (тьфу тьфу), влесс заходит на нее и уходит в туннель до Германии

Если есть ru-vps, то на нем можно:

  • трафик wg, openvpn подхватывать в tproxy просто правилом на уровне входного интерфейса.
  • трафик vless-клиентов прогонять через nginx или haproxy с поддержкой PROXY 1/2. И там и там без проблем передается первоначальный адрес клиента. Хотя зачем? Такое лучше не логгировать на vps, особенно ru )

Валить сразу всё подхваченное на зарубежную vps конечно не стоит. Потому что клиент не будет париться с разделением трафика (или просто сразу при включении vpn) все его банковские приложения и прочие яндексы полетят с забугорного vps, давая пищу для размышлений )

Второй вариант поддерживает udp?

Клиенты vless заворачивают и tcp и udp. В v2rayng используется xudp, в логе это выглядит как включение mux.

Ничего не понял. Nginx или haproxy c proxy protocol могут гонять что-то кроме vless tls? Напр xtls, или openvpn tcp/udp, в гугле не нашел

Nginx умеет гонять через PROXY и tcp и udp на уровне модуля stream, haproxy только tcp.

Через простой stream без proxy protocol в nginx то понятно, а на сайте написано “With the PROXY protocol, NGINX can learn the originating IP address from HTTP, SSL, HTTP/2, SPDY, WebSocket, and TCP”: про udp ничего нет, значит openvpn udp и wireguard так прокинуть не выйдет, придется через впн между впс

Вообще не понял, зачем нужны все эти сложные схемы. Почему нельзя на российском VPS банально настроить xray в режиме клиента и сервера одновременно? Делается vless инбаунд, к которому подключаются клиенты в россии, и vless аутбаунд для подключения к зарубежному VPS. Можно даже сделать реверс схему, где зарубежный VPS подключается к российскому. Итог будет тот же самый, только настраивается это гораздо проще. Исходные ip, конечно, сохраняться не будут, но это вообще не проблема, т.к. можно всё контролировать внутри xray через uuid клиентов.