این دوره برای کسی است که کار پایه با shell، sudo، فایل و شبکهٔ لینوکس را می‌داند و می‌خواهد Nginx را از یک سایت ساده تا ورودی امن چند برنامه اداره کند. در ۵۰ جلسه، نصب و server block، TLS و HTTP/2، هدرها، پراکسی معکوس، WebSocket و SSE، اعتماد به پراکسی و IP، محدودیت نرخ، WordPress با PHP-FPM، ModSecurity، کانتینر، TCP/UDP stream و سخت‌سازی همراه با ممیزی CIS را تمرین می‌کنیم.

هر درس یک هدف، محیط مشخص، مراحل، روش بررسی و بازگشت دارد. فرمان‌ها و پاسخ‌های نمونه، دستور تمرین و نتیجهٔ مورد انتظار هستند؛ اجرای واقعی همهٔ آن‌ها روی چهار توزیع در محیط تهیهٔ این نوشته ادعا نمی‌شود. هر جا نسخه یا مخزن تعیین‌کننده است، نسخهٔ نصب‌شده و مستندات همان release را بررسی می‌کنیم.

ترتیب و زمان جلسات#

برای هر شب حدود ۴۵ تا ۹۰ دقیقه در نظر بگیر؛ نصب WordPress، ساخت ModSecurity و بازیابی می‌تواند بیشتر طول بکشد. شب‌های ۲ و ۳ شاخه‌های جایگزین نصب و شب‌های ۲۹ و ۳۰ شاخه‌های جایگزین FPM هستند؛ برای پوشش هر چهار توزیع، تمرین هر شاخه را روی هر دو عضو آن تکرار کن. تمرین‌ها به هم وابسته‌اند، ولی شاخهٔ ACME عمومی، edge جدا، mTLS، cache و PROXY protocol با baseline مستقل انجام می‌شوند.

پیش از هر شاخه Snapshot بگیر و بعد baseline سالم را برگردان. تمام قطعه‌ها را هم‌زمان به یک فایل اضافه نکن: server block کامل معمولاً جایگزین قبلی است، snippet داخل context مشخص قرار می‌گیرد و load_module/zone/map هم‌نام نباید دوبار تعریف شوند. روش خودکار لینک نوشتهٔ قبلی/بعدی سایت ترتیب زمانی را نشان می‌دهد؛ ترتیب دوره را از فهرست شماره‌دار همین راهنما دنبال کن.

نسخه‌ها و مسیرهای دو خانواده#

baseline آموزشی، Debian 13، Ubuntu 24.04 LTS و AlmaLinux/Rocky Linux 9 به‌روز است. release و lifecycle بسته‌ها باید هنگام اجرای تمرین بررسی شود؛ موفق بودن نصب، اثبات وجود patch امنیتی لازم نیست. منابع بستهٔ توزیع، nginx.org و build دستی را در یک VM بدون طرح migration مخلوط نکن.

موردDebian / UbuntuAlmaLinux / Rocky Linux
سرویسnginxnginx
کاربر worker معمولwww-datanginx
فایل اصلی/etc/nginx/nginx.conf/etc/nginx/nginx.conf
فایل سایتsites-available و symlink در sites-enabledconf.d داخل http
فایل تنظیم مشترک HTTP/etc/nginx/conf.d/00-lab-http.confهمان مسیر
loader ماژولغالباً modules-enabled در maininclude ماژول‌های بسته در main
حفاظت اجباریAppArmor در صورت policy فعالSELinux در حالت Enforcing
PHP نمونهDebian: 8.4؛ Ubuntu: 8.3stream php:8.3 در AppStream، در صورت موجود بودن
socket pool اختصاصی/run/php/nginx-wp.sock/run/php-fpm/nginx-wp.sock
streamnginx-full و libnginx-mod-streamnginx-mod-stream سازگار با core

