Нужна помощь с проксированием трафика из docker контейнеров
Всем привет, ситуация такая: есть VPS1, на которой прокси сервер с vless и VPS2, трафик с которой нужно проксировать через VPS1, для этого на нее установил sing-box и с помощью chatgpt создал такой скрипт:
#!/bin/bash
# /opt/singbox/update_openai_ips.sh
# Обновляет список IP для указанных доменов и ставит iptables-правила:
# — через ipset, если он установлен
# — или «чистыми» правилами, если ipset отсутствует
# Параметры
IPSET_NAME="openai"
FWMARK="233"
TABLE="sing"
DOMAINS=( "openai.com" "chatgpt.com" )
# Функция для «чистого» iptables-режима
function update_without_ipset() {
echo "ℹ️ Обновляем правила iptables без ipset..."
# Удаляем старые правила SING_MARK
iptables -t mangle -F SING_MARK 2>/dev/null || iptables -t mangle -N SING_MARK
# Удаляем старую привязку OUTPUT → SING_MARK
iptables -t mangle -D OUTPUT -j SING_MARK 2>/dev/null
# Добавляем новую
iptables -t mangle -A OUTPUT -j SING_MARK
# В SING_MARK для каждого IP добавляем правило маркировки
for domain in "${DOMAINS[@]}"; do
echo " ↳ Resolving $domain..."
for ip in $(dig +short "$domain" | grep -Eo '^[0-9.]+$'); do
echo " • $ip"
# Удаляем старое (на всякий) и добавляем новое
iptables -t mangle -D SING_MARK -d "$ip" -j MARK --set-mark $FWMARK 2>/dev/null
iptables -t mangle -A SING_MARK -d "$ip" -j MARK --set-mark $FWMARK
done
done
}
# Функция для режима с ipset
function update_with_ipset() {
echo "ℹ️ Обновляем ipset '$IPSET_NAME'..."
ipset create $IPSET_NAME hash:ip -exist
ipset flush $IPSET_NAME
for domain in "${DOMAINS[@]}"; do
echo " ↳ Resolving $domain..."
for ip in $(dig +short "$domain" | grep -Eo '^[0-9.]+$'); do
echo " • $ip"
ipset add $IPSET_NAME $ip -exist
done
done
echo "ℹ️ Обновляем правила iptables для ipset..."
# Создать цепочку, если надо
iptables -t mangle -F SING_MARK 2>/dev/null || iptables -t mangle -N SING_MARK
# Привязать OUTPUT → SING_MARK
iptables -t mangle -D OUTPUT -j SING_MARK 2>/dev/null
iptables -t mangle -A OUTPUT -j SING_MARK
# В цепочке SING_MARK одно правило: если dst в ipset → MARK
iptables -t mangle -D SING_MARK -m set --match-set $IPSET_NAME dst -j MARK --set-mark $FWMARK 2>/dev/null
iptables -t mangle -A SING_MARK -m set --match-set $IPSET_NAME dst -j MARK --set-mark $FWMARK
}
# Основной блок
if command -v ipset &>/dev/null; then
update_with_ipset
else
echo "⚠️ ipset не найден — переключаемся на чистый iptables"
update_without_ipset
fi
# Политика маршрутизации (выполнить один раз; последующие – будут игнорироваться):
# echo "100 $TABLE" >> /etc/iproute2/rt_tables
ip rule add fwmark $FWMARK table $TABLE 2>/dev/null || true
ip route add default dev tun0 table $TABLE 2>/dev/null || true
echo "✅ Обновление завершено. Домены: ${DOMAINS[*]}"
Далее проверяю: curl https://openai.com вне докера.
Проксирование присутствует, делаю то же самое из docker контейнера и проксирования нет. Что нужно сделать, что б ко всем контейнерам (к подсети докера?) применялись те же правила? Спасибо.
Ответы (1 шт):
Если вы продолжите спрашивать у chatgpt, то он возможно вам расскажет, что трафик от контейнеров не идёт через OUTPUT, так как докер создает виртуальный интерфейс, и к трафику контейнеров применяются правила PREROUTING и FORWARD.
Поэтому вам нужно добавить правила меток в цепочку PREROUTING.
Как понять, что пакет идёт из контейнера? Это самый хитрый момент во всей этой истории. Опыт показывает, что если докеру явным образом не указать диапазон адресов для подсетки, то для каждого контейнера он будет создавать новую подсетку от 172.18.0.0/16 до 172.31.0.0/16. Все эти подсетки покрываются маской 172.16.0.0/12, включая подсетку docker0 172.17.0.0/24.
добавьте переменную
DOCKER_SUBNET=172.16.0.0/12
Если у вас какие-то специфические настройки, то пара идей в приведены в конце этого поста.
Добавьте в цикл update_without_ipset
iptables -t mangle -D PREROUTING -s $DOCKER_SUBNET -d "$ip" -j MARK --set-mark $FWMARK 2>/dev/null
iptables -t mangle -A PREROUTING -s $DOCKER_SUBNET -d "$ip" -j MARK --set-mark $FWMARK
то есть для всех пакетов из подсети докера в адрес chatgpt применять правило -j MARK --set-mark $FWMARK
про ipset я не в курсе, но из общих соображений
iptables -t mangle -D PREROUTING -d $DOCKER_SUBNET -m set --match-set $IPSET_NAME dst -j MARK --set-mark $FWMARK 2>/dev/null
iptables -t mangle -A PREROUTING -d $DOCKER_SUBNET -m set --match-set $IPSET_NAME dst -j MARK --set-mark $FWMARK
НЕ ТЕСТИРОВАЛ!
Собственно, как узнать подсеть контейнера. Если вы знаете имя контейнера, то вот вам скрипт
# Имя контейнера, которое вы хотите инспектировать
CONT_NAME="ваш-контейнер"
# Получаем ID первой сети, к которой подключен контейнер
NETWORK_NAME=$(docker inspect "$CONT_NAME" | jq -r '.[0].NetworkSettings.Networks | keys | .[0]')
# Если имя сети найдено, получаем её подсеть
if [ -n "$NETWORK_NAME" ]; then
DOCKER_SUBNET=$(docker network inspect "$NETWORK_NAME" | jq -r '.[0].IPAM.Config[0].Subnet')
else
echo "Ошибка: Не удалось найти сетевое имя для контейнера $CONT_NAME." >&2
exit 1
fi
Либо вы можете руками задать параметры сети в вашем compose.yaml и задать DOCKER_SUBNET равным подсетке из compose.yaml