
به اشتراک بگذارید

به اشتراک بگذارید
سرور شما دو یا سه IP دارد؛ پنل پیامک یا درگاه بانک کدام را میبیند؟ ip خروجی سرور را کرنل لینوکس از جدول مسیریابی انتخاب میکند، مگر اینکه برنامه خودش به یک آدرس bind شود. پس سه اهرم دارید: مسیر، خود برنامه، یا قانون SNAT برای یک مقصد.
srcsnat در nftablessrc روی مسیر پیشفرض، ماندگار در Netplan💡 کمگسترهترین تغییری که کار را راه میاندازد
اگر برنامه آدرس مبدا را تعیین نکند، کرنل src مسیر را برمیدارد؛ در نبود آن، آدرسی از کارت خروجی.
✅ گستره تغییر را کوچک نگه دارید: اول یک مقصد یا یک برنامه، آخر از همه کل سرور.
شریک آدرس دوم را در وایت لیست ip پنل پیامک خود ثبت کرده، اما درخواستها هنوز با آدرس اول به او میرسند. معیار ما کمترین تغییری است که فقط همان ترافیک را جابهجا کند.
📌 سه اهرم و یک فرمان سنجش
◀️ انتخاب آدرس مبدا در کرنل
◀️ سه راه: مسیر، برنامه یا SNAT
◀️ تنظیم آدرس در Postfix و Nginx
◀️ سنجش آدرس خروجی

