صفر تا صد SSH ( قسمت 1: آشنایی و کاربرد)

SSH zero to hero-part 1
صفر تا صد SSH

SSH چیه؟‌

پروتکل SSH یا Secure Shell ، روشی برای ایجاد یک کانال ارتباطی امن بین دو کامپیوتر تحت شبکه است. این پروتکل به سیستم ادمین‌ها اجازه میده که دریک شبکه ناامن مثل اینترنت ارتباطی امن با سیستم مقصد خودشون برقرار کنن و کارای مدیریتی رو انجام بدن. اس اس اچ ارتباط بین کلاینت و سرور رو رمزگذاری می کنه، وقتی می گیم ارتباطو رمزگذاری می کنه یعنی احراز هویت، دستورات، خروجی ها و فایلهایی که این بین رد و بدل می شه رمزگذاری و پس از رسیدن به مقصد چه تو کلاینت و چه تو سرور رمزگشایی می شن.

آقای Tatu Ylönen محققی در دانشگاه هلسینکی این پروتکل رو ابداء کرده. ایده کار زمانی به ذهنش رسیده که شبکه دانشگاه تحت حمله پسورد اسنیفینگ قرار گرفته چون پروتکل‌های اون زمان اعتبار سنجی و احراز هویت خوبی نداشتن. ایشون این پروتکل رو با هدف جایگزین شدن با پروتکل‌های مرسوم اون زمان یعنی telnet ، ftp ، rlogin طراحی و توسعه دادن که بسیار هم پروژه موفقی بوده. همین اول کاری چنتا اصطلاحی که ممکنه تو متن بهش بر بخورید رو می‌نویسم تا دچار سردرگمی نشید.

  • SSH : با حروف بزرگ به طور کلی اشاره به پروتکل SSH
  • ssh : نرم‌افزار سمت کلاینت
  • SSHD : در‌واقع Daemon سرویس SSH تو لینوکسه
  • SSH1 : اولین نسخه
  • SSH2 : نسخه تجاری و غیر رایگان محصول شرکت Tectia
  • Open SSH : نسخه متن باز و رایگان SSH که معمولاً از این نسخه تو سیستم عامل های مرسوم استفاده می کنیم.

SSH چطور کار می کنه؟

این پروتکل به ما این امکانو میده که کامپیوتری که تو ریموت هست رو مثل سیستم خودمون مدیریت کنیم. سرور روی یه پورت از پیش تعیین شده‌ای، نتورک رو مانیتور می کنه و متنظر می مونه تا کلاینت ارتباط رو شروع کنه. اولین مرحله در برقراری این ارتباط مطمئن شدن از صحت اطلاعات میزبان هست و کلاینت در ابتدای برقراری ارتباط، سرور را اعتبار سنجی میکنه. در ادامه به صورت مرحله به مرحله برقراری این کانکشو بررسی می کنیم.

برقراری ارتباط SSH

برای اینکه به سرور وصل بشید باید با استفاده از نسخه کلاینت که معمولاً از Open SSH استفاده میشه، یه رکوئست که حاوی اطلاعات حداقلی شامل یوزرنیم و آدرس سرور هست، به سرور ارسال بشه. شکل این دستور به شکل زیره:‌

ssh [username]@[server_ip_or_hostname]

وقتی سرور رکوئست برقراری ارتباطو دریافت می کنه، سرور پروتکل‌های رمزنگاریی که ازش ساپورت می کنه رو میفرسته.در مقابل کلاینت پروتکل‌های ارسالی سرورو با مال خودش مقایسه می کنه و اگر مورد مشابهی پیدا کرد هردو ماشین توافق می‌کنن که با اون پروتکل رمزنگاری ارتباطو برقرار کنن.

تصدیق میزبان

دقت کنید که هنوز به مرحله اهراز هویت کاربر نرسیدیم و فعلاً هر دو ماشین به صورت خودکار دارن یک سری مذاکرات بر سر اینکه چطوری میخوان داده‌ها رو رمزنگاری و به طرف مقابل ارسال کنن انجام میدن. سرور کلید عمومی خودشو به کلاینت ارسال می کنه و کلاینت با اضافه کردن این کلید به یه لیست، سرورُ به خاطر می سپاره این کلید در احراز هویت‌های آتی مورد استفاده قرار می گیره. لیست مورد بحث یه فایل تکست خیلی عادی به اسم known_hosts هست که معمولاً داخل دایرکتوری.ssh تو هوم یوزر خودتون هست. اطلاعاتی که در هر خط این فایل قرار می گیره شامل : نام هاست ( می تونه آی پی یا دامین باشه)، شماره پورت(اگه غیر از ۲۲ باشه) ، نوع پروتکل رمزنگاری و نهایتاً کلید عمومی سرور.