مسیرهای واقعی را با package list و nginx -T بررسی کن. RHEL ممکن است server پیش‌فرض را مستقیماً در nginx.conf داشته باشد؛ پیش از ایجاد default_server جدید، نسخهٔ قبلی را backup و conflict را حل کن. sites-enabled الگوی بسته‌بندی Debian است، directive اختصاصی Nginx نیست. از Apache درس‌های دیگر، سرویس یا listener روی همین VM فعال نباشد.

قرارداد شبکهٔ آزمایشگاه#

یک شبکهٔ host-only جدا و یک NAT فقط برای دریافت بسته‌ها بساز. VM وب تازه با حداقل دو هسته، چهار گیگابایت حافظه و فضای کافی برای Snapshot/backup نقطهٔ شروع است؛ برای build و بار واقعی ظرفیت را افزایش بده. console هایپروایزر را پیش از تغییر firewall باز نگه دار.

ماشینIPv4IPv6 اختیارینقش
وب Debian/Ubuntu192.168.57.10/24fd57:80::10/64یکی از دو توزیع در هر اجرا
وب Alma/Rocky192.168.57.30/24fd57:80::30/64یکی از دو توزیع در هر اجرا
کلاینت مجاز192.168.57.20/24fd57:80::20/64آزمون مثبت
کلاینت غیرمجاز192.168.57.21/24fd57:80::21/64آزمون منفی واقعی
edge اختیاری192.168.57.40/24—تنها همتای trusted تمرین چندپراکسی

IPها را پیش از assign از نظر conflict بررسی کن. در درس‌های عمومی، .10 نوشته شده است؛ در شاخهٔ RHEL آن را به .30 تغییر بده. curl --interface روی یک ماشین واحد جای داشتن source واقعی در شبکه را نمی‌گیرد؛ دو کلاینت جدا برای نتیجهٔ منفی لازم‌اند.

روی کلاینت‌ها نام‌های زیر را در /etc/hosts به IP وب همان اجرای فعلی نگاشت کن:

192.168.57.10 www.nginx.test docs.nginx.test app.nginx.test wp.nginx.test admin.nginx.test

برای اجرای RHEL همین خط با .30 است؛ یک نام را هم‌زمان به دو VM نگاشت نکن. دامنهٔ .test عمومی نیست و برای ACME قابل صدور عمومی نیست. شب ۱۳ شاخه‌ای جدا با دامنهٔ عمومی تحت اختیار خودت و شرایط اعتبارسنجی واقعی دارد. backend.nginx.test فقط در سناریوی TLS داخلی شب ۲۴ روی وب‌سرور به loopback نگاشت می‌شود.

CA خصوصی روی کلاینت ۲۰ ایجاد می‌شود و private key آن از آن ماشین خارج نمی‌شود. گواهی وب شامل www، app، wp و admin است؛ docs در baseline صرفاً HTTP دارد. CA عمومی را به کلاینت ۲۱ و edge هم منتقل کن تا رد دسترسی ناشی از ACL را با failure اعتماد TLS اشتباه نگیری. گواهی leaf آزمایشگاهی عمر کوتاه دارد؛ پیش از شب‌های بعد انقضا را بررسی کن.

firewall پایه و IPv6#

برای دیدن ACL خود Nginx، ۲۰ و ۲۱ باید بتوانند به پورت وب برسند؛ بعد Nginx درخواست ۲۱ را در endpoint محدود رد می‌کند. اگر firewall از ابتدا ۲۱ را ببندد، policy برنامه را آزمایش نکرده‌ای. backend و database به شبکهٔ عمومی publish نمی‌شوند.

در VM تازهٔ Debian/Ubuntu با UFW، ابتدا SSH_SOURCE را به IP واقعی مدیر و پورت SSH را به مقدار واقعی تنظیم کن. نمونهٔ زیر SSH روی 22 و source یک IPv4 را فرض می‌کند؛ console و rule قبلی را حفظ کن. apt install ufw در صورت نبودن ابزار لازم است.