کرنل لینوکس اول مسیر را پیدا میکند و بعد آدرس مبدا را. صفحه راهنمای ip-route پارامتر src را «آدرس مبدایی که هنگام ارسال به مقصدهای این مسیر ترجیح داده میشود» تعریف کرده است. بی src، کرنل آدرسی از کارت خروجی برمیدارد؛ برای همین ip خروجی سرور با اضافه شدن IP دوم عوض نمیشود.
همسایههای خانهای دونبش شما را در همان کوچهای میبینند که از درش بیرون رفتهاید. عادت رفتن از در شمالی، در سرور دوآدرسی همان src است؛ تا عوضش نکنید، کسی شما را در کوچه جنوبی نمیبیند.
یک ماشین لینوکسی با iproute2 نسخه 6.1 ساختیم و دو آدرس 192.0.2.10 و 192.0.2.20 را به یک کارت شبکه دادیم. خروجیهای مقاله از همین ماشین است، با نام کارت eth0 و نشانیهای RFC 5737. IP دوم را از کجا بگیرید و چطور روی کارت بنشانید، راهنمای آیپی اختصاصی میگوید.
$ ip -4 addr show dev eth0
inet 192.0.2.10/24 scope global eth0
inet 192.0.2.20/24 scope global secondary eth0
$ ip route get 203.0.113.50
203.0.113.50 via 192.0.2.1 dev eth0 src 192.0.2.10 uid 0
هر دو آدرس در یک زیرشبکهاند. کرنل اولی را اصلی گرفت و اتصالها با 192.0.2.10 رفتند. آدرس دوم فعلاً تماشاچی بود.
برنامهای که سوکت را پیش از اتصال bind کند، از همان آدرس بیرون میرود؛ گزینه from همین را شبیهسازی میکند. این خروجی با src پیشفرض 192.0.2.20 گرفته شد؛ باز from برنده شد:
$ ip route get 203.0.113.50 from 192.0.2.10
203.0.113.50 from 192.0.2.10 via 192.0.2.1 dev eth0 uid 0
برای تعیین ip خروجی سرور سه راه دارید که در دامنه اثر، ماندگاری و خطر قطع دسترسی فرق میکنند. با ستون «دامنه اثر» باریکترین گزینه کارساز را پیدا کنید.
| روش | دامنه اثر | ماندگاری بعد از ریبوت | ریسک | مناسب برای |
|---|---|---|---|---|
| آدرس src روی مسیر پیشفرض | اتصالهای بدون bind | با فرمان ip نه؛ با Netplan بله | بالا؛ via اشتباه SSH را قطع میکند | کل سرور |
| مسیر میزبان با src | یک مقصد | مثل ردیف بالا | کم | نشانی ثابت یک API |
| تنظیم bind در برنامه | یک سرویس | در فایل تنظیم برنامه | کم | سرویسهایی مثل Postfix و Nginx |
| قانون snat در nftables | ترافیک منطبق با قانون | اگر در فایل nftables ذخیره شود | متوسط؛ در ip route get دیده نمیشود | برنامه بدون گزینه bind |
کنار جدول، این خط ip route get را کلمه به کلمه بخوانید؛ src را پیش از آن روی 192.0.2.20 گذاشته بودیم:
203.0.113.50 via 192.0.2.1 dev eth0 src 192.0.2.20 uid 0
203.0.113.50: نشانی API شریکvia 192.0.2.1 و کارت dev eth0: راهی که بسته میرودsrc 192.0.2.20: آنچه شریک میبیند، به شرط نبودن bind در برنامه یا SNATفرمان change مسیر موجود را با via، dev و metric همان خط پیدا میکند؛ فقط src عوض میشود. پس خط کامل را از ip route show default بردارید؛ پیش از اجرا این دو کادر را بخوانید.
⚠️ یک via اشتباه و نشست SSH میافتد
اگر via، dev یا metric غلط باشد، change مسیر پیشفرض را خراب میکند؛ SSH همان لحظه قطع میشود. کنسول سرور را از قبل باز کنید. تغییر فرمان ip با ریبوت پاک میشود، پس ریبوت از کنسول راه برگشت است. آدرس اصلی را برای جابهجایی حذف نکنید: در همان ماشین، با promote_secondaries برابر صفر (پیشفرض کرنل)، حذف 192.0.2.10 آدرس ثانویه و مسیر پیشفرض را هم برد. فایل sysctl پیشفرض systemd این مقدار را ۱ میکند؛ روی اوبونتو، دبیان و RHEL معمولاً آدرس دوم جایگزین میشود. مقدار سرورتان را با sysctl ببینید.
🔶 آدرسی که در src مینویسید همان است که شریک میبیند
سرورهای وبداده IP مستقیم دارند و آدرس عمومی روی کارت شبکه خود سرور مینشیند؛ NATی در میان نیست، پس src یا bind همان آدرسی است که پنل پیامک ثبت کرده. کنسول VNC پنل سرور همان کنسول باز است.
$ ip route show default
default via 192.0.2.1 dev eth0 proto dhcp src 192.0.2.10 metric 100
$ ip route change default via 192.0.2.1 dev eth0 proto dhcp src 192.0.2.20 metric 100
$ ip route add 203.0.113.50 via 192.0.2.1 dev eth0 src 192.0.2.20
بدون metric 100، همان فرمان با خطای RTNETLINK answers رد شد. آدرسی که روی سرور نیست هم با Invalid prefsrc address رد شد. خط آخر فقط یک مقصد را جابهجا میکند؛ اگر IPهای نام دامنه شریک عوض میشوند، bind در برنامه امنتر است.
🔸 ماندگار کردن src در Netplan
کلید from زیر routes همان src است؛ مستندات Netplan آن را آدرس مبدا ترافیک این مسیر تعریف کرده و gateway4 را منسوخ میداند، پس اگر فایل فعلی gateway4 دارد، حذفش کنید. نمونه زیر برای آدرس ثابت است. اگر cloud-init فایل را میسازد، اول network: {config: disabled} را در /etc/cloud/cloud.cfg.d/ بگذارید. بهجای apply از netplan try استفاده کنید؛ بی تأیید در ۱۲۰ ثانیه، تنظیم برمیگردد.
network:
version: 2
ethernets:
eth0:
addresses:
- 192.0.2.10/24
- 192.0.2.20/24
routes:
- to: default
via: 192.0.2.1
from: 192.0.2.20
وقتی برنامه گزینه bind ندارد، قانون snat در زنجیره postrouting آدرس مبدا را فقط برای همان مقصد عوض میکند. شکل فرمانها از ویکی رسمی nftables است:
nft add table ip srcip
nft 'add chain ip srcip postrouting { type nat hook postrouting priority 100 ; }'
nft add rule ip srcip postrouting ip daddr 203.0.113.50 oif eth0 snat to 192.0.2.20
نام جدول nat نیست، چون Docker یا firewalld ممکن است جدول ip nat خودشان را داشته باشند. درخواست به 203.0.113.50 با 192.0.2.20 رسید، اما ip route get هنوز src 192.0.2.10 نشان داد؛ SNAT بعد از تصمیم مسیریابی آدرس را عوض میکند.

سرویسهای Postfix و Nginx پارامتر bind خودشان را دارند؛ فقط همان سرویس از آدرس دوم بیرون میرود و خطا هم از همان سرویس فراتر نمیرود.
مستندات postconf پارامتر smtp_bind_address را آدرسی میداند که کلاینت SMTP پستفیکس در اتصال IPv4 به آن bind میشود؛ مقدار پیشفرض خالی است:
postconf -e 'smtp_bind_address = 192.0.2.20'
postfix reload
برای یک سرویس خاص، -o smtp_bind_address=192.0.2.20 را به خط همان سرویس در master.cf اضافه کنید؛ نسخه IPv6 آن smtp_bind_address6 است. اگر inet_interfaces تنها یک IPv4 غیر loopback دارد، همان خودکار آدرس خروجی میشود.
اگر Nginx درخواست را با proxy_pass به یک API بیرونی میفرستد، طبق مستندات ngx_http_proxy_module دستور proxy_bind آدرس محلی آن اتصال را تعیین میکند؛ جایش http، server یا location است.
location /sms/ {
proxy_bind 192.0.2.20;
proxy_ssl_server_name on;
proxy_pass https://api.example.com;
}
پیشفرض proxy_ssl_server_name خاموش است و بدون آن SNI فرستاده نمیشود؛ API پشت CDN ممکن است دستدهی TLS را رد کند. proxy_pass بدون مسیر، /sms/… را همانطور میفرستد. بعد nginx -t، سپس nginx -s reload.

