নিয়ন্ত্রণগুলো 21-compliance.md-এর section 14-এ আছে—অডিটর যে নামে সেগুলো চান। এখানে প্রক্রিয়া; এটি যে record তৈরি করে সেগুলোই প্রমাণ।
Key-গুলো
সংরক্ষিত প্রতিটি credential নতুন data encryption key (DEK) দিয়ে seal করা হয়। Master key (KEK) DEK-কে wrap করে, আর master key-এর reference পাশে (key_ref) অথবা packed value-র ভেতরে সংরক্ষিত থাকে। Master key রোটেট করলে DEK আবার wrap হয়। Credential কখনো decrypt বা পুনরায় encrypt করা হয় না।
| Setting | অর্থ |
|---|---|
QUIRE_MASTER_KEY |
বর্তমান master key: 32 byte, base64। প্রতিটি নতুন secret এর অধীনে wrap হয় |
QUIRE_MASTER_KEY_VERSION |
সংস্করণ label। সেট না থাকলে v1। Key বদলালেই এটি বাড়ান |
QUIRE_MASTER_KEY_RETIRED |
আগে তৈরি secret এখনও যে key-তে থাকতে পারে, সেই পুরোনো key-গুলো v1=<base64>,v0=<base64> আকারে। শুধু read, কখনো write নয় |
Web tier, worker এবং bun run kek:rotate command একই তিনটি setting পড়ে। সবখানে মান এক হতে হবে, নইলে একটির seal করা তথ্য অন্যটি খুলতে পারবে না।
QUIRE_MASTER_KEY না থাকলে প্রতিটি subsystem QUIRE_SECRET_KEY থেকে তৈরি key ব্যবহার করতে থাকে। এটি চলতে থাকে, তবে System health page-এ degraded দেখায়; master key সেট করার পরও এটি পড়া যায়, তাই প্রথম rotation-এ সব তথ্য সেখান থেকে সরানো সম্ভব। Process environment পড়তে পারা যে কেউ সব সংরক্ষিত credential decrypt করতে পারে; তাই production install-এ master key secret store-এ এবং database backup থেকে আলাদা রাখা উচিত।
রোটেট করা
Platform console-এর Security, Master key-তে key-এর বয়স দেখা যায়, এবং quire.secrets.master_key.age metric-এও (দিন হিসেবে)। দৈনিক platform.key_age schedule (03:41 UTC) key-এর বয়স 365 দিনে পৌঁছালে platform audit chain-এ reminder লেখে, এরপর rotation না হওয়া পর্যন্ত প্রতি 30 দিনে আবার লেখে। Reminder পেলে এবং key প্রকাশ হয়ে থাকতে পারে এমন যেকোনো সময়ে রোটেট করুন।
- নতুন key তৈরি করুন:
openssl rand -base64 32। QUIRE_MASTER_KEY-এ সেটি এবংQUIRE_MASTER_KEY_VERSION-এ পরের label (v2) দিন। পুরোনো key-টিQUIRE_MASTER_KEY_RETIRED-এv1=<old base64>হিসেবে সরান। দুটির copy এই host ছাড়া অন্য কোথাও রাখুন।- নতুন setting-সহ web tier ও worker deploy করুন। নতুন secret এখন
env:QUIRE_MASTER_KEY:v2-এর অধীনে wrap হবে; পুরোনোগুলো এখনও retired key দিয়ে খোলা যাবে। - Audit trail-এ থাকার মতো কারণসহ rotation request করুন:
- console-এ: Security, Master key, Rotate the master key; অথবা
- একই environment-সহ shell-এ:
bun run kek:rotate request --reason "Annual rotation, ticket SEC-114"।
- Worker প্রতি মিনিটে এক অংশ re-wrap করে (scheduler-এর
platform.key_rotationschedule) এবং restart-এর পর যেখানে থেমেছিল সেখান থেকে চলে। একবারেই শেষ করতে:bun run kek:rotate run।bun run kek:rotate statusদিয়ে অগ্রগতি দেখুন। - Record-এ rotation সম্পন্ন এবং শূন্য অমীমাংসিত ও শূন্য ব্যর্থ দেখালে
QUIRE_MASTER_KEY_RETIREDথেকে retired key সরিয়ে আবার deploy করুন। ততক্ষণ এটি রাখুন: যেসব value সরানো যায়নি সেগুলো এখনও পুরোনো key-তে wrap করা।
Job কোনগুলো পরীক্ষা করে
যে store-এ wrap করা DEK থাকে তার সবগুলো: SEALED_STORES-এ থাকা store (apps/worker/src/key-rotation.ts)। Control database-এর store সেখানেই পরীক্ষা হয়; organisation-এর store-গুলো row-level security-র অধীনে একবারে একটি organisation ধরে পরীক্ষা হয়, যে database-এ সেটি আছে সেখানেই—তাই নির্দিষ্ট database-এ আবদ্ধ tenant তার সেই database-এই rotate হয়। তালিকায় উল্লেখ নেই এমন wrapped-key column যোগ হলে একটি test ব্যর্থ হয়; credential review কোনো sealed column শনাক্ত করলেও তালিকায় না থাকলে আরেকটি test ব্যর্থ হয়।
Record
ops.key_rotation: প্রতি rotation-এ একটি row, যেখানে কারণ, কে request করেছেন, অবস্থা এবং মোট হিসাব (re-wrapped, ইতিমধ্যে বর্তমান, অমীমাংসিত, ব্যর্থ) থাকে।ops.key_rotation_progress: পরীক্ষা হওয়া প্রতিটি store ও scope-এ একটি row, যেখানে পড়া যায়নি এমন key reference ও প্রতিটির অধীনে থাকা value-র সংখ্যা থাকে। আবার শুরু করা rotation এগুলো এড়িয়ে যায়।- Platform audit chain:
platform/key_rotation_request(কারণসহ), প্রতি store-এর গণনাসহ একটি করেplatform/key_rotation_store, এবংplatform/key_rotation_completeঅথবাplatform/key_rotation_fail; reminder-এর জন্যplatform/key_age_reminder। - Metric:
quire.secrets.master_key.ageএবংquire.secrets.rewrap.outstanding(শেষ rotation-এ সরানো যায়নি এমন value)।
Value অমীমাংসিত থাকলে
অমীমাংসিত value এমন key reference-এর অধীনে wrap করা, যা এই installation-এর কাছে নেই, অথবা এর column যে format প্রতিশ্রুতি দেয় তা মেলে না। Progress record-এ reference দেখা যায় (যেমন env:QUIRE_MASTER_KEY:v0 (unreadable))। সেই key-টি QUIRE_MASTER_KEY_RETIRED-এ ফিরিয়ে দিয়ে আবার rotation চালান, অথবা key চিরতরে হারিয়ে গেলে প্রতিষ্ঠানের administrator-কে credential আবার দিতে বলুন: সেটি বর্তমান key-তে seal হবে। ব্যর্থ rotation-এ record-এ error দেখা যায়; কারণ ঠিক করে আবার request করুন।
Signing key
Master key থেকে আলাদা: প্রতিটি প্রতিষ্ঠান নিজস্ব RSA key দিয়ে OpenID Connect token ও LTI message sign করে, যা /.well-known/jwks.json-এ প্রকাশ করা হয়। এখানে operator-এর কিছু করার দরকার নেই। ঘণ্টায় একবার চলা platform.signing_keys schedule বর্তমান key-এর 90 দিন পূর্ণ হওয়ার সাত দিন আগে পরের key প্রকাশ করে; এক সপ্তাহ পর নতুনটি sign করা শুরু করে এবং পুরোনোটি retiring হয়; আরও 90 দিন পরে পুরোনোটি মুছে key set থেকে সরে যায়। প্রতিটি ধাপ platform audit chain-এ platform/signing_key_advance entry হয়।
প্রকাশের পরপরই প্রতিষ্ঠানের key বদলাতে চাইলে, যেমন key উন্মুক্ত হয়ে গেলে:
- console-এ: Security, Master key, Publish a new signing key (
platform/keys_manageদরকার); অথবা - worker-এর environment-সহ shell-এ:
bun run kek:rotate signing-keys rotate --tenant <slug or id> --reason "Key exposed, INC-3310".bun run kek:rotate signing-keys statusপ্রতিটি প্রতিষ্ঠানের key-কে stage অনুযায়ী তালিকাভুক্ত করে।
নতুন key সঙ্গে সঙ্গে প্রকাশিত হয় এবং সাত দিন পর, পুরোনোটি retiring হলে, sign শুরু করে। এক সপ্তাহের ব্যবধান ইচ্ছাকৃত: relying party-গুলো key set cache করে; ব্যবধান ছোট হলে একই সময়ে সব tool ব্যর্থ হতে পারে। ইতিমধ্যে sign করা token যাচাই চলতে দিতে retiring key আরও 90 দিন key set-এ থাকে। Key উন্মুক্ত হওয়ায় আরও আগে বিশ্বাস করা বন্ধ করতে হলে operator নিজের database access দিয়ে পরিবর্তনের record অনুসারে এর row মুছতে পারেন (break-glass access শুধু read-এর জন্য); এরপর সেটি দিয়ে sign করা token verification-এ ব্যর্থ হবে। জোরপূর্বক rotation কারণসহ audit chain-এ platform/signing_key_rotate হিসেবে লেখা হয়। নতুন key wrap করতে worker-এর web tier-এর মতোই QUIRE_MASTER_KEY setting দরকার; master key-এর bun run kek:rotate অন্য সবকিছুর সঙ্গে signing key-ও re-wrap করে (oauth_signing_key SEALED_STORES-এ আছে)।
Production-এ জরুরি প্রবেশাধিকার
কেউ production-এ স্থায়ী access রাখেন না। কোনো কাজ অপেক্ষা করতে না পারলে owner জরুরি access grant দেন: Platform console, Security, Break-glass access।
- Grant-এর scope থাকে (একটি প্রতিষ্ঠান অথবা platform registry), incident বা ticket উল্লেখ করা অন্তত 20 character-এর কারণ থাকে এবং 5 থেকে 240 মিনিটের সময়সীমা থাকে। এটি নিজে মেয়াদোত্তীর্ণ হয়: প্রতিটি statement-এর সময় clock দেখে যাচাই করা হয়।
- যিনি grant দিচ্ছেন সেই owner-কেও দেওয়া যায়, অথবা অন্য owner-কে (দুই-ব্যক্তির পদ্ধতি)। যাঁকে দেওয়া হয়েছে শুধু তিনিই ব্যবহার করতে পারেন। দেওয়ার জন্য
platform/break_glass_issueএবং ব্যবহারের জন্যplatform/break_glass_useদরকার; default-এ উভয় অনুমতিই শুধু owner-এর। - Database login দিয়ে নয়, gateway দিয়ে statement চলে: শুধু read, একবারে একটি, নির্দিষ্ট প্রতিষ্ঠান বা control registry-র মধ্যে সীমিত, পাঁচ সেকেন্ড timeout এবং সর্বোচ্চ 500 row। Binary value-র আকার দেখানো হয়।
- Platform audit chain-এ grant দেওয়া (কারণসহ), প্রত্যাহার, চালানোর আগে প্রতিটি statement (
platform/break_glass_statement, প্রত্যাখ্যাত হলে ফলdenied) এবং প্রতিটি result (platform/break_glass_result) রেকর্ড হয়।ops.break_glass_statement-এ audit entry ID থাকে, তাই grant-এর record-এর সঙ্গে audit entry যুক্ত করা যায়। - Write করার সুযোগ নেই। Release-এর জন্য অপেক্ষা করতে না পারা পরিবর্তন এই product-এর বাইরে operator-এর নিজস্ব database access ও নিজস্ব change record দিয়ে করতে হয়; সেই record-এ এখানে ব্যবহৃত incident reference উল্লেখ করা উচিত।
কেন database credential দেবেন না: Postgres login চাওয়া session-এর চেয়েও বেশি সময় টিকে থাকে, application-নির্ভর row-level security এড়িয়ে যায় এবং এই product-এর audit chain-এ লিখতে পারে না; ফলে server log কেউ পাঠালে ততটুকুই statement audit হয়। Gateway access-এর চারপাশের অভ্যাস না করে audit trail-কে access-এর অন্তর্নিহিত বৈশিষ্ট্য করে।
Audit request-এর উত্তর দিতে নির্দিষ্ট সময়ের grant তালিকাভুক্ত করুন (Break-glass access), statement ও audit entry ID দেখতে grant-এর history খুলুন এবং platform audit chain-এর entry পড়ুন (bun run audit:verify --platform প্রমাণ করে chain অক্ষত আছে)।