سروری که تازه تحویل گرفته‌اید از همان لحظه‌ی اول روی اینترنت است. ربات‌هایی که دائماً پورت ۲۲ را به دنبال رمزهای ضعیف اسکن می‌کنند منتظر نمی‌مانند تا شما کارتان را تمام کنید؛ کافی است چند ساعت بعد از راه‌اندازی نگاهی به /var/log/auth.log بیندازید تا تلاش‌های ناموفق ورود را ببینید.

خبر خوب این است که بیشتر این خطرها با چند کار ساده و تکراری از بین می‌روند. در این نوشته همان کارها را به ترتیب انجام می‌دهیم: به‌روزرسانی، ساختن کاربر غیر root، ورود فقط با کلید، دیوار آتش، به‌روزرسانی امنیتی خودکار، fail2ban و تنظیم ساعت. همه‌ی دستورها برای Ubuntu 24.04 نوشته شده‌اند و فرض بر این است که الان با root یا کاربری که سرویس‌دهنده ساخته وارد شده‌اید.

اول از همه: به‌روزرسانی

ایمیج‌هایی که سرویس‌دهنده‌ها استفاده می‌کنند معمولاً چند هفته یا چند ماه از آخرین به‌روزرسانی عقب‌اند. پیش از هر کار دیگری بسته‌ها را به‌روز کنید:

bash
apt update
apt full-upgrade -y

اگر بعد از این کار فایل /var/run/reboot-required وجود داشت، یعنی هسته یا کتابخانه‌ای اساسی عوض شده و بهتر است همین حالا، که هنوز سرویسی روی سرور نیست، یک بار ری‌استارت کنید:

bash
[ -f /var/run/reboot-required ] && reboot

کاربر غیر root با دسترسی sudo

کار روزمره با root یعنی هر اشتباه تایپی با بالاترین سطح دسترسی اجرا می‌شود. یک کاربر معمولی می‌سازیم و فقط هر جا لازم بود با sudo دسترسی می‌گیریم. در این نوشته اسم کاربر را deploy می‌گذاریم؛ شما هر اسمی که می‌خواهید انتخاب کنید.

bash
adduser deploy
usermod -aG sudo deploy

adduser یک رمز عبور می‌پرسد. این رمز برای ورود از راه SSH استفاده نخواهد شد، اما sudo آن را می‌خواهد؛ پس یک رمز قوی بگذارید و در مدیر رمز عبورتان نگه دارید.

ورود فقط با کلید SSH

رمز عبور را می‌شود حدس زد، اما کلید Ed25519 را عملاً نمی‌شود. هدف این بخش این است که ورود با رمز و ورود مستقیم root به‌کلی بسته شود.

ساختن و کپی‌کردن کلید

اگر روی سیستم خودتان هنوز کلید ندارید، یکی بسازید (این دستور روی لپ‌تاپ اجرا می‌شود، نه روی سرور):

bash
ssh-keygen -t ed25519 -C "laptop"

اگر سرویس‌دهنده موقع ساخت سرور کلید شما را برای root گذاشته، ساده‌ترین راه این است که همان را برای کاربر جدید کپی کنید. روی سرور و با root:

bash
mkdir -p /home/deploy/.ssh
cp /root/.ssh/authorized_keys /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys

اگر ورود با رمز هنوز باز است، از لپ‌تاپ ssh-copy-id deploy@SERVER_IP هم همین کار را می‌کند. حالا در یک ترمینال جدید امتحان کنید که ssh deploy@SERVER_IP بدون رمز وارد می‌شود و sudo -v کار می‌کند. تا این مرحله درست کار نکرده، جلو نروید.

فایل drop-in برای sshd