در روش مسیر، ip route get با نشانی شریک تصمیم کرنل درباره ip خروجی سرور را عیناً چاپ میکند. برای bind یا SNAT باید ببینید از بیرون چه آدرسی رسیده:
ip route get 203.0.113.50
curl --interface 192.0.2.20 https://api.ipify.org
ss -tn dst 203.0.113.50
پاسخ 192.0.2.20 در curl یعنی درخواستی که به این آدرس bind شود، با همین آدرس دیده میشود. برای خود Postfix یا Nginx، وقتی سرویس به مقصد وصل است، ستون Local Address در ss باید 192.0.2.20 باشد. SNAT را با لاگ سمت شریک بسنجید.
ip route change default via 192.0.2.1 dev eth0 proto dhcp src 192.0.2.10 metric 100
ip route del 203.0.113.50
nft delete table ip srcip
خط اول همان خط اولیه مسیر است و در تست ما پاسخ بعدی باز از 192.0.2.10 آمد. حذف جدول srcip به قانونهای Docker یا firewalld دست نمیزند. در Postfix یا Nginx خط اضافهشده را بردارید و reload کنید.
ترافیک ورودی، ریبوت، کد PHP، ویندوز و Postfix؛ سرور چندآدرسی با شریکی که یک IP را میشناسد، معمولاً سر همینها گیر میکند:
نه. src فقط مبدا اتصالهایی است که سرور شروع میکند؛ در همان ماشین، اتصال ورودی تازه به 192.0.2.10 برقرار شد.
نه. این فرمان فقط جدول در حال اجرای کرنل را عوض میکند؛ ماندگاری کار Netplan است.
گزینه CURLOPT_INTERFACE را با curl_setopt روی 192.0.2.20 بگذارید؛ طبق مستندات PHP، این گزینه نام کارت، IP یا نام میزبان میپذیرد.
پرچم SkipAsSource. مستندات Set-NetIPAddress میگوید آدرس با مقدار True برای ترافیک خروجی به کار نمیرود و در DNS هم ثبت نمیشود؛ پس آن را روی بقیه آدرسها بگذارید: Set-NetIPAddress -IPAddress 192.0.2.10 -SkipAsSource $true.
ویکی nftables، masquerade را حالت خاص SNAT میداند که آدرس کارت خروجی را خودش برمیدارد؛ برای انتخاب یکی از چند آدرس، snat to لازم است.
بله؛ بهطور پیشفرض هشدار لاگ میکند و تحویل را ادامه میدهد. از Postfix 3.7 با smtp_bind_address_enforce = yes تحویل به تعویق میافتد.
بله، با یک مسیر میزبان برای هر مقصد؛ در محیط آزمایشی دو مقصد همزمان دو آدرس متفاوت دیدند.
جمعبندی؛ ip خروجی سرور با باریکترین تغییر. این آدرس نتیجه یک تصمیم کرنل است: آدرس از src مسیر یا از کارت شبکه میآید، مگر اینکه برنامه bind کرده باشد. برای یک API، مسیر میزبان یا bind در همان برنامه بس است؛ Postfix یا Nginx هم بدون دست زدن به جدول مسیریابی همین کار را میکنند. قانون snat، در جدولی جدا، برای برنامه بدون bind است؛ ip route get اثرش را نمیبیند. تغییر src مسیر پیشفرض پرخطرترین گزینه است؛ آن را با کنسول باز، از روی خط کامل ip route show default امتحان کنید. وقتی نتیجه درست بود، با netplan try ماندگارش کنید.
🚀 آدرسی که شریک باید بشناسد
آدرس را src یا bind ثابت کرد؛ ماندن همین آدرس در فهرست شریک، بعد از هر مهاجرت، کار مرحله بعد است.
یک خط از سرور شما کافی است: خروجی ip route get برای نشانی شریک با 192.0.2.x بهجای آدرس واقعی. کنارش بگویید سرور چند IP دارد و کدام سرویس باید از آدرس دوم برود. دست آخر کدام اهرم جواب داد؟
منابع