Започнах отвън, както би направил потребител: въведох името в браузъра на телефон с мобилни данни и гледах как връзката виси, докато не изтече времето. Името се разрешаваше, значи DNS беше наред. Страницата за пренасочване на портове на домашния рутер също беше задънена улица, защото WAN адресът ѝ показваше 100.64.31.42, а това не е адрес, към който доставчик ще ме маршрутизира от интернет. Това число обяснява цялата повреда: линията е зад carrier-grade NAT, така че публичният адрес от моята страна на тунела принадлежи на доставчика. Следва пътят, който изминах от онзи закачен раздел до услуга, която отговаря.

Започни от клиента и върви навътре

Преди да промениш което и да е правило, докажи къде спира веригата. На лаптопа изпълних dig +short files.example.net и получих адреса на VPS обратно, което изключи остарял DNS на моята машина. След това curl -v --connect-timeout 5 files.example.net:5022 изтече, вместо да бъде отказан. Тази разлика е важна: изтичането обикновено означава, че пакетите се изхвърлят по пътя, докато отказът означава, че нещо е отговорило и е казало не.

Следващата стъпка навътре беше самият VPS. ss -ltnp не показа нищо, което да слуша на 5022, което се очакваше преди да съществува правилото DNAT. На същата машина wg show (или ip -br a show tun0 за OpenVPN) потвърди, че домашният peer е активен със свеж handshake. Ако peer-ът е долу, никое NAT правило не помага, а зелената лампичка на VPN страницата на домашния рутер не значи нищо.

Потвърди, че домашната линия наистина е зад CGNAT

Сравни адреса, който рутерът отчита на WAN интерфейса си, с това, което вижда външна услуга. Всичко в 100.64.0.0/10, тоест от 100.64.x.x до 100.127.x.x, е диапазонът споделени адреси от RFC 6598 и не е достъпен от интернет. До същия извод стигаш, когато рутерът твърди, че има обикновен частен адрес, а публична проверка връща нещо несвързано. Линия CGNAT може да има и публичен адрес, който важи само до следващия lease, така че пренасочване на порт изглежда да работи час и после тихо умира.

Някои доставчици наистина дават истински публичен адрес, но блокират входящи портове 80 и 443 на домашни абонаменти. Ако услугата ти е само уеб и WAN адресът е наистина публичен, попитай поддръжката, преди да строиш relay. Ако не е, долният дизайн важи еднакво за всеки порт, и си струва да спреш експеримента от страна на рутера: преводачът в мрежата на доставчика прекратява връзката, преди тя някога да стигне до твоя WAN интерфейс.

Насочи името към адреса на VPS

DNS не препредава трафик, затова A записът на услугата трябва да съдържа публичния адрес на VPS. A запис към 192.168.1.20 се разрешава полезно само за клиенти, които вече са във VPN; от интернет се разрешава към адрес, който никой не може да маршрутизира. За административния достъп до самия VPS държа отделно име, за да не объркам никога към кой хост сочи запис.

Ако и VPS, и домашната машина обслужват HTTP, дай на всяка собствено име и остави VPS да реши кой отговаря. Да ги слееш в едно име и да се надяваш, че проксито ще избере правилния backend, е точно начинът да прекараш цял следобед в отстраняване на грешки на грешния хост.

Един порт през DNAT, SSH остава на 22

Директната опция е NAT на местоназначението на VPS. Включи forwarding за работещото ядро и дръж адреса на тунела на домашния сървър в правилото, не неговия LAN адрес:

sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A PREROUTING -p tcp -d 198.51.100.10 --dport 5022 -j DNAT --to-destination 10.8.0.2

Остави SSH към самия VPS на 22 и пренасочи друг порт, например 5022, към домашната машина. Ако пренасочиш 22 към изключена машина, губиш единствения път, който е можел да я поправи. Същата логика важи за проксито: дръж управляващия порт на VPS извън правилата за relay.

Направи правилата постоянни, щом проработят. На Debian или Ubuntu apt install iptables-persistent и след това netfilter-persistent save ги записват в /etc/iptables/rules.v4. На хост с firewalld еквивалентът е постоянно пренасочване на порт в зоната, което преди да се приложи, се нуждае от firewall-cmd --reload. Правила, които живеят само в паметта, изчезват при първия сервизен рестарт на доставчика, обикновено в лош час.

Поправи обратния път или избегни нуждата от него

DNAT преписва само местоназначението. Домашният сървър вижда връзка от оригиналния адрес на клиента и ако маршрутът му по подразбиране сочи към домашния рутер, отговорът излиза през домашния WAN, където преводачът на доставчика го изхвърля. Тази асиметрия беше втората повреда, на която попаднах, след като правилото DNAT изглеждаше напълно правилно.

Две поправки работят. Дай на домашната машина политически маршрут, така че трафикът от тунела да се връща през тунела:

ip rule add from 10.8.0.2 table 100
ip route add default dev wg0 table 100

Или остави VPS да препише източника, така че отговорът да има адрес, който може да достигне, и остави conntrack да го обърне на изхода:

iptables -t nat -A POSTROUTING -d 10.8.0.2 -p tcp --dport 5022 -j SNAT --to-source 10.8.0.1

Версията със SNAT скрива истинския адрес на клиента от домашния сървър, така че логовете на приложението показват 10.8.0.1 като peer. Предай оригиналния адрес на ниво приложение или приеми загубата на логване по клиент, но реши го съзнателно, вместо да го откриеш по време на инцидент.

Провери целия път отвън

Провери от мрежа, която не е нито VPS, нито дома: телефон с мобилни данни или евтин shell другаде. nc -vz files.example.net 5022 трябва да отчете успешна връзка, а openssl s_client -connect files.example.net:443 -servername files.example.net трябва да покаже сертификата за публичното име. После провери security group или облачната защитна стена на доставчика. Правило DNAT, което никога не вижда пакет, обикновено е блокирано един слой над хоста.

Накрая реши какво става, когато тунелът падне. Правило DNAT продължава да приема връзки и тихо ги подава в мъртъв тунел, което изглежда точно като повредена услуга. Проверка на здравето на VPS, която отхвърля порта, докато peer-ът е долу, струва няколко реда и спестява час догадки. След това спри да редактираш правила и го наблюдавай един ден, преди да му се довериш.

Подобни статии