Vaultwarden কি আসলেই নিরাপদ? নিরাপত্তা নিশ্চিত করার উপায়
Vaultwarden ক্লায়েন্ট সাইডে ডেটা এনক্রিপ্ট করে, তাই সার্ভারে কোনো প্লেইনটেক্সট থাকে না। তবে দুর্বল অ্যাডমিন টোকেন এবং ব্যাকআপ ফাইলের নিরাপত্তা ঝুঁকি এড়াতে এই গাইডটি অনুসরণ করুন।
Vaultwarden কি নিরাপদ? সংক্ষিপ্ত উত্তর
Vaultwarden সবচেয়ে গুরুত্বপূর্ণ জায়গায় নিরাপদ, কারণ প্রতিটি vault item সার্ভারে পৌঁছানোর আগেই আপনার ডিভাইসে এনক্রিপ্ট করা হয়। সার্ভার এমন সব ডেটা বা 'blobs' জমা রাখে যা সে নিজে পড়তে পারে না। কেউ যদি পুরো ডাটাবেস কপিও করে নেয়, তবুও সেখান থেকে কোনো কিছু উদ্ধার করতে হলে তাকে master password জানতেই হবে।
এই উত্তরটি অনেকগুলো বিষয়ের ওপর নির্ভর করে, এবং যে অংশগুলো সাধারণত দুর্বল হয়ে পড়ে তা মূলত আপনার কনফিগারেশনের ওপর নির্ভর করে। যেমন: সহজে অনুমান করা যায় এমন টোকেনের পেছনে থাকা admin panel, ইন্টারনেটে সরাসরি উন্মুক্ত থাকা container port, plaintext config.json, অথবা একই সার্ভারের home directory-তে রাখা backup tarball। এগুলোর কোনোটিই ক্রিপ্টোগ্রাফির সমস্যা নয়। বরং এগুলোই সেই কারণ, যার ফলে self-hosted vault-এর তথ্য চুরি হয়।
নিচের সবকিছু একটি কার্যকর ইন্সটলেশনের ওপর ভিত্তি করে লেখা হয়েছে। আপনার যদি এখনো ইন্সটলেশন করা না থাকে, তবে প্রথমে VPS-এ Vaultwarden ইন্সটল করার নির্দেশিকা অনুসরণ করে তা সম্পন্ন করুন, তারপর এই তালিকা অনুযায়ী কাজ শুরু করুন।
সার্ভারে আসলে কী সংরক্ষিত থাকে
Vaultwarden, Bitwarden-এর ডেটা মডেল অনুসরণ করে। আপনার মাস্টার পাসওয়ার্ড থেকে তৈরি একটি কি (key) ব্যবহার করে ক্লায়েন্ট সাইডেই ভল্ট আইটেমের নাম, ইউজারনেম, পাসওয়ার্ড, নোট এবং URI এনক্রিপ্ট করা হয়; সার্ভারে পাঠানোর আগেই এই প্রক্রিয়া সম্পন্ন হয়। অ্যাটাচমেন্ট ফাইলের বিষয়বস্তুও একইভাবে এনক্রিপ্ট করা থাকে। সার্ভার শুধুমাত্র একটি UUID (universally unique identifier) সহ অস্পষ্ট (opaque) ডেটা গ্রহণ করে।
কিছু তথ্য সাইফারটেক্সট নয়, এবং আপনার ঠিক জানা উচিত সেগুলো কী কী:
- আপনার অ্যাকাউন্টের ইমেইল অ্যাড্রেস, যা প্লেইনটেক্সটে থাকে।
- আপনার KDF (key derivation function) সেটিংস এবং সল্ট (salt), কারণ পরবর্তী লগইনের সময় ক্লায়েন্টের কি (key) পুনর্নির্মাণের জন্য এগুলোর প্রয়োজন হয়।
- ক্লায়েন্ট যে মাস্টার পাসওয়ার্ড হ্যাশ পাঠায়, তার একটি সার্ভার-সাইড হ্যাশ, যা লগইন অথেন্টিকেশনের জন্য ব্যবহৃত হয়।
- মেটাডেটা: অর্গানাইজেশন মেম্বারশিপ, ডিভাইসের নাম, সর্বশেষ লগইনের সময়।
- Vaultwarden লগইন সুরক্ষার জন্য ব্যবহৃত টু-ফ্যাক্টর মেথডের সিক্রেট। এটি
twofactorটেবিলে এনক্রিপ্ট না করা অবস্থায় থাকে, কারণ আপনার দেওয়া কোডের সাথে তুলনা করার জন্য সার্ভারকে প্রত্যাশিত কোডটি গণনা করতে হয়। এটি ভল্ট আইটেমের ভেতরে সংরক্ষিত TOTP (time-based one-time password) সিক্রেটের মতো নয়, যা অন্য যেকোনো ফিল্ডের মতোই এনক্রিপ্ট করা থাকে।
ডেটা ফোল্ডারটি আকারে ছোট। Docker ইনস্টলেশনের ক্ষেত্রে এটি সেই ফোল্ডার যা আপনি /data-এ মাউন্ট করেছেন।
sudo ls -l /vw-data/db.sqlite3 প্রায় সমস্ত স্টেট ধরে রাখে। attachments/ আপলোড করা ফাইলগুলো ধারণ করে, প্রতি UUID-এর জন্য একটি করে ফাইল, এবং এটিই একমাত্র গুরুত্বপূর্ণ ডেটা যা ডেটাবেস টেবিলে থাকে না। sends/ সেন্ড (Send) অ্যাটাচমেন্টগুলো ধারণ করে এবং এটি সাময়িক ব্যবহারের জন্য। icon_cache/ ফেলে দেওয়ার মতো ডেটা। rsa_key.pem এবং এর সহযোগী ফাইলগুলো লগইন করা ব্যবহারকারীদের JWT (JSON web tokens) সাইন করে, তাই এই প্রাইভেট কি-এর একটি কপি ব্যবহার করে ভল্ট লগইন সেশন জাল (forge) করা সম্ভব। config.json শুধুমাত্র তখনই তৈরি হয় যখন আপনি অ্যাডমিন পেজ চালু করেন, এবং প্রজেক্টের নথিতে স্পষ্টভাবে বলা আছে: এটি অ্যাডমিন টোকেন এবং আপনার SMTP ক্রেডেনশিয়াল প্লেইনটেক্সটে সংরক্ষণ করে।
সুতরাং, বাস্তব হুমকির মডেলটি হলো ফাইলসিস্টেম অ্যাক্সেস, নেটওয়ার্ক ক্রিপ্টোগ্রাফি নয়। ওই ডিরেক্টরিতে রিড অ্যাক্সেস থাকলে যেকোনো ব্যবহারকারীর ইমেইল অ্যাড্রেস, তাদের লগইন 2FA সিক্রেট, সেশন জাল করার কি (key) এবং প্রতিটি ভল্টের একটি অফলাইন কপি পাওয়া সম্ভব, যা দিয়ে অবসরে আক্রমণ চালানো যায়। নিচের প্রতিটি পদক্ষেপ ওই ডিরেক্টরি থেকে অন্যদের দূরে রাখার জন্যই তৈরি করা হয়েছে।
প্রথমে অ্যাডমিন টোকেন ঠিক করুন
/admin একটি পূর্ণাঙ্গ কন্ট্রোল প্যানেল: এখানে ব্যবহারকারী তালিকা, আমন্ত্রণ, মুছে ফেলা এবং প্রতিটি রানটাইম সেটিংস নিয়ন্ত্রণ করা যায়। এটি শুধুমাত্র একটি শেয়ারড সিক্রেট (shared secret) দ্বারা সুরক্ষিত, অন্য কিছু নয়। এখানে কোনো ইউজারনেম নেই। প্রতি ব্যবহারকারীর জন্য আলাদা কোনো টু-ফ্যাক্টর অথেন্টিকেশনও নেই।
পুরানো গাইডগুলোতে openssl rand -base64 48 ব্যবহার করে ADMIN_TOKEN জেনারেট করতে বলা হয়। এটি কাজ করে এবং সিক্রেটটিকে প্লেইন টেক্সট হিসেবে config.json এবং আপনার compose ফাইলে লিখে রাখে। Vaultwarden একটি Argon2 PHC (password hashing competition) স্ট্রিংও গ্রহণ করে, তাই সংরক্ষিত মানটি একটি হ্যাশ হিসেবে থাকে। একটি চলমান কন্টেইনারের বিপরীতে এটি জেনারেট করুন:
docker exec -it vaultwarden /vaultwarden hashঅথবা চলমান কন্টেইনার স্পর্শ না করেই এটি করতে পারেন:
docker run --rm -it vaultwarden/server /vaultwarden hashএটি দুইবার পাসওয়ার্ড চাইবে এবং তারপর $argon2id$ দিয়ে শুরু হওয়া একটি লাইন প্রিন্ট করবে। বেয়ার-মেটাল ইন্সটলেশনের ক্ষেত্রে, ./vaultwarden hash চালান। আপনি যদি সরাসরি argon2 CLI ব্যবহার করতে চান, তবে প্রজেক্টের ডকুমেন্টেশনে OWASP-এর ন্যূনতম প্যারামিটারগুলো দেওয়া আছে:
echo -n 'MySecretPassword' | argon2 "$(openssl rand -base64 32)" -e -id -k 19456 -t 2 -p 1এখন সেই ফাঁদটি দেখুন যা অনেকের এক ঘণ্টা সময় নষ্ট করে। একটি PHC স্ট্রিংয়ে প্রচুর $ ক্যারেক্টার থাকে এবং Docker Compose $-কে ভেরিয়েবল ইন্টারপোলেশন হিসেবে গণ্য করে। এটিকে এস্কেপ না করে environment: ব্লকে পেস্ট করলে কন্টেইনারে পৌঁছানো মানটি বিকৃত হয়ে যায়, ফলে /admin আপনার সঠিক টোকেনটিকেও প্রত্যাখ্যান করে। দুটি নিরাপদ উপায় আছে। docker-compose.yml-এ, প্রতিটি $-কে ডাবল করে দিন:
environment:
ADMIN_TOKEN: $$argon2id$$v=19$$m=19456,t=2,p=1$$UUZxK1FZMkZoRHFQRlVrTXZvS0E3bHpNQW55c2dBN2NORzdsa0Nxd1JhND0$$cUoId+JBUsJutlG4rfDZayExfjq4TCt48aBc9qsc3UIএকটি .env ফাইলে, কোনো এস্কেপিংয়ের প্রয়োজন নেই, তবে সিঙ্গেল কোট ব্যবহার করুন:
ADMIN_TOKEN='$argon2id$v=19$m=65540,t=3,p=4$MmeKRnGK5RW5mJS7h3TOL89GrpLPXJPAtTK8FTqj9HM$DqsstvoSAETl9YhnsXbf43WeaUwJC6JhViIvuPoig78'এরপর প্যানেলটির জন্য রেট লিমিট সেট করুন এবং এর সেশন সংক্ষিপ্ত করুন:
ADMIN_RATELIMIT_SECONDS=300
ADMIN_RATELIMIT_MAX_BURST=3
ADMIN_SESSION_LIFETIME=20পাঁচ মিনিটের মধ্যে তিনবার ভুল প্রচেষ্টার পর প্যানেলটি সেই ক্লায়েন্টের অনুরোধ গ্রহণ করা বন্ধ করে দেয়। কোনো কার্যকলাপ না থাকলে অ্যাডমিন সেশন 20 মিনিট পর শেষ হয়ে যায়।
এর চেয়েও ভালো উপায় হলো: পেজটি বন্ধ করে দেওয়া। অধিকাংশ ইন্সট্যান্সে এটি একবারই প্রয়োজন হয়—SMTP কনফিগার করতে এবং প্রথম ব্যবহারকারীদের আমন্ত্রণ জানাতে—এরপর আর প্রয়োজন হয় না। এটি নিষ্ক্রিয় করতে, ADMIN_TOKEN বা DISABLE_ADMIN_TOKEN কোনোটিই সেট করবেন না, config.json থেকে যেকোনো "admin_token" কি (key) সরিয়ে ফেলুন এবং তারপর কন্টেইনারটি পুনরায় তৈরি করুন। ফাইল থেকে কি-টি মুছে ফেলা জরুরি কারণ অ্যাডমিন পেজ সেখানে সেটিংস লেখে এবং config.json-এ যা থাকে তা এনভায়রনমেন্টের চেয়ে বেশি প্রাধান্য পায়। শুধুমাত্র ভেরিয়েবলটি সরিয়ে ফেললে পেজটি খোলা থেকে যাবে।
ডোমেইনটি খুঁজে পাওয়ার আগেই রেজিস্ট্রেশন বন্ধ করুন
SIGNUPS_ALLOWED=false
SIGNUPS_VERIFY=true
SHOW_PASSWORD_HINT=falseSIGNUPS_ALLOWED ডিফল্টভাবে true থাকে। এটি এভাবেই রেখে দিলে যে কেউ আপনার ডোমেইনে এসে অ্যাকাউন্ট খুলতে পারবে এবং তাদের ডেটা আপনার ডেটার সাথেই একই db.sqlite3-এ জমা হবে। এটিকে false-এ সেট করুন এবং অ্যাডমিন পেজ থেকে ইনভাইটেশন পাঠানোর মাধ্যমে নতুন ব্যবহারকারী যোগ করুন, যার জন্য একটি কার্যকর SMTP প্রয়োজন। INVITATIONS_ALLOWED ডিফল্টভাবে true থাকে এবং এটি অর্গানাইজেশন মালিকদের অন্যদের ইনভাইট করার সুযোগ দেয়। আপনি যদি ব্যবহারকারীদের বিশ্বাস করেন তবে এটি ঠিক আছে, কিন্তু সিঙ্গেল-ইউজার ইনস্ট্যান্সের ক্ষেত্রে এটি false থাকা উচিত। যদি শুধুমাত্র নির্দিষ্ট কিছু ডোমেইনের ব্যবহারকারীরাই রেজিস্ট্রেশন করতে পারে এমনটি চান, তবে SIGNUPS_DOMAINS_WHITELIST=example.com ব্যবহার করতে পারেন; এটি ওপেন সাইনআপের চেয়ে সীমাবদ্ধ হলেও ইনভাইটেশন সিস্টেমের চেয়ে অনেক কম নিরাপদ।
SHOW_PASSWORD_HINT ডিফল্টভাবে false থাকে এবং এটি এভাবেই রাখা উচিত। এটি চালু থাকলে, লগইন ফর্মে কোনো বৈধ ইমেইল অ্যাড্রেস টাইপ করলে সেই অ্যাকাউন্টের মাস্টার পাসওয়ার্ড হিন্ট দেখা যায়, যা হিন্টটি ফাঁস করে এবং ইমেইল অ্যাড্রেসটি যে সিস্টেমে বিদ্যমান তা নিশ্চিত করে।
আপনার ইনস্ট্যান্স যদি কোনো সময়ের জন্য ওপেন সাইনআপ মোডে থাকে, তবে অ্যাডমিন পেজে যান এবং আপনিই একমাত্র ব্যবহারকারী কি না তা নিশ্চিত করতে ইউজার লিস্টটি যাচাই করুন।
যে পোর্টটি আপনি উন্মুক্ত করতে চাননি
Docker ইমেজটি কন্টেইনারের ভেতরে 80 নম্বর পোর্টে লিসেন করে। বেয়ার-মেটাল ইনস্টলেশনে ডিফল্ট পোর্ট হলো ROCKET_PORT=8000। নথিপত্রে উল্লেখিত রান কমান্ডটি এটিকে এভাবে পাবলিশ করে:
--publish 127.0.0.1:8000:80127.0.0.1: প্রিফিক্সটিই এখানে মূল বিষয়। এর পরিবর্তে -p 8000:80 লিখলে Docker এটিকে 0.0.0.0-এ বাইন্ড করে, এবং এটি nat টেবিলে DNAT (destination network address translation) রুল লেখার মাধ্যমে সম্পন্ন হয়। এই রুলগুলো ufw দ্বারা পরিচালিত filter চেইনের আগেই মূল্যায়ন করা হয়, তাই ufw status পোর্টটিকে ডিনাইড (denied) হিসেবে দেখালেও পোর্টটি ইন্টারনেটে সক্রিয় থাকে। এই পুরো প্রক্রিয়াটি সম্পর্কে বিস্তারিত জানতে Docker পোর্ট বাইপাসিং ufw সংক্রান্ত নির্দেশিকা পড়ুন।
বর্তমানে কী লিসেন করছে তা পরীক্ষা করুন:
sudo ss -tlnp | grep 8000একটি সঠিক ফলাফলে শুধুমাত্র 127.0.0.1:8000-এ বাইন্ড করা একটি লাইন থাকবে। যদি কোনো লাইন 0.0.0.0:8000-এ বাইন্ড করা থাকে, তার মানে ভল্টটি সরাসরি উন্মুক্ত রয়েছে। ম্যাপিংটি ঠিক করুন এবং তারপর কন্টেইনারটি পুনরায় তৈরি করুন, কারণ কন্টেইনার তৈরির সময় পোর্ট বাইন্ডিং নির্ধারিত হয় এবং docker compose restart এটি পরিবর্তন করবে না:
docker compose up -d --force-recreateপুরানো নির্দেশিকাগুলোতে আরও একটি পোর্ট দেখা যায়: 3012, যা আলাদা একটি WebSocket পোর্ট। Vaultwarden 1.31.0 ভার্সন থেকে এর সাপোর্ট সরিয়ে নেওয়া হয়েছে, কারণ নোটিফিকেশন ট্রাফিক এখন মূল HTTP পোর্টে চলে এসেছে। 1.29.0 ভার্সন থেকে WEBSOCKET_ENABLED এবং WEBSOCKET_PORT উপেক্ষা করা হচ্ছে। বর্তমান সুইচটি হলো ENABLE_WEBSOCKET, যা ডিফল্টভাবে true থাকে। যদি আপনার ফায়ারওয়াল বা compose ফাইলে এখনও 3012 পোর্ট খোলা থাকে, তবে তা বন্ধ করে দিন।
Rocket-এ নয়, বরং একটি reverse proxy-তে TLS terminate করুন
Vaultwarden তার web framework Rocket-এর মাধ্যমে নিজেই TLS (transport layer security) পরিচালনা করতে পারে, তবে production পরিবেশে এটি না করার পরামর্শ দেওয়া হয়। Rocket-এর বিল্ট-ইন TLS-এ কঠোর SNI (server name indication) সমর্থনের অভাব রয়েছে। এই কারণেই নিরাপত্তা নির্দেশনায় বলা হয় যে, আপনার instance-এ সবসময় hostname ব্যবহার করে প্রবেশ করুন, সরাসরি IP address ব্যবহার করবেন না। পাবলিক IP রেঞ্জগুলো সবসময় স্ক্যান করা হয়, আর যে vault সরাসরি IP address-এ সাড়া দেয়, তা সহজেই খুঁজে পাওয়া যায়।
nginx server block-এর যে অংশগুলো গুরুত্বপূর্ণ:
client_max_body_size 525M;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}nginx-এর ডিফল্ট client_max_body_size হলো 1 MB, তাই এই লাইনটি না থাকলে attachment আপলোড ব্যর্থ হয় এবং nginx error log-এ 413 Request Entity Too Large দেখা যায়, অথচ Vaultwarden-এর লগে কিছুই পাওয়া যায় না। Upgrade এবং Connection হেডারগুলো WebSocket হ্যান্ডশেককে /notifications/hub-এ নিয়ে যায়। এগুলো বাদ দিলে vault কাজ করবে ঠিকই, কিন্তু আপনি পৃষ্ঠাটি নিজে reload না করা পর্যন্ত অন্য ডিভাইসে পরিবর্তনগুলো দেখা যাবে না।
Caddy ব্যবহার করলে কনফিগারেশন ছোট হয় এবং এটি নিজেই certificate সংগ্রহ করে নেয়:
vw.example.com {
reverse_proxy 127.0.0.1:8000 {
header_up X-Real-IP {remote_host}
}
}এরপর Vaultwarden-কে এই বিষয়ে জানান:
DOMAIN=https://vw.example.com
IP_HEADER=X-Real-IPIP_HEADER ডিফল্টভাবেই X-Real-IP থাকে, তাই নিশ্চিত করুন যে proxy সঠিকভাবে এই হেডারটি সেট করছে। যদি তা না হয়, তবে প্রতিটি লগ লাইন এবং login rate limit-এ 127.0.0.1 (অর্থাৎ proxy নিজেই) দেখা যাবে। এর মানে হলো, একজন আক্রমণকারীর ব্যর্থ প্রচেষ্টা আপনার instance-এর সকল ব্যবহারকারীর ওপর প্রভাব ফেলবে। DOMAIN-এ সঠিক https URL সেট করুন, কারণ Vaultwarden এটি ব্যবহার করেই invitation এবং password reset লিঙ্ক তৈরি করে, এবং WebAuthn security key-গুলো এই origin-এর সাথেই যুক্ত থাকে।
একটি বিষয় যা অনেকেই এড়িয়ে যান: WebSocket সংযোগটি session token-কে query string হিসেবে /notifications/hub?access_token=[JWT]-এর মাধ্যমে পাঠায়। এটি আপনার proxy access log-এ plain text হিসেবে জমা হয়। তাই log format থেকে access_token প্যারামিটারটি সরিয়ে ফেলুন (redact করুন), অথবা নিশ্চিত করুন যে এই লগগুলো আপনার নিয়ন্ত্রণের বাইরের কোনো স্থানে পাঠানো হচ্ছে না।
লগইন এন্ডপয়েন্টে ব্রুট ফোর্স আক্রমণ প্রতিরোধ
রেট লিমিট ডিফল্টভাবেই চালু থাকে (LOGIN_RATELIMIT_SECONDS=60, LOGIN_RATELIMIT_MAX_BURST=10)। এগুলো আক্রমণকারীর গতি কমিয়ে দেয়, কিন্তু পুরোপুরি থামাতে পারে না। fail2ban এটি করতে পারে, তবে এর জন্য Vaultwarden-কে একটি লগ ফাইল লিখতে হয়, যা ডিফল্টভাবে সক্রিয় থাকে না:
LOG_FILE=/data/vaultwarden.log
LOG_LEVEL=info
EXTENDED_LOGGING=trueএকটি ব্যর্থ লগইন ঠিক একটি লাইন তৈরি করে এবং আপনার ফিল্টারকে এই স্ট্রিংটির সাথে মিল খুঁজে বের করতে হবে:
[2026-08-07 09:14:22][vaultwarden::api::identity][ERROR] Username or password is incorrect. Try again. IP: 203.0.113.10. Username: user@example.com.ফিল্টারটি /etc/fail2ban/filter.d/vaultwarden.local-এ লিখুন:
[INCLUDES]
before = common.conf
[Definition]
failregex = ^.*?Username or password is incorrect\. Try again\. IP: <ADDR>\. Username:.*$
ignoreregex =এবং জেলটি /etc/fail2ban/jail.d/vaultwarden.local-এ লিখুন:
[vaultwarden]
enabled = true
port = 80,443
filter = vaultwarden
banaction = %(banaction_allports)s
logpath = /vw-data/vaultwarden.log
maxretry = 3
bantime = 14400
findtime = 14400যদি আপনি অ্যাডমিন পেজটি রেখে দিয়ে থাকেন, তবে দ্বিতীয় একটি জেল যোগ করুন যার failregex হবে ^.*Invalid admin token\. IP: <ADDR>.*$, কারণ অ্যাডমিন ব্যর্থতার লগ ভিন্ন মেসেজে রেকর্ড হয় এবং লগইন ফিল্টার সেগুলো দেখতে পায় না। এরপর আপনার কাজ যাচাই করুন:
sudo systemctl restart fail2ban
sudo fail2ban-client status vaultwardenএকটি কার্যকর জেল File list-এর অধীনে আপনার লগ ফাইলটি তালিকাভুক্ত করে এবং Currently failed: 0 রিপোর্ট করে। ভিন্ন কোনো নেটওয়ার্ক থেকে তিনবার ভুল পাসওয়ার্ড দিন, দেখবেন কাউন্টার বাড়ছে এবং এরপর ঠিকানাটি Banned IP list-এর অধীনে প্রদর্শিত হচ্ছে। যদি কাউন্টার না বাড়ে, তবে সাধারণ কারণ হলো logpath: এটি অবশ্যই হোস্টের ফাইলের পাথ হতে হবে, কন্টেইনারের ভেতরের /data/... পাথ নয়। দ্বিতীয় সাধারণ কারণ হলো X-Real-IP অনুপস্থিত থাকা, যার ফলে প্রতিটি ব্যান আপনার নিজস্ব প্রক্সিকে লক্ষ্যবস্তু করে। SSH জেলসহ বাকি সেটআপ, যা আপনার ইতিমধ্যে চালানো উচিত, তা Ubuntu 24.04-এর জন্য fail2ban গাইড-এ রয়েছে।
মাস্টার পাসওয়ার্ডই পুরো সিস্টেমের মূল চাবিকাঠি
Client-side encryption-এর অর্থ হলো মাস্টার পাসওয়ার্ডই হলো আসল কি (key)। কোনো ইনস্ট্যান্সের ডাটাবেস যদি আক্রমণকারী কপি করে নেয়, তবে সেখানে ছোট মাস্টার পাসওয়ার্ড কোনো সুরক্ষা দেয় না। কারণ আক্রমণকারী তার নিজস্ব হার্ডওয়্যারের গতি অনুযায়ী অফলাইনে সেই কপির ওপর আক্রমণ চালাতে পারে। সার্ভারের কোনো সেটিংস আক্রমণকারীর নিজস্ব মেশিনে কার্যকর হয় না।
PASSWORD_ITERATIONS=600000 হলো KDF iteration count, যা নতুন অ্যাকাউন্ট তৈরির সময় ক্লায়েন্টদের দেওয়া হয়। বিদ্যমান অ্যাকাউন্টগুলো তাদের তৈরির সময়কার মানই বজায় রাখে, তাই এটি বাড়িয়ে দিলে গত বছর অ্যাকাউন্ট খোলা ব্যবহারকারীদের কোনো পরিবর্তন হবে না। তাদের নিজেদেরই web vault-এর security settings-এ গিয়ে এটি পরিবর্তন করতে হবে, যা তাদের কি (key) পুনরায় এনক্রিপ্ট করবে। ব্যবহারকারীদের এই বিষয়টি জানান, কারণ ইন্টারফেসে স্বয়ংক্রিয়ভাবে এমন কোনো বার্তা দেওয়া হয় না।
এরপর প্রতিটি অ্যাকাউন্টের জন্য two-factor authentication চালু করুন। এটি সাইফারটেক্সটকে সুরক্ষা দেয় না, কারণ ভল্ট কি (vault key) শুধুমাত্র মাস্টার পাসওয়ার্ড থেকেই তৈরি হয়। তবে এটি চুরি হওয়া পাসওয়ার্ড দিয়ে লগইন করা এবং ডাটাবেসের কপি সিঙ্ক করা প্রতিরোধ করে। REQUIRE_DEVICE_EMAIL=true একটি ইমেইল কনফার্মেশন ধাপ যোগ করে, যা কোনো অপরিচিত ডিভাইস থেকে প্রথমবার লগইন করার সময় কার্যকর হয়।
ব্যাকআপের ক্ষেত্রে self-hosted vault-এর ভুলগুলো
একই VPS-এর home directory-তে রাখা data folder-এর একটি tar czf উপরের সব নিরাপত্তা ব্যবস্থাকে অকার্যকর করে দেয়। সেই archive-এ থাকে প্রতিটি ব্যবহারকারীর ciphertext সম্বলিত db.sqlite3, login session তৈরি করার ক্ষমতা সম্পন্ন rsa_key.pem, এবং admin token ও SMTP password সম্বলিত plaintext config.json। ওই একটি ফাইলে read access থাকা মানেই পুরো vault-এর তথ্যে access থাকা।
এই সমস্যার সমাধানে দুটি নিয়ম মেনে চলতে হবে। archive-টিকে সার্ভার থেকে সরিয়ে ফেলুন। সার্ভার থেকে বের করার আগেই সেটিকে encrypt করুন।
এখানে তথ্যের সঠিকতা নিয়েও একটি সমস্যা আছে। service চলাকালীন cp ব্যবহার করে db.sqlite3 কপি করলে এমন একটি ফাইল তৈরি হতে পারে যা লেখার মাঝামাঝি অবস্থায় আছে এবং সেটি খুলবে না; আর আপনি তা জানতে পারবেন কেবল restore করার সময়। এর পরিবর্তে SQLite-এর নিজস্ব snapshot ব্যবহার করুন:
sqlite3 /vw-data/db.sqlite3 ".backup '/tmp/vw-db-backup.sqlite3'"restore করার প্রক্রিয়াটি, যা সাধারণত কেউ পরীক্ষা করে না, তা Vaultwarden backup and restore guide-এ বিস্তারিত আলোচনা করা হয়েছে।
Hosted Bitwarden-এর বিপরীতে আপনি যা ত্যাগ করছেন
সততার সাথে হিসাব করলে, Bitwarden-এর hosted service এমন ব্যক্তিদের দ্বারা পরিচালিত হয় যাদের পূর্ণকালীন কাজই হলো এটি পরিচালনা করা। তাদের তৃতীয় পক্ষের অডিট রিপোর্ট প্রকাশিত থাকে এবং ভোর 3টার সময়ও কোনো সমস্যা হলে তা দেখার জন্য কেউ না কেউ দায়িত্বরত থাকেন। নিজে হোস্ট (self-hosting) করলে আপনাকে সেই দায়িত্ব নিজের কাঁধে নিতে হবে এবং প্যাচ আপডেট করার সময়সূচী নিজেকেই ঠিক করতে হবে।
Vaultwarden সাধারণ releases হিসেবে নিরাপত্তা সংক্রান্ত ফিক্সগুলো প্রদান করে। 24 জুলাই 2026-এ রিলিজ হওয়া Version 1.37.0 আগস্ট 2026 পর্যন্ত বর্তমান ভার্সন হিসেবে গণ্য, এবং এর নোটে ব্যবহারকারীদের দ্রুত আপডেট করার পরামর্শ দেওয়া হয়েছে। আপনি এক বছর আগে যে instance সেটআপ করেছিলেন এবং ভুলে গেছেন, সেটি এক বছর পুরনো কোডেই চলছে। latest ট্যাগটি নিজে থেকে কোনো সাহায্য করে না: একটি চলমান container ততক্ষণ পর্যন্ত পুরনো image-ই ব্যবহার করবে যতক্ষণ না আপনি docker compose pull কমান্ড চালিয়ে সেটিকে পুনরায় তৈরি (recreate) করছেন। হোস্ট প্যাকেজগুলোর জন্য Ubuntu-তে unattended upgrades চালু রাখুন এবং container আপডেট করার জন্য এমন একটি ক্যালেন্ডার রিমাইন্ডার সেট করুন যা আপনি নিয়মিত দেখবেন।
একজন সৎ পাঠকের যে সিদ্ধান্তে আসা উচিত: এখানে ব্যবহৃত ক্রিপ্টোগ্রাফি Bitwarden-এর নিজস্ব ডিজাইনের এবং এটি অত্যন্ত শক্তিশালী, কিন্তু পরিচালনার ঝুঁকি সম্পূর্ণ আপনার ওপর বর্তায়। আপনি যদি নিয়মিত প্যাচ আপডেট করেন এবং অন্য কোথাও ব্যাকআপ রাখেন, তবে আপনার নিয়ন্ত্রণে থাকা VPS-এ Vaultwarden instance রাখা পাসওয়ার্ড সংরক্ষণের জন্য একটি যুক্তিসঙ্গত উপায়। যদি এই দুটি অভ্যাস বজায় রাখা সম্ভব না হয়, তবে hosted service-এর জন্য অর্থ প্রদান করুন এবং আপনার মনোযোগ অন্য কাজে ব্যয় করুন। ফিচার অনুযায়ী তুলনামূলক আলোচনা এখানে দেওয়া হলো: Vaultwarden বনাম self-hosted Bitwarden।
কন্টেইনারের নিচের হোস্টটিকে সুরক্ষিত করা
Vaultwarden লিনাক্স বক্সে একটি প্রসেস হিসেবে চলে। অ্যাপ্লিকেশনটি যেভাবে কনফিগার করা হোক না কেন, ওই বক্সের root ইউজার /vw-data ফাইলটি পড়তে পারে। আপনার compose ফাইলে user: "1000:1000" ব্যবহার করে কন্টেইনারটিকে একজন unprivileged ইউজার হিসেবে চালান। ডেটা ফোল্ডারের মালিকানা সেই ইউজারের সাথে মিলিয়ে দিন এবং যে ফাইলগুলোতে কন্টেইনারের লেখার প্রয়োজন নেই, সেগুলোকে :ro ব্যবহার করে read-only হিসেবে মাউন্ট করুন। এরপর মূল প্রবেশপথটি বন্ধ করুন: SSH hardening on a VPS নিবন্ধে key-only লগইন এবং পাসওয়ার্ড অথেন্টিকেশন নিষ্ক্রিয় করার পদ্ধতি আলোচনা করা হয়েছে, যা সাধারণ আক্রমণগুলো ঠেকানোর জন্য অপরিহার্য।
FAQ
Vaultwarden ডাটাবেস চুরি হলে কেউ কি আমার পাসওয়ার্ড পড়তে পারবে?
সরাসরি পারবে না। প্রতিটি ভল্ট আইটেম ক্লায়েন্ট সাইডে মাস্টার পাসওয়ার্ড থেকে তৈরি একটি কি (key) দিয়ে এনক্রিপ্ট করা থাকে, তাই db.sqlite3-এ শুধুমাত্র সাইফারটেক্সট থাকে। তারা তাৎক্ষণিকভাবে যা পাবে তা হলো প্রতিটি অ্যাকাউন্টের ইমেইল অ্যাড্রেস, KDF সেটিংস, লগইন ও ডিভাইসের মেটাডেটা এবং twofactor টেবিলের টু-ফ্যাক্টর সিক্রেট। এগুলো এনক্রিপ্ট করা থাকে না কারণ সার্ভারকে প্রত্যাশিত কোডটি গণনা করতে হয়। তারা ভল্টের সাইফারটেক্সট নিয়ে অফলাইনে যত খুশি সময় ধরে আক্রমণ চালাতে পারে, আর এই কারণেই মাস্টার পাসওয়ার্ডের দৈর্ঘ্যই নির্ধারণ করে আপনার নিরাপত্তা কতটা শক্তিশালী হবে।
আমার কি ADMIN_TOKEN ব্যবহার করা উচিত নাকি অ্যাডমিন পেজটি পুরোপুরি বন্ধ করে দেওয়া উচিত?
সম্ভব হলে এটি বন্ধ করে দিন, কারণ বেশিরভাগ ইনস্ট্যান্সে SMTP কনফিগার করা এবং ইউজারদের ইনভাইট করার জন্য এটি একবারই প্রয়োজন হয়। এটি বন্ধ করতে ADMIN_TOKEN বা DISABLE_ADMIN_TOKEN কোনোটিই সেট করবেন না, config.json থেকে যেকোনো "admin_token" কি (key) মুছে ফেলুন এবং তারপর কন্টেইনারটি পুনরায় তৈরি করুন। শুধুমাত্র এনভায়রনমেন্ট ভেরিয়েবল মুছে ফেলা যথেষ্ট নয়, কারণ অ্যাডমিন পেজের মাধ্যমে লেখা সেটিংসগুলো config.json-এ থাকে এবং সেগুলোর অগ্রাধিকার বেশি। যদি পেজটি রাখতেই হয়, তবে টোকেনটিকে প্লেইনটেক্সট হিসেবে না রেখে vaultwarden hash দ্বারা তৈরি Argon2 হ্যাশ হিসেবে সংরক্ষণ করুন এবং ADMIN_RATELIMIT_MAX_BURST=3 সেট করুন।
আমার ADMIN_TOKEN সঠিক কিন্তু /admin পেজ তা গ্রহণ করছে না। সমস্যা কী?
এটি প্রায় সবসময়ই $ ইন্টারপোলেশনের সমস্যা। একটি Argon2 PHC স্ট্রিং-এ বেশ কয়েকটি $ ক্যারেক্টার থাকে এবং Docker Compose সেগুলোকে docker-compose.yml environment: ব্লকের ভেতরে ভেরিয়েবল হিসেবে প্রসারিত (expand) করে। ফলে আপনার ফাইল দেখতে সঠিক মনে হলেও কন্টেইনারটি একটি বিকৃত মান গ্রহণ করে। কম্পোজ ফাইলে প্রতিটি $-কে ডাবল করে $$ করুন, অথবা মানটিকে একটি .env ফাইলে সিঙ্গেল কোটেশনের ভেতরে রাখুন, যেখানে কোনো এস্কেপিংয়ের প্রয়োজন হয় না। এরপর কন্টেইনারটি পুনরায় তৈরি করুন, কারণ শুধুমাত্র রিস্টার্ট করলে এনভায়রনমেন্টের পরিবর্তন কার্যকর হয় না।
নোটিফিকেশনের জন্য আমার কি এখনও 3012 পোর্ট খোলা রাখতে হবে?
না। Vaultwarden 1.31.0 ভার্সনে 3012 পোর্টে WebSocket ট্রাফিকের সাপোর্ট সরিয়ে ফেলা হয়েছে, কারণ নোটিফিকেশনগুলো এখন মূল HTTP পোর্টে চলে এসেছে। এছাড়া 1.29.0 ভার্সন থেকে WEBSOCKET_ENABLED এবং WEBSOCKET_PORT উপেক্ষা করা হচ্ছে। বর্তমান সেটিং হলো ENABLE_WEBSOCKET, যা ডিফল্টভাবে true থাকে। ফায়ারওয়ালে 3012 পোর্টটি বন্ধ করুন এবং আপনার কম্পোজ ফাইল থেকে এটি মুছে ফেলুন। এরপর নিশ্চিত করুন যে আপনার রিভার্স প্রক্সি Upgrade এবং Connection হেডারগুলো ফরোয়ার্ড করছে, কারণ রিয়েল-টাইম সিঙ্ক এখন মূলত এগুলোর ওপরই নির্ভর করে।