با این اطلاعات یوزر از درست بودن اطلاعات سرور مطمئن می‌شه، این تکنیک جلوی حملات مرد میانی میگیره چون شما کلید عمومی سرور را از قبل به عنوان یک میزبان مورد اعتماد به سیستم اضافه کردید، سرور داده‌ها رو با کلید خصوصی خودش رمزگذاری میکنه و به کلاینت میفرسته و چون کلاینت از قبل کلید عمومیو داره میتونه رمز داده‌ها رو بشکنه. خیلی منطقیه که کلید عمومی از راه‌های امن تر و قبل از اتصال شما به سرور به دستتون رسیده باشه مثلاً ایمیل، نامه، کبوتر نامه رسان و یا اینکه خودتون برید پشت سرور و کلیدو بنویسید!

The authenticity of host 'server-us (xxx.xxx.xxx.xxx)' can't be established.
ED25519 key fingerprint is SHA256:+Is8IqfaBQZTI/XXXXXXXXXXXXXXXXXXXXXXX.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? 

اگر کلاینت هیچ اطلاعاتی از میزبان یا همون سرور در اختیار نداشته باشه با یک پیغامی شبیه بالا بهتون اطلاع میده و اثر انگشت(fingerprint) کلیدعمومی سرورو بهمون نشون میده اگر اثر انگشت کلید سرور از قبل براتون ارسال کردن یا به طریقی ازش اطلاع دارید می تونید صحتشو بررسی کنید و اگر گزینه yes رو انتخاب کنیدکلید عمومی میزبان اضافه میشه. در صورتی که به هر دلیلی کلید عمومی سرور تغییر کنه، متوجه این موضوع میشه و با نشون دادن پیغام هشدار دهنده ای مثل پایین بهتون میگه که احتمال داره یکی این وسط داره یه کار کثیف انجام میده و شمارو از وصل شدن به سرور منع می کنه. شما باید به طریقی از مجاز بودن تغییر این کلید با خبر بشید. و بعد از پاک کردن کلید قبلی ذخیره شده تو known_hosts و اضافه کردن دوباره شه که می تونید دوباره به سرور متصل بشید.

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
xx:xx:xx.
Please contact your system administrator.
Add correct host key in /home/hostname /.ssh/known_hosts to get rid of this message.
Offending RSA key in /var/lib/sss/pubconf/known_hosts:4
RSA host key for user has changed and you have requested strict checking.
Host key verification failed.

مذاکرات رمزنگاری جلسه

بعد از اینکه کلاینت از مورد اعتماد بودن سرور مطمئن شد نوبت به میرسه به مذاکره بر سر چگونگی رمزنگاری جلسه (Session) اتصال SSH . دلیل اینکه پروتکل Secure Shell امنیت خوبی داره دقیقاً به همینجا برمی گرده چون جدا از روش‌های متنوع برای احراز هویت کاربران، کل ارتباط به شکل دو طرفه و بلااستثناء رمزگذاری میشه. برای این کار از متدهای مختلفی استفاده می شه که یکی از اونها الگوریتم Diffie-Hellman هست و برای مبادله کلیدهایی استفاده میشه که هر دو طرف در رمزگذاری داده‌های ارسالی از اون استفاده می کنند.

روش کار این متد به صورت زیر و مرحله به مرحله انجام میشه:‌

  • هر دو ماشین روی یک عدد توافق می‌کنند: عددی که انتخاب میشه یک عدد اوله که برای جلوگیری از حملات بروت فورس حداقل 300 رقمی خواهد بود. یک عدد دلخواه بزرگ که p در نظر می‌گیریم پیمانه عمل ضرب هست. و یک مقدار اولیه دیگه که g در نظر میگیرم. این مرحله به صورت پابلیک انجام میشه و مقادیر توافق شده بدون رمزنگاری بین دو طرف مبادله می شه.
  • حالا هر دو طرف یک مقدار پنهانی برای خودشون انتخاب می‌کنند طرف اول عدد(a=6) و با جایگذاری اون در این معادله (ga mod p) نتیجه رو به طرف دوم ارسال می کنه. ۸ = (۲۳ ۵۶mod)
  • طرف دوم با انتخاب مقدار پنهانی(b=15) و جایگذاری در معادله مشابه نتیجه رو به طرف اول جلسه ارسال میکنه. ۱۹ = (۲۳ ۵۱۵mod)
  • طرف اول مقدار ارسال شده توسط طرف دوم را در معادله Insert Formula (b^b mod p )^a mod pجایگذاری کرده و کلید رمز مشترک را محاسبه می کند. ۲ = (۲۳ ۱۹۶mod)
  • طرف دوم مقدار ارسال شده توسط طرف اول را در معادله Insert Formula (b^b mod p )^a mod pجایگذاری کرده و کلید رمز مشترک را محاسبه می کند.۲ = (۲۳ ۸۱۵mod)

