SSD Nodes Learn Hosting plans →
নির্দেশিকা Matt Connorদ্বারা Matt Connor · আপডেট করা হয়েছে 2026-11-03

Vaultwarden কি আসলেই নিরাপদ? নিরাপত্তা নিশ্চিত করার উপায়

Vaultwarden ক্লায়েন্ট সাইডে ডেটা এনক্রিপ্ট করলেও সার্ভারে মেটাডেটা প্লেইনটেক্সটে থাকে। আপনার অ্যাডমিন টোকেন, ব্যাকআপ ফাইল এবং ভুল কনফিগারেশন কীভাবে ঝুঁকি বাড়ায় তা জানুন।

Vaultwarden কি নিরাপদ? সংক্ষিপ্ত উত্তর

Vaultwarden সবচেয়ে গুরুত্বপূর্ণ দিক থেকে নিরাপদ, কারণ প্রতিটি vault আইটেম সার্ভারে পৌঁছানোর আগেই আপনার ডিভাইসে এনক্রিপ্ট করা হয়। সার্ভার এনক্রিপ্ট করা vault কন্টেন্টের পাশাপাশি প্লেইনটেক্সট অ্যাকাউন্ট মেটাডেটা সংরক্ষণ করে। কেউ যদি পুরো ডাটাবেস কপিও করে, তবুও এর কন্টেন্ট পড়ার জন্য vault-এর এনক্রিপশন কি (encryption keys) প্রয়োজন হবে; তবে তারা তাৎক্ষণিকভাবে অ্যাকাউন্টের ইমেইল ঠিকানা এবং পাসওয়ার্ড হিন্ট পেয়ে যাবে।

এই উত্তরটি অনেকগুলো বিষয়ের ওপর নির্ভর করে, এবং যে অংশগুলো দুর্বল হতে পারে তা মূলত আপনার কনফিগারেশনের ওপর নির্ভর করে। যেমন: অনুমানযোগ্য টোকেনের পেছনে থাকা অ্যাডমিন প্যানেল, পুরো ইন্টারনেটে উন্মুক্ত কন্টেইনার পোর্ট, প্লেইনটেক্সট config.json, অথবা একই মেশিনের হোম ডিরেক্টরিতে রাখা ব্যাকআপ টারবল (tarball)। এগুলোর কোনোটিই ক্রিপ্টোগ্রাফি সংক্রান্ত সমস্যা নয়। বরং এগুলোই সেই কারণ, যার ফলে সেলফ-হোস্টেড vault হ্যাক হয়।

নিচের সবকিছু একটি কার্যকর ইন্সটলেশনের ওপর ভিত্তি করে লেখা। আপনার যদি এখনো ইন্সটলেশন করা না থাকে, তবে প্রথমে VPS-এর জন্য Vaultwarden ইন্সটলেশন গাইড অনুসরণ করে সেটআপ করুন, তারপর এই তালিকা অনুযায়ী কাজ শুরু করুন।

সার্ভারে আসলে কী সংরক্ষিত থাকে

Vaultwarden, Bitwarden-এর ডেটা মডেল অনুসরণ করে। কোনো রিকোয়েস্ট পাঠানোর আগেই ক্লায়েন্ট সাইডে একটি র‍্যান্ডমলি জেনারেট করা আইটেম কি (item key) ব্যবহার করে ভল্ট আইটেমের নাম, ইউজারনেম, পাসওয়ার্ড, নোট এবং URI এনক্রিপ্ট করা হয়। সেই কি-টি আবার ব্যবহারকারী বা প্রতিষ্ঠানের সিমেট্রিক কি (symmetric key) দ্বারা সুরক্ষিত থাকে; আর পাসওয়ার্ড থেকে তৈরি কি (password-derived key) ব্যবহারকারীর সিমেট্রিক কি-কে সুরক্ষা দেয়। অ্যাটাচমেন্ট ফাইলের কন্টেন্ট একটি আলাদা অ্যাটাচমেন্ট কি ব্যবহার করে, যা আবার আইটেম কি দ্বারা সুরক্ষিত। সার্ভার একটি UUID (universally unique identifier) যুক্ত অস্বচ্ছ (opaque) ডেটা গ্রহণ করে।