در Ubuntu 24.04 فایل اصلی /etc/ssh/sshd_config در همان خطوط اول، همه‌ی فایل‌های /etc/ssh/sshd_config.d/*.conf را Include می‌کند. به جای ویرایش فایل اصلی، تنظیمات خودمان را در یک فایل جدا می‌گذاریم؛ این‌طوری به‌روزرسانی بسته‌ی openssh-server هم سر تغییر فایل اصلی سؤالی نمی‌پرسد.

یک نکته‌ی ظریف: در sshd برای هر گزینه اولین مقداری که خوانده می‌شود اعمال می‌شود، نه آخرین1. روی بسیاری از سرورهای ابری، cloud-init فایلی مثل 50-cloud-init.conf با PasswordAuthentication yes می‌سازد. برای اینکه تنظیمات ما برنده شود، اسم فایل را طوری انتخاب می‌کنیم که در ترتیب الفبایی زودتر بیاید:

/etc/ssh/sshd_config.d/00-hardening.conf
# Only key-based logins, never as root
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey

# Fewer chances per connection, shorter idle handshakes
MaxAuthTries 3
LoginGraceTime 30

# Nothing on this server needs X11
X11Forwarding no

# Only these users may log in over SSH
AllowUsers deploy

خط AllowUsers قوی‌ترین و در عین حال خطرناک‌ترین خط این فایل است: هر کاربری که اسمش اینجا نباشد، حتی با کلید درست، وارد نمی‌شود. اسم را دقیق بنویسید.

پیش از اعمال، درستی نحو فایل را بررسی کنید و ببینید sshd در عمل چه مقادیری را برداشته است:

bash
sudo sshd -t
sudo sshd -T | grep -Ei '^(permitrootlogin|passwordauthentication|kbdinteractiveauthentication|allowusers)'

خروجی دستور دوم باید permitrootlogin no، passwordauthentication no و kbdinteractiveauthentication no را نشان دهد. اگر هنوز yes می‌بینید، یعنی فایل دیگری زودتر همین گزینه را تنظیم کرده است.

پیش از reload: نشست دوم را باز نگه دارید

این مهم‌ترین توصیه‌ی کل این نوشته است. پیش از reload کردن sshd، یک نشست SSH دوم باز کنید و تا آخر کار نبندیدش. reload و حتی restart سرویس SSH اتصال‌های موجود را قطع نمی‌کند؛ پس اگر تنظیمات اشتباه باشد، آن نشست باز تنها راه شما برای درست‌کردنش است، بدون اینکه کارتان به کنسول اضطراری سرویس‌دهنده بکشد.

bash
sudo systemctl reload ssh

در Ubuntu اسم سرویس ssh است، نه sshd. حالا از یک ترمینال سوم امتحان کنید:

bash
# Must work
ssh deploy@SERVER_IP

# Must fail with "Permission denied (publickey)"
ssh root@SERVER_IP
ssh -o PubkeyAuthentication=no -o PreferredAuthentications=password deploy@SERVER_IP

فقط وقتی هر سه نتیجه همان بود که انتظار داشتید، نشست‌های قبلی را ببندید.

دیوار آتش با ufw

ufw روی Ubuntu نصب است اما فعال نیست. سیاست پیش‌فرض را «همه‌ی ورودی‌ها بسته، همه‌ی خروجی‌ها باز» می‌گذاریم و فقط SSH و وب را باز می‌کنیم. ترتیب مهم است: اول SSH را مجاز کنید، بعد ufw را روشن کنید، وگرنه اتصال خودتان را می‌بندید.

bash
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose

OpenSSH یک پروفایل آماده است که پورت ۲۲ را باز می‌کند؛ فهرست پروفایل‌ها را با sudo ufw app list ببینید. اگر وب‌سرورتان HTTP/3 را پشتیبانی می‌کند، 443/udp را هم باز کنید.

دو نکته که زیاد غافلگیر می‌کند:

  • پورت‌هایی که Docker با -p منتشر می‌کند از کنار ufw رد می‌شوند، چون Docker قواعد iptables خودش را مستقیم می‌نویسد. اگر Docker دارید، سرویس‌های داخلی را فقط روی 127.0.0.1 منتشر کنید.
  • اگر سرویس‌دهنده‌تان دیوار آتش ابری هم دارد، هر دو لایه باید با هم هماهنگ باشند؛ داشتن هر دو اشکالی ندارد و حتی بهتر است.

به‌روزرسانی‌های امنیتی خودکار

بیشتر نفوذها از آسیب‌پذیری‌هایی است که وصله‌شان مدت‌ها پیش منتشر شده بود. بسته‌ی unattended-upgrades دقیقاً برای همین است: وصله‌های امنیتی را هر روز خودش نصب می‌کند. روی Ubuntu 24.04 معمولاً از قبل نصب است، اما مطمئن شوید:

bash
sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades

دستور دوم فایل زیر را می‌سازد یا به‌روز می‌کند؛ محتوایش باید این باشد:

/etc/apt/apt.conf.d/20auto-upgrades
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";

بعضی وصله‌ها، مثل وصله‌های هسته، تا ری‌استارت اعمال نمی‌شوند. اگر سرور شما چند دقیقه قطعی در نیمه‌شب را تحمل می‌کند، ری‌استارت خودکار را روشن کنید. به جای دست‌زدن به فایل 50unattended-upgrades، تغییرات را در یک فایل جدا می‌گذاریم که بعد از آن خوانده شود:

/etc/apt/apt.conf.d/52unattended-upgrades-local
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "04:00";
Unattended-Upgrade::Remove-Unused-Dependencies "true";

ساعت 04:00 به وقت منطقه‌ی زمانی سرور است؛ برای همین تنظیم ساعت را پایین‌تر جدی می‌گیریم. برای اینکه ببینید همه‌چیز درست تنظیم شده:

bash
sudo unattended-upgrade --dry-run --debug

گزارش اجراها در /var/log/unattended-upgrades/ نوشته می‌شود.

fail2ban برای SSH

وقتی ورود با رمز بسته است، حدس‌زدن رمز دیگر جواب نمی‌دهد. پس fail2ban اینجا بیشتر برای کم‌کردن سروصدا و بار بیهوده است: IPهایی که پشت سر هم تلاش ناموفق دارند برای مدتی مسدود می‌شوند و لاگ‌ها خواناتر می‌مانند.

bash
sudo apt install -y fail2ban python3-systemd

فایل jail.conf را ویرایش نکنید؛ با هر به‌روزرسانی ممکن است بازنویسی شود. تنظیمات خودمان را در jail.local می‌نویسیم:

/etc/fail2ban/jail.local
[DEFAULT]
bantime  = 1h
findtime = 10m
maxretry = 5
# Never ban localhost; add your own static IP here if you have one
ignoreip = 127.0.0.1/8 ::1

[sshd]
enabled = true
backend = systemd

با backend = systemd لاگ‌ها مستقیم از journal خوانده می‌شوند و به وجود فایل لاگ خاصی وابسته نیستیم. سرویس را روشن و وضعیت را بررسی کنید:

bash
sudo systemctl enable --now fail2ban
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd

اگر روزی IP خودتان مسدود شد، از کنسول سرویس‌دهنده یا از سروری دیگر:

bash
sudo fail2ban-client set sshd unbanip 203.0.113.7

منطقه‌ی زمانی و همگام‌سازی ساعت

ساعت درست چیز تزئینی نیست: گواهی‌های TLS، توکن‌های زمان‌دار، ترتیب لاگ‌ها و زمان اجرای cron همه به آن وابسته‌اند. وضعیت فعلی را ببینید:

bash
timedatectl

برای منطقه‌ی زمانی دو انتخاب معقول وجود دارد. UTC برای سرورهایی که چند منطقه را پوشش می‌دهند یا لاگ‌هایشان با سرورهای دیگر مقایسه می‌شود بهتر است؛ وقت محلی برای سروری که کاربرانش در یک منطقه‌اند و cron‌هایش به ساعت کاری گره خورده راحت‌تر است. مهم این است که آگاهانه انتخاب کنید:

bash
sudo timedatectl set-timezone Asia/Tehran
# or keep it universal:
# sudo timedatectl set-timezone Etc/UTC

Ubuntu 24.04 برای همگام‌سازی ساعت از systemd-timesyncd استفاده می‌کند. مطمئن شوید روشن است و ساعت همگام شده:

bash
sudo timedatectl set-ntp true
timedatectl show -p NTPSynchronized
timedatectl timesync-status

خروجی دستور دوم باید NTPSynchronized=yes باشد. اگر نیست، احتمالاً دیوار آتش سرویس‌دهنده ترافیک خروجی UDP روی پورت ۱۲۳ را بسته است.

چک‌لیست ساعت اول

این فهرست را کنار دستتان نگه دارید و برای هر سرور جدید از اول تا آخر مرورش کنید:

  1. apt update && apt full-upgrade و در صورت نیاز یک ری‌استارت.
  2. ساختن کاربر غیر root و افزودنش به گروه sudo.
  3. کپی کلید عمومی به ~/.ssh/authorized_keys همان کاربر، با دسترسی‌های 700 و 600.
  4. امتحان ورود با کلید و کار‌کردن sudo در یک ترمینال جدید.
  5. ساختن /etc/ssh/sshd_config.d/00-hardening.conf و بررسی با sshd -t و sshd -T.
  6. باز نگه‌داشتن یک نشست دوم، سپس systemctl reload ssh.
  7. امتحان اینکه root و ورود با رمز رد می‌شوند.
  8. ufw: اول allow OpenSSH، بعد ۸۰ و ۴۴۳، بعد enable.
  9. فعال‌بودن unattended-upgrades و تصمیم درباره‌ی ری‌استارت خودکار.
  10. fail2ban با jail.local و بررسی fail2ban-client status sshd.
  11. منطقه‌ی زمانی آگاهانه و NTPSynchronized=yes.

جمع‌بندی

هیچ‌کدام از این قدم‌ها پیچیده نیست و همه با هم کمتر از یک ساعت وقت می‌گیرند، اما همین چند کار سرور را از فهرست هدف‌های آسان بیرون می‌آورد. اگر فقط یک چیز از این نوشته در ذهنتان بماند، این باشد: پیش از هر تغییری در تنظیمات SSH یا دیوار آتش، یک راه برگشت باز نگه دارید. قدم بعدی، که موضوع یک نوشته‌ی جداست، نسخه‌ی پشتیبانی است که واقعاً بشود از آن بازگردانی کرد.

  1. در صفحه‌ی راهنمای sshd_config آمده است که برای هر کلیدواژه اولین مقدار به‌دست‌آمده استفاده می‌شود. با man sshd_config آن را ببینید. ↩