سخت‌سازی یعنی سطح دسترسی و قابلیت‌ها با نقش واقعی سرویس هماهنگ شود. امشب فایل، شبکه، module و systemd را محدود می‌کنیم و سالم ماندن HTTP، PHP و stream را می‌سنجیم.

این جلسه بخشی از دورهٔ انجین‌اکس در ۵۰ شب است. قرارداد آزمایشگاه، مسیرها و روال اعمال تغییر در راهنمای دوره آمده است.

محیط: Snapshot آزمایشگاه نهایی و فهرست قابلیت‌های لازم؛ console و rollback در دسترس است. هر تغییر systemd ممکن است restart بخواهد و اتصال‌های فعال را قطع کند.

مالکیت و قابلیت لازم#

با nginx -V، package list و nginx -T ماژول‌های build‌شده، loaderهای dynamic و استفادهٔ واقعی در config را جدا ثبت کن. ماژول static با حذف یک include از باینری حذف نمی‌شود؛ rebuild/vendor package باید سنجیده شود. ماژول dynamic بی‌استفاده را فقط پس از حذف directiveهای وابسته unload کن.

config و moduleها root-owned و غیرقابل نوشتن برای worker باشند. کلید میزبان root:root با mode 600 است، چون master معمولی root آن را می‌خواند؛ نمونهٔ non-root کانتینر طبق شب ۴۱ مالکیت محدود متفاوت دارد. web root کد قابل خواندن است و تنها upload/cache لازم قابل نوشتن‌اند. با namei مسیر والد و با getfacl مجوزهای افزوده را بررسی کن؛ chmod به‌تنهایی همهٔ ACLها را توصیف نمی‌کند.

در context http، تنظیم‌های موجود را به policy مطلوب ویرایش کن، نه اینکه duplicate اضافه کنی:

server_tokens off;
autoindex off;

server_tokens off نسخه را از پاسخ‌های خود Nginx کم می‌کند، ولی پنهان کردن همهٔ fingerprintها یا patch امنیتی نیست. هدر backend و expose_php را جدا بررسی کن. advisoryهای Nginx و security updateهای vendor را با نسخهٔ package کامل تطبیق بده.

شبکه و status#

پورت‌های backend فقط loopback/private، socket FPM محلی و trusted proxy فقط از همتای مشخص قابل دسترسی باشد. IPv6 نیز policy مستقل می‌خواهد. rate limit را بر IP قابل اعتماد و endpoint درست اعمال کن؛ برای کاربران پشت NAT پیامد false positive را اندازه بگیر. stream فاقد فیلتر URI و هدر HTTP است.

اگر stub_status در build موجود است، یک server HTTP loopback مستقل برای مانیتورینگ بساز:

server {
    listen 127.0.0.1:18090;
    server_name localhost;
    location = /nginx_status {
        stub_status;
        allow 127.0.0.1;
        deny all;
    }
    location / { return 404; }
}

curl محلی باید شمارنده بدهد و کلاینت بیرونی نباید به IP شبکه روی 18090 وصل شود. اجازهٔ loopback این server را با realip عمومی مخلوط نکن. وضعیت status به‌تنهایی سلامت WordPress یا upstream را اثبات نمی‌کند.

drop-in سنجیدهٔ systemd#

با systemctl cat nginx محدودیت‌های vendor را ببین. در drop-in اختصاصی /etc/systemd/system/nginx.service.d/lab-hardening.conf نمونهٔ آغازین:

[Service]
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true

این نمونه با config/tls در /etc/nginx و محتوا در /var/www هماهنگ است؛ فایل داخل home کاربر برای Nginx قابل استفاده نخواهد بود. ProtectSystem=full مسیرهای سیستم را readonly می‌کند، ولی /var را کامل readonly نمی‌کند. برای strict باید همهٔ مسیرهای writable مانند PID، cache، log و temp را بر اساس نصب واقعی تعریف کنی؛ فهرست حدسی نگذار.

پس از daemon-reload، در پنجرهٔ نگه‌داری restart کن و journal، nginx -t، TLS، PHP، WebSocket، SSE، stream و log rotation را بررسی کن. systemd-analyze security nginx.service راهنمای محدودیت‌های systemd است و امتیاز CIS یا آزمون واقعی برنامه نیست. policy SELinux/AppArmor را فعال نگه دار و خطا را با context و مجوز لازم حل کن.

بازگشت: فقط drop-in اضافهٔ همین تمرین را بردار، daemon-reload و restart کن؛ همهٔ overrideهای کاربر را با systemctl revert حذف نکن. config و firewall قبلی را همراه آزمون سالم/منفی بازگردان.

منبع: advisoryهای Nginx ، stub_status ، محدودیت‌های systemd .