تغییر پورت SSH در Rocky Linux و AlmaLinux با SELinux
برای تغییر پورت SSH در توزیعهای RHEL باید SELinux و firewalld را همزمان تنظیم کنید. با این راهنما بدون قطع دسترسی و مواجهه با خطای Permission denied، پورت جدید را ایمن فعال کنید.
چرا تغییر پورت SSH در اینجا به سه مرحله نیاز دارد
برای تغییر پورت SSH در Rocky Linux، AlmaLinux، CentOS Stream یا Fedora، یک ویرایش کافی نیست. سه سیستم مجزا تعیین میکنند که آیا اتصال روی پورت جدید کار میکند یا خیر. firewalld تصمیم میگیرد که آیا بسته به ماشین میرسد یا نه. SELinux تصمیم میگیرد که آیا sshd اصلاً اجازه دارد به آن شماره پورت متصل (bind) شود یا خیر. sshd_config تعیین میکند که دیمون (daemon) کدام پورت را درخواست کند. اگر مرحله SELinux را انجام ندهید، دیمون از شروع به کار خودداری میکند. اگر مرحله firewalld را نادیده بگیرید، سرویس شروع میشود و گوش میدهد، اما هیچکس نمیتواند به آن دسترسی پیدا کند.
در Ubuntu همین کار با یک ویرایش و یک راهاندازی مجدد انجام میشود، زیرا Ubuntu بهجای SELinux از AppArmor استفاده میکند و هیچ پروفایلی که محدود کند sshd به کدام پورتها متصل شود، ارائه نمیدهد. اگر ufw در آنجا فعال باشد، فقط باید یک قانون (rule) اضافه کنید. تفاوت دقیقاً همین است. خانواده RHEL بهصورت پیشفرض با firewalld فعال و SELinux در حالت enforcing نصب میشوند و هر دوی آنها به شماره پورتها حساس هستند.
این کار را به ترتیب زیر انجام دهید تا نشست (session) فعلی شما در تمام مراحل باز بماند:
- پورت جدید را در firewalld باز کنید و پورت 22 را فعلاً باز نگه دارید.
- برچسب SELinux را برای پورت جدید با
semanageاضافه کنید. - پورت را در پیکربندی sshd تنظیم کنید.
sshdرا مجدداً راهاندازی کنید، سپس پیش از بستن ترمینال اول، از یک ترمینال دوم با پورت جدید وارد شوید.
پیش از شروع، کنسول وب (VNC یا سریال) ارائهدهنده خود را پیدا کنید و بررسی کنید که میتوانید از طریق آن وارد شوید. آن کنسول راه بازگشت شما در صورت بروز خطا در تغییرات است. تغییر پورت یکی از رایجترین دلایلی است که باعث میشود کاربران دسترسی خود را به سروری که بهتازگی اجاره کردهاند، از دست بدهند.
ابتدا semanage را نصب کنید
semanage ابزاری است که تنظیمات خطمشی SELinux را ویرایش میکند و در نصب حداقلی Rocky Linux یا AlmaLinux گنجانده نشده است. این ابزار در policycoreutils-python-utils قرار دارد.
sudo dnf install -y policycoreutils-python-utilsاجرای این دستور پیش از نصب بسته مذکور منجر به sudo: semanage: command not found میشود و این همان نقطهای است که بسیاری از کاربران تصور میکنند SELinux نصب نشده است و از این مرحله صرفنظر میکنند. SELinux نصب شده است؛ تنها ابزار مدیریت آن وجود ندارد. اگر سینتکس dnf برای شما جدید است، معادلهای دستورات dnf و apt آن را به آنچه از قبل میدانید مرتبط میکند.
انتخاب یک پورت و اطمینان از آزاد بودن آن
هر پورت TCP آزاد بین 1024 تا 65535 مناسب است. پیش از نهایی کردن انتخاب، دو بررسی انجام دهید:
sudo ss -tlnp | grep -w 2222
sudo semanage port -l | grep -w 2222دستور اول نشان میدهد که آیا پردازشی در حال حاضر روی آن شماره در حال گوش دادن (listening) است یا خیر. دستور دوم مشخص میکند که آیا سیاست SELinux آن را به نوع سرویس دیگری اختصاص داده است یا نه. اگر پورت آزاد باشد، هیچکدام از این دستورات خروجی نخواهند داشت. اگر سیاست SELinux از قبل آن را رزرو کرده باشد، دستور semanage port -a در مرحله 2 با خطای ValueError: Port tcp/2222 already defined مواجه میشود و راهحل آن، انتخاب یک شماره پورت متفاوت است.
در این راهنما از 2222 به عنوان مثال استفاده شده است. از آنجا که این پورت اولین پورتی است که اسکنرها پس از 22 بررسی میکنند، برای یک سرور واقعی پورتی را انتخاب کنید که کمتر در معرض توجه باشد.
گام 1: باز کردن پورت در firewalld
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reload
sudo firewall-cmd --list-ports--permanent قانون را در فایل zone روی دیسک مینویسد و تغییری در فایروال در حال اجرا ایجاد نمیکند. --reload پیکربندی روی دیسک را در فایروال در حال اجرا بارگذاری میکند. اگر مرحله reload را نادیده بگیرید، قانون وجود خواهد داشت اما تا زمانی که firewalld مجدداً راهاندازی نشود، هیچ کاری انجام نمیدهد؛ این یکی از رایجترین دلایلی است که باعث میشود کل این فرآیند بدون دلیل شکستخورده به نظر برسد.
فعلاً ورودی سرویس ssh را تغییر ندهید. این ورودی همان چیزی است که پورت 22 را باز نگه میدارد و در حین تست، راه نجات (fallback) شماست.
پنل کنترل ارائهدهنده خود را نیز بررسی کنید. بسیاری از میزبانها یک فایروال شبکه در مقابل VPS، خارج از سیستمعامل اجرا میکنند، بنابراین پورتی که در firewalld باز کردهاید ممکن است همچنان در لایه بالادستی مسدود شود. راهنمای اصول اولیه firewalld برای VPS اگر این مدل برای شما جدید است، مناطق (zones) و تفاوت بین runtime و permanent را پوشش میدهد.
گام 2: برچسبگذاری پورت برای SELinux
sudo semanage port -a -t ssh_port_t -p tcp 2222
sudo semanage port -l | grep ssh_port_tدستور -a یک انتساب پورت جدید اضافه میکند. -t ssh_port_t نوعی است که پورتهای SSH با آن شناخته میشوند. دستور دوم تمام مواردی را که ssh_port_t اکنون پوشش میدهد فهرست میکند تا بتوانید پیش از دست زدن به daemon، از ثبت شدن شماره پورت خود اطمینان حاصل کنید.
چرا SELinux پورت را مسدود میکند
سیستم SELinux (مخفف Security-Enhanced Linux) به هر شیء در سیستم یک برچسب اختصاص میدهد و شماره پورتهای TCP نیز مانند سایر اشیاء هستند. سرویس SSH در دامنهای به نام sshd_t اجرا میشود. خطمشی (Policy) به sshd_t اجازه میدهد به پورتهای TCP با برچسب ssh_port_t متصل شود؛ بهصورت پیشفرض، تنها پورت دارای این برچسب، پورت 22 است. اگر از سرویس بخواهید به پورت 2222 متصل شود، هسته سیستم برچسب را بررسی میکند، نوع عمومی اختصاصیافته به آن شماره را در خطمشی مییابد و مجوز name_bind را برای آن سوکت رد میکند.
به همین دلیل است که این خطا شبیه مشکلات فایروال نیست. هسته سیستم پیش از آنکه سوکتِ در حال گوش دادن (listening socket) ایجاد شود، درخواست را رد میکند؛ بنابراین sshd خطا را گزارش کرده و متوقف میشود. مشکل فایروال دقیقاً برعکس است: سرویس در حال اجرا و سالم است، اما بستههای شبکه در مسیر ورود دور ریخته میشوند.
دستور getenforce به شما میگوید که سیستم در چه حالتی قرار دارد. در حالت Permissive، موارد منعشده ثبت میشوند اما اعمال نمیگردند؛ بنابراین تغییر پورت ممکن است در ظاهر کار کند، اما در روزی که شخصی دستور setenforce 1 را اجرا کند یا سیستم در حالت enforcing راهاندازی شود، با مشکل مواجه خواهید شد. در هر صورت، باید پورت را برچسبگذاری کنید. راهنمای اصول SELinux برای سرور بهطور کامل به بررسی حالتها، زمینهها (contexts) و متغیرهای بولی میپردازد.
گام 3: تنظیم پورت در پیکربندی sshd
در Rocky Linux 9 و 10، AlmaLinux 9 و 10، و نسخههای فعلی Fedora، فایل /etc/ssh/sshd_config با یک خط include شروع میشود؛ بنابراین جای مناسب برای اعمال تغییرات شما، یک فایل drop-in است. با این روش، بهروزرسانیهای بسته با ویرایش شما تداخل پیدا نمیکنند.
grep -n '^Include' /etc/ssh/sshd_config
echo 'Port 2222' | sudo tee /etc/ssh/sshd_config.d/10-port.conf
sudo sshd -tاگر grep هیچ خط Include پیدا نکرد—که در Rocky Linux 8 و سایر ایمیجهای قدیمیتر وضعیت به همین صورت است—مقدار Port 2222 را مستقیماً در /etc/ssh/sshd_config قرار دهید. ابزار sshd -t کل پیکربندی، شامل فایلهای drop-in را تحلیل کرده و خطاهای دستوری را گزارش میکند. پیش از راهاندازی مجدد، هر خطایی که گزارش میشود را اصلاح کنید؛ زیرا پیکربندیای که در تحلیل با خطا مواجه شود، به معنای daemonای است که دیگر بالا نمیآید.
ممکن است Port بیش از یک بار ظاهر شود و sshd روی تمام پورتهای ذکر شده گوش میدهد. نگه داشتن Port 22 در کنار Port 2222 برای روز اول، یک لایه امنیتی ارزانقیمت است، به شرطی که حذف آن را فراموش نکنید.
آیا sshd شما توسط یک socket unit اجرا میشود؟
برخی از ایمیجها SSH را به جای یک سرویس در حال اجرا (long-running)، از طریق قابلیت socket activation در systemd راهاندازی میکنند. در مواردی که این تنظیمات اعمال شده باشد، systemd مالکیت سوکت شنود (listening socket) را در اختیار دارد و اتصالات را به sshd تحویل میدهد؛ بنابراین خط Port در فایل sshd_config بهطور کامل نادیده گرفته میشود. پیش از راهاندازی مجدد هر چیزی، این مورد را بررسی کنید:
systemctl is-enabled sshd.socketپاسخ enabled به این معناست که پورت در socket unit تنظیم شده است، نه در sshd_config:
sudo systemctl edit sshd.socket[Socket]
ListenStream=
ListenStream=2222استفاده از ListenStream= خالی الزامی است. مقادیر در فایلهای drop-in با هم جمع میشوند، بنابراین بدون یک مقداردهی خالی برای پاکسازی لیست، سوکت همچنان روی پورت 22 و همچنین 2222 گوش میدهد. این تغییر را با sudo systemctl daemon-reload و سپس sudo systemctl restart sshd.socket اعمال کنید. اگر این unit غیرفعال است یا روی سرور شما وجود ندارد، این بخش برای شما کاربردی ندارد.
گام 4: راهاندازی مجدد و تست از ترمینال دوم
sudo systemctl restart sshd
systemctl status sshd
sudo ss -tlnp | grep sshdاین ترمینال را باز نگه دارید. از آن خارج نشوید. یک ترمینال دوم روی سیستم خود باز کنید و با پورت جدید متصل شوید:
ssh -p 2222 youruser@203.0.113.10فقط پس از آنکه ورود دوم با موفقیت انجام شد، نشست اول را ببندید. اگر اتصال برقرار نشد، هنوز یک shell در اختیار دارید که میتوانید همه تغییرات را از طریق آن بازگردانید. این عادت ساده، تفاوت بین یک تغییر 5 دقیقهای و صرف یک بعدازظهر کامل در کنسول ارائهدهنده خدمات است.
مسدود شدن توسط فایروال یا عدم دسترسی SELinux؟ چگونه تفاوت آنها را تشخیص دهیم
از دید لپتاپ شما، این دو خطا تقریباً یکسان به نظر میرسند. اما در سرور، ماهیت آنها کاملاً متفاوت است.
- اگر
systemctl status sshdنشان میدهد که واحد (unit) با شکست مواجه شده است، دیمون (daemon) هرگز سوکت خود را دریافت نکرده است. این یک خطای پیکربندی یا عدم دسترسی SELinux است. - اگر واحد فعال است و
ss -tlnpنشان میدهد که sshd به پورت جدید متصل (bound) شده است، دیمون مشکلی ندارد و مشکل در مسیر شبکه است: firewalld، فایروال مجزای ارائهدهنده سرویس، یا آدرس و پورتی که برای اتصال استفاده کردهاید.
برای مورد SELinux، بهجای حدس زدن، رکورد audit را بخوانید:
sudo ausearch -m AVC -ts recent
sudo journalctl -u sshd -n 50 --no-pagerیک عدم دسترسی name_bind در کلاس tcp_socket، نام پردازش را در comm="sshd"، شماره پورت را در src= و برچسبی که پورت در حال حاضر دارد را در tcontext= مشخص میکند. آن فیلد آخر پاسخ شماست. هر چیزی غیر از ssh_port_t به این معنی است که مرحله 2 برای پورتی که استفاده میکنید اعمال نشده است؛ که معمولاً به دلیل اشتباه تایپی در شماره پورت یا استفاده از پروتکل نادرست است. اگر ترجیح میدهید sealert رکورد را به یک جمله قابلفهم تبدیل کند، setroubleshoot-server را نصب کنید.
پیامی که خود sshd هنگام امتناع هسته (kernel) از bind شدن مینویسد، به این صورت است:
error: Bind to port 2222 on 0.0.0.0 failed: Permission denied.Permission denied روی پورتی بالاتر از 1024، جایی که برای bind شدن نیازی به دسترسی root نیست، امضای SELinux است. Address already in use در همان خط، خطای متفاوتی است: پردازش دیگری پورت را اشغال کرده است. از سمت کلاینت، تفاوت بین connection refused و connection timed out دو حالت شبکه را از هم جدا میکند؛ زیرا refusal به این معنی است که بسته شما به میزبان رسیده و هیچچیز در حال گوش دادن نبوده است، در حالی که timeout به این معنی است که اصلاً پاسخی دریافت نشده است.
بستن پورت 22 و بهروزرسانی کلاینتها
پس از آنکه چندین بار ورود موفقیتآمیز از طریق پورت جدید انجام شد، پورت 22 را مسدود کنید:
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --reload
sudo firewall-cmd --list-allبرچسب SELinux روی پورت 22 را تغییر ندهید. این برچسب از سیاست پایه (base policy) میآید و هنگامی که فایروال دیگر بستهای را از این پورت عبور ندهد، هیچ دسترسی خاصی ایجاد نمیکند.
سپس کلاینتها را اصلاح کنید، زیرا تمام ابزارهایی که پورت پیشفرض را فرض میکردند، اکنون نیاز به تنظیم مجدد دارند. بهجای تایپ همیشگی -p، یکبار آن را در ~/.ssh/config روی سیستم خود قرار دهید:
Host myvps
HostName 203.0.113.10
Port 2222
User youruserابزارهای scp، sftp، rsync و Ansible همگی این فایل را میخوانند. کارهای پشتیبانگیری، بررسیهای مانیتورینگ و اسکریپتهای cron که پورت 22 را بهصورت hardcode دارند، از این فایل استفاده نمیکنند؛ بنابراین تا زمانی که تغییرات در ذهن شما تازه است، آنها را پیدا و اصلاح کنید.
تغییر پورت چه دستاوردی دارد و چه دستاوردی ندارد
این کار باعث کاهش نویز در لاگها میشود. اسکنرهای خودکار دائماً پورت 22 را هدف قرار میدهند و تغییر آن، بخش بزرگی از این خطوط را از journal حذف میکند که باعث میشود رویدادهای واقعی راحتتر دیده شوند. این یک کنترل امنیتی نیست. هر اسکنری که محدوده کامل پورتها را بررسی کند، daemon شما را پیدا کرده و بنر نسخه آن را میخواند. تغییر پورت را به عنوان یک اقدام نظافتی در نظر بگیرید و حفاظت واقعی را با استفاده از احراز هویت مبتنی بر کلید (key-only) و غیرفعال کردن ورود با رمز عبور اعمال کنید، که راهنمای مقاومسازی SSH برای VPS آن را گامبهگام توضیح میدهد.
تمام موارد فوق در هر دو توزیع اصلی بازسازیشده از RHEL به یک شکل عمل میکنند، زیرا از منابع یکسانی ساخته شدهاند. اگر هنوز در حال انتخاب بین آنها هستید، مقایسه Rocky Linux و AlmaLinux را ببینید. پیش از دنبال کردن هر راهنمای قدیمی، با استفاده از cat /etc/os-release بررسی کنید که دقیقاً چه نسخهای به شما داده شده است. راهنماهایی که برای Rocky Linux 8 نوشته شدهاند هنوز رتبه خوبی دارند و مراحل semanage و firewall-cmd آنها همچنان درست است، اما Rocky 8 خط include مربوط به sshd_config.d و unit سوکت برای در نظر گرفتن ندارد، بنابراین بخش sshd در آن راهنماها با یک سیستم امروزی مطابقت ندارد.
باید پورت جدید را به fail2ban معرفی کنید
نرمافزار fail2ban در مخازن پایه وجود ندارد. این بسته از طریق EPEL (بستههای اضافی برای لینوکس سازمانی) ارائه میشود:
sudo dnf install -y epel-release
sudo dnf install -y fail2ban fail2ban-firewalldزیربسته fail2ban-firewalld باعث میشود fail2ban مسدودسازیها را از طریق firewalld اعمال کند؛ این همان چیزی است که در سروری که مدیریت قوانین آن بر عهده firewalld است، به آن نیاز دارید.
jail پیشفرض sshd مقدار port = ssh را تنظیم میکند و این نام از طریق /etc/services به پورت 22 ترجمه میشود. پس از تغییر پورت، این jail پورت دیگری را نظارت میکند که کسی به آن حمله نمیکند؛ در نتیجه با وجود تلاشهای ناموفق برای ورود به پورت 2222، هیچکس مسدود نمیشود. پورت را به صورت عددی در /etc/fail2ban/jail.local تنظیم کنید:
[sshd]
enabled = true
port = 2222
backend = systemd
maxretry = 5
bantime = 3600backend = systemd به جای خواندن از /var/log/secure، خطاها را از journal میخواند که در نصبهای مینیمال که ممکن است rsyslog در آنها موجود نباشد، انتخاب امنتری است. آن را با sudo systemctl enable --now fail2ban شروع کنید و وضعیت jail را با sudo fail2ban-client status sshd بررسی نمایید. ساختار jail همان ساختاری است که در راهاندازی fail2ban برای SSH در اوبونتو 24.04 استفاده شده است. تنها منبع بسته و اکشن مسدودسازی متفاوت هستند.
اولویت وصلهکردن سیستم نسبت به تغییر پورت
سروری که پورت SSH آن تغییر یافته اما چهار ماه است وصلههای امنیتی را دریافت نکرده، وضعیت ناامنتری نسبت به سروری دارد که روی پورت 22 قرار داشته و هر شب بهطور خودکار بهروزرسانی میشود. در همان نشست کاری که دسترسی root دارید، بهروزرسانیهای خودکار را فعال کنید: بهروزرسانی خودکار dnf در Rocky Linux و AlmaLinux شامل تنظیمات تایمر و انتخاب بین دانلود یا اعمال خودکار وصلهها است.
FAQ
چرا پس از تغییر پورت در Rocky Linux، سرویس sshd اجرا نمیشود؟
تقریباً همیشه دلیل آن، نبود برچسب پورت SELinux است. sshd در دامنه sshd_t بهصورت محدود اجرا میشود و سیاستهای امنیتی فقط اجازه اتصال به پورتهایی را میدهند که با برچسب ssh_port_t مشخص شدهاند؛ این برچسب بهصورت پیشفرض فقط برای پورت 22 تعریف شده است. هسته سیستمعامل از اتصال جلوگیری میکند، بنابراین دیمون بهجای گوش دادن روی پورت، متوقف میشود و journalctl -u sshd خطی با فرمت error: Bind to port 2222 on 0.0.0.0 failed: Permission denied. را ثبت میکند. دستور sudo semanage port -a -t ssh_port_t -p tcp 2222 را با شماره پورت خود اجرا کنید و سپس سرویس را ریاستارت کنید. اگر دستور semanage یافت نشد، ابتدا بسته policycoreutils-python-utils را نصب کنید.
آیا اگر SELinux در حالت permissive باشد، همچنان به semanage نیاز دارم؟
بله. در حالت permissive، موارد منعشده ثبت میشوند اما اتصال همچنان مجاز است، بنابراین به نظر میرسد تغییرات بهدرستی اعمال شدهاند. در حالی که برچسب همچنان وجود ندارد. به محض اینکه کسی دستور setenforce 1 را اجرا کند، یا سیستم با تنظیمات SELINUX=enforcing در فایل /etc/selinux/config بوت شود، sshd دیگر روی آن پورت اجرا نخواهد شد. افزودن برچسب تنها یک دستور نیاز دارد و از بروز خطایی که ممکن است هفتهها بعد بدون دلیل مشخص ظاهر شود، جلوگیری میکند.
پورت برچسبگذاری شده و sshd در حال اجراست، پس چرا اتصال من با timeout مواجه میشود؟
اجرای دیمون به این معناست که SELinux مشکلی ندارد، بنابراین بستههای شبکه در مسیر ورود مسدود میشوند. وضعیت پورت خود را در sudo firewall-cmd --list-ports بررسی کنید و مطمئن شوید که پس از اعمال قانون --permanent، دستور firewall-cmd --reload را اجرا کردهاید؛ زیرا قوانین دائمی (permanent) بدون بارگذاری مجدد، روی فایروال در حال اجرا اعمال نمیشوند. سپس پنل کنترل میزبان خود را برای بررسی فایروال شبکه که پیش از VPS قرار دارد، چک کنید. این دومین جایی است که کاربران معمولاً در آن مسدود میشوند و هیچ ابزاری در داخل سیستمعامل آن را نشان نمیدهد.
بهجای پورت 22 از چه پورتی استفاده کنم؟
هر پورت TCP آزاد بین 1024 تا 65535. از انتخاب 2222 و 22222 در سرورهای واقعی خودداری کنید، زیرا اسکنرها بلافاصله پس از 22، این پورتها را امتحان میکنند. با دستور sudo ss -tlnp آزاد بودن شماره پورت را تایید کنید، با دستور sudo semanage port -l مطمئن شوید که سیاستهای SELinux قبلاً آن را اشغال نکرده باشند، و از پورتهایی که به سرویسهایی که ممکن است بعداً نصب کنید اختصاص یافتهاند، صرفنظر کنید. انتخاب یک شماره بالا و غیرقابلحفظکردن مشکلی ندارد، زیرا شما آن را یکبار در ~/.ssh/config وارد میکنید و دیگر نیازی به تایپ مجدد آن نخواهید داشت.