কিছু তথ্য সাইফারটেক্সট (ciphertext) নয়, এবং আপনার ঠিকঠাক জানা উচিত সেগুলো কী কী:

  • আপনার অ্যাকাউন্টের ইমেইল অ্যাড্রেস এবং মাস্টার পাসওয়ার্ড হিন্ট, যা প্লেইনটেক্সটে থাকে।
  • আপনার KDF (key derivation function) সেটিংস এবং সল্ট (salt), কারণ পরবর্তী লগইনের সময় কি (key) পুনর্গঠনের জন্য ক্লায়েন্টের এগুলো প্রয়োজন হয়।
  • ক্লায়েন্ট যে মাস্টার পাসওয়ার্ড হ্যাশ পাঠায়, তার একটি সার্ভার-সাইড হ্যাশ, যা লগইন অথেন্টিকেশনের কাজে ব্যবহৃত হয়।
  • মেটাডেটা: প্রতিষ্ঠানের সদস্যপদ, ডিভাইসের নাম, সর্বশেষ লগইনের সময়।
  • অথেন্টিকেটর-ভিত্তিক টু-ফ্যাক্টর লগইনের জন্য TOTP সিক্রেট। এটি 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) সাইন করে, তাই এই প্রাইভেট কি-এর একটি কপি ব্যবহার করে ভল্ট লগইন সেশন জালিয়াতি করা সম্ভব। config.json-এ অ্যাডমিন পেজের মাধ্যমে সেভ করা সেটিংস থাকে, যার মধ্যে প্লেইনটেক্সটে থাকা SMTP ক্রেডেনশিয়াল এবং কনফিগার করা অ্যাডমিন টোকেনের মান অন্তর্ভুক্ত। নিচের Argon2 নির্দেশনা অনুসরণ করলে প্লেইনটেক্সট পাসওয়ার্ডের পরিবর্তে অ্যাডমিন টোকেনের হ্যাশ সংরক্ষিত হয়।

সুতরাং, বাস্তব হুমকির মডেলটি হলো ফাইলসিস্টেম অ্যাক্সেস, নেটওয়ার্ক ক্রিপ্টোগ্রাফি নয়। ওই নির্দিষ্ট ডিরেক্টরিতে রিড অ্যাক্সেস পাওয়ার অর্থ হলো প্রতিটি ব্যবহারকারীর ইমেইল অ্যাড্রেস, তাদের লগইন 2FA সিক্রেট, সেশন জালিয়াতির কি এবং প্রতিটি ভল্টের একটি অফলাইন কপি পেয়ে যাওয়া, যা দিয়ে অবসরে আক্রমণ চালানো সম্ভব। নিচের প্রতিটি ধাপ ওই ডিরেক্টরি থেকে অন্যদের দূরে রাখার জন্য তৈরি করা হয়েছে।

প্রথমে অ্যাডমিন টোকেন ঠিক করুন

/admin একটি পূর্ণাঙ্গ কন্ট্রোল প্যানেল: এখানে ব্যবহারকারীর তালিকা, আমন্ত্রণ, মুছে ফেলার সুবিধা এবং প্রতিটি রানটাইম সেটিং থাকে। এটি শুধুমাত্র একটি শেয়ারড সিক্রেট (shared secret) দ্বারা সুরক্ষিত, অন্য কিছু নয়। এখানে কোনো ইউজারনেম নেই। প্রতি ব্যবহারকারীর জন্য আলাদা কোনো টু-ফ্যাক্টর অথেন্টিকেশনও নেই।

পুরানো গাইডগুলোতে ADMIN_TOKEN তৈরি করার জন্য openssl rand -base64 48 ব্যবহার করতে বলা হয়। এটি কাজ করলেও, আপনি যেখানেই কনফিগার করুন না কেন, সিক্রেটটি সেখানে প্লেইনটেক্সট হিসেবে থেকে যায়। 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

এটি প্রতি ক্লায়েন্ট আইপি-র জন্য তিনটি অ্যাডমিন লগইন অনুরোধের একটি বাস্ট (burst) অনুমোদন করে, এরপর প্রতি পাঁচ মিনিটে একটি অনুরোধের হারে অনুমতি পুনরায় পূরণ করে। সঠিক এবং ভুল উভয় পাসওয়ার্ডই এখানে গণনা করা হয়, কারণ পাসওয়ার্ড যাচাইয়ের আগেই লিমিটার কাজ করে। লিমিট অতিক্রম করা অনুরোধগুলো HTTP 429 এবং Too many requests, try again later. পায়। অ্যাডমিন সেশন লগইন করার 20 মিনিট পর শেষ হয়ে যায়, আপনি প্যানেল ব্যবহার করছেন কি না তা বিবেচ্য নয়, কারণ কোনো অ্যাক্টিভিটি সেশনের মেয়াদ বাড়ায় না।

