এই পর্বে যা শিখবেন
- প্রথম সংযোগে কী ঘটে, আর host key বদলের সতর্কবার্তা দেখলে ঠিক কী করবেন
- Key login ভেতরে কীভাবে কাজ করে, আর কোন ফাইলের কী permission লাগে
~/.ssh/config-এর নিয়ম, scp, rsync আর tmux-এর ব্যবহার-Lআর-Rtunnel-এর পার্থক্য ও ঝুঁকি- Bastion, session manager, sshd hardening, key rotation আর SSH certificate
১ · Telnet থেকে SSH — দূরের server-এ নিরাপদ দরজা কেন লাগে
Cloud-এ VM বানালেন। সেটি হয়তো অন্য দেশের কোনো data center-এ। তাতে কিবোর্ড নেই, মনিটরও নেই। তাহলে কাজ করবেন কীভাবে? এখানেই আসে SSH (Secure Shell). এটি নিজের ল্যাপটপ থেকে দূরের কম্পিউটারের terminal খোলার নিরাপদ পথ।
SSH-এর আগে প্রচলিত ছিল telnet. সমস্যা হলো, telnet-এ সব কিছু সরাসরি লেখা অবস্থায় যায়। Password-ও। পথের যেকোনো যন্ত্র চাইলে সেটি পড়তে পারে।
Telnet হলো ডাকঘরের পোস্টকার্ড। পিয়ন থেকে পাশের বাড়ির চাচা, যাঁর হাতে পড়বে তিনিই পড়তে পারবেন। SSH হলো সিলগালা করা খাম, যা শুধু প্রাপক খুলতে পারেন। সাথে একটি বাড়তি সুবিধা আছে। প্রাপকও পরিচয়পত্র দেখিয়ে প্রমাণ করেন যে তিনিই আসল লোক।
১৯৯৫ সালে ফিনল্যান্ডের Helsinki University of Technology-র গবেষক Tatu Ylönen-এর বিশ্ববিদ্যালয়ের network-এ password চুরির একটি ঘটনা ঘটে। পথের traffic থেকেই password তুলে নেওয়া হয়েছিল। এর জবাবে তিনি SSH-এর প্রথম সংস্করণ লেখেন। তিনি port 22 নিয়েছিলেন, যা ftp-র 21 আর telnet-এর 23-এর মাঝখানে। ১৯৯৯ সালে OpenBSD প্রকল্প থেকে আসে মুক্ত সংস্করণ OpenSSH, যা আজ Linux server-এর প্রায় সর্বত্র। SSH-2 প্রোটোকল ২০০৬ সালে IETF-এর RFC 4251–4254 হিসেবে মানসম্মত হয়।
| বিষয় | Telnet | SSH |
|---|---|---|
| Password আর লেখা | সরাসরি লেখা অবস্থায় যায় | এনক্রিপ্টেড |
| Server-এর পরিচয় | যাচাই নেই | host key দিয়ে যাচাই |
| পথে বদলে দিলে | ধরা পড়ে না | ধরা পড়ে, সংযোগ ভাঙে |
| Default port | 23 | 22 |
| আজকের ব্যবহার | server login-এ এড়িয়ে চলুন | server login-এর মানদণ্ড |
দুটি নাম আলাদা করে চিনে নিন। আপনার ল্যাপটপে চলে client ssh. Server-এ চলে একটি daemon, sshd, যা port 22-এ অপেক্ষা করে। নিচের সব সংযোগ শুধু উদাহরণ। server.example.com আর 203.0.113.10 নমুনা ঠিকানা, কোনো আসল server নয়।
# user@host: log in as deploy on this server
ssh deploy@server.example.com
# a different port, and an IP address instead of a name
ssh -p 2222 deploy@203.0.113.10
# run one command, print its result, disconnect
ssh deploy@server.example.com 'uptime'SSH তিনটি স্তরে কাজ করে। Transport স্তর এনক্রিপ্টেড পথ বানায় আর server চেনে। User authentication স্তর আপনাকে চেনে। Connection স্তর সেই একটি পথের ভেতরে অনেকগুলো চ্যানেল চালায়: shell, ফাইল পাঠানো, tunnel. এজন্যই একটি SSH সংযোগ এত রকম কাজ করতে পারে।
২ · প্রথম সংযোগ — host key, known_hosts আর trust on first use
প্রতিটি server-এর নিজের একটি key pair থাকে, যাকে বলে host key. এটি server-এর পরিচয়পত্র। আপনার ল্যাপটপ যেসব server চেনে, তাদের public host key রাখে known_hosts ফাইলে। প্রথমবার যখন কোনো নতুন server-এ যান, ফাইলে তার নাম থাকে না। নিচের ধাপগুলো দেখুন। “পরে” চাপলে পথের অবস্থা বদলাবে।
- ল্যাপটপ
ssh~/.ssh/known_hosts - Server
sshd :22203.0.113.10
| কী ঘটছে | |
| known_hosts | |
| পথের অবস্থা |
- TCP সংযোগ। ল্যাপটপ
203.0.113.10-এর port22-এ যুক্ত হলো। এখনো কিছু এনক্রিপ্টেড নয়। - পরিচয় বিনিময়। দুই পক্ষ version বলে, আর কোন algorithm-এ দুজনেই রাজি তা ঠিক করে। এই অংশ খোলা থাকলেও কোনো গোপন কিছু এতে নেই।
- Key exchange. Key exchange-এ দুই পক্ষ এমন একটি গোপন মান হিসাব করে, যা তারে কখনো যায় না। Server সেই বিনিময়ের সারাংশে নিজের host private key দিয়ে স্বাক্ষর করে, আর public host key পাঠায়।
- চেনা, না অচেনা? Client স্বাক্ষর যাচাই করে। স্বাক্ষর ঠিক, মানে এই key-এর মালিকই কথা বলছে। কিন্তু key-টি আসল server-এর কি না?
known_hosts-এ তার নাম নেই, তাই ssh আপনাকে fingerprint দেখিয়ে জিজ্ঞেস করে। - আপনার সিদ্ধান্ত। আপনি fingerprint-টি provider-এর console বা অন্য নিরাপদ পথের সাথে মিলিয়ে
yesলিখলেন। Key-টিknown_hosts-এ জমা হলো। - পথ প্রস্তুত। এখন সব এনক্রিপ্টেড। এই পথেই পরের ধাপে আপনার পরিচয় যাচাই হবে। লক্ষ করুন: আপনার পরিচয় দেওয়ার আগেই server তার পরিচয় দিল।
এই নীতির নাম trust on first use (TOFU): প্রথমবার মেনে নেওয়া, তারপর বদল হলে আপত্তি করা। প্রথম সংযোগে আপনি এমন একটি বার্তা দেখবেন:
# নমুনা আউটপুট — version-ভেদে আলাদা হতে পারে; fingerprint-টি ভুয়া
The authenticity of host 'server.example.com (203.0.113.10)' can't be established.
ED25519 key fingerprint is SHA256:EXAMPLE-fake-not-real.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'server.example.com' (ED25519) to the list of known hosts.
TOFU-র দুর্বল মুহূর্ত ঠিক একটি: প্রথম yes. তাই ভালো অভ্যাস হলো fingerprint মিলিয়ে নেওয়া। Server-এ ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub চালালে fingerprint দেখা যায়। অনেক provider-এর console বা boot log-এও এটি পাওয়া যায়। পরে key বদলে গেলে কী হয়, নিচের ছোট মডেলে চালিয়ে দেখুন।
// A toy model of known_hosts and trust on first use. No real crypto here.
const knownHosts = new Map(); // host -> saved key
const fingerprint = key => 'SHA256:fake-' + key;
function connect(day, host, serverKey, userSays) {
const saved = knownHosts.get(host);
let result;
if (saved === undefined) { // never seen: ask the human
if (userSays === 'yes') {
knownHosts.set(host, serverKey);
result = 'first time, you said yes: saved ' + fingerprint(serverKey);
} else {
result = 'first time, you said no: connection stopped';
}
} else if (saved === serverKey) { // same key as before
result = 'key matches, connected silently';
} else { // different key: stop, never ask
result = 'WARNING host key CHANGED, saved ' + fingerprint(saved) +
' got ' + fingerprint(serverKey) + ': refused';
}
console.log(`day ${day}: ${host} -> ${result}`);
}
const host = 'server.example.com';
connect(1, 'new.example.com', 'key-X', 'no');
connect(1, host, 'key-A', 'yes');
connect(2, host, 'key-A');
connect(3, host, 'key-B'); // server rebuilt from an image, new key
// Fingerprint of key-B checked in the provider console. Only then:
knownHosts.delete(host); // same effect as: ssh-keygen -R server.example.com
connect(3, host, 'key-B', 'yes');
connect(4, host, 'key-B');
console.log('known_hosts now:', [...knownHosts.entries()].join(' '));আউটপুটের মূল লাইন দুটি:
day 2: server.example.com -> key matches, connected silently
day 3: server.example.com -> WARNING host key CHANGED, saved SHA256:fake-key-A got SHA256:fake-key-B: refused
দিন ২-এ key মিলে গেছে, তাই কোনো প্রশ্ন নেই। দিন ৩-এ key আলাদা। এবার ssh প্রশ্নও করে না, সরাসরি থামে। যাচাইয়ের পরই পুরোনো লাইন মোছা হলো, আর key-B নতুন করে জমা পড়ল। শেষ লাইনে known_hosts now: server.example.com,key-B, অর্থাৎ ফাইলে এখন শুধু নতুন key.
আসল ssh-এ এই থামার মুহূর্তটি বড় বড় অক্ষরে আসে:
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
# নমুনা আউটপুট, সংক্ষেপিত — version-ভেদে আলাদা হতে পারে
Host key verification failed.
এই সতর্কবার্তার সম্ভাব্য কারণ দুটি। হয় server নতুন করে বানানো হয়েছে, নয়তো পথে কেউ বসে আছে (man-in-the-middle). Cloud-এ প্রথমটিই বেশি ঘটে, কারণ একই IP নতুন VM পেতে পারে। তবু ধরে নেবেন না। আগে অন্য পথে নতুন fingerprint মিলিয়ে দেখুন। তারপরই ssh-keygen -R server.example.com দিয়ে পুরোনো লাইন মুছুন। StrictHostKeyChecking no দিয়ে সতর্কবার্তা চাপা দেবেন না।
Automation-এ মানুষ yes লিখতে পারে না। সেখানে আগে থেকে বানানো known_hosts ফাইল দেওয়া সবচেয়ে ভালো। আধুনিক OpenSSH-এর StrictHostKeyChecking accept-new নতুন host মেনে নেয়, কিন্তু বদলে যাওয়া key আটকায়। এটি no-এর চেয়ে নিরাপদ, তবে প্রথম সংযোগের ঝুঁকি থেকেই যায়।
৩ · Key pair দিয়ে লগইন — password-এর চেয়ে ভালো কেন
Server চেনা হলো। এবার আপনার পরিচয়ের পালা। Password দিলে সেটি এনক্রিপ্টেড পথে server পর্যন্ত যায়, আর অনুমান করে ধরার ঝুঁকিও থাকে। Key pair-এ দুটি মিলিত ফাইল থাকে। Public key server-এ রাখা হয়, এটি ফাঁস হলেও ক্ষতি নেই। Private key শুধু আপনার কাছে থাকে।
ব্যাংকের নমুনা স্বাক্ষরের কথা ভাবুন। ব্যাংকের কাছে আপনার নমুনা আছে, তাই তারা মিলিয়ে দেখতে পারে। কিন্তু নমুনা দেখে কেউ আপনার মতো নতুন চেকে সই বানাতে পারে না। Public key হলো সেই নমুনা। Private key আপনার হাত। পার্থক্য একটাই: গণিতের এই স্বাক্ষর নকল করা বাস্তবে অসম্ভব, আর প্রতিটি চেকের সই আলাদা।
# on your laptop: create a key pair (it asks for a passphrase)
ssh-keygen -t ed25519 -C "rafi-laptop-2026"
# ~/.ssh/id_ed25519 private key: never copy or share
# ~/.ssh/id_ed25519.pub public key: this one goes to the server
# put the public key on a server you can already reach
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@server.example.comCloud-এ VM বানানোর সময়ই public key দেওয়া যায়। তখন প্রথম লগইনেই password লাগে না (পর্ব ১৫ আর পর্ব ১৬-তে দেখবেন)। Key-টি জমা পড়ে server-এর authorized_keys ফাইলে। প্রতিটি লাইনে একটি public key থাকে। সামনে শর্তও বসানো যায়:
# ~deploy/.ssh/authorized_keys (keys below are fake and truncated)
ssh-ed25519 AAAAC3...fake-truncated rafi-laptop-2026
from="203.0.113.0/24",no-port-forwarding,no-agent-forwarding ssh-ed25519 AAAAC3...fake-truncated ci-deploy| ধরন | কখন | মন্তব্য |
|---|---|---|
| ed25519 | নতুন key-এ প্রথম পছন্দ | ছোট, দ্রুত; OpenSSH-এ ২০১৪ সাল থেকে আছে |
| RSA | পুরোনো যন্ত্রের সাথে মেলাতে | সাধারণ পরামর্শ অন্তত ৩০৭২ bit; নীতিমালা-ভেদে আলাদা হতে পারে |
| DSA | ব্যবহার করবেন না | আধুনিক OpenSSH-এ বন্ধ বা বাদ |
Hardware key (-sk) | বেশি সংবেদনশীল কাজে | Private অংশ যন্ত্র থেকে বের হয় না; আধুনিক OpenSSH লাগে |
File permission — ssh নিজেই পাহারা দেয়
অন্য কেউ পড়তে বা বদলাতে পারলে ssh ফাইলটি ব্যবহার করতে চায় না। Server-এ এই পরীক্ষার নাম StrictModes, যা default-এ চালু থাকে। Permission-এর তত্ত্ব পড়তে পারেন Linux-এ user, group আর permission পর্বে।
| ফাইল | Mode | কেন |
|---|---|---|
~/.ssh/ | 700 | শুধু মালিক ঢুকতে পারবেন |
~/.ssh/id_ed25519 | 600 | Private key: শুধু মালিক পড়বেন |
~/.ssh/id_ed25519.pub | 644 | Public, সবাই পড়লে সমস্যা নেই |
~/.ssh/authorized_keys | 600 | অন্য কেউ যেন key যোগ করতে না পারে |
| Home folder | group/others লিখতে পারবে না | নইলে পুরো .ssh বদলে দেওয়া সম্ভব |
# নমুনা আউটপুট — version-ভেদে আলাদা হতে পারে
Permissions 0644 for '/home/rafi/.ssh/id_ed25519' are too open.
# সমাধান: chmod 600 ~/.ssh/id_ed25519
Key login ভেতরে কীভাবে হয়
অনেকে ভাবেন, server একটি এলোমেলো প্রশ্ন পাঠায় আর client উত্তর দেয়। ধারণাটি কাছাকাছি, তবে SSH-2-তে “প্রশ্ন” হিসেবে কাজ করে session id. এটি key exchange থেকে তৈরি হয়, প্রতিটি সংযোগে আলাদা। নিচের challenge-response ধাপগুলো দেখুন। শেষ সারিটি বিশেষভাবে খেয়াল করুন।
| কোথায় কাজ | |
| কী ঘটল | |
| তারে গেল | |
| Private key |
- প্রস্তাব। Client জানায়: আমি
deploy, এই public key দিয়ে আসতে চাই। এখনো কোনো স্বাক্ষর নেই। - Server খোঁজে। sshd
deploy-এরauthorized_keysপড়ে। Key আছে, ফাইলের permission-ও ঠিক। তাই সে জানায়, এই key গ্রহণযোগ্য। - বার্তা বানানো। Client session id, user নাম আর key জুড়ে একটি বার্তা বানায়। Session id এই সংযোগের জন্যই অনন্য।
- স্বাক্ষর। ssh-agent বা ssh নিজে private key দিয়ে বার্তায় digital signature করে। শুধু স্বাক্ষর তারে যায়, key নয়।
- যাচাই। sshd public key দিয়ে স্বাক্ষর পরীক্ষা করে। মিলে গেছে, তাই লগইন সফল।
- Shell. Shell খুলল। এই স্বাক্ষর কেউ রেকর্ড করে পরে ব্যবহার করতে পারবে না, কারণ পরের সংযোগের session id আলাদা হবে। সারি জুড়ে দেখুন, private key একবারও ল্যাপটপ ছাড়েনি।
Passphrase আর ssh-agent
Private key ফাইলটি চুরি হলে কী হবে? Passphrase দিলে ফাইলটি ডিস্কে এনক্রিপ্টেড থাকে, তাই চোর শুধু ফাইল দিয়ে কিছু করতে পারে না। কিন্তু প্রতিবার passphrase লেখা বিরক্তিকর। এই কাজ সহজ করে ssh-agent. এটি আপনার ল্যাপটপে চলা একটি ছোট প্রোগ্রাম, যা খোলা key মেমরিতে রাখে আর চাইলে স্বাক্ষর করে দেয়। ভালো অভ্যাস হলো মেয়াদ বেঁধে key যোগ করা:
# start an agent for this shell, if your desktop has not started one
eval "$(ssh-agent -s)"
ssh-add -t 1h ~/.ssh/id_ed25519 # keep this key for one hour
ssh-add -l # list fingerprints of loaded keys
ssh-add -D # remove all keys when you are done৪ · ~/.ssh/config — লম্বা কমান্ডকে ছোট নামে আনা
পাঁচটি server, প্রতিটিতে আলাদা user, আলাদা key, আলাদা পথ। প্রতিবার সব লেখা অসম্ভব। SSH client config ফাইলে এগুলো একবার লিখে রাখলে ssh web-1-ই যথেষ্ট। এটি রান্নাঘরের রেসিপি খাতার মতো: বিস্তারিত একবার লেখা, পরে শুধু নাম ধরে ডাকা।
# more specific blocks first, the general block last
Host web-* !web-old
User deploy
ProxyJump bastion
Host web-1
HostName 10.0.2.11
Host bastion
HostName server.example.com
User ops
Host *
User ubuntu
IdentityFile ~/.ssh/id_ed25519Host: যে নামে আপনি ডাকবেন।*আর?pattern চলে।!দিলে সেই নাম বাদ।HostName: আসল ঠিকানা। না দিলে ডাকা নামটিই ঠিকানা ধরা হয়।User,IdentityFile: কোন user, কোন key.ProxyJump: কোন মধ্যবর্তী host হয়ে যাবেন (অংশ ৭-এ দেখবেন)।
ssh ফাইলটি ওপর থেকে নিচে পড়ে। মিলে যাওয়া সব Host block প্রযোজ্য হয়, আর প্রতিটি option-এর প্রথম পাওয়া মানই থাকে। তাই নির্দিষ্ট block ওপরে, সাধারণ Host * নিচে। ব্যতিক্রম IdentityFile: একাধিক মিললে সবগুলো চেষ্টার তালিকায় যোগ হয়। IdentitiesOnly yes দিলে শুধু config-এর key চেষ্টা হয়।
নিচের ছোট simulator ঠিক এই ফাইলটি পড়ে প্রতিটি নামের চূড়ান্ত মান বের করে:
// Toy reader for ~/.ssh/config: glob patterns, !negation, first value wins.
const configText = `
Host web-* !web-old
User deploy
ProxyJump bastion
Host web-1
HostName 10.0.2.11
Host bastion
HostName server.example.com
User ops
Host *
User ubuntu
IdentityFile ~/.ssh/id_ed25519`;
const blocks = [];
for (const line of configText.trim().split('\n')) {
const [key, ...rest] = line.trim().split(/\s+/);
if (key === 'Host') blocks.push({ patterns: rest, opts: [] });
else blocks.at(-1).opts.push([key, rest.join(' ')]);
}
const globMatch = (p, name) => new RegExp('^' +
p.replace(/[.+]/g, '\\$&').replace(/\*/g, '.*').replace(/\?/g, '.') + '$').test(name);
function blockApplies(patterns, name) {
const no = patterns.filter(p => p[0] === '!').map(p => p.slice(1));
const yes = patterns.filter(p => p[0] !== '!');
return yes.some(p => globMatch(p, name)) && !no.some(p => globMatch(p, name));
}
function resolve(name) {
const used = {};
for (const b of blocks.filter(b => blockApplies(b.patterns, name)))
for (const [k, v] of b.opts) used[k] ??= v; // first value wins
console.log(name, '->', JSON.stringify({ HostName: name, ...used }));
}
['web-1', 'web-2', 'web-old', 'bastion'].forEach(resolve);আউটপুটের মূল তিনটি লাইন:
web-1 -> {"HostName":"10.0.2.11","User":"deploy","ProxyJump":"bastion","IdentityFile":"~/.ssh/id_ed25519"}
web-old -> {"HostName":"web-old","User":"ubuntu","IdentityFile":"~/.ssh/id_ed25519"}
bastion -> {"HostName":"server.example.com","User":"ops","IdentityFile":"~/.ssh/id_ed25519"}
web-1 তিনটি block-এ মিলেছে। User deploy আগে পাওয়া গেছে, তাই নিচের User ubuntu বাদ। web-old-কে !web-old প্রথম block থেকে বাদ দিয়েছে, তাই সে পেয়েছে User ubuntu, আর ProxyJump নেই। আসল ssh-এ ssh -G web-1 লিখলে চূড়ান্ত মান দেখা যায়, সংযোগ না করেই। সমস্যা খুঁজতে ssh -v web-1 চালিয়ে দেখুন ssh কোন ফাইল আর কোন key ব্যবহার করছে।
Permission denied (publickey) এলে কী দেখবেন
নতুনদের সবচেয়ে পরিচিত error এটি। মনে রাখুন, server নিরাপত্তার কারণে আসল কারণ client-কে বলে না। তাই দুই দিক থেকে খুঁজতে হয়। ল্যাপটপে ssh -v দেখায় কোন key চেষ্টা হলো। Server-এর log জানায় কেন প্রত্যাখ্যান হলো।
| সম্ভাব্য কারণ | কীভাবে ধরবেন | সমাধান |
|---|---|---|
| ভুল user নাম | ssh -G-এ user লাইন দেখুন | Image-এর সঠিক user ব্যবহার করুন (image-ভেদে আলাদা) |
| ভুল key চেষ্টা হচ্ছে | ssh -v-এ Offering public key লাইন | IdentityFile আর IdentitiesOnly yes |
| Key server-এ নেই | Server-এর authorized_keys | Public key যোগ করুন, পুরো এক লাইনে |
| Server-এর permission আলগা | Server log-এ bad ownership or modes | 700 আর 600, সঠিক মালিক |
| User অনুমোদিত নয় | Server log-এ AllowUsers-এর কথা | AllowUsers তালিকা দেখুন |
৫ · ফাইল পাঠানো আর লম্বা কাজ — scp, rsync, tmux
একই SSH পথে ফাইলও যায়। scp সাধারণ কপির জন্য। rsync শুধু বদলানো অংশ পাঠায়, তাই বারবার একই folder পাঠাতে অনেক ভালো। আধুনিক OpenSSH-এ (৯.০ থেকে) scp ভেতরে SFTP প্রোটোকল ব্যবহার করে, কিন্তু কমান্ড একই থাকে।
# one file to the server, one file back
scp app.tar.gz deploy@server.example.com:/tmp/
scp deploy@server.example.com:/var/log/app.log ./
# see first what rsync WOULD do: --dry-run changes nothing
rsync -avz --dry-run ./site/ web-1:/var/www/site/
# then run it for real (the config name web-1 works here too)
rsync -avz ./site/ web-1:/var/www/site/rsync-এ উৎসের শেষে / অর্থ বদলায়। ./site/ মানে folder-এর ভেতরের জিনিস, ./site মানে পুরো folder. আর --delete গন্তব্যের বাড়তি ফাইল মুছে দেয়। তাই আগে --dry-run দেখুন, বিশেষ করে --delete ব্যবহার করলে।
tmux — সংযোগ কাটলেও কাজ চলবে
৪০ মিনিটের একটি database backup চালাচ্ছেন। হঠাৎ লোডশেডিংয়ে রাউটার বন্ধ, SSH সংযোগ কেটে গেল। Terminal-এর সাথে যুক্ত প্রোগ্রাম তখন সাধারণত SIGHUP সংকেত পেয়ে থেমে যায়। tmux এই সমস্যার সমাধান। এর session server-এই চলে, আপনি শুধু তাতে যুক্ত হন আর আলাদা হন। পুরোনো সমতুল্য টুল GNU screen.
| কাজ | tmux | screen |
|---|---|---|
| নামসহ session শুরু | tmux new -s backup | screen -S backup |
| আলাদা হওয়া | Ctrl-b তারপর d | Ctrl-a তারপর d |
| Session-এর তালিকা | tmux ls | screen -ls |
| আবার যুক্ত হওয়া | tmux attach -t backup | screen -r backup |
tmux একবারের কাজের জন্য। যা সবসময় চলবে, যেমন app, তা systemd service হওয়া উচিত (পর্ব ১১). Server reboot হলে tmux session থাকে না।
৬ · Port forwarding — SSH-এর ভেতরে গোপন নল
Database শুধু private network-এ আছে। ল্যাপটপ থেকে একবার দেখতে চান। Database-কে internet-এ খোলা ভয়ংকর ভুল। SSH-এর port forwarding (tunnel) এখানে কাজে আসে। এটি এনক্রিপ্টেড পথের ভেতর দিয়ে একটি TCP নল বানায়।
-L: local forwarding — দূরের জিনিস কাছে আনা
# local port 5433 -> through bastion -> db.internal.example:5432
ssh -N -L 127.0.0.1:5433:db.internal.example:5432 bastion
# in a second terminal on the laptop
psql -h 127.0.0.1 -p 5433 -U app_read appdbLocal forwarding (-L)
ল্যাপটপের port থেকে দূরের service
-R: remote forwarding — নিজের জিনিস দূরে দেখানো
উল্টো কাজ। আপনার ল্যাপটপে একটি dev app চলছে। Server-এর কেউ বা কিছু সেটিকে ডাকতে চায়, কিন্তু ল্যাপটপ NAT-এর পেছনে, বাইরে থেকে পৌঁছানো যায় না। -R দিলে server-এর একটি port ল্যাপটপের দিকে নল হয়ে আসে। লক্ষ করুন, সংযোগ খোলে ল্যাপটপ, কিন্তু request আসে উল্টো দিকে।
# server port 9000 -> back through the tunnel -> laptop port 3000
ssh -N -R 127.0.0.1:9000:localhost:3000 deploy@server.example.com
# on the server: reaches the app that runs on your laptop
curl http://127.0.0.1:9000/healthRemote forwarding (-R)
Server-এর port থেকে ল্যাপটপের service
-R বেশি সংবেদনশীল।| Tunnel | কী করে | ঝুঁকি | নিয়ন্ত্রণ |
|---|---|---|---|
-L | দূরের service ল্যাপটপের port-এ আনে | ল্যাপটপের যেকোনো প্রোগ্রাম সেই port ব্যবহার করতে পারে; 0.0.0.0-তে বাঁধলে পাশের যন্ত্রও পারে | 127.0.0.1-তে বাঁধা, কাজ শেষে বন্ধ, read-only DB user |
-R | ল্যাপটপের service server-এর port-এ দেখায় | Firewall-এর ভেতরের যন্ত্রে বাইরের দিক থেকে পথ তৈরি হয় | GatewayPorts no, প্রতিষ্ঠানের অনুমতি, AllowTcpForwarding নীতি |
Tunnel দিয়ে security নিয়ম এড়িয়ে যাওয়া যায় বলে অনেক প্রতিষ্ঠান একে নীতির বিষয় হিসেবে দেখে। অনুমতি ছাড়া অফিসের network-এ -R খুলবেন না। Server-এ দরকার না হলে AllowTcpForwarding no রাখুন। -D নামে আরেকটি রূপ আছে, যা SOCKS proxy বানায়। এর দৈনন্দিন ব্যবহার কম।
৭ · Bastion, ProxyJump আর port 22 ছাড়া প্রবেশ
ভালো নকশায় app server আর database থাকে private subnet-এ (পর্ব ২৫ আর পর্ব ২৭ দেখুন)। তাদের public IP নেই। তাহলে ঢুকবেন কীভাবে? ঐতিহ্যবাহী উত্তর bastion বা jump host: public subnet-এ একটি ছোট, শক্ত করে সাজানো server, যা শুধু দরজার কাজ করে। সব SSH সেখান দিয়ে যায়, তাই পাহারা আর log এক জায়গায় থাকে।
ল্যাপটপ → bastion → private server
Security group ঠিক করে দেয় কে কোথা থেকে আসতে পারবে
Bastion হয়ে যাওয়ার আধুনিক উপায় ProxyJump. কমান্ড লাইনে এটি -J: ssh -J ops@server.example.com deploy@10.0.2.11. আগের config-এ ProxyJump bastion লেখা আছে, তাই শুধু ssh web-1 লিখলেই চলে। ভেতরে কী ঘটে দেখুন।
- ল্যাপটপ
ssh web-1key এখানেই - Bastion
server.example.comশুধু নল - web-1
10.0.2.11private subnet
| ল্যাপটপ ↔ bastion | |
| bastion → web-1 | |
| ল্যাপটপ ↔ web-1 | |
| Bastion যা দেখে |
- Config পড়া। ssh হিসাব করে:
web-1মানে10.0.2.11, userdeploy, যেতে হবেbastionহয়ে। - প্রথম সংযোগ। ল্যাপটপ bastion-এ user
opsহিসেবে সাধারণ SSH লগইন করে। Bastion-এর host key যাচাই হয়, key login হয়। - নল খোলা। ল্যাপটপ bastion-কে অনুরোধ করে:
10.0.2.11:22-এ একটি TCP পথ খুলুন। Bastion-এ কোনো shell লাগে না, শুধু forwarding-এর অনুমতি লাগে। - দ্বিতীয় সংযোগ, সরাসরি। সেই নলের ভেতর দিয়ে ল্যাপটপ নিজেই web-1-এর সাথে নতুন SSH শুরু করে। Key exchange ল্যাপটপ আর web-1-এর মধ্যে। Bastion শুধু এনক্রিপ্টেড বাইট বহন করে।
- লগইন। web-1-এর জন্য স্বাক্ষরও ল্যাপটপেই হয়। Private key বা agent, কিছুই bastion-এ রাখতে হয়নি।
- Shell. আপনি web-1-এ। একটি নলের ভেতরে আরেকটি এনক্রিপ্টেড পথ। Bastion আপস হলেও সে web-1-এর ভেতরের কথা পড়তে পারে না।
Agent forwarding (ssh -A, ForwardAgent yes) এড়িয়ে চলুন। Agent forwarding-এ bastion-এ একটি socket তৈরি হয়, যা আপনার ল্যাপটপের agent-কে স্বাক্ষরের অনুরোধ পৌঁছে দেয়। Key চুরি হয় না, কিন্তু bastion-এর root সংযোগ থাকাকালীন আপনার হয়ে স্বাক্ষর করাতে পারে। ProxyJump-এ এই socket-ই নেই। ProxyJump দিয়ে গন্তব্যে পৌঁছানো গেলে forwarding-এর দরকারই নেই। Host *-এর নিচে ForwardAgent yes তো কখনোই নয়।
Port 22 খোলা না রেখে প্রবেশ
নতুন ধারণাটি এমন: VM-এর ভেতরে একটি agent চলে, যা নিজে থেকে provider-এর সার্ভিসে বাইরের দিকে সংযোগ রাখে। আপনি provider-এর API দিয়ে IAM পরিচয়ে লগইন করেন, আর সেই পথে shell পান। ফলে VM-এ ঢোকার কোনো port খোলা থাকে না। এই ধরনকে আমরা বলব session manager. বিভিন্ন provider-এর নাম আর আচরণ আলাদা, এবং সময়ের সাথে বদলায়।
| বিষয় | Bastion + SSH | Session manager ধরনের প্রবেশ |
|---|---|---|
| খোলা port | Bastion-এ 22 | সাধারণত কোনো inbound port নয় |
| পরিচয় | SSH key বা certificate | Cloud IAM, MFA-সহ |
| Log | নিজে সাজাতে হয় | সাধারণত audit log-এ; session রেকর্ডও সম্ভব |
| নির্ভরতা | শুধু OpenSSH, যেকোনো জায়গায় চলে | VM-এ agent, provider-এর সার্ভিস, network পথ |
| উদাহরণ (লেখার সময়ে) | নিজের ছোট VM | AWS Systems Manager Session Manager; Google Cloud-এর IAP TCP forwarding; Azure Bastion (provider-চালিত ব্যবস্থা) |
হাতে-কলমে চেষ্টা করতে চাইলে আসল cloud VM লাগবে না। নিজের কম্পিউটারের একটি ভার্চুয়াল মেশিনেই SSH শেখা যায়। Cloud VM ব্যবহার করলে খরচ হতে পারে। কাজ শেষে VM আর তার public IP মুছে দিন, আর পরীক্ষার key-ও বাদ দিন।
৮ · sshd শক্ত করা — log, fail2ban আর security group
Internet-এ port 22 খুলে রাখলে স্বয়ংক্রিয় scanner কিছুক্ষণের মধ্যেই লগইনের চেষ্টা শুরু করে। এটি সাধারণ পটভূমির গোলমাল, তবে অবহেলা করার নয়। Server-এর config /etc/ssh/sshd_config আর তার sshd_config.d/ folder-এর ফাইলে থাকে।
# /etc/ssh/sshd_config.d/00-hardening.conf
# Keep comments on their own lines: older OpenSSH rejects them after a value.
# Log in as a user, then sudo.
PermitRootLogin no
# Keys or certificates only. The second line closes the other password path.
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
# Every other account is refused.
AllowUsers deploy ops
MaxAuthTries 3
X11Forwarding no
AllowAgentForwarding no
# Set yes only on a host that needs it, such as a bastion.
AllowTcpForwarding noনিজেকে আটকে ফেলবেন না। নতুন config-এ ভুল থাকলে আপনি দরজার বাইরে পড়ে থাকবেন। নিরাপদ ক্রম: (১) key login কাজ করছে কি না দেখুন, (২) sudo sshd -t দিয়ে syntax পরীক্ষা করুন, (৩) sudo sshd -T | grep -i passwordauth দিয়ে চূড়ান্ত মান দেখুন, (৪) service reload করুন (নাম distro-ভেদে ssh বা sshd), (৫) পুরোনো session খোলা রেখে নতুন window থেকে লগইন করে দেখুন। আটকে গেলে provider-এর serial console বা session manager একমাত্র ভরসা।
sshd_config-এও প্রথম পাওয়া মানই থাকে, তাই একই keyword দুবার থাকলে ওপরেরটি জেতে। অনেক cloud image নিজের একটি ফাইল যোগ করে (যেমন 50-cloud-init.conf). আপনার ফাইলের নাম তার আগে সাজাতে হয়। নিচের ছোট checker একটি config লেখা পড়ে দুর্বল setting জানায়। এটি কোনো server স্পর্শ করে না।
# Reports weak settings in sshd_config text. It reads text only, it touches no server.
set -euo pipefail
config=$(cat <<'CONF'
# sample file: a careless config
PermitRootLogin yes
PasswordAuthentication yes
PasswordAuthentication no
PubkeyAuthentication yes
X11Forwarding yes
MaxAuthTries 6
CONF
)
# Like sshd: the first value of a keyword wins.
value() {
awk -v k="$1" 'tolower($1) == tolower(k) { print $2; exit }' <<< "$config"
}
weak=0
check() { # keyword, safe value, built-in default if the line is missing
local got
got=$(value "$1")
got=${got:-$3}
if [ "$got" = "$2" ]; then
printf 'OK %-29s %s\n' "$1" "$got"
else
printf 'WEAK %-29s %s (want %s)\n' "$1" "$got" "$2"
weak=$((weak + 1))
fi
}
check PermitRootLogin no prohibit-password
check PasswordAuthentication no yes
check KbdInteractiveAuthentication no yes
check PubkeyAuthentication yes yes
check X11Forwarding no no
[ -n "$(value AllowUsers)" ] || { echo 'WEAK AllowUsers missing'; weak=$((weak + 1)); }
echo "weak settings: $weak"আসল আউটপুট:
WEAK PermitRootLogin yes (want no)
WEAK PasswordAuthentication yes (want no)
WEAK KbdInteractiveAuthentication yes (want no)
OK PubkeyAuthentication yes
WEAK X11Forwarding yes (want no)
WEAK AllowUsers missing
weak settings: 5
নমুনায় PasswordAuthentication no লেখা আছে, তবু ফল yes, কারণ তার ওপরের yes আগে পাওয়া গেছে। ঠিক এই ভুলেই মানুষ ভাবেন password বন্ধ হয়ে গেছে। KbdInteractiveAuthentication ফাইলেই নেই, তাই checker তার built-in default yes ধরেছে। Default OpenSSH-এর version-ভেদে বদলাতে পারে, আর distro নিজের মানও দেয়। তাই আসল server-এ sshd -T দেখাই চূড়ান্ত।
Log পড়া আর fail2ban
sshd প্রতিটি চেষ্টা log-এ লেখে। Debian ধরনের সিস্টেমে /var/log/auth.log, Red Hat ধরনে /var/log/secure, আর অনেক সিস্টেমে journalctl -u ssh (পর্ব ৯ আর পর্ব ১১-এর কৌশল এখানে কাজে লাগে)। নিচের script একটি নমুনা log থেকে অচেনা host আর ব্যর্থ লগইন গোনে। সব ঠিকানা documentation-এর জন্য সংরক্ষিত 203.0.113.0/24 থেকে নেওয়া।
# Finds unknown hosts in a sample sshd log. All addresses are documentation addresses.
set -euo pipefail
log=$(cat <<'LOG'
Oct 30 02:14:05 web-1 sshd[2101]: Failed password for invalid user admin from 203.0.113.45 port 51122 ssh2
Oct 30 02:14:09 web-1 sshd[2101]: Failed password for root from 203.0.113.45 port 51130 ssh2
Oct 30 02:14:13 web-1 sshd[2102]: Failed password for root from 203.0.113.45 port 51138 ssh2
Oct 30 02:15:13 web-1 sshd[2108]: Failed password for invalid user test from 203.0.113.77 port 40222 ssh2
Oct 30 02:16:40 web-1 sshd[2113]: Accepted publickey for deploy from 203.0.113.10 port 53010 ssh2
Oct 30 02:18:02 web-1 sshd[2120]: Failed password for deploy from 203.0.113.99 port 33100 ssh2
Oct 30 02:18:07 web-1 sshd[2120]: Failed password for deploy from 203.0.113.99 port 33100 ssh2
Oct 30 02:18:15 web-1 sshd[2121]: Accepted password for deploy from 203.0.113.99 port 33108 ssh2
Oct 30 02:20:31 web-1 sshd[2130]: Failed password for ops from 203.0.113.11 port 44120 ssh2
LOG
)
awk -v known='203.0.113.10 203.0.113.11' '
BEGIN { n = split(known, list, " "); for (i = 1; i <= n; i++) ok[list[i]] = 1 }
/Failed password|Accepted / {
for (i = 1; i <= NF; i++) if ($i == "from") ip = $(i + 1)
seen[ip] = 1
if (/Failed password/) failed[ip]++; else accepted[ip]++
}
END {
for (ip in seen) {
tag = (ip in ok) ? "known" : "UNKNOWN"
alert = (tag == "UNKNOWN" && accepted[ip] > 0) ? " <-- check this login now" : ""
printf "%-14s failed=%d accepted=%d %s%s\n", ip, failed[ip], accepted[ip], tag, alert
}
}' <<< "$log" | sort -t= -k2,2nr -k1,1
echo "total failed logins: $(grep -c 'Failed password' <<< "$log")"আসল আউটপুট:
203.0.113.45 failed=3 accepted=0 UNKNOWN
203.0.113.99 failed=2 accepted=1 UNKNOWN <-- check this login now
203.0.113.11 failed=1 accepted=0 known
203.0.113.77 failed=1 accepted=0 UNKNOWN
203.0.113.10 failed=0 accepted=1 known
total failed logins: 7
সবচেয়ে বেশি ব্যর্থতা .45-এর, কিন্তু আসল বিপদ .99. অচেনা ঠিকানা থেকে দুবার ব্যর্থ হয়ে password দিয়ে লগইন সফল হয়েছে। Incident-এ এই লাইনই আগে দেখতে হয়। Password login বন্ধ থাকলে এই ঘটনা ঘটতই না।
fail2ban এই log পাহারা দেয়। নির্দিষ্ট সময়ে বেশি ব্যর্থ চেষ্টা হলে সেই IP-কে কিছুক্ষণের জন্য firewall-এ আটকায়। এটি গোলমাল কমায়, কিন্তু মূল সুরক্ষা নয়। অনেক IP থেকে ধীরে চেষ্টা হলে এটি কম কাজে আসে (অনুশীলন ১-এ হিসাব দেখবেন)।
# /etc/fail2ban/jail.local
[sshd]
enabled = true
maxretry = 5
findtime = 10m
bantime = 1hসবচেয়ে কার্যকর স্তর কিন্তু server-এর বাইরে। Security group (পর্ব ২৭) দিয়ে port 22 শুধু অফিসের IP, VPN বা bastion থেকে খোলা রাখুন। তাহলে packet sshd পর্যন্ত পৌঁছায়ই না। 0.0.0.0/0-তে 22 রাখবেন না। Port বদলে 2222 করলে log-এর গোলমাল কমতে পারে, তবে এটি নিরাপত্তা নয়, কারণ scan করলেই port খুঁজে পাওয়া যায়।
৯ · Key rotation, revocation আর SSH certificate
মেস থেকে একজন চলে গেলে কী করেন? তালা বদলান, কারণ তাঁর হাতে চাবি আছে। Server-এও তাই। কর্মী চলে গেলে, ল্যাপটপ হারালে বা key ফাঁসের সন্দেহ হলে key বাতিল করতে হয়। এই কাজের দুটি নাম। Key rotation মানে নিয়ম করে পুরোনো key বদলে নতুন key আনা। Revocation মানে কোনো key সাথে সাথে অকেজো করা।
- Rotation-এর ক্রম: নতুন key যোগ, পরীক্ষা, তারপর পুরোনো key মোছা। আগে মুছলে নিজেই আটকে যাবেন।
- Revocation: প্রতিটি server-এর
authorized_keysথেকে লাইন মোছা, অথবাRevokedKeysফাইলে যোগ করা। ১০০টি server হলে কাজটি Ansible-এর মতো টুলে করতে হয় (পর্ব ৫৬). - সমস্যা: কোন key কার, কোথায় আছে, তার হিসাব হারিয়ে যায়। এজন্য key-এর comment-এ মালিকের নাম রাখা আর ভাগ করা key এড়ানো জরুরি।
SSH certificate — গেট পাসের ধারণা
অফিসে অতিথি এলে রিসেপশন একটি সাময়িক গেট পাস দেয়, যাতে নাম আর মেয়াদ লেখা। প্রতিটি দারোয়ানকে আলাদা করে নাম মুখস্থ করাতে হয় না। তাঁরা শুধু রিসেপশনের সিল চেনেন। SSH certificate ঠিক এমন। একটি certificate authority (CA) আপনার public key-এ স্বাক্ষর করে, সাথে login নাম আর মেয়াদ বসায়। Server শুধু CA-র public key বিশ্বাস করে। প্রতিটি server-এর authorized_keys আর ছোঁয়া লাগে না।
# the CA signs rafi's public key: login name deploy, valid 8 hours
ssh-keygen -s ca_key -I rafi-2026-10-30 -n deploy -V +8h id_ed25519.pub
# this creates id_ed25519-cert.pub; inspect it with:
ssh-keygen -L -f id_ed25519-cert.pub
# on every server, in sshd_config
TrustedUserCAKeys /etc/ssh/trusted_user_ca.pub
RevokedKeys /etc/ssh/revoked_keys| বিষয় | authorized_keys | SSH certificate |
|---|---|---|
| নতুন কর্মী | প্রতিটি server-এ key যোগ | CA থেকে certificate নেওয়া |
| মেয়াদ | মুছে না ফেলা পর্যন্ত চলে | নিজে থেকেই শেষ, যেমন ৮ ঘণ্টা |
| কর্মী চলে গেলে | সব server-এ খুঁজে মোছা | নতুন certificate বন্ধ; পুরোনোটি মেয়াদে শেষ |
| বড় ঝুঁকি | হিসাবহীন পুরোনো key | CA key ফাঁস: এটি পাহারায় রাখতে হয় (পর্ব ৩৩) |
Host-এর জন্যও certificate হয়। ল্যাপটপের known_hosts-এ @cert-authority *.example.com ssh-ed25519 AAAA...fake ধরনের একটি লাইন থাকলে সেই CA-র স্বাক্ষরিত সব server চেনা হয়ে যায়। তখন প্রথম সংযোগের yes প্রশ্নও চলে যায়।
আরও গভীরে: SSH-এর key exchange সাধারণত curve25519-sha256 ব্যবহার করে (RFC 8731, ২০২০), যা Diffie-Hellman-এর একটি রূপ (Diffie-Hellman; তত্ত্ব Cryptography-র ভিত্তি পর্বে)। প্রতি সংযোগে নতুন গোপন মান হয়, তাই পরে কোনো host key ফাঁস হলেও পুরোনো রেকর্ড করা traffic পড়া যায় না। একে forward secrecy বলে। সাম্প্রতিক OpenSSH ভবিষ্যতের quantum কম্পিউটারের ঝুঁকি ভেবে hybrid post-quantum key exchange যোগ করেছে। ঠিক কোন version-এ কোনটি default, তা release note-এ দেখে নেবেন।
পরীক্ষা ও ইন্টারভিউতে:
- SSH-এর default port? TCP
22. - known_hosts আর authorized_keys-এর পার্থক্য? প্রথমটি client-এ, server চেনার জন্য। দ্বিতীয়টি server-এ, user চেনার জন্য।
-Lআর-R-এর পার্থক্য?-L-এ শোনার port client-এ,-R-এ শোনার port server-এ।- ProxyJump কেন agent forwarding-এর চেয়ে ভালো? Bastion-এ agent socket থাকে না, আর শেষ সংযোগ end-to-end এনক্রিপ্টেড।
- Password বন্ধ করার আগে কী নিশ্চিত করবেন? Key login কাজ করছে, আর একটি session খোলা আছে।
১০ · AI-এর যুগে SSH — দ্রুত সমাধান, কিন্তু খোলা দরজা
SSH-এর error পেলে AI প্রায়ই সবচেয়ে ছোট পথ দেখায়, যা নিরাপত্তা বন্ধ করে দিয়ে “সমাধান” করে। Cloud আর DevOps-এর অন্য ভুলের সাথে এগুলো মিলিয়ে দেখুন:
| AI যা বলতে পারে | ঝুঁকি | ভালো পথ |
|---|---|---|
StrictHostKeyChecking no, UserKnownHostsFile=/dev/null | Host যাচাই পুরো বন্ধ | Fingerprint মিলিয়ে দেখা; automation-এ আগে বানানো known_hosts |
chmod 777 ~/.ssh | ssh নিজেই key প্রত্যাখ্যান করবে, অন্যরাও পড়তে পারবে | ৭০০ আর ৬০০ mode |
Security group-এ 0.0.0.0/0 থেকে 22 | পুরো internet-এর সামনে দরজা | অফিসের IP, bastion বা session manager |
ForwardAgent yes, Host *-এর নিচে | প্রতিটি server আপনার agent পায় | ProxyJump |
PermitRootLogin yes | সবচেয়ে বড় ক্ষমতা সরাসরি বাইরে | আলাদা user আর sudo |
| Private key prompt-এ পেস্ট করতে বলা, repo-তে key রাখা | Secret ফাঁস | কখনো নয়; ফাঁস হলে সাথে সাথে বাতিল |
| নেই এমন flag বা option | Config ভাঙে, লগইন আটকায় | man ssh_config, man sshd_config, sshd -t |
rm -rf, rsync --delete, terraform destroy, kubectl delete বিনা সতর্কতায় | ডেটা হারানো | --dry-run, terraform plan, diff পড়া |
IAM-এ * অনুমতি, public storage, latest tag, পুরোনো API version, খরচ না ভাবা | পরের মডিউলগুলোর সব ঝুঁকি | Official docs, throwaway পরিবেশ, budget alert |
AI-কে error দেওয়ার সময় আগেই শর্ত বেঁধে দিন, আর নিজের key, token বা আসল IP দেবেন না:
AI-এর দেওয়া SSH পরামর্শ চালানোর আগে — চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন, তারপর “→ উত্তর” চাপুন।
প্র ০১Web-এ HTTPS-এর জন্য certificate authority আছে। SSH তাহলে “প্রথমবার বিশ্বাস” করার মতো দুর্বল পথ কেন বেছে নিল?
কারণটি ইতিহাস আর ব্যবহারযোগ্যতায়। ১৯৯৫ সালে Ylönen-এর সমস্যা ছিল জরুরি: বিশ্ববিদ্যালয়ের network থেকে password চুরি হচ্ছে। তখন বিশ্বজোড়া CA ব্যবস্থায় একটি সাধারণ Unix মেশিনের জন্য certificate নেওয়া বাস্তবসম্মত ছিল না। SSH এমন কিছু চেয়েছিল, যা ইনস্টল করলেই চলবে, কোনো তৃতীয় পক্ষ ছাড়া।
TOFU সেই সমঝোতা। এটি একটি কঠিন আক্রমণ বাকি রাখে, যা শুধু প্রথম সংযোগের মুহূর্তে সম্ভব। কিন্তু পরের সব সংযোগ রক্ষা করে। আর পুরো পথের গোপন শোনা ঠিকই বন্ধ করে। Telnet-এর তুলনায় এটি বিশাল উন্নতি। নিখুঁত ব্যবস্থা না থাকার চেয়ে কাজের ব্যবস্থা ভালো।
দুর্বলতাটি মানুষের দিকে। ২০১১ সালে Peter Gutmann-এর একটি লেখায় বাস্তব পর্যবেক্ষণের কথা আছে যে খুব কম মানুষই fingerprint মিলিয়ে দেখেন। প্রায় সবাই yes লেখেন। অর্থাৎ নকশা নিরাপদ হলেও অভ্যাস সেই নিরাপত্তা খেয়ে ফেলে।
তারপর উন্নতি এসেছে ধাপে ধাপে। ২০০৬ সালে RFC 4255 DNS-এ fingerprint রাখার SSHFP record আনে, যা DNSSEC-এর সাথে বেশি অর্থবহ। OpenSSH-এর নিজস্ব certificate (২০১০ সালের আশেপাশে) বড় প্রতিষ্ঠানে host চেনার সমস্যা অনেকটা মেটায়। সেখানে CA-টি প্রতিষ্ঠানের নিজের, বিশ্বজোড়া নয়। Web আর SSH-এর পার্থক্যও মনে রাখুন: browser লক্ষ অচেনা সাইটে যায়, কিন্তু আপনি অল্প কিছু নিজের server-এ যান। তাই একবার চিনে রাখার পদ্ধতি SSH-এ যতটা মানায়, web-এ ততটা মানাত না। Cloud-এ VM যখন প্রতিদিন নতুন করে তৈরি হয়, তখন এই সুবিধা আবার কমে যায়। কারণ প্রতিটি নতুন VM নতুন host key আনে।
মূল উপলব্ধি: নিরাপত্তা নকশায় “কোনটি সবচেয়ে শক্ত” প্রশ্নের চেয়ে “কোনটি মানুষ আসলেই ব্যবহার করবে” প্রশ্ন কম গুরুত্বপূর্ণ নয়। তবে স্কেল বাড়লে দুর্বল ধাপটিকে automation আর certificate দিয়ে সরিয়ে দিন।
প্র ০২Session manager থাকলে bastion কি পুরোনো হয়ে গেল? নতুন একটি প্রকল্পে আপনি কোনটি বেছে নেবেন?
দুটোই “এক দরজা, এক log” নীতি মানে। পার্থক্য হলো দরজাটি কে চালায়, আর পরিচয় কে যাচাই করে।
- Bastion-এর সুবিধা: OpenSSH সবখানে আছে। এক provider থেকে আরেকটিতে, নিজের data center-এও একই পদ্ধতি চলে। কোনো provider-এর সার্ভিস বন্ধ হলেও পথ থাকে।
- Bastion-এর দাম: এটিও একটি server. Patch করতে হয়, চালিয়ে রাখার খরচ আছে। Public দিকে port খোলা, আর key-এর হিসাব আপনার দায়িত্ব। আপস হলে ভেতরের সব server ঝুঁকিতে পড়ে।
- Session manager-এর সুবিধা: Inbound port নেই, পরিচয় IAM আর MFA দিয়ে, কর্মী চলে গেলে এক জায়গায় বন্ধ করা যায়, আর audit log নিজে থেকেই পাওয়া যায়। অনেক সময় SSH key রাখারই দরকার পড়ে না।
- Session manager-এর দাম: VM-এ agent চালু থাকতে হবে আর সেটির provider-এ পৌঁছানোর পথ লাগবে। Private subnet-এ এজন্য NAT বা private endpoint দরকার হতে পারে। Agent ভেঙে গেলে দরজাও বন্ধ। আচরণ provider-ভেদে আলাদা, তাই lock-in বাড়ে।
নতুন প্রকল্পে এক provider হলে অনেক দল session manager দিয়ে শুরু করেন, কারণ port না খুললে একটি পুরো ঝুঁকির শ্রেণিই চলে যায়। Hybrid বা একাধিক provider হলে, অথবা অডিটের কড়া শর্ত থাকলে, certificate-ভিত্তিক bastion ভালো হতে পারে। জরুরি মুহূর্তের জন্য একটি দ্বিতীয় পথ, যেমন serial console, পরীক্ষা করে রাখুন। ছোট দলের জন্য আরেকটি হিসাব আছে: চব্বিশ ঘণ্টা চলা bastion-এর খরচ অল্প হলেও পাহারার সময়ের খরচ কম নয়। আবার provider-এর সার্ভিসের নিজস্ব দাম বা শর্ত থাকতে পারে, তাই ব্যবহারের আগে pricing পাতা দেখে নিন।
মূল উপলব্ধি: সুরক্ষা মানে শুধু দরজা শক্ত করা নয়। দরজাটি দরকার কি না, সেই প্রশ্নও করতে হয়। আর যে দরজা রাখবেন, তার ব্যর্থতার সময়ের পথও ভেবে রাখুন।
প্র ০৩২০০৮ সালের Debian OpenSSL ঘটনা SSH key rotation সম্পর্কে কী শেখায়?
২০০৮ সালের মে মাসে Debian-এর Luciano Bello একটি ভয়ংকর জিনিস আবিষ্কার করেন (CVE-2008-0166). প্রায় দুই বছর আগে Debian-এর একটি পরিবর্তনে OpenSSL-এর random সংখ্যা তৈরির কোডের কিছু লাইন বাদ পড়েছিল, কারণ একটি বিশ্লেষণ টুল সেগুলো নিয়ে সতর্কবার্তা দিচ্ছিল। ফলে এলোমেলোতার প্রায় একমাত্র উৎস হয়ে দাঁড়ায় process id. নির্দিষ্ট ধরন আর আকারের জন্য সম্ভাব্য key-এর সংখ্যা নেমে আসে কয়েক হাজার থেকে কয়েক দশ হাজারে।
এর মানে, Debian আর Ubuntu-তে সেই সময়ে বানানো SSH key অনুমান করে ফেলা সম্ভব ছিল। Key-এর দৈর্ঘ্য যতই হোক, লাভ ছিল না। Software আপডেট করলেই সমস্যা মিটত না। প্রতিটি দুর্বল key নতুন করে বানিয়ে বদলাতে হয়েছিল, সব server-এ। Debian একটি কালো তালিকার ব্যবস্থাও এনেছিল, যা জানা দুর্বল key ধরত। ঘটনাটি সবচেয়ে বেশি ভুগিয়েছিল তাদের, যাঁদের key-এর কোনো তালিকা ছিল না। অন্য distro-র ব্যবহারকারীরাও পুরো নিরাপদ ছিলেন না, কারণ দুর্বল public key অন্যের server-এর authorized_keys-এ বসে থাকতে পারত।
এই ঘটনা থেকে তিনটি শিক্ষা পাওয়া যায়:
- হিসাব রাখা: কোন key কে, কবে, কোথায় বানিয়েছে না জানলে আপনি সেগুলো খুঁজেই পাবেন না।
- অভ্যাস: Rotation বছরে একবারও না হলে জরুরি মুহূর্তে সেই পদ্ধতি কেউ জানবেন না। মহড়া দেওয়া key rotation বিপদের দিনে দ্রুত হয়।
- ছোট মেয়াদ: ৮ ঘণ্টার certificate নিজেই বদলাতে থাকে। তখন rotation আলাদা প্রকল্প থাকে না, দৈনন্দিন ঘটনা হয়ে যায়। এর ঝুঁকি CA-তে সরে আসে, যেখানে পাহারা দেওয়া সহজ।
মূল উপলব্ধি: ধরে নিন, যেকোনো key একদিন বদলাতে হবে, হঠাৎ করেই। তাই প্রশ্নটি “key কি শক্ত?” নয়। প্রশ্ন হলো “আজ রাতে সব key বদলাতে হলে আপনি কত সময়ে পারবেন?”
প্র ০৪২০২৪ সালের xz backdoor sshd-কে লক্ষ্য করেছিল। Hardening করা server-ও কি তাহলে ঝুঁকিতে ছিল? এর শিক্ষা কী?
২০২৪ সালের মার্চে Microsoft-এর প্রকৌশলী Andres Freund লক্ষ করেন, SSH লগইনে প্রায় আধা সেকেন্ড বাড়তি সময় লাগছে। খুঁজতে গিয়ে তিনি compression library xz/liblzma-এর release-এ লুকানো একটি backdoor পান (CVE-2024-3094). OpenSSH-এর নিজের কোডে ত্রুটি ছিল না। কিছু Linux distro-র প্যাচের কারণে sshd পরোক্ষভাবে এই library লোড করত। ধরা পড়ার সময় ক্ষতিগ্রস্ত version মূলত উন্নয়নাধীন আর পরীক্ষামূলক distro-তে ছিল, ব্যাপক stable release-এ পৌঁছায়নি।
এখন প্রশ্ন: PermitRootLogin no আর PasswordAuthentication no কি এখানে বাঁচাত? উত্তর হলো, সম্ভবত না। Backdoor sshd-র প্রমাণীকরণ ধাপের ভেতরেই বসতে চেয়েছিল। Config-এর নিয়ম ধরে নেয়, sshd প্রোগ্রামটি নিজে সৎ।
কিন্তু একটি স্তর ঠিকই কাজ করত। যদি security group port 22-কে শুধু bastion বা VPN থেকে খোলা রাখত, তাহলে internet-এর অচেনা কেউ sshd পর্যন্ত পৌঁছাতেই পারত না। Session manager ধরনে port খোলাই না থাকলে তো আরও ভালো। একই বছরের জুলাইয়ে OpenSSH-এর নিজের একটি গুরুতর ত্রুটি (regreSSHion, CVE-2024-6387) প্রকাশ পায়, যার শিক্ষাও একই: পৌঁছানোর পথ কমান, আর দ্রুত patch করুন।
এখানেই defense in depth-এর অর্থ। প্রতিটি স্তর আলাদা ধরনের ব্যর্থতা ঠেকায়। Hardening ঠেকায় ভুল config আর অনুমান করা password. Network সীমা ঠেকায় অজানা ত্রুটি। Log আর alert ধরে অস্বাভাবিকতা। Supply chain যাচাই (পর্ব ৪১ আর পর্ব ৬১) কমায় বিষাক্ত package. কোনো একটি স্তর ভেঙে পড়লেও পরের স্তর সময় দেয়, আর সেই সময়েই patch করা যায়। তাই hardening-কে অপ্রয়োজনীয় ভাববেন না। এটি একটি নির্দিষ্ট ধরনের ঝুঁকি কমায়, সব ঝুঁকি নয়।
মূল উপলব্ধি: এই ঘটনা ধরা পড়েছিল একজন মানুষের আধা সেকেন্ডের কৌতূহলে, কোনো scanner-এ নয়। অস্বাভাবিক আচরণ উপেক্ষা করবেন না, আর কখনো একটি মাত্র স্তরের ওপর ভরসা করবেন না।
প্র ০৫“Production server-এ SSH করার দরকার পড়াই একটি ব্যর্থতা” — এই কথাটি কতটা সত্য?
Cloud জগতে একটি পরিচিত উপমা আছে: pets vs cattle. পোষা প্রাণীর নাম থাকে, অসুস্থ হলে যত্ন নেওয়া হয়। খামারের গরুর ক্ষেত্রে সিদ্ধান্ত হয় পালের হিসেবে। উপমাটি সাধারণত Bill Baker-এর বলে ধরা হয়, আর Randy Bias একে cloud-এর আলোচনায় জনপ্রিয় করেন। হাতে SSH করে বদলানো server পোষা প্রাণী: কেউ জানে না তার ভেতরে কী আছে।
সমস্যাগুলো বাস্তব।
- Drift: হাতের পরিবর্তন code-এ নেই, তাই পরের deploy-এ হারিয়ে যায় বা নতুন server-এ থাকে না (পর্ব ৫১).
- Audit: কে কী করেছিল, তার হিসাব দুর্বল হয়ে যায়।
- ঝুঁকি: সবচেয়ে ক্ষমতাবান পথটি প্রতিদিন খোলা থাকে।
তাই পরিণত দলে নিয়ম হলো, পরিবর্তন যাবে pipeline আর IaC দিয়ে (পর্ব ৫৬ আর পর্ব ৫৯)। Log আর metric আসবে কেন্দ্রীয় ব্যবস্থায় (মডিউল ১১), যাতে সমস্যা খুঁজতে server-এ ঢুকতে না হয়। খারাপ instance বদলে ফেলা হবে, মেরামত নয়।
কিন্তু কথাটি পুরো সত্য নয়। নতুন ধরনের incident-এ, যখন log-এ উত্তর নেই, একজন মানুষকে ভেতরে দেখতেই হয়। Kernel-এর আচরণ, disk-এর সমস্যা বা network-এর অদ্ভুত ঘটনা সবসময় dashboard-এ আসে না। Database-এর মতো stateful server সহজে “গরু” হয় না। তাই ভালো নীতি হলো break-glass: প্রবেশ থাকবে, কিন্তু ব্যতিক্রম হিসেবে। সময়সীমা, অনুমোদন আর পূর্ণ log-সহ। প্রতিবার ঢুকলে পরে প্রশ্ন করুন: এই তথ্য কেন বাইরে থেকে পাওয়া গেল না? উত্তরটি পরের কাজের তালিকায় তুলুন। এভাবে প্রতিটি incident ভবিষ্যতের প্রবেশের দরকার একটু কমিয়ে দেয়। ছোট দলে অবশ্য একটি মাত্র VM থাকতে পারে। সেখানে SSH-ই সবচেয়ে সরল পথ, আর সেটি ব্যবহার করায় লজ্জার কিছু নেই।
মূল উপলব্ধি: SSH একটি জরুরি হাতিয়ার, দৈনন্দিন deploy পদ্ধতি নয়। আপনার লক্ষ্য SSH নিষিদ্ধ করা নয়, এর প্রয়োজন ধীরে ধীরে কমানো।
অনুশীলন
সবগুলো কাগজে বা মাথায় করা যায়। আসল server লাগবে না।
-
fail2ban-এর হিসাব: ধরে নিই একটি IP থেকে ৫ বার ভুল হলে ১০ মিনিট ban হয়, আর এই ৫টি চেষ্টা প্রায় সাথে সাথেই হয়। (ক) এক IP দিনে সর্বোচ্চ কতটি password চেষ্টা করতে পারবে? (খ) ১,০০,০০০ শব্দের একটি তালিকা শেষ করতে এক IP-র কত দিন লাগবে? (গ) ১০০টি IP একসাথে চেষ্টা করলে কত সময়?
(ক) দিনে ১০ মিনিটের ভাগ ২৪ × ৬ = ১৪৪টি, তাই ১৪৪ × ৫ = ৭২০টি চেষ্টা। (খ) ১,০০,০০০ ÷ ৭২০ ≈ ১৩৮.৯ দিন। (গ) ১৩৮.৯ ÷ ১০০ ≈ ১.৩৯ দিন, অর্থাৎ প্রায় ৩৩ ঘণ্টা।
অর্থাৎ fail2ban গতি কমায়, বন্ধ করে না। আসল সমাধান
PasswordAuthentication no: তখন তালিকার আকার যাই হোক, password চেষ্টার কোনো অর্থই নেই। -
ফল আগে বলুন: ল্যাপটপের config এমন:
Host web-* !web-old(নিচেUser deploy,ProxyJump bastion), তারপরHost *(নিচেUser ubuntu). এবারssh -G web-old | grep -E '^(user|proxyjump) 'চালালে কী দেখাবে?ssh -G web-9-এর ক্ষেত্রে কী হবে?web-oldপ্রথম block থেকে বাদ পড়ে, তাই শুধু একটি লাইন আসে:user ubuntu.proxyjumpলাইন থাকে না।ssh -Goption-এর নাম ছোট হাতের অক্ষরে দেখায়।web-9প্রথম block-এ মেলে, তাইuser deployআরproxyjump bastion.Host *-এরUser ubuntuবাদ, কারণ প্রথম মান আগেই পাওয়া গেছে। -
নকশা সমালোচনা: একটি দলের ব্যবস্থা এমন। সবার জন্য একটি মাত্র
team.pemkey, যা chat-এ পাঠানো হয়েছে। Security group-এ22খোলা0.0.0.0/0. Laptop config-এHost *→ForwardAgent yes. Database tunnel চলে-L 0.0.0.0:5433:...দিয়ে, read-write user-এ। সমস্যাগুলো ধরুন, আর উন্নত নকশা দিন।- ভাগ করা key: কে ঢুকেছে বোঝা যায় না, একজন চলে গেলে সবার key বদলাতে হয়। প্রতি মানুষে আলাদা key বা ছোট মেয়াদের certificate লাগবে।
- Chat-এ key: এটি ফাঁস ধরে নিয়ে এখনই বাতিল করুন।
0.0.0.0/0: Bastion বা session manager, আর উৎস সীমিত করুন।ForwardAgent yes: বাদ দিন,ProxyJumpব্যবহার করুন।- Tunnel:
127.0.0.1-তে বাঁধুন, read-only user নিন, কাজ শেষে বন্ধ করুন।
-
Config লিখুন: Bastion আছে
server.example.com-এ, userops. Private app server10.0.2.20, userdeploy. আপনিssh appদিয়ে ঢুকতে চান। লিখুন: (ক)~/.ssh/config, (খ)db.internal.example:5432-কে bastion হয়ে ল্যাপটপের5433-এ আনার কমান্ড, (গ) config ঠিক আছে কি না সংযোগ না করে দেখার কমান্ড।# (a) ~/.ssh/config Host bastion HostName server.example.com User ops Host app HostName 10.0.2.20 User deploy ProxyJump bastion # (b) the tunnel ssh -N -L 127.0.0.1:5433:db.internal.example:5432 bastion # (c) print the final values, no connection is made ssh -G app
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমনতুন server-এ SSH key login-এর ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- Port
22-এ TCP সংযোগ - Version আর algorithm-এর সমঝোতা
- Key exchange আর server-এর স্বাক্ষর
- Host key-কে
known_hosts-এর সাথে যাচাই - Private key দিয়ে user authentication
- Shell খোলা
SSH client-এর known_hosts ফাইলের কাজ কী?
- Server-এ কোন user লগইন করতে পারবেন তা ঠিক করা
- আগে দেখা server-গুলোর public host key জমা রেখে তাদের চেনা
- ব্যবহারকারীর private key নিরাপদে রাখা
- Host নাম থেকে IP ঠিকানা বের করা
একটি server-এ SSH করতে গিয়ে REMOTE HOST IDENTIFICATION HAS CHANGED সতর্কবার্তা এলো। সবচেয়ে ভালো প্রথম কাজ কোনটি?
StrictHostKeyChecking noদিয়ে আবার চেষ্টা করা- সাথে সাথে
known_hostsথেকে লাইনটি মুছে দেওয়া - আলাদা নিরাপদ পথে নতুন fingerprint মিলিয়ে দেখা
- নিজের ল্যাপটপে নতুন key pair বানানো
SSH public key login-এ পুরোনো রেকর্ড করা স্বাক্ষর দিয়ে পরে লগইন করা যায় না কেন?
- স্বাক্ষর করা বার্তায় থাকা session id প্রতি সংযোগে আলাদা হয়
- Server লগইনের পর private key মুছে ফেলে
- প্রতি সংযোগের পর public key বদলে যায়
- প্রতি সংযোগে passphrase নতুন করে তৈরি হয়
কমান্ড: ssh -N -L 127.0.0.1:5433:db.internal.example:5432 bastion. এতে কী হয়?
- Bastion-এর port 5433 থেকে ল্যাপটপের database-এ পথ খোলে
- Database-এর ফাইল ল্যাপটপে কপি হয়
- Database সরাসরি internet-এ খুলে যায়
- ল্যাপটপের port 5433-এর সংযোগ bastion হয়ে database-এর 5432-এ পৌঁছায়
Bastion হয়ে private server-এ যেতে agent forwarding-এর চেয়ে ProxyJump ভালো কেন?
- এটি সবসময় অনেক দ্রুত
- Bastion আপনার agent-কে ব্যবহার করতে পারে না, আর ভেতরের সংযোগ end-to-end থাকে
- এতে private key bastion-এ কপি করে রাখা যায়
- এতে bastion-এ কোনো পরিচয় যাচাই লাগে না
একটি sshd_config-এ ওপরে PasswordAuthentication yes আর কয়েক লাইন পরে PasswordAuthentication no আছে। ফল কী?
- Password login বন্ধ, কারণ শেষ মান জেতে
- sshd চালু হতেই পারবে না
- Password login চালু থাকে, কারণ প্রথম মান জেতে
- প্রতিটি user আলাদাভাবে বেছে নিতে পারবেন
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন, অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- SSH-এ আগে server পরিচয় দেয় (
known_hosts, TOFU), পরে আপনি (authorized_keys, স্বাক্ষর). - Private key ল্যাপটপ ছাড়ে না। নতুন key হিসেবে ed25519, সাথে passphrase আর ssh-agent.
- Client আর server config, দুটোতেই প্রথম মান জেতে।
ssh -Gআরsshd -Tদিয়ে যাচাই করুন। -Lদূরের জিনিস কাছে আনে,-Rনিজের জিনিস দূরে দেখায়। Private server-এ যেতে ProxyJump, agent forwarding নয়।- Password আর root login বন্ধ, security group-এ উৎস সীমিত, পুরোনো session খোলা রেখে পরীক্ষা, আর স্কেলে certificate.
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ১৩ · Server-এর network হাতিয়ার — ip, ss, dig, curl আর সমস্যা খোঁজাপরবর্তী পর্বServer-এর network সমস্যা ধাপে ধাপে খোঁজা।
- ক্লাউড কম্পিউটিং ও DevOps · কোর্স হোম৮১টি পর্বপুরো কোর্সের রোডম্যাপ।
- Cryptography-র ভিত্তি — symmetric, public key, hash আর signatureগভীরে যানKey exchange আর digital signature-এর তত্ত্ব।
- Linux-এ user, group আর permissionপাশের ট্র্যাকrwx, setuid আর sudo.
- Firewall আর VPNপাশের ট্র্যাকSSH tunnel-এর বাইরে network-স্তরের নিরাপদ পথ।