sudo ufw allow from "$SSH_SOURCE" to any port 22 proto tcp
sudo ufw default deny incoming
sudo ufw default allow outgoing
for LAB_CLIENT_IP in 192.168.57.20 192.168.57.21; do
  sudo ufw allow from "$LAB_CLIENT_IP" to any port 80 proto tcp
  sudo ufw allow from "$LAB_CLIENT_IP" to any port 443 proto tcp
done
sudo ufw enable
sudo ufw status verbose

روی نصب موجود، ابتدا ruleهای باز گسترده را review کن؛ این مثال آن‌ها را خودکار حذف نمی‌کند. اگر IPv6 فعال است، متناظر fd57:80::20 و fd57:80::21 و SSH واقعی را هم explicit مجاز کن؛ IPV6=yes و listenerها را بررسی کن. برای برگشت، rule اضافهٔ مشخص را حذف/بازگردان؛ flush همهٔ firewall نکن.

در Alma/Rocky با firewalld، interface آزمایشگاه و zone واقعی را با firewall-cmd --get-active-zones ببین. در zone همان interface، rich ruleهای source محدود برای ۲۰/۲۱ و TCP 80/443 و rule واقعی SSH بساز. نمونهٔ runtime برای یک source/port در zone عمومی، در صورتی که همان zone درست باشد:

sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="192.168.57.20/32" port port="443" protocol="tcp" accept'
sudo firewall-cmd --zone=public --list-all

این rule را برای source ۲۱ و port 80 هم متناسب تکرار کن؛ پس از آزمون مثبت/منفی، مقادیر تأییدشده را با --permanent ثبت و reload کن. service/rule عمومی http/https یا ssh می‌تواند restriction source را بی‌اثر کند؛ ابتدا SSH محدود را از نشست جدید و console تأیید کن و بعد rule عمومی نامتناسب را بردار. IPv6 rich rule family خودش را دارد. برای برگشت، --remove-rich-rule با همان متن و وضعیت runtime/permanent لازم است.

پورت‌های 9001/TCP، 5354/UDP و 16443/TCP تنها هنگام درس stream، و edge ۴۰ تنها هنگام شاخهٔ trust مجاز می‌شوند. در درس‌های کانتینر، bind پایه localhost است؛ رفتار published port Docker را از بیرون هم تست کن و فرض نکن تمام ruleهای سرویس میزبان روی آن اعمال شده‌اند.

متغیرها و فایل‌های مشترک#

این مقدارها را در هر shell جدید و مطابق شاخه تنظیم کن؛ متغیر shell در فایل Nginx جایگزین نمی‌شود. $host، $uri و $remote_addr داخل config متغیرهای خود Nginx هستند.

Debian/Ubuntu:

NGINX_USER=www-data
WWW_CONF=/etc/nginx/sites-available/100-www.conf
APP_CONF=/etc/nginx/sites-available/120-app.conf
WP_CONF=/etc/nginx/sites-available/140-wp.conf
HTTP_LAB=/etc/nginx/conf.d/00-lab-http.conf
LAB_PYTHON=python3
PHP_BRANCH=8.3

در Debian 13 مقدار PHP_BRANCH برابر 8.4 است؛ در Ubuntu 24.04 برابر 8.3. Alma/Rocky:

NGINX_USER=nginx
WWW_CONF=/etc/nginx/conf.d/100-www.conf
APP_CONF=/etc/nginx/conf.d/120-app.conf
WP_CONF=/etc/nginx/conf.d/140-wp.conf
HTTP_LAB=/etc/nginx/conf.d/00-lab-http.conf
LAB_PYTHON=python3.12

Debian/Ubuntu برای تمرین‌های برنامه python3-venv، و Alma/Rocky 9 به‌روز python3.12 و python3.12-pip می‌خواهند؛ Python سیستم RHEL را replace نکن. ابزارهای curl، openssl و ویرایشگر نصب باشند؛ semanage در شاخهٔ RHEL از بستهٔ policycoreutils-python-utils می‌آید. نسخهٔ Python باید نیاز کتابخانهٔ WebSocket انتخاب‌شده را برآورده کند.