এর চেয়েও ভালো উপায় হলো: পেজটি বন্ধ করে দেওয়া। অধিকাংশ ইন্সট্যান্সে এটি একবারই প্রয়োজন হয়, SMTP কনফিগার করতে এবং প্রথম ব্যবহারকারীদের আমন্ত্রণ জানাতে, এরপর আর প্রয়োজন হয় না। এটি নিষ্ক্রিয় করতে, ADMIN_TOKEN বা DISABLE_ADMIN_TOKEN কোনোটিই সেট করবেন না, config.json থেকে যেকোনো "admin_token" কি (key) মুছে ফেলুন, তারপর কন্টেইনারটি পুনরায় তৈরি করুন। ফাইল থেকে কি (key) মুছে ফেলা জরুরি, কারণ অ্যাডমিন পেজ সেখানে সেটিংস লেখে এবং config.json-এ যা থাকে তা এনভায়রনমেন্টের চেয়ে বেশি প্রাধান্য পায়। শুধুমাত্র ভেরিয়েবলটি মুছে ফেললে পেজটি খোলা থেকে যায়।

ডোমেইনটি খুঁজে পাওয়ার আগেই রেজিস্ট্রেশন বন্ধ করুন

SIGNUPS_ALLOWED=false
SIGNUPS_VERIFY=true
SHOW_PASSWORD_HINT=false

SIGNUPS_ALLOWED ডিফল্টভাবে true থাকে। এটি এভাবেই রেখে দিলে যে কেউ আপনার ডোমেইনে পৌঁছে অ্যাকাউন্ট তৈরি করতে পারবে এবং তাদের ডেটা আপনার ডেটার মতোই একই db.sqlite3-এ জমা হবে। এটিকে false-এ সেট করুন এবং অ্যাডমিন পেজ থেকে ইনভাইটেশনের মাধ্যমে লোক যোগ করুন; SMTP থাকলে ইনভাইটেশন ইমেইল ডেলিভারি হবে, আর তা না থাকলে সার্ভার সেই ঠিকানার জন্য একটি ইনভাইটেশন রেকর্ড করে রাখবে। INVITATIONS_ALLOWED ডিফল্টভাবে true থাকে এবং এটি অর্গানাইজেশন অ্যাডমিনদের অন্যদের ইনভাইট করার সুযোগ দেয়। আপনি যদি ব্যবহারকারীদের বিশ্বাস করেন তবে এটি ঠিক আছে, কিন্তু সিঙ্গেল-ইউজার ইনস্ট্যান্সের ক্ষেত্রে এটি false করে রাখা উচিত। যদি শুধুমাত্র নির্দিষ্ট কিছু ডোমেইন থেকে রেজিস্ট্রেশন করার অনুমতি দিতে চান, তবে SIGNUPS_DOMAINS_WHITELIST=example.com ব্যবহার করা ওপেন সাইনআপের চেয়ে সীমাবদ্ধ, যদিও এটি ইনভাইটেশন সিস্টেমের চেয়ে অনেক কম নিরাপদ।

SHOW_PASSWORD_HINT ডিফল্টভাবে false থাকে এবং এটি সেভাবেই রাখা উচিত। এটি চালু থাকা অবস্থায় যদি SMTP কনফিগার করা না থাকে, তবে লগইন ফর্মে কোনো বৈধ ইমেইল অ্যাড্রেস লিখলে তা ওই অ্যাকাউন্টের মাস্টার পাসওয়ার্ড হিন্ট প্রদর্শন করে। এতে হিন্ট ফাঁস হয়ে যায় এবং ইমেইল অ্যাড্রেসটি যে সার্ভারে বিদ্যমান তা নিশ্চিত হয়ে যায়।

আপনার ইনস্ট্যান্স যদি কোনো সময়ের জন্য ওপেন সাইনআপ মোডে চলে থাকে, তবে শুধুমাত্র আপনিই একমাত্র ব্যবহারকারী—এমনটা ধরে নেওয়ার আগে অ্যাডমিন পেজে যান এবং ইউজার লিস্ট চেক করুন।

যে port আপনি publish করতে চাননি

Docker image-টি container-এর ভেতরে port 80-এ listen করে। Bare-metal install-এর default হলো ROCKET_PORT=8000। Documentation-এ দেওয়া run command-এ এটি এভাবে publish করা হয়:

--publish 127.0.0.1:8000:80

127.0.0.1: prefix-টির উদ্দেশ্য এটাই। এর বদলে -p 8000:80 লিখলে Docker 0.0.0.0 bind করে। Docker এটি করে nat table-এ DNAT (destination network address translation) rule লিখে। এই rule-গুলো ufw পরিচালিত filter chain-এর আগে evaluate হয়। তাই ufw status port-টিকে denied দেখালেও port-টি Internet থেকে অনুরোধ গ্রহণ করে। পুরো প্রক্রিয়াটি Docker port কীভাবে ufw bypass করে তার নির্দেশিকা-তে ব্যাখ্যা করা হয়েছে।