حالا هر دو ماشین بروی یک مقدار توافق کردند، البته که هیچ وقت چنین مقادیر کوچک قابل حدسی استفاده نمی شه و مقادیر انتخابی باید به درستی انتخاب بشوند. در غیر این صورت با استفاده الگوریتم هایی قابل محاسبه هستند هر چند هنوز الگوریتم بسیاری سریعی برای اینکار وجود ندارد. اگر نفر سومی روی خط ارتباطی قرار بگیرد می‌توانه بدون شناسایی با هر دو طرف به صورت جداگانه توافق کند و پیام‌های ارسالی توسط هر دو طرف رو بخواند. برای جلوگیری از این نوع حملات از کلیدهای عمومی و خصوصی دو طرف برای محاسبه کلید مشترک رمزنگاری استفاده می‌کند.

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

حالا که این نوع از تبادل کلید رو بررسی کردیم لازمه که بگم این روش امروزه به عنوان روشی ضعیف تلقی میشه در مقابل الگوریتم های بسیار امن تری مثل curve25519-sha256 و به صورت دیفالت در ورژن جدید SSH استفاده نمی شه. برای اینکه الگوریتم هایی که ورژن ssh نصب شده رو سیستم خودتون، ساپورت می کنه رو ببینید می تونید از دستور زیر استفاده کنید.

ssh -Q kex
=== output ====
diffie-hellman-group1-sha1
diffie-hellman-group14-sha1
diffie-hellman-group14-sha256
diffie-hellman-group16-sha512
diffie-hellman-group18-sha512
diffie-hellman-group-exchange-sha1
diffie-hellman-group-exchange-sha256
ecdh-sha2-nistp256
ecdh-sha2-nistp384
ecdh-sha2-nistp521
curve25519-sha256
curve25519-sha256@libssh.org

احراز هویت کاربر

خب بالاخره رسیدیم به مرحله‌ای که کاربر با استفاده از روش‌هایی که سرور بهش اجازه می‌ده با یوزر خودش لاگین کنه. بهترین روش برای اینکار استفاده از کلیدهای ssh هست. کاربرها می تونن با استفاده از پسورد هم لاگین رو انجام بدن در اینصورت احراز هویت کاربر فقط و فقط وابسته به میزان پیچیدگی پسورد خواهد بود و اگر کاربر از پسورد ضعیف استفاده کنه در برابر حملات حدس پسورد آسیب‌پذیر خواهد بود. البته که می تونیم از ترکیب کلید و پسورد هم استفاده کنم.

کلیدها از نوع نامتقارن هستند که یعنی کلیدی که کاربر برای رمزگذاری استفاده می کنه با کلیدی که سرور برای رمزگشایی استفاده می کنه یکی نیست و در نوع متقارن رمزگذاری و رمزگشایی توسط یک کلید انجام میشه. برای اینکه کلید خودمون بسازیم، ssh یه ابزار برای اینکار داره به نام ssh-keygen . خیلی ساده اجراش می‌کنید حتماً یادتون باشه که به کلیداتون پسورد بدید مگر اینکه دلیل قانع کننده ای داشته باشید و بعد اوکیه.

ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/user/.ssh/id_rsa): /home/user/id_rsa
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/amin/test_rsa
Your public key has been saved in /home/amin/test_rsa.pub
The key fingerprint is:
SHA256:CuGhn5ZUMfnVtr81Hl3FDW2tesWaK8qS40YLHwYUYt4 amin@HOME-PC
The key's randomart image is:
+---[RSA 3072]----+
|    o +o   .  .+o   |
|   o ooo  . o   B  |
|    +.E. . . . +.     | 
|   o +. .   . . +     |
|  . +  .S    o +o   |
|   o +..+   . =oo  |
|    = .= +   ..+o   |
|   .    B.  . o.      |
|       o.oo. .       |
+----[SHA256]-----+

