<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>WireGuard &#8211; REMADMIN</title>
	<atom:link href="https://remadmin.com/tags/wireguard/feed/" rel="self" type="application/rss+xml" />
	<link>https://remadmin.com</link>
	<description>Удалённый системный администратор</description>
	<lastBuildDate>Tue, 08 Sep 2026 20:37:34 +0000</lastBuildDate>
	<language>ru-RU</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.3</generator>
	<item>
		<title>WireGuard офис ↔ VPS: маршруты, DNS, split-tunnel и проверка что трафик в туннеле</title>
		<link>https://remadmin.com/blog/linux/wireguard-ofis-vps-marshruty-dns-split-tunnel-i-proverka-chto-trafik-v-tunnele/</link>
					<comments>https://remadmin.com/blog/linux/wireguard-ofis-vps-marshruty-dns-split-tunnel-i-proverka-chto-trafik-v-tunnele/#respond</comments>
		
		<dc:creator><![CDATA[Cursor Blog]]></dc:creator>
		<pubDate>Tue, 08 Sep 2026 20:37:34 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[DNS]]></category>
		<category><![CDATA[VPN]]></category>
		<category><![CDATA[VPS]]></category>
		<category><![CDATA[WireGuard]]></category>
		<category><![CDATA[безопасность]]></category>
		<category><![CDATA[сеть]]></category>
		<guid isPermaLink="false">https://remadmin.com/?p=7418</guid>

					<description><![CDATA[Как я собираю WireGuard между офисом и VPS: AllowedIPs как маршрут, split-tunnel против 0.0.0.0/0, DNS который не утекает мимо, и проверка tcpdump что рабочий трафик реально в wg0.]]></description>
										<content:encoded><![CDATA[<p>Типичная заявка: «подняли WireGuard на VPS, рукопожатие есть, сайты открываются, а в офис до внутренней админки как не ходили, так и не ходят». Или наоборот: «весь интернет офиса теперь через виртуалку, YouTube тормозит, трафик на тарифе хостера улетел в космос, а до NAS в соседней комнате пакеты почему-то идут через Франкфурт».</p>
<p>Смотрю конфиг — почти всегда одно и то же. На пире стоит <code>AllowedIPs = 0.0.0.0/0</code> «чтобы точно работало», DNS прописан 8.8.8.8, на VPS <code>ip_forward</code> выключен, а проверку сделали командой <code>ping 10.66.66.1</code>. Пинг до адреса туннеля проходит. Это доказывает только то, что два пира видят друг друга. Это не доказывает, что рабочий трафик идёт в туннель, что DNS не утекает мимо, и что обратный путь из офисной сети существует.</p>
<p>Ниже — как я собираю офис <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2194.png" alt="↔" class="wp-smiley" style="height: 1em; max-height: 1em;" /> VPS так, чтобы маршруты, DNS и split-tunnel не врали, и чем проверяю, что пакет реально ушёл в <code>wg0</code>, а не красиво показал handshake в <code>wg show</code>. Без «поставьте GUI» и без схемы «все в 0.0.0.0/0 и будет счастье».</p>
<h5>Два разных туннеля, которые путают в одном конфиге</h5>
<p>Мне на стол приносят три задачи, и их пытаются решить одним <code>[Peer]</code>:</p>
<ul>
<li>С ноутбука в офисе ходить на сервисы на VPS: SSH, панель, приватный git, админка, которая слушает только внутренний адрес.</li>
<li>С VPS ходить в офисную сеть: NAS, принтер, контроллер, камера, которой «интернет не нужен».</li>
<li>Выгнать весь офисный интернет через VPS, «чтобы как на работе».</li>
</ul>
<p>Это не один туннель с разными галочками. Это разный <code>AllowedIPs</code>, разный форвардинг и разный DNS. Первые две задачи я почти всегда делаю split-tunnel: в туннель уезжает только нужная подсеть. Третья — полный туннель, и за неё отдельно платят CPU, трафиком и сломанным Zoom, если MTU не поправили.</p>
<p>Адреса в примерах ниже документационные, не боевые. Публичный VPS — <code>203.0.113.10</code>, белый офиса — <code>198.51.100.20</code>, туннель — <code>10.66.66.0/24</code>, LAN офиса — <code>192.168.40.0/24</code>. Свои цифры подставьте, чужие из интернета не копируйте вместе с ключами.</p>
<h5>AllowedIPs — это маршрут, а не «список друзей»</h5>
<p>В WireGuard <code>AllowedIPs</code> делает две вещи сразу. Снаружи это криптоключ: пакет с таким источником примут только от этого пира. Изнутри <code>wg-quick</code> по этому списку ставит маршруты. Написали <code>0.0.0.0/0</code> — получите дефолт через туннель (у <code>wg-quick</code> ещё и отдельная таблица, обычно <code>51820</code>). Написали <code>10.66.66.0/24</code> — в туннель пойдёт только эта сеть.</p>
<p>Поэтому «handshake есть, а сайт на VPS открывается по белому IP» — нормально для split-tunnel и баг, если вы хотели полный туннель. Браузер пошёл на <code>203.0.113.10</code> напрямую, UDP 51820 при этом живой. Туннель тут ни при чём.</p>
<p>Смотрю, что система думает про маршрут, не что написано в комментарии к конфигу:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">wg show
ip -4 addr show dev wg0
ip -4 route show
ip -4 route show table all | grep -E 'wg0|51820'
ip -4 route get 10.66.66.1
ip -4 route get 203.0.113.10
ip -4 route get 8.8.8.8</pre>
<p><code>ip route get</code> врёт меньше, чем ощущения. Если до внутреннего адреса VPS маршрут не через <code>wg0</code>, дальше можно не гадать — трафик в туннель не собирался.</p>
<h5>Split-tunnel: офис ходит на VPS, интернет офиса не трогаем</h5>
<p>Это тот вариант, который я ставлю по умолчанию. Офисный пир (ноутбук или маленький шлюз) держит туннель, в <code>AllowedIPs</code> только сеть туннеля и, если надо, приватные сети на стороне VPS. Исходящий веб офиса идёт как раньше, через местного провайдера.</p>
<p>VPS:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="ini">[Interface]
Address = 10.66.66.1/24
ListenPort = 51820
PrivateKey = &lt;ключ VPS&gt;

[Peer]
PublicKey = &lt;ключ офиса&gt;
AllowedIPs = 10.66.66.2/32</pre>
<p>Офис (за NAT, поэтому keepalive):</p>
<pre class="EnlighterJSRAW" data-enlighter-language="ini">[Interface]
Address = 10.66.66.2/24
PrivateKey = &lt;ключ офиса&gt;

[Peer]
PublicKey = &lt;ключ VPS&gt;
Endpoint = 203.0.113.10:51820
AllowedIPs = 10.66.66.0/24
PersistentKeepalive = 25</pre>
<p>Если на VPS сервисы торчат в docker-сети или во втором внутреннем интерфейсе, в <code>AllowedIPs</code> офиса добавляю <em>эту</em> сеть, не «весь мир». И на VPS тогда уже нужен форвардинг и фильтр, иначе пакет дойдёт до ядра и умрёт в <code>FORWARD</code>.</p>
<p>На VPS для split-tunnel NAT наружу обычно не нужен. Нужен, когда офис должен ходить не на сам VPS, а <em>через</em> него дальше. Пока задача «SSH на 10.66.66.1» — без маскарада.</p>
<p><code>PersistentKeepalive = 25</code> ставлю на том конце, который за NAT или с динамическим белым. Без него UDP-маппинг у провайдера офиса схлопывается, handshake в <code>wg show</code> становится старше пары минут, и «туннель встал сам» ровно до первой попытки зайти с другой стороны.</p>
<h5>Когда VPS должен видеть LAN офиса</h5>
<p>Вторая заявка: бэкап с VPS на NAS, мониторинг контроллера, SSH на станок, который «в интернет не смотрит». Тогда на стороне VPS в пире офиса мало <code>/32</code> туннельного адреса. Нужна офисная подсеть:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="ini"># на VPS, peer = офисный шлюз
[Peer]
PublicKey = &lt;ключ офисного шлюза&gt;
AllowedIPs = 10.66.66.2/32, 192.168.40.0/24</pre>
<p>Офисный шлюз должен форвардить. Это уже не «WireGuard на ноутбуке бухгалтера». Ноутбук не маршрутизатор: пакет с VPS на <code>192.168.40.15</code> дойдёт до ноутбука и останется там, NAS его не увидит, а ответ NAS пойдёт в локальный шлюз офиса и умрёт, потому что шлюз про <code>10.66.66.0/24</code> ничего не знает.</p>
<p>На шлюзе офиса:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">sysctl -w net.ipv4.ip_forward=1
# постоянное место — /etc/sysctl.d/, не «в сессии до ребута»

iptables -C FORWARD -i wg0 -o eth0 -s 10.66.66.0/24 -d 192.168.40.0/24 -j ACCEPT 2&gt;/dev/null || \
iptables -A FORWARD -i wg0 -o eth0 -s 10.66.66.0/24 -d 192.168.40.0/24 -j ACCEPT
iptables -C FORWARD -i eth0 -o wg0 -s 192.168.40.0/24 -d 10.66.66.0/24 -j ACCEPT 2&gt;/dev/null || \
iptables -A FORWARD -i eth0 -o wg0 -s 192.168.40.0/24 -d 10.66.66.0/24 -j ACCEPT</pre>
<p>Имена интерфейсов свои. <code>eth0</code> в статье — заглушка. На машине смотрю <code>ip -br link</code>, не копирую с чужого скриншота.</p>
<p>Обратный маршрут в LAN часто забывают. NAS отвечает на запрос с <code>10.66.66.1</code> через дефолтный шлюз офиса. Шлюз этот пакет не ждал — в лучшем случае дроп, в худшем NAT в интернет. Варианты, которые работают:</p>
<ul>
<li>Статический маршрут на роутере офиса: <code>10.66.66.0/24 via 192.168.40.2</code>, где <code>.2</code> — шлюз с WireGuard.</li>
<li>Или маскарад на офисном WG-хосте в сторону LAN, если роутер трогать нельзя. Работает, в логах NAS все запросы с одного адреса шлюза. Для бэкапа терпимо, для разбора «кто стучится» — нет.</li>
</ul>
<p>Пересечение подсетей ловлю сразу. Офис на <code>192.168.1.0/24</code>, docker на VPS тоже <code>192.168.1.0/24</code> — туннель поднимется, маршруты будут врать по очереди. Одну из сетей надо сменить. Спорить, «чья 192.168.1 важнее», бесполезно: важнее та, которую проще перенумеровать.</p>
<h5>Полный туннель: 0.0.0.0/0, таблица 51820 и NAT на VPS</h5>
<p><code>AllowedIPs = 0.0.0.0/0</code> на офисном пире означает: весь IPv4, кроме того что <code>wg-quick</code> сам вычтет для Endpoint, должен идти в туннель. <code>wg-quick</code> для этого поднимает policy routing, не просто <code>ip route add default</code>. Поэтому «я убрал default из main, а туннель живой» — ещё не победа. Смотреть надо <code>ip rule</code> и таблицу, которую создал WireGuard:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">ip rule
ip route show table 51820
ip route get 8.8.8.8
ip route get 203.0.113.10</pre>
<p>До Endpoint маршрут обязан остаться в физический интерфейс. Иначе туннель пытается нести сам себя и тихо умирает после первого же пересчёта маршрутов.</p>
<p>На VPS для чужого интернета нужны три вещи, не одна:</p>
<ul>
<li><code>net.ipv4.ip_forward=1</code></li>
<li>NAT с туннеля в внешний интерфейс, иначе провайдер VPS не пропустит чужие RFC1918 как source</li>
<li>FORWARD в файрволе в обе стороны, не только INPUT на 51820/udp</li>
</ul>
<pre class="EnlighterJSRAW" data-enlighter-language="generic"># на VPS, внешний интерфейс уточнить: ip -br addr
sysctl -w net.ipv4.ip_forward=1

iptables -t nat -C POSTROUTING -s 10.66.66.0/24 -o ens3 -j MASQUERADE 2&gt;/dev/null || \
iptables -t nat -A POSTROUTING -s 10.66.66.0/24 -o ens3 -j MASQUERADE

iptables -C FORWARD -i wg0 -o ens3 -j ACCEPT 2&gt;/dev/null || iptables -A FORWARD -i wg0 -o ens3 -j ACCEPT
iptables -C FORWARD -i ens3 -o wg0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 2&gt;/dev/null || \
iptables -A FORWARD -i ens3 -o wg0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT</pre>
<p>Правила в статье — эскиз под iptables. На машине с nftables я пишу в ту таблицу, которая уже живая, а не добавляю второй файрвол «рядом». <code>ufw route allow</code> и <code>iptables -A FORWARD</code> одновременно — классический способ получить «у меня же разрешено», когда пакет режет другая цепочка.</p>
<p>Полный туннель я не ставлю «на всякий случай». Он нужен, когда офисный канал дырявый, нужен выход с фиксированного белого VPS, или политика «весь трафик только через нас». Для доступа к одной админке на VPS это дорогой способ сломать DNS, MTU и чужой Zoom.</p>
<h5>DNS: туннель живой, а имена резолвятся мимо</h5>
<p>Самая частая ложь после <code>wg show</code>. Туннель есть, <code>curl https://10.66.66.1</code> работает, а <code>curl https://panel.internal</code> идёт неизвестно куда или не идёт вовсе. Причина почти всегда в резолвере, не в маршрутах.</p>
<p><code>DNS =</code> в <code>wg-quick</code> — не магия. На systemd-resolved это попытка прикрутить домены к интерфейсу. На машине без resolved это может записаться в <code>/etc/resolv.conf</code> и затереть офисный DNS. На Windows-клиенте это отдельная история с «использовать DNS этого адаптера». Я не верю строке в конфиге, пока не увижу, куда реально уходит запрос.</p>
<p>Проверка с офисного пира:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">resolvectl status
resolvectl query panel.internal
getent hosts panel.internal

# куда уходит UDP/53, пока стучите в имя
tcpdump -ni wg0 port 53
tcpdump -ni eth0 port 53</pre>
<p>Если дамп на физическом интерфейсе показывает запросы на <code>8.8.8.8</code>, а на <code>wg0</code> тихо — split-tunnel сделал ровно то, что вы написали: маршрут до публичного DNS прямой. Внутреннее имя при этом либо не резолвится, либо резолвится в публичный адрес, и вы снова обходите туннель.</p>
<p>Рабочая схема для split-tunnel, которой я пользуюсь:</p>
<ul>
<li>На VPS крутится DNS, который знает внутренние имена. Слушает <code>10.66.66.1</code>, не <code>0.0.0.0</code> в интернет.</li>
<li>У офисного пира <code>DNS = 10.66.66.1</code>, и этот адрес уже входит в <code>AllowedIPs</code>.</li>
<li>Если нужен только суффикс <code>internal</code>, в resolved указываю домен на интерфейс туннеля, а не подменяю весь резолвер офиса.</li>
</ul>
<p>Для полного туннеля публичный DNS тоже должен быть достижим <em>через</em> туннель. Иначе получаете курицу и яйцо: чтобы поднять туннель, нужен DNS Endpoint, а DNS вы уже отправили в туннель, которого нет. Поэтому Endpoint я пишу IP, не именем. Имя в Endpoint оставляю только там, где белый VPS прыгает и без DNS туннель не собрать — тогда DNS офиса должен резолвить этот хост <em>до</em> подъёма <code>wg0</code>.</p>
<h5>IPv6 утекает отдельно, MTU ломает «сайты открываются через раз»</h5>
<p>Настроили IPv4, про IPv6 забыли. Браузер берёт AAAA, пакеты идут мимо туннеля в родной канал. <code>curl -4</code> при этом красивый. Если полный туннель — в <code>AllowedIPs</code> нужен и <code>::/0</code>, и на VPS форвардинг/NAT6, либо IPv6 на офисном пире надо выключить осознанно, не «само как-нибудь».</p>
<p>Для split-tunnel я проверяю оба стека:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">ip -6 route get 2001:db8::1
curl -4 -s https://ifconfig.me
curl -6 -s https://ifconfig.me</pre>
<p>Документационный <code>2001:db8::1</code> в <code>route get</code> нужен, чтобы увидеть, в какой интерфейс ядро отправит «чужой» v6. Если полного туннеля нет, прямой маршрут — ожидаемо. Если полный туннель заявлен, а v6 уходит в <code>eth0</code> — дыра.</p>
<p>MTU. UDP + WireGuard + внешний Ethernet обычно просит 1420, на части каналов — ниже. Симптом не «туннель мёртв», а «SSH живой, HTTPS висит, крупные POST отваливаются, мессенджеры отваливаются на картинках». Ловлю так:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">ip link show wg0
ping -M do -s 1372 10.66.66.1
ping -M do -s 1200 10.66.66.1</pre>
<p>Если 1372 с DF не проходит, опускаю MTU на <code>wg0</code>, пока не начнёт. На VPS с jumbo или с PPPoE за NAT цифры другие — подбираю, не копирую 1280 «потому что все так пишут», если канал нормально держит больше.</p>
<h5>Проверка, что рабочий трафик в туннеле, а не только handshake</h5>
<p>Чеклист, которым я пользуюсь после <code>wg-quick up</code>. По порядку, не выборочно.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic"># 1. пир живой
wg show
# handshake секунды-минуты, не часы; transfer растёт, когда вы сами генерируете трафик

# 2. адрес и маршрут
ip route get 10.66.66.1
# ожидаю dev wg0

# 3. ICMP внутри туннеля — необходимое, но не достаточное
ping -c 3 10.66.66.1

# 4. TCP на сервис, который слушает только внутренний адрес
curl -v --connect-timeout 5 http://10.66.66.1:8080/
ss -tlnp | grep 8080   # это уже на VPS: сервис не на 0.0.0.0:80 случайно?</pre>
<p>Дальше — дамп. Это единственный способ перестать спорить. На офисном пире в одном окне генерирую трафик, в двух других:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">tcpdump -ni wg0 host 10.66.66.1
tcpdump -ni eth0 udp port 51820
tcpdump -ni eth0 host 203.0.113.10 and not udp port 51820</pre>
<p>Нормальная картина split-tunnel, когда я хожу на внутренний сервис: на <code>wg0</code> видны пакеты до <code>10.66.66.1</code>, на физическом интерфейсе до VPS — только UDP 51820. Если на физическом интерфейсе всплыл обычный TCP на белый <code>203.0.113.10:443</code>, вы открыли публичный nginx, не туннель.</p>
<p>Для полного туннеля добавляю:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic"># с офисного пира
curl -4 -s https://ifconfig.me
# должен вернуться белый VPS, не белый офиса

ip route get 1.1.1.1
# dev wg0 или таблица 51820, не eth0

tcpdump -ni eth0 tcp port 443
# во время curl на внешний сайт здесь не должно быть голого TCP
# только UDP 51820</pre>
<p>Если <code>ifconfig.me</code> показал белый офиса — трафик мимо. Если показал белый VPS, но на <code>eth0</code> всё равно виден TCP 443 — смотрю второй интерфейс, IPv6, браузерный DoH, «умный» антивирус со своим туннелем. Бывает, что <code>curl</code> в туннеле, а Firefox живёт своей жизнью.</p>
<p>На самой VPS в это время:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="generic">tcpdump -ni wg0
tcpdump -ni ens3 udp port 51820
# conntrack, если ping есть, а TCP нет
conntrack -L | grep 10.66.66</pre>
<p>Ещё одна ловушка: сервис на VPS слушает <code>127.0.0.1</code> или только публичный адрес. Туннель доставляет пакет на <code>10.66.66.1</code>, nginx его не принимает. Это не «WireGuard сломался». Это <code>ss -tlnp</code> до настройки туннеля, не после.</p>
<h5>Что я проверяю, когда «подключилось, но не работает»</h5>
<p>Короткий разбор по симптомам, без перебора всего man.</p>
<ul>
<li>Handshake старше нескольких минут, transfer ноль — NAT, файрвол хостера на UDP 51820, неверный Endpoint, часы сильно разъехались реже, но бывает. С офиса: <code>nc -u -v 203.0.113.10 51820</code> ничего не доказывает (UDP без ответа), нужен дамп на VPS в момент <code>wg-quick up</code>.</li>
<li>Handshake живой, ping до <code>10.66.66.x</code> нет — AllowedIPs не покрывает адрес, или адреса туннеля пересеклись у двух пиров. Два клиента с <code>10.66.66.2/24</code> одновременно — классика копипаста.</li>
<li>Ping есть, TCP нет — MTU, или сервис не слушает адрес туннеля, или FORWARD/фильтр режет не ICMP, а tcp.</li>
<li>До VPS по внутреннему IP есть, до NAS в офисе нет — на VPS в AllowedIPs нет LAN, на офисе нет forward, на роутере офиса нет обратного маршрута.</li>
<li>Браузер открывает сайт по домену «не туда» — DNS мимо туннеля или A-запись смотрит на белый, а вы думали, что ходите внутрь.</li>
<li>После сна ноутбука туннель «есть», трафика нет — keepalive, stale UDP mapping, помогает <code>wg-quick down; wg-quick up</code>, а постоянно — keepalive и иногда <code>NetworkManager</code> dispatcher, не вечная сессия с 2023 года.</li>
</ul>
<p>Ключи и чужие IP в тикет не кладу. В логах для разбора достаточно последних байт публичного ключа из <code>wg show</code> и документационных адресов. Боевой Endpoint заказчика в статью и в переписку с подрядчиком не тащу.</p>
<h5>Что оставляю в конфиге, когда уже заработало</h5>
<p>Минимальный набор, который я не выкидываю «потом допишем»:</p>
<ul>
<li>Endpoint только IP, порт явно 51820 или тот, что реально открыт у хостера. У части облаков UDP на дефолтном порту режут, тогда слушаю другой и это же пишу в Endpoint.</li>
<li><code>AllowedIPs</code> по задаче, не максимальный. Split-tunnel — сети, которые правда надо унести. Полный туннель — осознанный <code>0.0.0.0/0</code> плюс IPv6-решение.</li>
<li><code>PersistentKeepalive</code> на NAT-стороне.</li>
<li>DNS только тот, который достижим через получившиеся маршруты.</li>
<li>Проверка дампом один раз после подъёма, не «пинг прошёл — закрываем задачу».</li>
</ul>
<p>Конфиг без этой проверки — это VPN на скриншоте. Скриншот <code>wg show</code> с handshake я в отчёт тоже прикладываю, но рядом кладу <code>ip route get</code> до рабочего адреса и кусок tcpdump, где видно UDP 51820 снаружи и целевой трафик внутри. Если второго нет, туннеля для работы тоже нет — есть только красивый интерфейс <code>wg0</code>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://remadmin.com/blog/linux/wireguard-ofis-vps-marshruty-dns-split-tunnel-i-proverka-chto-trafik-v-tunnele/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>