আসলে কোন port listen করছে তা পরীক্ষা করুন:

sudo ss -tlnp | grep 8000

সঠিক ফলাফলে 127.0.0.1:8000-এ bound একটি মাত্র line থাকবে। 0.0.0.0:8000-এ bound line থাকলে vault সরাসরি Internet-এ exposed। Mapping ঠিক করুন, তারপর container recreate করুন। কারণ container তৈরি হওয়ার সময় port binding নির্ধারিত হয় এবং docker compose restart এটি পরিবর্তন করবে না:

docker compose up -d --force-recreate

পুরোনো guide-গুলোতে আরও একটি port দেখা যায়: 3012, আলাদা WebSocket port। Vaultwarden 1.31.0-এ এর support সরিয়ে দেওয়া হয়েছে, কারণ notification traffic এখন প্রধান HTTP port ব্যবহার করে। WEBSOCKET_ENABLED এবং WEBSOCKET_PORT 1.29.0 থেকে deprecated এবং এগুলো উপেক্ষা করা হয়। বর্তমান switch হলো ENABLE_WEBSOCKET, যার default value true। আপনার firewall বা compose file-এ এখনও 3012 open থাকলে এটি বন্ধ করুন।

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 হলো 1m, তাই এটি পরিবর্তন না করলে এর চেয়ে বড় request body ব্যর্থ হবে। nginx ক্লায়েন্টকে HTTP 413 ত্রুটি পাঠায় এবং client intended to send too large body: ... bytes লগ করে; প্রত্যাখ্যাত upload-টি Vaultwarden পর্যন্ত পৌঁছায় না। Upgrade এবং Connection হেডারগুলো WebSocket handshake-কে /notifications/hub-এ নিয়ে যায়। এগুলো বাদ দিলে vault কাজ করবে, কিন্তু browser এবং desktop client-এ WebSocket notification পৌঁছাবে না; mobile client-গুলো আলাদাভাবে কনফিগার করা push notification ব্যবহার করে।

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-IP

IP_HEADER-এর ডিফল্ট মান হলো X-Real-IP, তাই proxy যেন সঠিকভাবে এই হেডারটি সেট করে তা নিশ্চিত করতে হবে। Vaultwarden শুধুমাত্র IP_HEADER_TRUSTED_PROXIES-এ তালিকাভুক্ত ঠিকানা থেকেই এই হেডার গ্রহণ করে, যার ডিফল্ট মান local: অর্থাৎ যেকোনো non-global ঠিকানা, যা একই host বা একই container network-এর proxy-কে কভার করে। যদি proxy কোনো পাবলিক ঠিকানা থেকে সংযোগ করে, তবে তাকে অবশ্যই সেখানে যুক্ত করতে হবে। যদি হেডারটি অনুপস্থিত থাকে বা untrusted ঠিকানা থেকে আসে, তবে প্রতিটি লগ লাইন এবং login rate limit-এ proxy-র নিজস্ব ঠিকানা দেখা যাবে। এর অর্থ হলো, একজন আক্রমণকারীর ব্যর্থতার হিসাব আপনার instance-এর সকল ব্যবহারকারীর ওপর পড়বে। আপনি যদি IP_HEADER=X-Forwarded-For ব্যবহার করেন, তবে 1.37.4 সংস্করণটি এমন ডানদিকের ঠিকানাটি গ্রহণ করবে যা trusted proxy নয়। তাই চেইনে থাকা প্রতিটি proxy-র তালিকা দিন, যার মধ্যে nginx-এর সামনের CDN-ও অন্তর্ভুক্ত। DOMAIN-কে সঠিক https URL-এ সেট করুন, কারণ Vaultwarden এটি ব্যবহার করেই invitation এবং account recovery লিঙ্ক তৈরি করে এবং WebAuthn security key-গুলো সেই origin-এর সাথে আবদ্ধ থাকে।

একটি বিষয় যা অনেকেই এড়িয়ে যান: WebSocket সংযোগটি query string-এ session token পাঠায়, যা /notifications/hub?access_token=[JWT] হিসেবে পরিচিত। এটি আপনার proxy access log-এ plain text হিসেবে জমা হয়। লগ ফরম্যাটে access_token প্যারামিটারটি redact করুন, অথবা নিশ্চিত করুন যে এই লগগুলো এমন কোথাও পাঠানো হচ্ছে না যা আপনার নিয়ন্ত্রণে নেই।

লগইন এন্ডপয়েন্টে ব্রুট ফোর্স আক্রমণ প্রতিরোধ

