امنسازی SSH در سرور لینوکس: تغییر پورت، بستن ورود root و محدود کردن کاربران
یک سرور تازه که به اینترنت وصل میشود، ظرف چند ساعت شروع میکند به دریافت تلاشهای ورود ناموفق روی پورت ۲۲. کافی است یک بار lastb یا لاگ sshd را ببینید تا صف بلند تلاشها برای ورود با کاربر root و پسوردهای حدسی را مشاهده کنید. این ترافیک هدفمند نیست؛ رباتها کل رنجهای IP را اسکن میکنند و هر پورت ۲۲ بازی را امتحان میکنند. کاری که باید بکنید هم پیچیده نیست: چند خط در sshd_config عوض میشود و بخش بزرگی از این سطح حمله بسته میشود.
این راهنما مسیر کامل امنسازی SSH را به همان ترتیبی که در عمل باید انجام دهید میآورد: اول کاربر و کلید را آماده میکنیم، بعد سراغ بستن درها میرویم. ترتیب مهم است، چون اگر برعکس عمل کنید احتمال زیادی هست که خودتان را از سرور بیرون بیندازید.
قدم صفر: سرور را بهروز کنید
پیش از هر تغییری بستهها را بهروز کنید تا خود openssh-server آخرین وصلههای امنیتی را داشته باشد:
# AlmaLinux / Rocky Linux / RHEL 8 و بالاتر dnf update -y # دبیان / اوبونتو apt update && apt upgrade -y
دستور yum update -y که در راهنماهای قدیمی میبینید مربوط به نسل CentOS 6 و ۷ است. CentOS 7 در تیر ۱۴۰۳ (ژوئن ۲۰۲۴) به پایان پشتیبانی رسید و دیگر وصلهی امنیتی نمیگیرد؛ اگر هنوز روی آن هستید، مهاجرت به AlmaLinux یا Rocky Linux مهمتر از هر تنظیمی در این مقاله است. روی نسخههای ۸ به بعد yum فقط یک نام مستعار برای dnf است و همان کار را میکند.
قدم اول: یک کاربر غیر root با دسترسی sudo بسازید
وقتی ورود مستقیم root را ببندید، باید از پیش راهی برای رسیدن به دسترسی روت داشته باشید. پس این کار را قبل از تغییر PermitRootLogin انجام دهید.
اول یک گروه بسازید تا بعداً بتوانید دسترسی SSH را فقط به اعضای همین گروه بدهید:
groupadd developers
حالا کاربر جدید را بسازید و همان موقع عضو گروه کنید:
useradd -m -G developers newuser passwd newuser
سوئیچ -m پوشهی خانگی کاربر را میسازد؛ روی دبیان و اوبونتو بدون آن پوشهی خانگی ساخته نمیشود و بعداً برای گذاشتن کلید عمومی به مشکل میخورید (روی خانوادهی ردهت ساخت پوشهی خانگی پیشفرض است، ولی نوشتن -m ضرری ندارد). دستور passwd را هم فراموش نکنید: کاربری که تازه ساخته شده پسورد ندارد و تا وقتی پسورد نگذارید نه میتواند با پسورد وارد شود و نه میتواند sudo بزند، چون sudo پسورد خودِ کاربر را میپرسد.
اگر کاربر از قبل وجود دارد و فقط میخواهید به گروه اضافهاش کنید:
usermod -aG developers newuser
حرف a در -aG حیاتی است. اگر آن را جا بیندازید و فقط -G بنویسید، کاربر از تمام گروههای فعلیاش حذف و فقط عضو گروه جدید میشود.
در آخر به کاربر اجازهی sudo بدهید:
# AlmaLinux / Rocky / RHEL usermod -aG wheel newuser # دبیان / اوبونتو usermod -aG sudo newuser
عضویت گروه فقط در نشست بعدی اعمال میشود؛ اگر کاربر همین حالا لاگین است باید یک بار خارج و دوباره وارد شود. حالا از یک ترمینال دوم با همین کاربر وارد شوید و sudo -i را امتحان کنید. تا وقتی این تست جواب نداده، سراغ قدمهای بعدی نروید.
قدم دوم: ورود با کلید را راهاندازی کنید
پسورد را میشود حدس زد؛ کلید را نه. این مهمترین کاری است که برای امنیت SSH انجام میدهید و بقیهی تنظیمات در مقایسه با آن فرعیاند.
روی کامپیوتر خودتان (نه روی سرور) یک جفت کلید بسازید:
ssh-keygen -t ed25519 -C "admin@onlineserver"
الگوریتم ed25519 امروز انتخاب پیشفرض است؛ کلیدش کوتاهتر و سریعتر از RSA است و امنیت بسیار خوبی میدهد. اگر با کلاینت یا سروری سر و کار دارید که آنقدر قدیمی است که از آن پشتیبانی نمیکند، از ssh-keygen -t rsa -b 4096 استفاده کنید. موقع ساخت کلید حتماً یک passphrase بگذارید تا اگر فایل کلید خصوصی از لپتاپتان بیرون رفت، بلافاصله قابل استفاده نباشد.
بعد کلید عمومی را روی سرور بگذارید:
ssh-copy-id newuser@SERVER_IP
اگر پورت SSH را از قبل عوض کردهاید، با سوئیچ -p پورت را بدهید: ssh-copy-id -p 1423 newuser@SERVER_IP. اگر ssh-copy-id در دسترس نبود، همین کار را دستی انجام دهید؛ محتوای فایل id_ed25519.pub را در سرور در فایل زیر بچسبانید:
mkdir -p /home/newuser/.ssh nano /home/newuser/.ssh/authorized_keys chmod 700 /home/newuser/.ssh chmod 600 /home/newuser/.ssh/authorized_keys chown -R newuser:newuser /home/newuser/.ssh
پرمیژنها را جدی بگیرید. گزینهی StrictModes در sshd بهصورت پیشفرض روشن است و پیش از پذیرفتن کلید، مالکیت و پرمیژن فایلها را چک میکند. اگر پوشهی .ssh، فایل authorized_keys یا حتی خودِ پوشهی خانگی کاربر برای گروه یا سایرین قابل نوشتن باشد، کلید بهکلی نادیده گرفته میشود و شما با پیام مبهم «Permission denied (publickey)» روبهرو میشوید بدون اینکه بدانید مشکل از پرمیژن است. پوشهی خانگی باید حداکثر 755 باشد و مالکش همان کاربر.
حالا با کلید وارد شوید و مطمئن شوید بدون پرسیدن پسورد سرور کار میکند. تا این تست موفق نشده، بند بعدی را اجرا نکنید.
قدم سوم: تغییر پورت SSH
عوض کردن پورت یک لایهی امنیتی واقعی نیست، اما در عمل حجم لاگ و تلاشهای خودکار ورود را بهشدت کم میکند. فایل پیکربندی را باز کنید:
nano /etc/ssh/sshd_config
خط زیر را پیدا کنید:
#Port 22
علامت # را بردارید و پورت دلخواهتان را بگذارید:
Port 1423
عددی بالای ۱۰۲۴ انتخاب کنید که سرویس دیگری روی سرور از آن استفاده نکند. با ss -tlnp (با کاربر root) میتوانید پورتهای در حال شنود را ببینید. یک نکته که کمتر گفته میشود: پورت را از بازهی پورتهای موقت سیستم انتخاب نکنید، وگرنه ممکن است بعد از یک ریبوت، پورت را برنامهی دیگری گرفته باشد و sshd بالا نیاید. این بازه را با دستور زیر ببینید (روی لینوکس معمولاً از ۳۲۷۶۸ شروع میشود):
cat /proc/sys/net/ipv4/ip_local_port_range
تا وقتی از درستی همه چیز مطمئن نشدهاید، میتوانید هر دو خط Port 22 و Port 1423 را کنار هم نگه دارید — Port از معدود گزینههایی است که چند بار نوشتنش مجاز است و هر دو پورت شنود میشوند. بعد از تست موفق، خط ۲۲ را حذف کنید.
پورت جدید را در فایروال باز کنید
این مرحله فراموششدنیترین قدم کل ماجراست و دقیقاً همانجایی است که آدمها خودشان را از سرور بیرون میاندازند. پیش از ریاستارت sshd، پورت را در فایروال باز کنید.
روی سرورهایی که CSF دارند، فایل /etc/csf/csf.conf را باز کنید و پورت جدید را به فهرست TCP_IN (و در صورت نیاز TCP_OUT) اضافه کنید:
nano /etc/csf/csf.conf
TCP_IN = "20,21,22,25,53,80,110,143,443,465,587,993,995,1423"
این خط را عیناً کپی نکنید. فقط شمارهی پورت جدید را به انتهای فهرستی که در فایل خودتان هست اضافه کنید. اگر فهرست بالا را جایگزین کنید، پورتهای کنترلپنل (مثلاً ۲۰۸۳ و ۲۰۸۷ در cPanel یا ۲۲۲ در دایرکتادمین) و هر سرویس دیگری که به آن وابستهاید بسته میشود.
سپس قوانین را بارگذاری مجدد کنید:
csf -r
اگر بهجای CSF از firewalld یا ufw استفاده میکنید:
# firewalld (AlmaLinux / Rocky / RHEL) firewall-cmd --permanent --add-port=1423/tcp firewall-cmd --reload # ufw (اوبونتو / دبیان) ufw allow 1423/tcp ufw status
در مورد ufw حواستان باشد که اگر بعداً بخواهید آن را با ufw enable فعال کنید، سیاست پیشفرضش «رد کردن ورودی» است؛ پس پیش از فعال کردن، حتماً قانون پورت SSH را اضافه کرده باشید.
اگر SELinux فعال است
روی خانوادهی ردهت، SELinux بهصورت پیشفرض فقط اجازهی شنود sshd روی پورتهای شناختهشده را میدهد. اگر این کار را نکنید، سرویس با وجود پیکربندی درست بالا نمیآید. اول وضعیت را ببینید:
getenforce
اگر خروجی Enforcing بود، پورت جدید را به SELinux معرفی کنید (اگر Permissive یا Disabled بود این مرحله لازم نیست):
dnf install -y policycoreutils-python-utils semanage port -a -t ssh_port_t -p tcp 1423
اگر پیام Port tcp/1423 already defined گرفتید یعنی آن پورت از قبل به نوع دیگری اختصاص دارد؛ در آن حالت بهجای -a از -m استفاده کنید. برای دیدن پورتهای مجاز sshd هم:
semanage port -l | grep ssh_port_t
یک نکته دربارهی اوبونتوهای جدید
در نسخههای اخیر اوبونتو (از جمله ۲۴.۰۴) سرویس SSH میتواند بهصورت socket activation بالا بیاید و در آن حالت گزینهی Port داخل sshd_config نادیده گرفته میشود، چون شنود روی پورت را systemd انجام میدهد. برای بررسی:
systemctl is-enabled ssh.socket
اگر فعال بود، پورت را باید در همان یونیت عوض کنید. با systemctl edit ssh.socket این چند خط را اضافه کنید:
[Socket] ListenStream= ListenStream=1423
خط خالی اول عمداً آنجاست و مقدار پیشفرض (پورت ۲۲) را پاک میکند؛ اگر آن را ننویسید، پورت ۲۲ هم باز میماند. سپس:
systemctl daemon-reload systemctl restart ssh.socket
قدم چهارم: بستن ورود مستقیم root
در فایل sshd_config خط زیر را پیدا کنید:
PermitRootLogin yes
و به این شکل درش بیاورید:
PermitRootLogin no
از این پس ورود مستقیم با root از طریق SSH ممکن نیست؛ با کاربر عادی وارد میشوید و با sudo -i به روت میرسید. (دستور su - پسورد خودِ root را میخواهد و روی اوبونتو که پسورد root معمولاً تنظیم نشده کار نمیکند؛ پس روی مسیر sudo حساب کنید.) اگر به هر دلیلی به ورود مستقیم root نیاز دارید، دستکم آن را به ورود با کلید محدود کنید:
PermitRootLogin prohibit-password
در این حالت root فقط با کلید میتواند وارد شود و ورود با پسورد برایش بسته است. مقدار قدیمی without-password هم دقیقاً همین معنا را دارد و هنوز بهعنوان نام مستعار پذیرفته میشود، اما نام درست و امروزیاش prohibit-password است.
قدم پنجم: محدود کردن کاربران مجاز SSH
بهصورت پیشفرض هر کاربری که روی سیستم حساب دارد و شل معتبر دارد میتواند SSH بزند. با AllowGroups این را به یک گروه مشخص محدود کنید. در sshd_config بنویسید:
AllowGroups developers
از این لحظه فقط اعضای گروه developers اجازهی ورود دارند. اگر ترجیح میدهید بهجای گروه، کاربران را تکتک نام ببرید:
AllowUsers newuser
سه نکته که در راهنماهای قدیمی جا میافتد و دردسر میسازد:
- یکی از این دو را انتخاب کنید، نه هر دو را. sshd این فهرستها را جداگانه و پشت سر هم بررسی میکند (اول
DenyUsersوAllowUsers، بعدDenyGroupsوAllowGroups). یعنی اگر هر دو را بنویسید، کاربر باید هر دو شرط را همزمان داشته باشد: هم نامش در فهرست باشد و هم عضو گروه. در عمل معمولاً همین باعث میشود کاربری که فکر میکردید مجاز است رد شود. برای مدیریت سادهتر فقطAllowGroupsرا نگه دارید. - این محدودیت شامل root هم میشود. اگر همین حالا با کاربر root وصل هستید و root عضو گروه
developersنیست، بعد از ریاستارت دیگر نمیتوانید با root وارد شوید — حتی اگرPermitRootLogin yesباشد. پیش از ریاستارت مطمئن شوید کاربر جایگزینتان تست شده است. - مراقب بلوکهای Match باشید. اگر فایل شما بخش
Matchدارد، خطوط جدید را بالاتر از اولینMatchبگذارید. هر چیزی که بعد از یکMatchبنویسید فقط برای همان شرط اعمال میشود، نه کل سرور. به همین دلیل توصیهی «این خطوط را به انتهای فایل اضافه کنید» که در آموزشهای قدیمی میبینید همیشه درست نیست.
قدم ششم: بستن احراز هویت پسوردی و GSSAPI
وقتی مطمئن شدید ورود با کلید کار میکند، پسورد را کامل ببندید. با این کار حملههای brute force عملاً بیاثر میشوند:
PubkeyAuthentication yes PasswordAuthentication no KbdInteractiveAuthentication no PermitEmptyPasswords no
گزینهی KbdInteractiveAuthentication مسیر دومی برای پرسیدن پسورد است و پیشفرضش yes است؛ اگر آن را باز بگذارید، بستن PasswordAuthentication ممکن است از طریق PAM دور زده شود. در فایلهای قدیمی همین گزینه با نام ChallengeResponseAuthentication نوشته شده؛ آن نام از OpenSSH 8.7 به بعد فقط یک نام مستعار منسوخ است و نام درست همان است که بالا آمد.
گزینهی بعدی GSSAPIAuthentication است که به احراز هویت مبتنی بر Kerberos مربوط میشود. پیشفرض خود OpenSSH برای این گزینه no است، اما فایل پیکربندی پیشفرض روی خانوادهی ردهت آن را yes میگذارد. روی یک سرور وب معمولی که Kerberos ندارد این فقط یک مسیر احراز هویت اضافی و بیاستفاده است؛ ببندیدش:
GSSAPIAuthentication no
مراقب فایلهای Include باشید
روی توزیعهای امروزی (اوبونتو ۲۲.۰۴ به بعد، دبیان ۱۱ به بعد، RHEL 9 و مشتقاتش مثل AlmaLinux و Rocky) معمولاً در ابتدای sshd_config این خط وجود دارد — فایل خودتان را نگاه کنید:
Include /etc/ssh/sshd_config.d/*.conf
در sshd_config برای هر گزینه اولین مقداری که خوانده شود برنده است. چون این خط در ابتدای فایل است، هر چیزی که داخل پوشهی sshd_config.d باشد بر تنظیمات شما در ادامهی فایل غلبه میکند. روی سرورهای ابری خیلی وقتها فایلی مثل 50-cloud-init.conf آنجا هست که PasswordAuthentication yes را برمیگرداند. پس قبل از هر چیز محتوای این پوشه را ببینید:
ls -la /etc/ssh/sshd_config.d/
و برای دیدن تنظیمات واقعاً اعمالشده پس از احتساب همهی این فایلها (این دستور را باید با کاربر root اجرا کنید):
sshd -T | grep -Ei 'port|permitrootlogin|passwordauthentication|kbdinteractive|allowgroups|allowusers|pubkey|gssapi'
تکلیف گزینهی Protocol چیست؟
در آموزشهای قدیمی — از جمله مطلبی که پیشتر در همین وبلاگ داشتیم — توصیه میشد خط #Protocol 2,1 را از حالت کامنت خارج کنید و به Protocol 2 تغییر دهید تا نسخهی ناامن ۱ پروتکل SSH غیرفعال شود. آن توصیه در زمان خودش درست بود، اما امروز موضوعیت ندارد.
پشتیبانی از SSH نسخهی ۱ در OpenSSH 7.6 (سال ۲۰۱۷) بهکلی از کد حذف شد و همراه آن خود گزینهی Protocol هم از مستندات و از کار افتاد. یعنی هر نسخهای که امروز روی سرورتان نصب است فقط و فقط با نسخهی ۲ کار میکند و نوشتن این خط هیچ اثری ندارد؛ sshd آن را بیسروصدا نادیده میگیرد (نه خطا میدهد و نه سرویس را زمین میزند). نتیجهی عملی ساده است: اگر چنین خطی در فایل شما هست حذفش کنید تا کسی گمراه نشود، و اگر نیست هیچ کاری لازم نیست. نسخهی نصبشده را میتوانید ببینید:
ssh -V
قدم آخر: تست پیکربندی و ریاستارت
پیش از ریاستارت، صحت نحو فایل را بررسی کنید:
sshd -t
اگر هیچ خروجیای نداد یعنی فایل سالم است. اگر دستور پیدا نشد، مسیر کامل را بدهید: /usr/sbin/sshd -t.
و مهمترین توصیهی کل این مقاله: نشست SSH فعلی خود را باز نگه دارید. ریاستارت sshd اتصالهای برقرار را قطع نمیکند، پس پنجرهی فعلی شما زنده میماند. با یک پنجرهی دوم روی پورت و کاربر جدید وارد شوید و فقط وقتی مطمئن شدید کار میکند، پنجرهی اول را ببندید. اگر جایی از پیکربندی اشتباه باشد، همین نشست باز تنها راه برگرداندن آن است؛ در غیر این صورت پشت در میمانید و باید از کنسول (KVM/VNC) دیتاسنتر یا پشتیبانی سرور کمک بگیرید.
حالا سرویس را ریاستارت کنید:
# AlmaLinux / Rocky / RHEL systemctl restart sshd # دبیان / اوبونتو systemctl restart ssh
روی همهی توزیعهای امروزی، دستورهای قدیمی service sshd restart و /etc/rc.d/init.d/sshd restart (یا /etc/init.d/sshd restart) منسوخاند. اولی فقط یک لایهی سازگاری است که درخواست را به systemd پاس میدهد و دومی روی بیشتر سرورها اصلاً وجود ندارد. شکل درست همان systemctl است. توجه کنید که نام یونیت روی خانوادهی ردهت sshd و روی دبیان و اوبونتو ssh است؛ در دستورهای بعدی هم همین نام را جایگزین کنید.
وضعیت سرویس را ببینید:
# خانوادهی ردهت systemctl status sshd # دبیان / اوبونتو systemctl status ssh
و اگر بالا نیامد، دلیلش را در لاگ پیدا کنید:
# خانوادهی ردهت journalctl -u sshd -n 50 --no-pager # دبیان / اوبونتو journalctl -u ssh -n 50 --no-pager
حالا از پنجرهی دوم تست کنید:
ssh -p 1423 newuser@SERVER_IP
وقتی ورود موفق شد و sudo -i هم کار کرد، برگردید و پورت ۲۲ را از sshd_config و از فایروال حذف کنید و یک بار دیگر سرویس را ریاستارت کنید.
یک لایهی آخر: جلوی تلاشهای مکرر را بگیرید
حتی با پورت غیراستاندارد و ورود کلیدی، بد نیست IPهایی که پشت سر هم ورود ناموفق دارند بهصورت خودکار بلاک شوند. اگر CSF نصب دارید، سرویس lfd همراه آن دقیقاً همین کار را میکند و تنظیمات مربوط به ورود ناموفق SSH در /etc/csf/csf.conf با کلیدواژهی LF_SSHD قابل تغییر است. روی سرورهای بدون CSF، fail2ban جایگزین رایج و رایگان همین کار است. اگر پورت SSH را عوض کردهاید، یادتان باشد پورت جدید را در پیکربندی این ابزار هم اعلام کنید، وگرنه چیزی را پایش نمیکند. ضمناً IP ثابت خودتان را در فهرست سفید (در CSF فایل /etc/csf/csf.allow) بگذارید تا یک اشتباه تایپی شما را بلاک نکند.
چکلیست نهایی
- سرور بهروز است و روی توزیعی با پشتیبانی فعال اجرا میشود.
- یک کاربر غیر root با پسورد و دسترسی
sudoساخته شده و تست شده است. - ورود با کلید
ed25519کار میکند و پرمیژن~/.sshبرابر ۷۰۰،authorized_keysبرابر ۶۰۰ و پوشهی خانگی حداکثر ۷۵۵ است. Portعوض شده و پورت جدید در CSF یا firewalld/ufw و در صورت نیاز در SELinux باز شده است.PermitRootLogin noتنظیم شده است.AllowGroupsفقط گروه مجاز را اجازه میدهد و کاربر شما عضو آن گروه است.PasswordAuthentication no،KbdInteractiveAuthentication noوGSSAPIAuthentication noتنظیم شدهاند.- پوشهی
sshd_config.dبررسی شده و خروجیsshd -Tبا چیزی که انتظار دارید جور در میآید. - گزینهی بیاثر
Protocolاز فایل حذف شده است. sshd -tبدون خطا اجرا شده و سرویس باsystemctlریاستارت شده است.- ورود از یک نشست دوم پیش از بستن نشست اول تست شده است.