برای راحتی کار با کلیدهاتون یک ابزار دیگه داریم که کارش نگهداری کلید و پسورداس و با استفاده از این ابزار می تونیم بدون نیاز به تایپ پسورد و به روش SSO(Single Sign On) به سرور کانکت بشیم. ssh-agent این کارو برامون انجام میده. توی بیشتر توزیع‌های گنو/لینوکسی در هنگام لاگین کاربر این، این ابزار هم به طور اتوماتیک ران میشه و اگر خودتون بخوایید می تونید کلیدهایی رو به صورت پیش‌فرض بهش اضافه کنید. در صورتی که سرویس در حال اجرا نباشه نمی تونید از کلیداتون برای لاگین کردن استفاده بکنید و هنگام استفاده از ssh-add با خطا مواجه خواهید شد.

راه اندازی ssh-agent

اگر سرویس ssh-agent در حال حاضر تو سیستمون در حال اجراس از این مرحله رد بشید.! اولین، ساده‌ترین و سریع‌ترین راه برای ران کردن این سرویس داخل محیط shell که در حال حاضر قصد دارید از ssh استفاده کنید اجراش کنید. برای این کار از این دستور استفاده می‌کنیم:

eval `ssh-agent`

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

touch .config/systemd/user/ssh-agent.service

و باهر ادیتوری که راحت هستید کانفیگ پایین رو داخلش کپی کنید:‌

[Unit]
Description=SSH key agent

[Service]
Type=simple
Environment=SSH_AUTH_SOCK=%t/ssh-agent.socket
# DISPLAY required for ssh-askpass to work
ExecStart=/usr/bin/ssh-agent -D -a $SSH_AUTH_SOCK
ExecStopPost=/bin/rm ${SSH_AUTH_SOCK}

[Install]
WantedBy=default.target

حالا باید یک متغیر محیطی ( Environment Variable ) ست کنید. بسته یه شلی که استفاده می‌کنید نام فایل کانفیگ شل متفاوته ولی دوتا از معروف تریناش فایل‌های .bash_profile یا .zprofile هست. خط پایینو به آخر فایل مورد نظرتون اضافه کنید.

export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent.socket

فقط یک مرحله دیگه باقی مونده و اون هم استارت سرویس ssh-agent و فعال کردنشه که به محض لاگین کردن با یوزر خودتون استارت میشه و تو بکگراند به کارش ادامه میده. کدای زیر رو به ترتیب وارد کنید. نیازی به اجرای دستورات با دسترسی sudo نیست چون در سطح کاربری خودتون اجرا میشه.

#Make ssh-agent.service start as soon as user login 
systemctl --user enable ssh-agent.service
#Start ssh-agent.service right now
systemctl --user start ssh-agent.service

حالا که agent هم اجرا شده کلید هم ساختید وقته شه که با کلید جدیدمون لاگین کنیم. اما صب کنید !!! سرور از کجا باید بدونه ما کی هستیم ؟ یادتونه که گفتم کلید های ssh نامتقارن هستن و در‌واقع یک جفت کلید هستن که با عنوان‌های کلید عمومی و خصوصی شناخته میشن. در این مرحله باید کلید عمومی رو به سرور بشناسونیم چه روشی بهتر از خود ssh برای ارسال کلید! کلیدهای شما معمولاً در مسیر .ssh داخل هوم خودتون ذخیر میشن و دو تا فایل هستن که کلید عمومی آخرش یه .pub داره. ssh این قابلیت رو داره که بهش یک کامند بدید و بعد از اتصال به سرور اون کامند رو ران کنه و بعد ارتباط رو قطع کنه ما هم از این روش برای ارسال کلید استفاده می‌کنیم.

دقت کنید که کلید رو باید داخل دایرکتوری home یوزری قرار بدید که قصد اتصال با اون یوزر رو دارید. شکل کلی دستور به زیر هست که باید بنابر مشخصات خودتون تغییرش بدید. یادتون باشه قبلش دایرکتوری .ssh روی سرور بسازید.

cat .ssh/vps_rsa.pub | ssh user@remote-ser  cat > ~/.ssh/authorized_keys 

حالا برگردید رو کلاینت و کلید رو به agent اضافه کنید . شکل کلی دستور به صورت زیر:‌

ssh-add .ssh/vps_rsa

اگر پسورد داده باشید ازتون پسورد رو میخواد و بعد می تونید به راحتی با کلید به سرورتون وصل بشید.

ssh user@remote-server

مثل آب خوردن به سرور وصل می شید. برای اینکه مطلب خیلی طولانی نشه تصمیم گرفتم به چند قسمت تقسیمش کنم. در این قسمت کلاً با SSH آشنا شدیم، در مورد روش‌های رمزنگاری، ساخت کلید و اتصال به سرور آشنا شدیم. در پارت بعدی با تنظیمات سمت کلاینت و سرور باشما خواهیم بود. منتظر باشید. :‌)‌