توی این درس یاد میگیری SSH (Secure Shell) راه استاندارد کار با یک سرور از راه دور است: ترمینالِ آن ماشین، روی یک کانال رمزنگاریشده. با ssh user@host وصل میشوی؛ بهجای رمز، با یک جفتکلید (کلید خصوصی پیش خودت، کلید عمومی روی سرور) که نوع امروزیاش ed25519 است وارد میشوی؛ با ssh-keygen میسازی و با ssh-copy-id روی سرور میگذاری؛ در ~/.ssh/config برای هر سرور یک اسم کوتاه تعریف میکنی؛ با scp و rsync -avz فایل جابهجا میکنی؛ و sshd را امنتر میکنی. تمرین: ورود بدون رمز با کلید به یک سرور را راه میاندازی.
مسئله: چطور از راه دور و امن به یک سرور وصل شویم؟
Section titled “مسئله: چطور از راه دور و امن به یک سرور وصل شویم؟”سرور معمولاً یک ماشین در یک دیتاسنتر دور است؛ فیزیکی به آن دسترسی نداری. باید از راه شبکه وارد شوی، بهطوری که کسی که ترافیک را میشنود نه محتوا را ببیند و نه رمزت را بدزدد، و مطمئن باشی واقعاً به همان سرور وصلی نه یک جعلی. SSH هر دو را حل میکند: رمزنگاری و شناسایی هر دو طرف.
تشبیه: قفل و کلید
Section titled “تشبیه: قفل و کلید”کلید عمومی مثل یک قفل است که میتوانی بدهی دست هر کسی و روی در سرور بزنند؛ قفل خودش بهتنهایی هیچچیز باز نمیکند. کلید خصوصی مثل کلیدی است که فقط تو داری و فقط قفل خودت را باز میکند. هر وقت وصل میشوی، سرور یک معمای تصادفی میدهد که فقط صاحب کلید خصوصی میتواند حلش کند، بدون اینکه کلید خصوصی هرگز از دستگاه تو بیرون برود.
مثالهای عملی
Section titled “مثالهای عملی”برای اینکه همهچیز واقعی باشد، هر دو سمت روی ماشین systemdدار آزمایشی من هستند: کاربر alice نقش «لپتاپ تو» را دارد و کاربر deploy نقش حساب روی سرور. اتصال از راه شبکه به نام ماشین (server) برقرار میشود، پس همان پروتکل و همان پیامهای واقعیاند. چون ssh یک ترمینال تعاملی میخواهد (رمز، پرسش اعتماد)، از ترمینال مجازی tmux استفاده میکنم تا صفحهی واقعی را نشانت بدهم (در پرسشها و رمزها، تایپشدههایم پنهان است مثل ترمینال واقعی).
مثال ۱: اولین اتصال با رمز
Section titled “مثال ۱: اولین اتصال با رمز”اول دو کاربر را میسازم (برای deploy یک رمز آزمایشی میگذارم) و دو تابع کمکی ترمینال مجازی را تعریف میکنم:
useradd -m -s /bin/bash aliceuseradd -m -s /bin/bash deployecho "deploy:lab-pass" | chpasswdPW=lab-passrun_in_tmux() { tmux -u kill-server 2>/dev/null; tmux -u new-session -d -s t -x "${TW:-100}" -y "${TH:-30}" "$@"; sleep 1; }screen() { tmux capture-pane -t t -p | sed -e :a -e '/^[[:space:]]*$/{$d;N;ba' -e '}'; }id alice deployuid=1002(alice) gid=1002(alice) groups=1002(alice)uid=1003(deploy) gid=1003(deploy) groups=1003(deploy)حالا alice (کلاینت) به server وصل میشود: ssh user@host، کاربر و میزبان:
TH=42run_in_tmux "su - alice"tmux send-keys -t t "ssh deploy@server" Enter; sleep 2tmux send-keys -t t "yes" Enter; sleep 2tmux send-keys -t t "$PW" Enter; sleep 3tmux send-keys -t t "whoami; hostname" Enter; sleep 2screentmux send-keys -t t "exit" Enter; sleep 1alice@server:~$ ssh deploy@serverThe authenticity of host 'server (172.17.0.4)' can't be established.ED25519 key fingerprint is SHA256:eQ9IL4cQjpWrXS4SUG7Tuj+iCCzpOr4DJ70oKKbGM8o.This key is not known by any other names.Are you sure you want to continue connecting (yes/no/[fingerprint])? yesWarning: Permanently added 'server' (ED25519) to the list of known hosts.deploy@server's password:Welcome to Ubuntu 24.04.5 LTS (GNU/Linux 7.0.14-linuxkit aarch64)
* Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/pro
This system has been minimized by removing packages and content that arenot required on a system that users do not log into.
To restore this content, you can run the 'unminimize' command.
The programs included with the Ubuntu system are free software;the exact distribution terms for each program are described in theindividual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted byapplicable law.
deploy@server:~$ whoami; hostnamedeployserverdeploy@server:~$سه چیز اتفاق افتاد:
- اعتماد به میزبان: چون این اولین بار است، ssh میگوید «اصالت
serverرا نمیتوانم تأیید کنم» و اثرانگشت (fingerprint) کلید میزبان را نشان داد. این همان «هویت سرور» است. اگرyesبزنی، ذخیره میشود و دفعهی بعد خودکار بررسی میشود. (روی سرور مهم، اثرانگشت را از مدیر سرور بپرس و با آنچه نشان میدهد مقایسه کن؛ فقط وقتیyesبزن که مطمئنی.) - رمز:
deploy@server's password:رمز حسابdeployرا خواست (هنگام تایپ دیده نمیشود). - ورود: پیام خوشآمد (MOTD)، علامت
deploy@server:~$و دستورهایwhoamiوhostnameروی سرور اجرا شدند:deployوserver. وexitاتصال را میبندد (که روی Ubuntu صفحه را هم پاک میکند؛ برای همین صفحه را قبل از آن گرفتم).
اثرانگشتها در فایل ~/.ssh/known_hosts کاربر alice ذخیره شدند:
echo "--- ~/.ssh/known_hosts (اسمها هش شدهاند؛ هر ستون اول یک میزبان است):"su - alice -c 'cut -c1-60 ~/.ssh/known_hosts'echo "--- ssh-keygen -F: آیا server را میشناسم؟"su - alice -c 'ssh-keygen -F server | head -1'echo "--- بعد از این از پیامهای طولانی MOTD خلاص میشویم (.hushlogin روی سرور):"touch /home/deploy/.hushloginchown deploy:deploy /home/deploy/.hushlogin--- ~/.ssh/known_hosts (اسمها هش شدهاند؛ هر ستون اول یک میزبان است):|1|L9GH6KegFkr/xgzbnFjkTnyf9B8=|jh+G7YEbXnufX1rtyc3F7rINARA=|1|ZDluSQn8b7ePg6WBYJbbXBqsx10=|vWU+LjIfEmavEdAmGgQiG/HU9pI=|1|qWQuhqfi4Oevucihn+vB8nsABXM=|JKNv19E3fHgyJgPlVjjw3oR1JdM=--- ssh-keygen -F: آیا server را میشناسم؟# Host server found: line 1--- بعد از این از پیامهای طولانی MOTD خلاص میشویم (.hushlogin روی سرور):(.hushlogin در پوشهی خانهی حساب سرور باعث میشود پیام خوشآمد چاپ نشود. فقط برای کوتاهشدن خروجیهای بعدی این درس.)
مثال ۲: ساختن جفتکلید ssh-keygen -t ed25519
Section titled “مثال ۲: ساختن جفتکلید ssh-keygen -t ed25519”ورود با رمز دو ایراد دارد: رمز را میشود حدس زد (حملهی brute-force) و باید هر بار تایپ شود. راه بهتر: جفتکلید. ssh-keygen یک کلید خصوصی و یک کلید عمومی میسازد. -t ed25519 الگوریتم مدرن، کوتاه و سریع را انتخاب میکند (توصیهی امروز؛ قدیمیها rsa را با اندازهی بزرگ میساختند) و -C یک توضیح (comment) روی کلید میگذارد تا بعداً بدانی کلید کیست:
run_in_tmux "su - alice"tmux send-keys -t t "ssh-keygen -t ed25519 -C alice@laptop" Enter; sleep 1echo "(سه بار Enter: مسیر پیشفرض، بدون passphrase، تکرار)"tmux send-keys -t t Enter; sleep 1tmux send-keys -t t Enter; sleep 1tmux send-keys -t t Enter; sleep 2screen(سه بار Enter: مسیر پیشفرض، بدون passphrase، تکرار)alice@server:~$ ssh-keygen -t ed25519 -C alice@laptopGenerating public/private ed25519 key pair.Enter file in which to save the key (/home/alice/.ssh/id_ed25519):Enter passphrase (empty for no passphrase):Enter same passphrase again:Your identification has been saved in /home/alice/.ssh/id_ed25519Your public key has been saved in /home/alice/.ssh/id_ed25519.pubThe key fingerprint is:SHA256:aE8UjJgY0HUkd9It+EjSl2z2nMUXEH/+Tc6xHb7hhOU alice@laptopThe key's randomart image is:+--[ED25519 256]--+|.o.+o==*oo .oo.. || o ++=oX.. o.. || o *.+ o .. .|| .o. + o || o S +o|| . o =+B|| . . E*|| o o|| o |+----[SHA256]-----+alice@server:~$ssh-keygen سه چیز پرسید: کجا ذخیره کنم (پیشفرض ~/.ssh/id_ed25519، همان را پذیرفتم) و passphrase (رمزی که خود فایل کلید خصوصی را قفل میکند؛ دو بار). برای این آزمایش آن را خالی گذاشتم، ولی روی کلید واقعیات حتماً passphrase بگذار (اگر لپتاپت دزدیده شد، کلید بیpassphrase مستقیم قابلاستفاده است). خروجی هم اثرانگشت کلید و یک تصویر ASCII (randomart) برای مقایسهی چشمی است. فایلها:
run_in_tmux "su - alice"tmux send-keys -t t "ls -l ~/.ssh" Enter; sleep 1tmux send-keys -t t "cat ~/.ssh/id_ed25519.pub" Enter; sleep 1tmux send-keys -t t "ssh-keygen -lf ~/.ssh/id_ed25519.pub" Enter; sleep 1screenalice@server:~$ ls -l ~/.sshtotal 16-rw------- 1 alice alice 399 Oct 3 22:04 id_ed25519-rw-r--r-- 1 alice alice 94 Oct 3 22:04 id_ed25519.pub-rw------- 1 alice alice 978 Oct 3 22:04 known_hosts-rw-r--r-- 1 alice alice 142 Oct 3 22:04 known_hosts.oldalice@server:~$ cat ~/.ssh/id_ed25519.pubssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINQPotQIugGlbNhln3bqgitwV9QQHnCCALOBmiC01Jhy alice@laptopalice@server:~$ ssh-keygen -lf ~/.ssh/id_ed25519.pub256 SHA256:aE8UjJgY0HUkd9It+EjSl2z2nMUXEH/+Tc6xHb7hhOU alice@laptop (ED25519)alice@server:~$id_ed25519: کلید خصوصی. دسترسی-rw-------(فقط خودت). هرگز آن را کپی نکن، ایمیل نکن، در چت نگذار و در گیت commit نکن. ssh اگر دسترسیاش بازتر باشد، آن را رد میکند (اشتباهات رایج).id_ed25519.pub: کلید عمومی، یک خط متن: نوع الگوریتم (ssh-ed25519)، خود کلید (رشتهی base64) و توضیح (alice@laptop). این را میشود به همه داد؛ همین را روی سرور میگذاری.
مثال ۳: ssh-copy-id، گذاشتن کلید عمومی روی سرور
Section titled “مثال ۳: ssh-copy-id، گذاشتن کلید عمومی روی سرور”کلید عمومی باید به فایل ~/.ssh/authorized_keys در حساب سرور اضافه شود. ssh-copy-id این کار را (با ساختن پوشه و دسترسیهای درست) انجام میدهد و یک بار رمز میخواهد:
run_in_tmux "su - alice"tmux send-keys -t t "ssh-copy-id deploy@server" Enter; sleep 3tmux send-keys -t t "$PW" Enter; sleep 3screenalice@server:~$ ssh-copy-id deploy@server/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/alice/.ssh/id_ed25519.pub"/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keysdeploy@server's password:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'deploy@server'"and check to make sure that only the key(s) you wanted were added.
alice@server:~$Number of key(s) added: 1: کلید اضافه شد. حالا ورود بدون رمز:
tmux send-keys -t t "clear" Entertmux send-keys -t t "ssh deploy@server" Enter; sleep 3tmux send-keys -t t "whoami; hostname" Enter; sleep 1tmux send-keys -t t "cut -c1-60 ~/.ssh/authorized_keys" Enter; sleep 1tmux send-keys -t t "ls -ld ~/.ssh; ls -l ~/.ssh/authorized_keys" Enter; sleep 1screentmux send-keys -t t "exit" Enter; sleep 1alice@server:~$ ssh deploy@serverdeploy@server:~$ whoami; hostnamedeployserverdeploy@server:~$ cut -c1-60 ~/.ssh/authorized_keysssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINQPotQIugGlbNhln3bqgitwdeploy@server:~$ ls -ld ~/.ssh; ls -l ~/.ssh/authorized_keysdrwx------ 2 deploy deploy 4096 Oct 3 22:04 /home/deploy/.ssh-rw------- 1 deploy deploy 94 Oct 3 22:04 /home/deploy/.ssh/authorized_keysdeploy@server:~$رمز نپرسید و مستقیم وارد شد. روی سرور هم authorized_keys همان یک خط کلید عمومی است، با دسترسیهای سختگیرانه: پوشهی ~/.ssh برابر 700 و فایل 600. (sshd اگر این دسترسیها بازتر باشد، به آن اعتماد نمیکند؛ اشتباهات رایج.)
کدام کلید را روی سرور میگذاری و کدام فقط پیش خودت میماند؟
کلید عمومی (id_ed25519.pub) روی سرور در authorized_keys؛ کلید خصوصی (id_ed25519) هرگز از دستگاه تو بیرون نمیرود.
مثال ۴: اجرای مستقیم دستور، گزینهها و -v
Section titled “مثال ۴: اجرای مستقیم دستور، گزینهها و -v”بعد از ssh user@host میتوانی یک دستور بدهی؛ روی سرور اجرا میشود و خروجیاش برمیگردد، بدون باز کردن shell. فلگهای پرکاربرد: -p پورت، -i فایل کلید مشخص، -v (verbose) برای عیبیابی. (BatchMode=yes یعنی هرگز رمز نپرس؛ در اسکریپتها عالی است: اگر ورود بیرمز نشد، بهجای گیرکردن، خطا بده.)
echo "--- اجرای دو دستور روی سرور:"su - alice -c 'ssh -o BatchMode=yes deploy@server "uptime -p; df -h / | tail -1"'echo "--- دستور با خروجی خطا و کد خروج (کد خروج دستور راه دور برمیگردد):"su - alice -c 'ssh -o BatchMode=yes deploy@server "ls /no-such-dir"; echo "کد خروج: $?"'echo "--- -v: کلید کدام فایل پیشنهاد شد و آیا پذیرفته شد؟ (فقط خطهای مهم)"su - alice -c 'ssh -v -o BatchMode=yes deploy@server true 2>&1' | grep -E 'Offering public key|Server accepts key' | cut -c1-110--- اجرای دو دستور روی سرور:up 1 day, 3 hours, 3 minutesoverlay 453G 22G 408G 6% /--- دستور با خروجی خطا و کد خروج (کد خروج دستور راه دور برمیگردد):ls: cannot access '/no-such-dir': No such file or directoryکد خروج: 2--- -v: کلید کدام فایل پیشنهاد شد و آیا پذیرفته شد؟ (فقط خطهای مهم)debug1: Offering public key: /home/alice/.ssh/id_ed25519 ED25519 SHA256:aE8UjJgY0HUkd9It+EjSl2z2nMUXEH/+Tc6xHbdebug1: Server accepts key: /home/alice/.ssh/id_ed25519 ED25519 SHA256:aE8UjJgY0HUkd9It+EjSl2z2nMUXEH/+Tc6xHb7دو خط آخر که از -v گرفتم، همان مکالمهی کلید (شکل بالا) است: «این کلید را پیشنهاد میکنم» (Offering) و «سرور آن را پذیرفت» (accepts). وقتی ورود بیرمز کار نکند، -v اولین ابزار توست.
مثال ۵: ~/.ssh/config، اسم کوتاه برای هر سرور
Section titled “مثال ۵: ~/.ssh/config، اسم کوتاه برای هر سرور”مجبور نیستی هر بار ssh -i ... -p ... user@host.example.com بنویسی. فایل ~/.ssh/config را بساز تا برای هر سرور یک اسم مستعار (alias) داشته باشی:
Host dev HostName server User deploy IdentityFile ~/.ssh/id_ed25519chown alice:alice /home/alice/.ssh/configchmod 600 /home/alice/.ssh/configecho "--- ssh -G dev: تنظیمات مؤثر برای آن اسم:"su - alice -c 'ssh -G dev | grep -E "^(hostname|user|port|identityfile) "'echo "--- حالا فقط:"su - alice -c 'ssh -o BatchMode=yes dev "echo من $(whoami) روی $(hostname) هستم"'--- ssh -G dev: تنظیمات مؤثر برای آن اسم:Pseudo-terminal will not be allocated because stdin is not a terminal.user deployhostname serverport 22identityfile ~/.ssh/id_ed25519--- حالا فقط:من alice روی server هستمدستور ssh dev همهی آنها را از فایل میخواند. کلیدهای پرکاربرد هر بلوک Host:
| کلید | معنی |
|---|---|
Host |
اسم مستعاری که روی خط فرمان مینویسی (میتواند الگو باشد: Host *.example.com) |
HostName |
آدرس یا IP واقعی سرور |
User |
نام کاربری روی سرور |
Port |
پورت (پیشفرض ۲۲) |
IdentityFile |
کدام کلید خصوصی |
ProxyJump |
عبور از یک سرور واسط (bastion) |
ServerAliveInterval |
هر چند ثانیه یک بستهی زندهبودن بفرست (جلوگیری از قطع شدن اتصال بیکار) |
فایل config را 600 نگه دار. بعد از این، scp و rsync و git هم همین اسم dev را میفهمند.
مثال ۶: انتقال فایل، scp و rsync -avz
Section titled “مثال ۶: انتقال فایل، scp و rsync -avz”scp (secure copy) مثل cp است ولی از راه ssh؛ یک سمتش host:مسیر است. در ترمینال مجازی تا نوار پیشرفت واقعی را ببینی:
run_in_tmux "su - alice"tmux send-keys -t t "echo 'سلام از لپتاپ' > notes.txt" Enter; sleep 1tmux send-keys -t t "scp notes.txt dev:/tmp/" Enter; sleep 2tmux send-keys -t t "ssh dev cat /tmp/notes.txt" Enter; sleep 2tmux send-keys -t t "scp dev:/tmp/notes.txt ./notes-back.txt" Enter; sleep 2tmux send-keys -t t "ls -l notes-back.txt" Enter; sleep 1screenalice@server:~$ echo 'سلام از لپتاپ' > notes.txtalice@server:~$ scp notes.txt dev:/tmp/notes.txt 100% 28 83.4KB/s 00:00alice@server:~$ ssh dev cat /tmp/notes.txtسلام از لپتاپalice@server:~$ scp dev:/tmp/notes.txt ./notes-back.txtnotes.txt 100% 28 52.6KB/s 00:00alice@server:~$ ls -l notes-back.txt-rw-rw-r-- 1 alice alice 28 Oct 3 22:04 notes-back.txtalice@server:~$scp فایل dev:/tmp/ بالا میبرد (upload)، scp dev:/tmp/فایل . پایین میآورد (download) و scp -r یک پوشه را بازگشتی میبرد. ولی برای کپی پوشهها rsync -avz بهتر است: فقط تفاوتها را میفرستد. -a (archive: دسترسیها و زمانها حفظ، بازگشتی)، -v پرحرف، -z فشردهسازی در راه:
su - alice -c 'mkdir -p proj/sub; echo a > proj/a.txt; echo b > proj/sub/b.txt'echo "=== بار اول: همهچیز میرود"su - alice -c 'rsync -avz proj/ dev:/tmp/proj-rs/'echo "=== یک فایل تازه اضافه میکنم، بار دوم: فقط همان میرود"su - alice -c 'echo c > proj/c.txt; rsync -avz proj/ dev:/tmp/proj-rs/'=== بار اول: همهچیز میرودsending incremental file listcreated directory /tmp/proj-rs./a.txtsub/sub/b.txt
sent 226 bytes received 100 bytes 652.00 bytes/sectotal size is 4 speedup is 0.01=== یک فایل تازه اضافه میکنم، بار دوم: فقط همان میرودsending incremental file listc.txt
sent 194 bytes received 36 bytes 460.00 bytes/sectotal size is 6 speedup is 0.03بار دوم فقط c.txt رفت (و اشارهی ./ و sub/ برای پوشهها)؛ برای پوشههای بزرگ این فرق شب و روز است. دو نکتهی مهم rsync:
echo "=== --delete: فایلهایی که در مبدأ نیستند در مقصد هم پاک شوند. اول با --dry-run (فقط نشان بده):"su - alice -c 'rm proj/a.txt; rsync -avz --dry-run --delete proj/ dev:/tmp/proj-rs/'echo "=== اجرای واقعی:"su - alice -c 'rsync -avz --delete proj/ dev:/tmp/proj-rs/'echo "=== اسلش آخر مبدأ (proj/ در برابر proj):"su - alice -c 'rsync -a proj dev:/tmp/rs-no-slash/ && ssh dev "ls /tmp/rs-no-slash"'=== --delete: فایلهایی که در مبدأ نیستند در مقصد هم پاک شوند. اول با --dry-run (فقط نشان بده):sending incremental file listdeleting a.txt
sent 130 bytes received 29 bytes 318.00 bytes/sectotal size is 4 speedup is 0.03 (DRY RUN)=== اجرای واقعی:sending incremental file listdeleting a.txt
sent 130 bytes received 29 bytes 318.00 bytes/sectotal size is 4 speedup is 0.03=== اسلش آخر مبدأ (proj/ در برابر proj):proj--dry-run(-n) فقط میگوید چه میکرد؛ قبل از هر--deleteحتماً بزن.- اسلش آخر مبدأ مهم است:
proj/یعنی «محتویات پوشه»، وprojیعنی «خود پوشه» (در مقصد یک پوشهیprojساخته میشود).
مثال ۷: تونل، دسترسی به سرویسی که فقط روی localhost سرور است
Section titled “مثال ۷: تونل، دسترسی به سرویسی که فقط روی localhost سرور است”در درس پورتها دیدی بعضی سرویسها (مثل دیتابیس) عمداً فقط روی 127.0.0.1 گوش میدهند و از بیرون در دسترس نیستند. با ssh میتوانی یک تونل بسازی: پورت یک سرویس سرور را روی ماشین خودت در دسترس میکند، از داخل کانال رمزنگاریشده. -L محلی:میزبان:دور:
python3 -m http.server 8099 --bind 127.0.0.1 --directory /tmp/proj-rs > /dev/null 2>&1 &sleep 1echo "--- مستقیم از بیرون (آدرس شبکهی سرور، پورت 8099): رد میشود؟"curl -s -m 3 -o /dev/null http://server:8099 && echo "پاسخ آمد" || echo "اتصال رد شد (فقط localhost)"echo "--- تونل: پورت 8081 من ← (از ssh) ← 127.0.0.1:8099 سرور"su - alice -c 'ssh -f -N -L 8081:127.0.0.1:8099 dev'sleep 1curl -s http://127.0.0.1:8081/ | grep -oE '<li><a href="[^"]+"' | head -3pkill -u alice -f 'ssh -f -N'pkill -f 'http.server 8099'--- مستقیم از بیرون (آدرس شبکهی سرور، پورت 8099): رد میشود؟اتصال رد شد (فقط localhost)--- تونل: پورت 8081 من ← (از ssh) ← 127.0.0.1:8099 سرور<li><a href="c.txt"<li><a href="sub/"-N یعنی دستور اجرا نکن، فقط تونل؛ -f یعنی بعد از وصل شدن به پسزمینه برو. حالا 127.0.0.1:8081 روی ماشین من در واقع همان سرویس 127.0.0.1:8099 سرور است. این راه استاندارد دسترسی امن به دیتابیس و پنلهای مدیریتی است.
مثال ۸: امنکردن sshd، بستن ورود با رمز
Section titled “مثال ۸: امنکردن sshd، بستن ورود با رمز”وقتی همه با کلید وارد میشوند، ورود با رمز را میبندیم تا حملهی حدس رمز بیاثر شود. تنظیمات sshd در /etc/ssh/sshd_config است و در Ubuntu هر فایل .conf در /etc/ssh/sshd_config.d/ هم خوانده میشود (فایل جدا بهتر از ویرایش فایل اصلی است). دو تنظیم مهم: PasswordAuthentication no و PermitRootLogin no (ورود مستقیم root ممنوع؛ با کاربر عادی وارد شو و sudo بزن):
cat > /etc/ssh/sshd_config.d/10-lx.conf <<'EOF'PasswordAuthentication noPermitRootLogin noEOFecho "--- تنظیمات را قبل از اعمال بررسی کن (sshd -t، بدون خروجی یعنی درست):"sshd -t && echo "sshd -t: درست است"systemctl reload sshecho "--- مقدار مؤثر:"sshd -T | grep -E '^(passwordauthentication|permitrootlogin) 'echo "--- ورود با رمز دیگر ممکن نیست:"su - alice -c 'ssh -o PubkeyAuthentication=no -o NumberOfPasswordPrompts=1 deploy@server true < /dev/null'echo "--- ولی ورود با کلید سالم است:"su - alice -c 'ssh -o BatchMode=yes dev "echo ورود با کلید"'echo "--- آزمایش تمام شد؛ تنظیم را برمیدارم تا بقیهی درس (که ورود با رمز میخواهد) کار کند:"rm /etc/ssh/sshd_config.d/10-lx.confsystemctl reload sshsshd -T | grep -E '^passwordauthentication '--- تنظیمات را قبل از اعمال بررسی کن (sshd -t، بدون خروجی یعنی درست):sshd -t: درست است--- مقدار مؤثر:permitrootlogin nopasswordauthentication no--- ورود با رمز دیگر ممکن نیست:deploy@server: Permission denied (publickey).--- ولی ورود با کلید سالم است:ورود با کلید--- آزمایش تمام شد؛ تنظیم را برمیدارم تا بقیهی درس (که ورود با رمز میخواهد) کار کند:passwordauthentication yessshd -T تنظیمات نهایی را چاپ میکند (با درنظرگرفتن همهی فایلها)، و Permission denied (publickey) یعنی سرور فقط کلید را قبول میکند. هشدار مهم: قبل از بستن ورود با رمز مطمئن شو ورود با کلید کار میکند؛ و هنگام تغییر sshd یک نشست باز را نگه دار و در نشست دوم آزمایش کن؛ اگر اشتباه کرده باشی، دیگر راه ورودی نداری (روی VPS با «کنسول» پنل ارائهدهنده برمیگردی). این را در پروژهی پایانی دوره کامل انجام میدهیم.
پشت پرده: رمزنگاری و فایلها
Section titled “پشت پرده: رمزنگاری و فایلها”اتصال SSH در دو مرحلهی اصلی است: اول یک کانال رمزنگاریشده با کلیدهای موقت ساخته میشود (تبادل کلید) تا کسی ترافیک را نشنود؛ بعد احراز هویت (رمز یا کلید) داخل همان کانال انجام میشود. این فایلها نقش دارند:
| فایل | کجا | کار |
|---|---|---|
~/.ssh/id_ed25519 |
کلاینت | کلید خصوصی تو (600) |
~/.ssh/id_ed25519.pub |
کلاینت | کلید عمومی تو |
~/.ssh/known_hosts |
کلاینت | اثرانگشت سرورهایی که قبلاً تأیید کردهای |
~/.ssh/config |
کلاینت | اسمهای مستعار و تنظیمات |
~/.ssh/authorized_keys |
سرور (در خانهی حساب) | کلیدهای عمومیای که اجازهی ورود به آن حساب دارند |
/etc/ssh/ssh_host_*_key |
سرور | کلیدهای هویت خود سرور |
/etc/ssh/sshd_config و sshd_config.d/ |
سرور | تنظیمات سرویس |
سرور هر ورود را در لاگ ثبت میکند. بعد از این اتصالها، آخرین رویدادها (که چه کسی با چه کلیدی از کجا وارد شد):
journalctl -u ssh --no-pager -o cat | grep -E 'Accepted|Failed|Invalid' | tail -3 | cut -c1-130echo "--- کلید میزبان سرور (فقط نوع و نام فایلها):"ls /etc/ssh/ssh_host_*_key.pubAccepted publickey for deploy from 172.17.0.4 port 47204 ssh2: ED25519 SHA256:aE8UjJgY0HUkd9It+EjSl2z2nMUXEH/+Tc6xHb7hhOUAccepted publickey for deploy from 172.17.0.4 port 47210 ssh2: ED25519 SHA256:aE8UjJgY0HUkd9It+EjSl2z2nMUXEH/+Tc6xHb7hhOUAccepted publickey for deploy from 172.17.0.4 port 47222 ssh2: ED25519 SHA256:aE8UjJgY0HUkd9It+EjSl2z2nMUXEH/+Tc6xHb7hhOU--- کلید میزبان سرور (فقط نوع و نام فایلها):/etc/ssh/ssh_host_ecdsa_key.pub/etc/ssh/ssh_host_ed25519_key.pub/etc/ssh/ssh_host_rsa_key.pubAccepted publickey for deploy from ... ssh2: ED25519 SHA256:... یعنی ورود با کلید و اثرانگشت همان کلید ثبت شد؛ برای ردیابی «چه کسی» عالی است. و کلیدهای میزبان در /etc/ssh/ssh_host_*: اگر اینها عوض شوند (مثلاً سرور را از صفر نصب کنی)، کلاینتها هشدار «REMOTE HOST IDENTIFICATION HAS CHANGED» میبینند (اشتباهات رایج).
جدولهای مرجع
Section titled “جدولهای مرجع”| دستور | کار |
|---|---|
ssh user@host |
ورود (پورت پیشفرض ۲۲) |
ssh -p 2222 user@host |
پورت دیگر |
ssh -i کلید user@host |
کلید مشخص |
ssh user@host 'دستور' |
اجرای یک دستور روی سرور |
ssh -v user@host |
عیبیابی (-vv و -vvv جزئیتر) |
ssh-keygen -t ed25519 -C "توضیح" |
ساختن جفتکلید |
ssh-keygen -lf ~/.ssh/id_ed25519.pub |
اثرانگشت یک کلید |
ssh-copy-id user@host |
نصب کلید عمومی روی سرور |
ssh-keygen -R host |
پاککردن میزبان از known_hosts |
scp فایل host:مسیر / scp -r پوشه host:مسیر |
کپی با ssh |
rsync -avz مبدأ/ host:مقصد/ |
همگامسازی فقط تفاوتها |
rsync -avz --delete --dry-run ... |
آزمایش بدون تغییر |
ssh -L 8081:127.0.0.1:8099 host |
تونل محلی |
sudo sshd -t / sudo sshd -T |
بررسی نحو / تنظیمات مؤثر |
اشتباهات رایج
Section titled “اشتباهات رایج”۱) دسترسی کلید خصوصی بازتر از 600
Section titled “۱) دسترسی کلید خصوصی بازتر از 600”su - alice -c 'chmod 644 ~/.ssh/id_ed25519; ssh -o BatchMode=yes dev true; chmod 600 ~/.ssh/id_ed25519'@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ WARNING: UNPROTECTED PRIVATE KEY FILE! @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Permissions 0644 for '/home/alice/.ssh/id_ed25519' are too open.It is required that your private key files are NOT accessible by others.This private key will be ignored.Load key "/home/alice/.ssh/id_ed25519": bad permissionsdeploy@server: Permission denied (publickey,password).ssh عمداً کلیدی را که دیگران میخوانند رد میکند (Permissions 0644 ... are too open) و بهخاطر نبودن کلید به ورود دیگر (اینجا کلید دیگری نبود) Permission denied میدهد. راهحل: chmod 600 ~/.ssh/id_ed25519 (و chmod 700 ~/.ssh).
۲) دسترسی بد روی سرور، authorized_keys نادیده گرفته میشود
Section titled “۲) دسترسی بد روی سرور، authorized_keys نادیده گرفته میشود”chmod 777 /home/deploy/.sshecho "--- ورود با کلید (از دید کلاینت):"su - alice -c 'ssh -o BatchMode=yes dev true'echo "--- چرا؟ لاگ سرور:"journalctl -u ssh --no-pager -o cat | grep 'bad ownership' | tail -1chmod 700 /home/deploy/.sshecho "--- بعد از chmod 700:"su - alice -c 'ssh -o BatchMode=yes dev "echo درست شد"'--- ورود با کلید (از دید کلاینت):deploy@server: Permission denied (publickey,password).--- چرا؟ لاگ سرور:Authentication refused: bad ownership or modes for directory /home/deploy/.ssh--- بعد از chmod 700:درست شدکلاینت فقط Permission denied (publickey) میبیند، ولی لاگ سرور علت واقعی را میگوید: Authentication refused: bad ownership or modes for directory. راهحل: پوشهی ~/.ssh برابر 700 و authorized_keys برابر 600 و هر دو مال همان کاربر. (و یادت باشد: علت «ورود با کلید کار نمیکند» را اول در لاگ سرور و ssh -v بجو.)
۳) «REMOTE HOST IDENTIFICATION HAS CHANGED»
Section titled “۳) «REMOTE HOST IDENTIFICATION HAS CHANGED»”echo "--- سرور از صفر نصب شد (کلیدهای میزبان تازه ساخته میشوند):"rm -f /etc/ssh/ssh_host_*ssh-keygen -A > /dev/nullsystemctl restart sshsu - alice -c 'ssh -o BatchMode=yes dev true'--- سرور از صفر نصب شد (کلیدهای میزبان تازه ساخته میشوند):@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 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 ED25519 key sent by the remote host isSHA256:vxDsVghlEvCE0R361l+DmMcEZZQKxJzSt6ZFYIUpQB8.Please contact your system administrator.Add correct host key in /home/alice/.ssh/known_hosts to get rid of this message.Offending ECDSA key in /home/alice/.ssh/known_hosts:3 remove with: ssh-keygen -f '/home/alice/.ssh/known_hosts' -R 'server'Host key for server has changed and you have requested strict checking.Host key verification failed.ssh اثرانگشت جدید را با قدیمی در known_hosts مقایسه کرد و نخواست ادامه بدهد، چون ممکن است کسی خود را جای سرور جا زده باشد (حملهی مرد میانی). اگر مطمئنی سرور واقعاً عوض شده (نصب دوباره، تغییر IP)، آن میزبان را از فایل پاک کن و دوباره وصل شو:
su - alice -c 'ssh-keygen -R server'su - alice -c 'ssh -o StrictHostKeyChecking=accept-new -o BatchMode=yes dev "echo دوباره وصل شدم"'# Host server found: line 1# Host server found: line 2# Host server found: line 3/home/alice/.ssh/known_hosts updated.Original contents retained as /home/alice/.ssh/known_hosts.oldWarning: Permanently added 'server' (ED25519) to the list of known hosts.دوباره وصل شدمهرگز بدون بررسی این هشدار را رد نکن؛ آیا واقعاً سرور را تغییر دادهاند؟
۴) Connection refused یا Permission denied (publickey)
Section titled “۴) Connection refused یا Permission denied (publickey)”echo "--- پورت اشتباه (سرور روی 22 گوش میدهد، نه 2222):"su - alice -c 'ssh -p 2222 -o BatchMode=yes deploy@server true'echo "--- کلیدی که سرور نمیشناسد:"su - alice -c 'ssh-keygen -q -t ed25519 -N "" -f ~/.ssh/id_other; ssh -i ~/.ssh/id_other -o IdentitiesOnly=yes -o BatchMode=yes deploy@server true'--- پورت اشتباه (سرور روی 22 گوش میدهد، نه 2222):ssh: connect to host server port 2222: Connection refused--- کلیدی که سرور نمیشناسد:deploy@server: Permission denied (publickey,password).Connection refused: روی آن پورت سرویسی نیست (پورت اشتباه یا sshd خاموش؛ درس پورتها). Permission denied (publickey): سرور رسید و کلید را نشناخت (کلید اشتباه یا کلید عمومیات روی سرور نیست). و اگر timed out بود، فایروال یا مسیر. این سه را همانطور که در درس پورتها دیدی از هم تشخیص بده.
۵) گیرکردن بعد از قطعشدن اتصال
Section titled “۵) گیرکردن بعد از قطعشدن اتصال”اتصال بیکار گاهی توسط فایروال یا NAT قطع میشود و ترمینال «فریز» میماند. راه خروج: دکمهی Enter سپس ~ و . (تیلدا و نقطه) اتصال را میبندد؛ و جلوگیری با ServerAliveInterval 30 در ~/.ssh/config.
بدون باز کردن shell، با یک دستور ssh فقط کاربر و نام ماشین سرور را چاپ کن (از کلید و اسم dev استفاده کن).
دیدن جواب
su - alice -c 'ssh -o BatchMode=yes dev "whoami; hostname"'deployserverتمرین اصلی: ورود بدون رمز با کلید به یک سرور را راه بینداز. برای کاربر جدید bob (کلاینت) یک جفتکلید ed25519 بساز، با ssh-copy-id (رمز آزمایشی lab-pass) روی حساب deploy بگذار و ثابت کن ssh -o BatchMode=yes (بدون رمز) کار میکند. (sshpass نصب نیست، پس ssh-copy-id را در ترمینال مجازی بزن.)
دیدن جواب
useradd -m -s /bin/bash bobsu - bob -c 'ssh-keygen -q -t ed25519 -N "" -C bob@laptop -f ~/.ssh/id_ed25519'TH=14run_in_tmux "su - bob"tmux send-keys -t t "ssh-copy-id -o StrictHostKeyChecking=accept-new deploy@server" Enter; sleep 3tmux send-keys -t t "$PW" Enter; sleep 3screenecho "--- آزمایش بدون رمز:"su - bob -c 'ssh -o BatchMode=yes deploy@server "echo ورود بدون رمز کار کرد: $(whoami)"'echo "--- کلیدهای مجاز حساب deploy (فقط نوع و توضیح):"awk '{print $1, $NF}' /home/deploy/.ssh/authorized_keys/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/bob/.ssh/id_ed25519.pub"/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keysdeploy@server's password:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh -o 'StrictHostKeyChecking=accept-new' 'deploy@server'"and check to make sure that only the key(s) you wanted were added.
bob@server:~$--- آزمایش بدون رمز:ورود بدون رمز کار کرد: bob--- کلیدهای مجاز حساب deploy (فقط نوع و توضیح):ssh-ed25519 alice@laptopssh-ed25519 bob@laptopیک بکاپ ساده بساز: با rsync -avz پوشهی ~/docs را از لپتاپ (alice) به /tmp/backup/ روی سرور (با اسم dev) بفرست. بعد یک فایل را تغییر بده و دوباره اجرا کن و ثابت کن فقط همان فایل رفت.
دیدن جواب
su - alice -c 'mkdir -p ~/docs && echo "گزارش ۱" > ~/docs/report.txt && echo "یادداشت" > ~/docs/note.txt'echo "=== بکاپ اول"su - alice -c 'rsync -avz ~/docs/ dev:/tmp/backup/'echo "=== تغییر یک فایل و بکاپ دوم"su - alice -c 'echo "گزارش ۲ (ویرایششده)" > ~/docs/report.txt; rsync -avz ~/docs/ dev:/tmp/backup/'echo "=== محتوای روی سرور:"su - alice -c 'ssh dev "cat /tmp/backup/report.txt"'=== بکاپ اولsending incremental file listcreated directory /tmp/backup./note.txtreport.txt
sent 223 bytes received 91 bytes 628.00 bytes/sectotal size is 29 speedup is 0.09=== تغییر یک فایل و بکاپ دومsending incremental file listreport.txt
sent 195 bytes received 41 bytes 472.00 bytes/sectotal size is 53 speedup is 0.22=== محتوای روی سرور:گزارش ۲ (ویرایششده)آزمونک
Section titled “آزمونک”کدام کلید را روی سرور میگذاری؟
کلید خصوصی هرگز از دستگاه تو بیرون نمیرود.
ssh-keygen -t ed25519 چه میکند؟
ed25519 الگوریتم امروزی و کوتاه است؛ برای گذاشتن روی سرور ssh-copy-id.
ssh میگوید «Permissions 0644 for id_ed25519 are too open». چه میکنی؟
کلید خصوصی نباید برای بقیه خواندنی باشد؛ ssh آن را رد میکند.
اثرانگشت میزبان را هنگام اولین اتصال چرا بررسی میکنیم؟
پس از تأیید، در known_hosts ذخیره میشود و تغییرش هشدار میدهد.
rsync -avz proj/ dev:/tmp/x/ در مقابل rsync -avz proj dev:/tmp/x/؟
اسلش آخر مبدأ بسیار مهم است؛ قبل از --delete با --dry-run بسنج.
با PasswordAuthentication no و ورود با کلید، «Permission denied (publickey)» میبینی. محتملترین علت؟
علت را در ssh -v و لاگ سرور (journalctl -u ssh) بجو.
جمعبندی
Section titled “جمعبندی”- SSH ترمینال رمزنگاریشدهی یک ماشین دور است:
ssh user@host، یا با دستور:ssh user@host 'دستور'. اولین بار اثرانگشت میزبان را تأیید میکنی (known_hosts). - ورود بهتر: جفتکلید.
ssh-keygen -t ed25519 -C "توضیح"(با passphrase)، عمومی روی سرور (ssh-copy-id user@host←~/.ssh/authorized_keys)، خصوصی هرگز بیرون نمیرود (600). ~/.ssh/configباHost،HostName،User،IdentityFile،Port… اسم کوتاه میدهد:ssh dev.- فایل:
scp(کپی ساده) وrsync -avz(فقط تفاوتها؛--dry-runقبل از--delete؛ اسلش آخر مبدأ مهم است). تونل:ssh -L. - امنکردن:
PasswordAuthentication no،PermitRootLogin no،sshd -tقبل ازreload، و همیشه یک نشست باز نگه دار. - عیبیابی:
ssh -v، لاگ سرور (journalctl -u ssh)، دسترسیهای~/.ssh(۷۰۰/۶۰۰)، و فرقrefused،timed outوPermission denied.
| دستور | کاری که میکند |
|---|---|
ssh user@host | ورود |
ssh -p 2222 -i ~/.ssh/key user@host | پورت و کلید مشخص |
ssh user@host "uptime; df -h /" | اجرای دستور روی سرور |
ssh-keygen -t ed25519 -C "alice@laptop" | ساختن جفتکلید |
ssh-copy-id user@host | گذاشتن کلید عمومی روی سرور |
ssh-keygen -R host | پاککردن اثرانگشت قدیمی از known_hosts |
ssh dev | با اسم از ~/.ssh/config |
scp فایل dev:/tmp/ / scp -r پوشه dev:/tmp/ | کپی با ssh |
rsync -avz --dry-run --delete src/ dev:/dst/ | آزمایش همگامسازی |
rsync -avz src/ dev:/dst/ | همگامسازی |
ssh -f -N -L 8081:127.0.0.1:8099 dev | تونل به سرویس localhost سرور |
sudo sshd -t && sudo systemctl reload ssh | اعمال امن تنظیمات sshd |