انجیناکس؛ شب ۴۵: پراکسی UDP با echo خصوصی و timeout مشخص
فهرست نوشته
UDP handshake و پایان اتصال TCP را ندارد. امشب با یک echo کوچک، تعداد پاسخ و timeout session را بررسی میکنیم؛ آزمایشگاه به DNS resolver عمومی تبدیل نمیشود.
این جلسه بخشی از دورهٔ انجیناکس در ۵۰ شب است. قرارداد آزمایشگاه، مسیرها و روال اعمال تغییر در راهنمای دوره آمده است.
محیط: stream شب ۴۳ و Python از قرارداد راهنما؛ backend روی loopback UDP/5353 و ورودی Nginx UDP/5354 است. firewall فقط برای ۲۰ و ۲۱ آزمایشگاه اجازهٔ این پورت میدهد.
backend مستقل#
فایل ~/nginx-lab/udp-echo.py را بساز و در ترمینال VM با $LAB_PYTHON اجرا کن:
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(("127.0.0.1", 5353))
while True:
data, peer = sock.recvfrom(2048)
sock.sendto(b"echo:" + data[:128], peer)
پاسخ اندازهٔ محدودی دارد و backend به شبکهٔ عمومی bind نمیشود. payload واقعی و راز نفرست. اگر mDNS روی همین پورت و address استفاده میشود، پورت آزاد دیگری را در هر دو فایل یکدست انتخاب کن.
در /etc/nginx/stream.d/20-udp.conf:
server {
listen 5354 udp reuseport;
allow 192.168.57.20;
deny all;
proxy_pass 127.0.0.1:5353;
proxy_responses 1;
proxy_requests 1;
proxy_timeout 3s;
}
log_format و access_log شب قبل در context stream مشترکاند؛ دوباره با همان نام تعریف نکن. reuseport توزیع UDP بین workerها را ممکن میکند. proxy_responses تعداد پاسخ مورد انتظار برای هر datagram را مشخص میکند و proxy_requests مرز session را بعد از تعداد درخواست تعیین میکند؛ مقدارها باید با protocol واقعی هماهنگ باشند.
کلاینت با timeout#
فایل زیر را روی هر دو کلاینت اجرا کن؛ در شاخهٔ RHEL آدرس را .30 کن:
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(4)
sock.sendto(b"nginx-lab", ("192.168.57.10", 5354))
try:
data, peer = sock.recvfrom(2048)
print(data.decode(), peer)
except TimeoutError:
print("no UDP response before deadline")
finally:
sock.close()
کلاینت ۲۰ باید echo:nginx-lab و آدرس ورودی Nginx را ببیند. کلاینت ۲۱ معمولاً timeout میگیرد؛ برای تشخیص ACL از افت بسته، log Nginx و دریافت backend را هم بررسی کن. پاسخ session ممکن است پس از پایان یا timeout در log ثبت شود. خاموش کردن backend نیز نباید پاسخ جعلی موفق بسازد.
در RHEL، bind UDP و اتصال backend را با policy SELinux و AVC بررسی کن؛ مجوز TCP پورت 9001 مجوز UDP پورت 5354 نیست. policy خانوادهٔ Fedora/RHEL مجوز bind به http_port_t را برای دامنهٔ وب دارد، ولی نسخهٔ نصبشده را با sesearch -A -s httpd_t -t http_port_t -c udp_socket -p name_bind بررسی کن؛ ابزار از setools-console میآید. پس از بررسی آزاد بودن پورت و نگاشت موجود، برای همین ورودی نمونه، sudo semanage port -a -t http_port_t -p udp 5354 را اضافه کن. اگر mapping وجود دارد، آن را با -a تکرار یا نوع سرویس دیگر را کور تغییر نده. شکست ارسال به backend را نیز از AVC جدا تشخیص بده.
برای DNS واقعی، query/response چندگانه، TCP fallback، EDNS، ACL و ریسک amplification به طراحی مستقل نیاز دارند؛ این echo فقط رفتار UDP را نشان میدهد.
بازگشت: backend foreground را قطع کن، فایل 20-udp.conf و دسترسی موقت پورت را بردار، validate/reload و نبودن listener را با ss بررسی کن. اگر نگاشت UDP/5354 را صرفاً برای این تمرین اضافه کردهای و سرویس دیگری به آن وابسته نیست، با sudo semanage port -d -p udp 5354 آن را بردار.
منبع: UDP و session در stream proxy ، listen و reuseport ، policy دامنهٔ وب در پروژهٔ SELinux خانوادهٔ Fedora/RHEL .