در Debian/Ubuntu فقط برای سایت جدید symlink مربوطه را بساز، مثلاً:

sudo ln -s "$APP_CONF" /etc/nginx/sites-enabled/120-app.conf

اگر لینک موجود است دوباره نساز؛ conf.d در RHEL خودش در http خوانده می‌شود. یک فایل را از دو include وارد نکن. پوشهٔ snippets را در هر دو شاخه با sudo install -d -m 755 /etc/nginx/snippets بساز.

مسیرکاربرد و مالکیت
/var/www/nginx-lab/publicstatic، کد root-owned، فایل 644 و پوشه 755
/var/www/nginx-lab/downloadsتمرین alias در پوشهٔ مستقل
/var/www/nginx-wordpress/publicکد WordPress root-owned
/var/www/nginx-wordpress/wp-config.phpرازها، root:wpapp با mode 640، بیرون public
wp-content/uploadsفقط این مسیر WordPress برای wpapp writable
/etc/nginx/tlsleaf key میزبان 600 و certificate/CA عمومی 644
/var/log/nginxlogهای Nginx و WAF با دسترسی محدود
/etc/nginx/stream.dفایل‌های فقط stream، include در main sibling http

همهٔ برنامه‌های demo مثل Python http.server صرفاً ابزار آزمایشگاه‌اند. برنامهٔ سرویس‌پذیر با Gunicorn و systemd در شب ۲۸ ساخته می‌شود. HTTP/2 و وراثت هدرها وابسته به نسخه‌اند؛ syntax یک نسخهٔ جدید را روی Ubuntu 24.04 قدیمی بی‌آزمون کپی نکن.

روال هر تغییر و ثبت نتیجه#

پیش از ویرایش، فایل قبلی و وضعیت بسته/سرویس را ثبت کن. با sudoedit فایل را تغییر بده؛ اگر sudo tee با heredoc به کار می‌بری، delimiter نقل‌قول‌شده باشد تا shell متغیرهای Nginx را expand نکند. فرمان‌های زیر به ترتیب اجرا می‌شوند؛ اگر nginx -t خطا داد، مرحلهٔ reload را اجرا نکن:

sudo nginx -t
sudo systemctl reload nginx
sudo journalctl -u nginx -n 30 --no-pager

پس از موفقیت، یک درخواست تازهٔ مثبت و یک درخواست منفی متناسب بفرست. نتیجهٔ reload در journal و runtime اهمیت دارد؛ nginx -T فقط config فعلی روی دیسک را چاپ می‌کند و ممکن است راز داشته باشد. پیش از restart، تغییر FPM stream، firewall یا permission گسترده، Snapshot و راه برگشت را آماده کن.

در دفتر آزمایش، تاریخ، نسخه‌ها، فایل تغییرکرده، expected/observed، status و log لازم را ثبت کن. header جعلی، credential غلط، نام TLS نادرست، توقف backend و restore را روی clone انجام بده. «خطا ندیدم» جای داشتن شاهد policy نیست. منابع نسخه‌های ModSecurity/CRS و benchmark CIS را در شب مربوط بررسی کن؛ نتیجهٔ audit به همان نسخه و scope محدود می‌شود.

فهرست ۵۰ شب#

نصب، server block و مسیریابی#

TLS، هدرها و مرز اعتماد#

ظرفیت، پراکسی و برنامه‌های متصل#

PHP-FPM و WordPress#

ModSecurity و OWASP CRS#

کانتینر و شبکهٔ برنامه#

stream و ارتباط TCP/UDP#

سخت‌سازی، CIS و تحویل#

منابع پایه#

رفتار directiveها از مستندات رسمی Nginx و بسته‌بندی از Debian ، Ubuntu و راهنمای خانوادهٔ RHEL بررسی می‌شود. هر درس منابع اختصاصی خودش را دارد. ادعاهای امنیتی را با patch و policy نصب واقعی تطبیق بده؛ این مسیر آموزشی جای benchmark انتخاب‌شده یا ارزیابی عملی سازمان را نمی‌گیرد.