انجیناکس در ۵۰ شب؛ از server block تا WordPress، stream و ممیزی CIS
این دوره برای کسی است که کار پایه با 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 / Ubuntu | AlmaLinux / Rocky Linux |
|---|---|---|
| سرویس | nginx | nginx |
| کاربر worker معمول | www-data | nginx |
| فایل اصلی | /etc/nginx/nginx.conf | /etc/nginx/nginx.conf |
| فایل سایت | sites-available و symlink در sites-enabled | conf.d داخل http |
| فایل تنظیم مشترک HTTP | /etc/nginx/conf.d/00-lab-http.conf | همان مسیر |
| loader ماژول | غالباً modules-enabled در main | include ماژولهای بسته در main |
| حفاظت اجباری | AppArmor در صورت policy فعال | SELinux در حالت Enforcing |
| PHP نمونه | Debian: 8.4؛ Ubuntu: 8.3 | stream php:8.3 در AppStream، در صورت موجود بودن |
| socket pool اختصاصی | /run/php/nginx-wp.sock | /run/php-fpm/nginx-wp.sock |
| stream | nginx-full و libnginx-mod-stream | nginx-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 باز نگه دار.
| ماشین | IPv4 | IPv6 اختیاری | نقش |
|---|---|---|---|
| وب Debian/Ubuntu | 192.168.57.10/24 | fd57:80::10/64 | یکی از دو توزیع در هر اجرا |
| وب Alma/Rocky | 192.168.57.30/24 | fd57:80::30/64 | یکی از دو توزیع در هر اجرا |
| کلاینت مجاز | 192.168.57.20/24 | fd57:80::20/64 | آزمون مثبت |
| کلاینت غیرمجاز | 192.168.57.21/24 | fd57: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/public | static، کد 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/tls | leaf key میزبان 600 و certificate/CA عمومی 644 |
| /var/log/nginx | logهای 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 و مسیریابی#
- شب ۱: آزمایشگاه و مسیر یک درخواست
- شب ۲: نصب Nginx روی Debian و Ubuntu
- شب ۳: نصب Nginx روی AlmaLinux و Rocky Linux
- شب ۴: contextها، Include و ماژولها
- شب ۵: اولین server block مستقل
- شب ۶: چند سایت و default_server
- شب ۷: انتخاب location، root، alias و try_files
- شب ۸: مجوز فایل، کاربر کارگر و SELinux
- شب ۹: لاگ درخواست، reload و عیبیابی
- شب ۱۰: redirect، rewrite و query string
TLS، هدرها و مرز اعتماد#
- شب ۱۱: گواهی آزمایشگاهی و نام معتبر
- شب ۱۲: HTTPS، SNI و ورودی ناشناس
- شب ۱۳: گواهی عمومی، ACME و تمدید
- شب ۱۴: سیاست TLS، HTTP/2 و HSTS
- شب ۱۵: هدرهای امنیتی، ارثبری و CSP
- شب ۱۶: فشردهسازی و cache فایل ثابت
- شب ۱۷: محدودیت IP و احراز هویت همزمان
- شب ۱۸: IP واقعی و مرز اعتماد به پراکسی
- شب ۱۹: mTLS و گواهی کلاینت
- شب ۲۰: rate limit درخواست با dry run
ظرفیت، پراکسی و برنامههای متصل#
- شب ۲۱: محدودیت همزمانی، body و اتصال کند
- شب ۲۲: اولین برنامه پشت پراکسی معکوس
- شب ۲۳: زیرمسیر، هدرها، redirect و cookie
- شب ۲۴: TLS معتبر تا backend
- شب ۲۵: upstream، توزیع بار و خرابی عضو
- شب ۲۶: WebSocket و تبادل واقعی پیام
- شب ۲۷: SSE، buffering و پاسخ جریانی
- شب ۲۸: برنامهٔ پایدار با systemd پشت Nginx
PHP-FPM و WordPress#
- شب ۲۹: PHP-FPM و socket روی Debian و Ubuntu
- شب ۳۰: PHP-FPM روی AlmaLinux و Rocky Linux
- شب ۳۱: WordPress، پایگاه داده و مالکیت کد
- شب ۳۲: server block کامل WordPress و جلوگیری از اجرای upload
- شب ۳۳: اعتماد HTTPS، ظرفیت FPM و خطر cache خصوصی
- شب ۳۴: backup قابل بازیابی، cron و استقرار WordPress
ModSecurity و OWASP CRS#
- شب ۳۵: ModSecurity، connector و سازگاری بستهها
- شب ۳۶: ساخت موتور و ماژول ModSecurity در clone
- شب ۳۷: OWASP CRS از DetectionOnly تا مسدودسازی
- شب ۳۸: تنظیم false positive بدون خاموش کردن WAF
کانتینر و شبکهٔ برنامه#
- شب ۳۹: اجرای Nginx در کانتینر با کاربر محدود
- شب ۴۰: Compose، شبکهٔ خصوصی و تغییر IP backend
- شب ۴۱: TLS، رازها و اعتماد در ورودی کانتینر
- شب ۴۲: درخواست، health check و ارتقای کانتینر
stream و ارتباط TCP/UDP#
- شب ۴۳: nginx-full، ماژول stream و context درست
- شب ۴۴: پراکسی TCP، توزیع اتصال و IP ACL
- شب ۴۵: پراکسی UDP با echo خصوصی و timeout مشخص
- شب ۴۶: TLS passthrough، SNI و PROXY protocol
سختسازی، CIS و تحویل#
- شب ۴۷: سختسازی Nginx، فایلها و سرویس
- شب ۴۸: ممیزی فقطخواندنی بر پایهٔ CIS NGINX
- شب ۴۹: اصلاح یافتههای ممیزی و آزمون بازگشت
- شب ۵۰: پروژهٔ نهایی، آزمون خرابی و تحویل دوره
منابع پایه#
رفتار directiveها از مستندات رسمی Nginx و بستهبندی از Debian ، Ubuntu و راهنمای خانوادهٔ RHEL بررسی میشود. هر درس منابع اختصاصی خودش را دارد. ادعاهای امنیتی را با patch و policy نصب واقعی تطبیق بده؛ این مسیر آموزشی جای benchmark انتخابشده یا ارزیابی عملی سازمان را نمیگیرد.