রেট লিমিট ডিফল্টভাবেই চালু থাকে (LOGIN_RATELIMIT_SECONDS=60, LOGIN_RATELIMIT_MAX_BURST=10)। এগুলো আক্রমণকারীর গতি কমিয়ে দেয়, কিন্তু পুরোপুরি থামাতে পারে না। fail2ban এটি করতে সক্ষম, তবে এর জন্য Vaultwarden-এর ব্যর্থ লগইন প্রচেষ্টার তথ্য এমন কোথাও থাকতে হবে যা এটি পড়তে পারে। Docker-এর ক্ষেত্রে সহজ উপায় হলো একটি লগ ফাইল, কিন্তু Vaultwarden ডিফল্টভাবে কোনো লগ ফাইল তৈরি করে না:

LOG_FILE=/data/vaultwarden.log
LOG_LEVEL=info
EXTENDED_LOGGING=true

একটি ভুল পাসওয়ার্ড দিয়ে লগইন করার চেষ্টা করলে নিচের ফরম্যাটে একটি লাইন তৈরি হয়, এবং আপনার ফিল্টারকে এই স্ট্রিংটিই শনাক্ত করতে হবে:

[2026-08-07 09:14:22.000][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-এ জেল (jail) কনফিগার করুন:

[vaultwarden]
enabled = true
backend = pyinotify
port = 80,443
filter = vaultwarden
banaction = %(banaction_allports)s
logpath = /vw-data/vaultwarden.log
maxretry = 3
bantime = 14400
findtime = 14400

যদি Vaultwarden কন্টেইনারের পরিবর্তে systemd সার্ভিস হিসেবে চলে, তবে আপনি LOG_FILE এড়িয়ে যেতে পারেন: সেক্ষেত্রে প্রজেক্টের fail2ban উইকি অনুযায়ী logpath-এর পরিবর্তে backend = systemd এবং Vaultwarden ইউনিটের ওপর একটি journalmatch ব্যবহার করে সরাসরি জার্নাল থেকে তথ্য পড়া যায়।

যদি আপনি অ্যাডমিন পেজটি রেখে দিয়ে থাকেন, তবে একটি দ্বিতীয় জেল যোগ করুন যার 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-এর অর্থ হলো মাস্টার পাসওয়ার্ড ব্যবহারকারীর symmetric key-কে সুরক্ষিত রাখে। কোনো আক্রমণকারী যদি ডাটাবেসের কপি সংগ্রহ করে ফেলে, তবে সেখানে ছোট মাস্টার পাসওয়ার্ড কোনো কাজে আসে না; কারণ তারা তাদের নিজস্ব হার্ডওয়্যার ব্যবহার করে অফলাইনে যত খুশি ততবার পাসওয়ার্ডটি অনুমান করার চেষ্টা করতে পারে। সার্ভারের কোনো সেটিংস আক্রমণকারীর নিজস্ব মেশিনে কোনো প্রভাব ফেলে না।

PASSWORD_ITERATIONS=600000 ভুলভাবে বোঝা সহজ। এটি ক্লায়েন্টের পাঠানো login hash-এর সার্ভার-সাইড হ্যাশের জন্য iteration সংখ্যা নির্ধারণ করে এবং প্রজেক্টের কনফিগারেশন টেমপ্লেট অনুযায়ী, মান পরিবর্তন করলে তা পরবর্তী লগইনের সময় বিদ্যমান ব্যবহারকারীদের ক্ষেত্রে কার্যকর হয়। এটি client-side KDF-কে স্পর্শ করে না, যা মূলত নির্ধারণ করে যে ভল্টের ওপর অফলাইন আক্রমণ কতটা কঠিন হবে। KDF সেটিংস প্রতিটি অ্যাকাউন্টের নিজস্ব এবং এটি web vault-এর Settings, Security, Keys মেনু থেকে পরিবর্তন করতে হয়, যা কি-টিকে পুনরায় এনক্রিপ্ট করে। Bitwarden-এর ডকুমেন্টেশন অনুযায়ী, তাদের ক্লায়েন্টরা 2026.2.1 ভার্সন থেকে 600,000-এর কম PBKDF2 iteration থাকলে ব্যবহারকারী যখন মাস্টার পাসওয়ার্ড দিয়ে লগইন বা আনলক করেন, তখন তা স্বয়ংক্রিয়ভাবে বাড়িয়ে দেয়। 10 অক্টোবর 2026 পর্যন্ত, Vaultwarden সার্ভারের ক্ষেত্রে এই আচরণটি নিশ্চিত নয় এবং এটি কখনোই Argon2id সেটিংস পরিবর্তন করে না। তাই যারা অনেক আগে অ্যাকাউন্ট খুলেছেন, তাদের KDF সেটিংস এখনো পুরনো এবং কম থাকতে পারে। এটি পরীক্ষা করুন এবং ব্যবহারকারীকে জানান, কারণ সার্ভার নিজে থেকে এটি করতে পারে না।

এরপর প্রতিটি অ্যাকাউন্টের জন্য two-factor authentication চালু করুন। এটি ciphertext-কে সুরক্ষিত করে না, কারণ এটি ভল্ট এনক্রিপশনের অংশ নয়। তবে এটি চুরি হওয়া পাসওয়ার্ড ব্যবহার করে লগইন করা এবং ভল্টের কপি সিঙ্ক করা প্রতিরোধ করে। ইমেইল কনফিগার করা থাকলে, REQUIRE_DEVICE_EMAIL=true নতুন ডিভাইসে লগইনের জন্য ইমেইল পাঠানো বাধ্যতামূলক করে: যদি ইমেইল পাঠানো ব্যর্থ হয়, তবে লগইনও ব্যর্থ হবে। যদি মেইল সার্ভিস সম্পূর্ণভাবে বন্ধ থাকে, তবে এই সেটিংস নতুন ডিভাইসে লগইন আটকাবে না।

ব্যাকআপের ক্ষেত্রে self-hosted vault-এ ভুল হওয়ার সম্ভাবনা থাকে

একই VPS-এর home directory-তে রাখা ডেটা ফোল্ডারের একটি tar czf উপরের সমস্ত নিরাপত্তামূলক পদক্ষেপকে ব্যর্থ করে দেয়। সেই আর্কাইভে থাকে db.sqlite3, যাতে প্রতিটি ব্যবহারকারীর ciphertext থাকে, rsa_key.pem, যা login session তৈরি করতে পারে এবং config.json, যাতে কনফিগার করা admin token-এর মান এবং SMTP password থাকে। Argon2-এর নির্দেশনা অনুসরণ করলে admin password-এর একটি hash সংরক্ষিত হয়; তবে SMTP password plaintext হিসেবেই থেকে যায়। সেই একটি ফাইলে read access থাকলে তা account metadata, server secret এবং encrypted vault ডেটা উন্মুক্ত করে দেয়।

এক্ষেত্রে দুটি নিয়ম মেনে চলতে হয়। আর্কাইভটিকে সার্ভার থেকে সরিয়ে ফেলুন। সার্ভার থেকে পাঠানোর আগেই সেটিকে encrypt করুন।

এছাড়া সঠিকতার একটি সমস্যাও রয়েছে। service চালু থাকা অবস্থায় cp ব্যবহার করে db.sqlite3 কপি করলে এমন একটি ফাইল তৈরি হতে পারে যা লেখার মাঝপথে আটকে গেছে এবং সেটি আর খোলা যাবে না; আর আপনি তা জানতে পারবেন কেবল restore করার সময়। এর পরিবর্তে SQLite-এর নিজস্ব snapshot ব্যবহার করুন:

sqlite3 /vw-data/db.sqlite3 ".backup '/tmp/vw-db-backup.sqlite3'"

restore করার প্রক্রিয়াটি, যা সাধারণত কেউ পরীক্ষা করে না, তা the Vaultwarden backup and restore guide-এ বিস্তারিত আলোচনা করা হয়েছে।

হোস্টেড Bitwarden-এর পরিবর্তে যা আপনি ত্যাগ করছেন

সততার সাথে হিসাব করলে, Bitwarden-এর হোস্টেড সার্ভিস এমন ব্যক্তিদের দ্বারা পরিচালিত হয় যাদের পূর্ণকালীন কাজই হলো এটি পরিচালনা করা, এবং তাদের কাজের ওপর তৃতীয় পক্ষের অডিট রিপোর্ট প্রকাশিত হয়। সেলফ-হোস্টিংয়ের ক্ষেত্রে এই সুবিধাটি আপনার নিজস্ব প্যাচিং রুটিনের ওপর নির্ভর করে।

Vaultwarden নিরাপত্তা সংক্রান্ত ফিক্সগুলো সাধারণ রিলিজ হিসেবে প্রকাশ করে এবং সেগুলো ঘন ঘন আসে। 2 নভেম্বর 2026 তারিখে প্রজেক্টের রিলিজ ফিড যাচাই করে দেখা গেছে, বর্তমান রিলিজ হলো 1.37.5, যা 1 নভেম্বর 2026-এ প্রকাশিত হয়েছে। এর নোটে দুটি মাঝারি মানের নিরাপত্তা সতর্কবার্তা রয়েছে এবং যত দ্রুত সম্ভব আপডেট করার পরামর্শ দেওয়া হয়েছে। এছাড়া, টু-স্টেপ লগইন পদ্ধতি চালু বা বন্ধ করার জন্য web vault 2026.7.1 বা তার পরবর্তী সংস্করণ এবং ইমার্জেন্সি অ্যাক্সেসের মাধ্যমে অ্যাকাউন্টের নিয়ন্ত্রণ নেওয়ার জন্য 2026.9.0 বা তার পরবর্তী সংস্করণ ব্যবহার করা আবশ্যক। 5 অক্টোবর 2026-এ প্রকাশিত পূর্ববর্তী রিলিজ 1.37.4-এ সাতটি নিরাপত্তা সতর্কবার্তা ছিল, যার মধ্যে সবচেয়ে গুরুতরটিকে 'High' হিসেবে রেট করা হয়েছে। 24 জুলাই 2026-এ 1.37.0 রিলিজ হওয়ার পর থেকে এটি ছিল চতুর্থ রিলিজ: 1.37.1 আসে 29 জুলাই, 1.37.2 আসে 22 আগস্ট (যা 2026.8.0 এবং তার পরবর্তী ক্লায়েন্ট ভার্সনের জন্য প্রয়োজনীয়), এবং 1.37.3 আসে 13 সেপ্টেম্বর, যা ক্রেডেনশিয়াল বা 2FA পরিবর্তনের সময় 2FA রিমেম্বার টোকেন বাতিল করে এবং prelogin ও auth request এন্ডপয়েন্টগুলোতে রেট লিমিট প্রয়োগ করে। আপনি যদি এমন কোনো অর্গানাইজেশন পরিচালনা করেন যেখানে অ্যাডমিনদের ওপর পুরোপুরি আস্থা নেই, তবে 1.37.4-এর নোটে আপডেট করার পর প্রতিটি অর্গানাইজেশনের API key পরিবর্তন করার পরামর্শ দেওয়া হয়েছে। 1.37.4-এ কোন অ্যাড্রেসটি X-Forwarded-For থেকে পড়া হবে তাও পরিবর্তিত হয়েছে, যা উপরের রিভার্স প্রক্সি সেকশনে আলোচনা করা হয়েছে। এক বছর আগে সেটআপ করা এবং ভুলে যাওয়া একটি ইনস্ট্যান্স এক বছর পুরোনো কোডেই চলতে থাকে। latest ট্যাগটি নিজে থেকে কোনো সাহায্য করে না: একটি চলমান কন্টেইনার সেই ইমেজেই চলতে থাকে যা দিয়ে সেটি শুরু হয়েছিল, যতক্ষণ না আপনি docker compose pull কমান্ডটি চালিয়ে সেটি পুনরায় তৈরি করছেন। হোস্ট প্যাকেজগুলোর জন্য Ubuntu-তে আনঅ্যাটেন্ডেড আপগ্রেড চালু রাখুন এবং কন্টেইনার আপডেটের বিষয়টি এমন একটি ক্যালেন্ডার রিমাইন্ডারে রাখুন যা আপনি নিয়মিত চেক করবেন।

একজন সৎ পাঠকের যে সিদ্ধান্তে আসা উচিত তা হলো: এখানে ব্যবহৃত ক্রিপ্টোগ্রাফি Bitwarden-এর নিজস্ব ডিজাইন এবং এটি অত্যন্ত শক্তিশালী, কিন্তু পরিচালনার ঝুঁকি পুরোপুরি আপনার ওপর বর্তায়। আপনি যদি এটি নিয়মিত প্যাচ করেন এবং অন্য কোথাও ব্যাকআপ রাখেন, তবে আপনার নিয়ন্ত্রণে থাকা VPS-এ একটি Vaultwarden ইনস্ট্যান্স পাসওয়ার্ড রাখার জন্য একটি যুক্তিসঙ্গত জায়গা। যদি এই দুটি অভ্যাস আপনার না থাকে, তবে হোস্টেড সার্ভিসের জন্য অর্থ প্রদান করুন এবং আপনার মনোযোগ অন্য কোথাও ব্যয় করুন। ফিচার অনুযায়ী তুলনামূলক আলোচনা Vaultwarden বনাম সেলফ-হোস্টেড Bitwarden সেকশনে দেওয়া আছে।

কন্টেইনারের নিচের হোস্টটিকে সুরক্ষিত করা

Vaultwarden হলো একটি Linux বক্সে চলমান একটি প্রসেস। অ্যাপ্লিকেশনটি যেভাবে কনফিগার করা থাকুক না কেন, ওই বক্সের root ইউজার /vw-data ফাইলটি পড়তে পারে। আপনার compose ফাইলে user: "1000:1000" ব্যবহার করে কন্টেইনারটিকে একজন unprivileged ইউজার হিসেবে চালান। ডেটা ফোল্ডারের মালিকানা সেই ইউজার অনুযায়ী সেট করুন। কন্টেইনারে যে ফাইলগুলো লেখার প্রয়োজন নেই, সেগুলোকে :ro ব্যবহার করে read-only হিসেবে মাউন্ট করুন। এরপর মূল প্রবেশপথটি বন্ধ করুন: SSH hardening on a VPS নিবন্ধটিতে key-only লগইন এবং পাসওয়ার্ড অথেন্টিকেশন নিষ্ক্রিয় করার পদ্ধতি আলোচনা করা হয়েছে। এটিই সেই সাধারণ আক্রমণগুলো প্রতিরোধ করে যা উপরের সব সুরক্ষাকে এড়িয়ে যেতে পারে।

FAQ

কেউ কি Vaultwarden ডাটাবেস চুরি করলে আমার পাসওয়ার্ড পড়তে পারবে?

সরাসরি পারবে না। প্রতিটি ভল্ট আইটেম ক্লায়েন্ট সাইডে একটি র‍্যান্ডমলি জেনারেট করা কি (key) দিয়ে এনক্রিপ্ট করা থাকে, যা ব্যবহারকারী বা প্রতিষ্ঠানের সিমেট্রিক কি দ্বারা সুরক্ষিত থাকে; পাসওয়ার্ড থেকে তৈরি কি (password-derived key) ব্যবহারকারীর সিমেট্রিক কি-কে সুরক্ষা দেয়। db.sqlite3-এ এনক্রিপ্ট করা ভল্ট কন্টেন্টের পাশাপাশি প্লেইনটেক্সট অ্যাকাউন্ট মেটাডেটা থাকে। তারা তাৎক্ষণিকভাবে যা পায় তা হলো প্রতিটি অ্যাকাউন্টের ইমেইল অ্যাড্রেস, পাসওয়ার্ড হিন্ট, KDF সেটিংস, লগইন ও ডিভাইসের মেটাডেটা এবং twofactor টেবিলের লগইন TOTP সিক্রেট। এগুলো এনক্রিপ্ট না করেই রাখা হয় কারণ সার্ভারকে প্রত্যাশিত কোডটি গণনা করতে হয়। তারা ভল্ট সাইফারটেক্সট নিয়ে অফলাইনে যত খুশি সময় ধরে আক্রমণ চালাতে পারে, তাই মাস্টার পাসওয়ার্ডের দৈর্ঘ্যই নির্ধারণ করে যে ফলাফল কী হবে।

আমার কি ADMIN_TOKEN ব্যবহার করা উচিত নাকি অ্যাডমিন পেজটি পুরোপুরি বন্ধ করে দেওয়া উচিত?

সম্ভব হলে এটি বন্ধ করে দিন, কারণ বেশিরভাগ ইন্সট্যান্সে SMTP কনফিগার করতে এবং ব্যবহারকারীদের আমন্ত্রণ জানাতে এটি একবারই প্রয়োজন হয়, এরপর আর লাগে না। এটি বন্ধ করতে, ADMIN_TOKEN বা DISABLE_ADMIN_TOKEN কোনোটিই সেট করবেন না, config.json থেকে যেকোনো "admin_token" কি সরিয়ে ফেলুন এবং তারপর কন্টেইনারটি পুনরায় তৈরি করুন। শুধুমাত্র এনভায়রনমেন্ট ভেরিয়েবল সরিয়ে ফেলা যথেষ্ট নয়, কারণ অ্যাডমিন পেজ দ্বারা লেখা সেটিংস 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 পোর্টে চলে এসেছে। এছাড়া WEBSOCKET_ENABLED এবং WEBSOCKET_PORT ভার্সন 1.29.0 থেকে ডেপ্রিকেটেড হয়ে গেছে এবং সেগুলোকে উপেক্ষা করা হয়। বর্তমান সেটিং হলো ENABLE_WEBSOCKET, যা ডিফল্টভাবে true থাকে। ফায়ারওয়ালে 3012 পোর্টটি বন্ধ করে দিন এবং আপনার কম্পোজ ফাইল থেকে এটি মুছে ফেলুন। এরপর নিশ্চিত করুন যে আপনার রিভার্স প্রক্সি Upgrade এবং Connection হেডারগুলো ফরোয়ার্ড করছে, কারণ ব্রাউজার এবং ডেস্কটপ WebSocket নোটিফিকেশন এগুলোর ওপর নির্ভর করে। মোবাইল ক্লায়েন্টরা আলাদাভাবে কনফিগার করা পুশ নোটিফিকেশন ব্যবহার করে।

এই লেখাটি কি কাজে লেগেছে?