সরাসরি পর্বে যান
পর্ব ৪৯ · ৬৪-এর মধ্যে · মডিউল ৭

Sandbox আর mandatory access control — seccomp, SELinux আর secure boot

Sandboxing & Mandatory Access Control — seccomp-bpf, Capabilities, Landlock, SELinux, AppArmor, Secure & Measured Boot

Taught in Bangla, this lesson explains sandboxing and mandatory access control: what a sandbox is, browser, mobile, desktop and WebAssembly sandboxes, seccomp-bpf system call filtering with no_new_privs, capabilities, namespaces and Landlock, the limits of chroot, DAC versus MAC, SELinux labels, type enforcement and reading denials, AppArmor path-based profiles, and Secure Boot and measured boot with shim, TPM and attestation.

এই পর্বে যা শিখবেন

  • Sandbox-এর মূল চিন্তা, আর Chrome, Android, iOS/macOS, Windows ও WebAssembly কীভাবে তা প্রয়োগ করে
  • seccomp-এর strict mode আর BPF filter নিজে C-তে বসিয়ে দেখা — ALLOW, ERRNO, KILL ফলাফল চেনা
  • Capability, no_new_privs, namespace, Landlock — আর chroot কেন যথেষ্ট নয়
  • DAC, MAC, Bell–LaPadula, Biba; SELinux-এর context, type enforcement, mode আর denial পড়া; AppArmor-এর profile
  • Secure Boot-এর key-ব্যবস্থা, shim/MOK, module signing, measured boot, TPM sealing আর remote attestation — আর AI-এর নিরাপত্তা-পরামর্শ যাচাই

১ · Sandbox কী — আক্রান্ত প্রোগ্রামকেও ঘেরাটোপে রাখা

পর্ব ৪৬ থেকে ৪৮-এ দেখেছেন OS কীভাবে প্রশ্ন করে “এই ব্যবহারকারী কি এই ফাইল পড়তে পারবেন?” — access matrix, Linux-এর rwx অনুমতি, authentication আর memory-সুরক্ষা। কিন্তু একটি কঠিন বাস্তবতা আছে। আপনার PDF reader আপনারই নামে চলে। আপনার যে সব ফাইল পড়ার অনুমতি আছে, PDF reader-এরও আছে। এখন ধরুন, ইমেইলে আসা একটি বিশেষভাবে বানানো PDF ফাইল reader-এর কোনো বাগ কাজে লাগিয়ে তার ভেতরে নিজের কোড চালিয়ে দিল। তখন সেই কোড আপনার সব ফাইল, ব্রাউজারের পাসওয়ার্ড আর SSH key পর্যন্ত পৌঁছাতে পারে — কারণ OS-এর চোখে এটি “আপনি”।

Sandbox-এর চিন্তা এখান থেকেই। প্রশ্নটি বদলে যায়: “এই প্রোগ্রামের কাজ করতে আসলে কী কী লাগে?” PDF দেখাতে লাগে একটি ফাইল পড়া আর স্ক্রিনে আঁকা। নেটওয়ার্ক, আপনার home ফোল্ডারের বাকি ফাইল, নতুন প্রোগ্রাম চালানো — কোনোটাই লাগে না। তাহলে আগেভাগেই সেগুলো কেড়ে নেওয়া হোক। পরে বাগ কাজে লাগিয়ে কেউ ঢুকলেও সে একটি ছোট ঘরে আটকে থাকবে। ১৯৭৫ সালে Jerome Saltzer আর Michael Schroeder তাঁদের বিখ্যাত গবেষণাপত্রে এই নীতির নাম দেন principle of least privilege: প্রতিটি প্রোগ্রাম আর ব্যবহারকারী কাজের জন্য ন্যূনতম যতটুকু ক্ষমতা দরকার, ঠিক ততটুকু পাবে। Sandbox হলো সেই নীতির হাতে-কলমে প্রয়োগ।

ব্যাংকের ক্যাশ কাউন্টারের কথা ভাবুন। ক্যাশিয়ার কাচের ঘেরা জায়গায় বসেন। তাঁর কাছে শুধু নিজের ড্রয়ারের চাবি, ভল্টের চাবি নয়; কাউন্টারের ফাঁক দিয়ে শুধু টাকা আর কাগজ আদান-প্রদান হয়। কেউ যদি ক্যাশিয়ারকে ভয় দেখিয়ে কাজ আদায়ও করে, ক্ষতি সীমিত থাকে ওই ড্রয়ারের টাকায়। ব্যাংক ক্যাশিয়ারকে অবিশ্বাস করে বলে এটি করে না — সবচেয়ে খারাপ দিনের জন্য ক্ষতির সীমা আগেই টেনে রাখে। ড্রয়ার = প্রোগ্রামের অনুমতি, কাচের ফাঁক = অনুমোদিত syscall, ভল্ট = বাকি সিস্টেম।

যেকোনো sandbox তিনটি প্রশ্নের উত্তর দেয়: (১) প্রোগ্রাম kernel-কে কী কী অনুরোধ করতে পারবে (কোন system call), (২) কোন রিসোর্সে হাত দিতে পারবে (কোন ফাইল, port, ডিভাইস), আর (৩) কী দেখতে পাবে (অন্য প্রসেস, নেটওয়ার্ক, পুরো file system — নাকি শুধু নিজের অংশ)। প্রোগ্রামের kernel-এর কাছে যাওয়ার যত পথ খোলা, তার সমষ্টিকে বলে attack surface. Sandbox এই পৃষ্ঠ ছোট করে।

একটি sandbox-এর স্তরগুলো — একটি ভাঙলেও পরেরটি থাকে

Defense in depth: অবিশ্বস্ত কোড থেকে kernel পর্যন্ত কয়েকটি স্বাধীন বাধা

  1. অবিশ্বস্ত কোডrenderer / parserধরে নিন আক্রান্ত
  2. Syscall ছাঁকনিseccompকোন অনুরোধ চলবে
  3. ক্ষমতা ছাঁটাcapabilities, nnproot-এর টুকরো নেই
  4. নীতিSELinux / AppArmor / Landlockকোন ফাইল, কোন port
  5. আলাদা দৃশ্যnamespacesকী দেখা যায়
ছবি ৪৯.১ — কোনো একটি স্তর নিখুঁত নয়; কয়েকটি স্বাধীন স্তর মিলে আক্রমণকারীর কাজ বহুগুণ কঠিন করে (defense in depth).

বড় প্রোগ্রাম প্রায়ই নিজেকে দুই ভাগে ভাগ করে — একে বলে privilege separation. একটি ছোট, বিশ্বস্ত “broker” প্রসেস বেশি ক্ষমতা রাখে আর সাবধানে যাচাই করে কাজ করে; ঝুঁকিপূর্ণ কাজ (অচেনা ডেটা parse করা, ওয়েবপেজ আঁকা) করে প্রায় ক্ষমতাহীন একটি sandboxed প্রসেস, যে দরকারে IPC-তে (পর্ব ১২) broker-এর কাছে অনুরোধ পাঠায়। আধুনিক ব্রাউজার এই নকশার সবচেয়ে বড় উদাহরণ। এটি পর্ব ৬-এর policy আর mechanism আলাদা রাখার নীতিরই প্রয়োগ: kernel যন্ত্র দেয় (seccomp, LSM), আর প্রতিটি প্রোগ্রাম বা distro নিজের নীতি লেখে।

মূল ধারণা

Sandbox বাগ ঠিক করে না, বাগের ফল সীমিত করে। “আমার কোডে বাগ নেই” — এই অনুমান বড় সফটওয়্যারে টেকে না। তাই নিরাপত্তা-প্রকৌশলের প্রশ্ন হলো: বাগ থাকলেও আক্রমণকারী কতদূর যেতে পারবে? উত্তর যত ছোট, নকশা তত ভালো।

২ · বাস্তবের sandbox — ব্রাউজার, মোবাইল, ডেস্কটপ আর WebAssembly

প্রতিদিন আপনি কয়েক ডজন sandbox ব্যবহার করেন, টেরও পান না। প্রতিটি প্ল্যাটফর্ম ভিন্ন যন্ত্র ব্যবহার করে, কিন্তু চিন্তা একই। নিচের বিবরণ সরলীকৃত — বিস্তারিত নকশা সংস্করণভেদে বদলায়, তাই নিজের কাজে লাগানোর আগে প্রতিটি প্রকল্পের অফিসিয়াল ডকুমেন্টেশন দেখুন।

Google Chrome (২০০৮) শুরু থেকেই বহু-প্রসেস নকশায়: একটি browser প্রসেস (broker) আর ওয়েবপেজ আঁকার জন্য আলাদা renderer প্রসেস। ওয়েবসাইটের HTML, JavaScript, ছবি — সবচেয়ে অবিশ্বস্ত ডেটা — renderer-এ প্রক্রিয়া হয়, আর renderer প্রায় কোনো ফাইল খুলতে বা নেটওয়ার্কে সরাসরি যেতে পারে না; সব কাজ browser প্রসেসের মাধ্যমে। Linux-এ renderer-কে আটকাতে Chrome namespace আর seccomp-bpf ব্যবহার করে বলে জানা যায়; Windows-এ restricted token, job object আর low integrity level; macOS-এ Apple-এর sandbox profile. ২০১৮ সালে Spectre-ধরনের হার্ডওয়্যার ত্রুটি প্রকাশের পর Chrome Site Isolation চালু করে — ভিন্ন ওয়েবসাইট সাধারণত ভিন্ন renderer প্রসেসে, যাতে একটি সাইট অন্যটির মেমরি পড়তে না পারে। Firefox-ও এখন একই ধরনের বহু-প্রসেস নকশায় চলে।

Android প্রথম সংস্করণ থেকেই প্রতিটি অ্যাপকে নিজস্ব Linux UID দেয়। ফলে পর্ব ৪৭-এর সাধারণ Linux অনুমতিই অ্যাপগুলোকে একে অপরের ফাইল থেকে আলাদা রাখে — bKash অ্যাপের ডেটা ফোল্ডার একটি গেম অ্যাপ পড়তে পারে না। এর ওপর Android ৫.০ (২০১৪) থেকে SELinux সব প্রসেসে enforcing মোডে চলে, আর Android ৮.০ থেকে অ্যাপ প্রসেসে একটি seccomp filter-ও বসানো হয়, যা অপ্রয়োজনীয় syscall বন্ধ রাখে। ক্যামেরা, লোকেশন বা কন্টাক্টের মতো সংবেদনশীল জিনিস চাইতে অ্যাপকে permission নিতে হয়, যা ব্যবহারকারী দেন বা ফিরিয়ে নেন।

iOS-এ প্রতিটি অ্যাপ নিজের “container” ফোল্ডারে বন্দি; অন্য অ্যাপের ফাইল দেখা যায় না, আর সব কোড Apple-এর স্বাক্ষর ছাড়া চলে না। অ্যাপ কোন বিশেষ ক্ষমতা পাবে (iCloud, push notification, Wi-Fi তথ্য…) তা লেখা থাকে স্বাক্ষরিত entitlement-এর তালিকায় — অ্যাপ নিজে এটি বদলাতে পারে না। macOS-এ Mac App Store-এর অ্যাপের জন্য App Sandbox বাধ্যতামূলক: entitlement-এ যা নেই, তা বন্ধ। ব্যবহারকারী ফাইল-খোলার ডায়ালগে যে ফাইল বাছেন, কেবল সেটির অনুমতি অ্যাপ পায়। Apple-এর ভেতরের বাস্তবায়ন সব প্রকাশ্য নয়, তাই খুঁটিনাটি সংস্করণভেদে আলাদা হতে পারে।

Windows Vista (২০০৭) থেকে প্রতিটি প্রসেসের একটি integrity level থাকে (Low, Medium, High, System); ডিফল্ট নিয়মে নিচু স্তরের প্রসেস উঁচু স্তরের বস্তুতে লিখতে পারে না — এটি এক ধরনের mandatory নিয়ম, যা পরে Biba মডেলে চিনবেন। Windows 8 (২০১২) আনে AppContainer: Store অ্যাপের জন্য একটি কম-ক্ষমতার token, যেখানে অ্যাপ শুধু ঘোষিত “capability” (যেমন internetClient, ছবি লাইব্রেরি) পায়। Edge আর অন্য কিছু প্রোগ্রামও নিজেদের ঝুঁকিপূর্ণ অংশ AppContainer-এ চালায়। Windows-এর ভেতরের বিস্তারিত পর্ব ৫৬-এ।

WebAssembly (Wasm) কোডের ভাষাগত sandbox: প্রোগ্রাম শুধু নিজের “linear memory”-র ভেতরে পড়তে-লিখতে পারে, প্রতিটি ঠিকানার সীমা runtime যাচাই করে, আর সরাসরি কোনো syscall করার উপায় নেই — host যে ফাংশন import করে দেয়, কেবল সেগুলোই ডাকা যায়। ব্রাউজারের বাইরে WASI নামের ইন্টারফেসে ফাইলের অনুমতিও capability-র মতো: runtime যে ফোল্ডার আগে থেকে খুলে দেয় (preopen), কেবল সেটিই দেখা যায়। তবে runtime-এ বাগ থাকলে এই সীমাও ভাঙতে পারে, তাই ব্রাউজারে Wasm চলে renderer sandbox-এর ভেতরেই — আবার সেই defense in depth.

প্ল্যাটফর্মকাকে আলাদা করেমূল যন্ত্রঅনুমতি কে ঠিক করে
Chrome (Linux)প্রতিটি সাইটের renderernamespace + seccomp-bpfব্রাউজারের নিজস্ব নীতি
Androidপ্রতিটি অ্যাপআলাদা UID + SELinux + seccompOS-এর নীতি + ব্যবহারকারীর permission
iOS / macOSপ্রতিটি অ্যাপsandbox profile + code signingস্বাক্ষরিত entitlement
WindowsStore অ্যাপ, ব্রাউজারের অংশAppContainer, integrity levelঘোষিত capability
WebAssemblyএকটি moduleসীমা-যাচাই করা memory, importhost runtime

৩ · seccomp — system call-এর দরজায় ছাঁকনি

পর্ব ৫-এ দেখেছেন, user-space প্রোগ্রাম kernel-এর কাছে যা চায়, সবই system call-এর দরজা দিয়ে চায়। Linux-এ কয়েকশো syscall আছে, কিন্তু একটি সাধারণ প্রোগ্রাম তার অল্প কয়েকটিই ব্যবহার করে। বাকি প্রতিটি দরজা kernel-এর কোডের একটি অংশ খুলে রাখে — আর kernel-এর বাগ মানে পুরো সিস্টেম। seccomp (secure computing) প্রসেসকে বলতে দেয়: “এখন থেকে আমি শুধু এই কয়েকটি দরজা ব্যবহার করব; অন্য কিছু চাইলে আমাকে থামিয়ে দিন।” একবার বসালে প্রসেস নিজে, এমনকি পরে আক্রান্ত হলেও, এই সীমা আর সরাতে পারে না।

প্রথম রূপ: strict mode (২০০৫)

Linux 2.6.12 (২০০৫)-এ Andrea Arcangeli seccomp আনেন, মূলত অন্যের কম্পিউটারে অবিশ্বস্ত হিসাব চালানোর একটি প্রকল্পের জন্য। এই strict mode-এ অনুমোদিত syscall মাত্র চারটি: read, write, _exit (শুধু exit, exit_group নয়) আর sigreturn. অর্থাৎ আগে থেকে খোলা file descriptor-এ পড়া-লেখা আর বেরিয়ে যাওয়া — ব্যস। অন্য যেকোনো syscall করলেই kernel প্রসেসটিকে SIGKILL দিয়ে শেষ করে। নিচের প্রোগ্রামে সন্তান-প্রসেস strict mode চালু করে, একটি write() করে, তারপর একটি ফাইল খুলতে চায়; বাবা waitpid() দিয়ে দেখে সন্তানের কী হলো।

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <linux/seccomp.h>

int main(void) {
    fflush(stdout);                      /* empty the buffer before fork */
    pid_t pid = fork();
    if (pid == 0) {                      /* child: lock itself down */
        if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT) != 0) {
            perror("prctl");
            _exit(1);
        }
        const char *m = "child: strict mode on, write() still works\n";
        write(STDOUT_FILENO, m, strlen(m));          /* allowed */
        open("/etc/hostname", O_RDONLY);             /* NOT allowed */
        write(STDOUT_FILENO, "child: never printed\n", 21);
        _exit(0);
    }
    int st;
    waitpid(pid, &st, 0);
    if (WIFSIGNALED(st))
        printf("parent: child killed by signal %d (%s)\n",
               WTERMSIG(st), strsignal(WTERMSIG(st)));
    else
        printf("parent: child exited normally, code %d\n", WEXITSTATUS(st));
    return 0;
}

এই পাতার রানারে একটি আসল রানের আউটপুট (প্রথম লাইনটি stderr-এ):

prctl: Invalid argument
parent: child exited normally, code 1

অবাক হওয়ার মতো, কিন্তু খুবই শিক্ষণীয়! এই রানার নিজেই একটি container-এর ভেতরে চলে, আর container runtime আগে থেকেই প্রতিটি প্রসেসে একটি seccomp filter বসিয়ে রেখেছে (নিচের bash ব্লকে Seccomp: 2 দেখবেন)। Kernel-এর নিয়ম: একবার filter mode চালু হলে প্রসেস আর strict mode-এ যেতে পারে না — seccomp-এর অবস্থা শুধু শক্ত করা যায়, বদলে অন্য পথে নেওয়া যায় না। তাই prctl ফেরত দিল EINVAL, আর সন্তান code 1 দিয়ে বেরোল। একটি সাধারণ Linux কম্পিউটারের টার্মিনালে (যেখানে shell-এর ওপর কোনো seccomp নেই) একই প্রোগ্রাম ছাপে:

child: strict mode on, write() still works
parent: child killed by signal 9 (Killed)

অর্থাৎ write() সফল, open()-এর মুহূর্তে SIGKILL — “never printed” লাইনটি কখনো আসে না। fflush(stdout) fork-এর আগে রাখা হয়েছে, যাতে বাবার বাফারে জমা লেখা সন্তানে নকল না হয় (পর্ব ১০)। খেয়াল করুন, সন্তান printf নয়, সরাসরি write() ব্যবহার করেছে: stdio লাইব্রেরি ভেতরে অন্য syscall (যেমন fstat) ডাকতে পারে, যা strict mode-এ মৃত্যু।

দ্বিতীয় রূপ: seccomp-bpf filter (২০১২)

Strict mode এত কড়া যে প্রায় কোনো বাস্তব প্রোগ্রাম এতে চলে না। Chrome-এর renderer sandbox-এর প্রয়োজনে Google-এর Will Drewry একটি নমনীয় রূপ তৈরি করেন, যা Linux 3.5 (২০১২)-এ যুক্ত হয়: seccomp-bpf. এখানে প্রসেস একটি ছোট প্রোগ্রাম kernel-কে দেয়, লেখা BPF (Berkeley Packet Filter)-এর ভাষায় — যে ভাষা আসলে নেটওয়ার্ক প্যাকেট ছাঁকার জন্য তৈরি হয়েছিল। প্রতিটি syscall-এর সময় kernel একটি ছোট কাঠামো seccomp_data বানায় — তাতে থাকে syscall নম্বর (nr), CPU আর্কিটেকচার (arch), নির্দেশের ঠিকানা আর ৬টি argument-এর কাঁচা মান — তারপর filter চালিয়ে একটি রায় নেয়:

রায় (return action)কী ঘটেকখন ব্যবহার
SECCOMP_RET_ALLOWsyscall স্বাভাবিকভাবে চলেঅনুমোদিত তালিকার syscall
SECCOMP_RET_ERRNOsyscall চলেই না; প্রোগ্রাম পায় −1 আর নির্দিষ্ট errno (যেমন EPERM)প্রোগ্রাম যেন ভদ্রভাবে ব্যর্থতা সামলায় — container-এ বেশি দেখা যায়
SECCOMP_RET_TRAPপ্রসেস SIGSYS signal পায়, যা handler দিয়ে ধরা যায়sandbox নিজেই অনুরোধটি broker-এ পাঠাতে চাইলে
SECCOMP_RET_LOGsyscall চলে, কিন্তু audit log-এ লেখা হয় (Linux 4.14 থেকে)নতুন filter পরীক্ষার সময় — কী আটকাত, তা জানতে
SECCOMP_RET_KILL_PROCESS / _THREADপুরো প্রসেস (বা থ্রেড) সাথে সাথে শেষ, যেন SIGSYS-এ মরেছেযা কখনো ঘটার কথা নয় — সন্দেহজনক আচরণ

এছাড়া SECCOMP_RET_TRACE (debugger-কে জানানো) আর SECCOMP_RET_USER_NOTIF (user-space-এর একটি তত্ত্বাবধায়ক প্রসেসকে সিদ্ধান্ত নিতে দেওয়া, Linux 5.0 থেকে) আছে। দুটি জিনিস মনে রাখুন। এক, filter উত্তরাধিকারে যায়: fork-এর সন্তান আর execve-র পরের প্রোগ্রামও একই filter-এ আটকা। দুই, filter শুধু যোগ করা যায়, সরানো যায় না। কয়েকটি filter থাকলে সবগুলো চলে, আর সবচেয়ে কড়া রায়টি জেতে (KILL > TRAP > ERRNO > … > LOG > ALLOW). নিচের ডায়াগ্রামে একটি allowlist filter-এর ভেতর দিয়ে চারটি ভিন্ন syscall-এর যাত্রা দেখুন:

1A = seccomp_data.arch2if A != X86_64      -> KILL_PROCESS3A = seccomp_data.nr4if A == 0   (read)  -> ALLOW5if A == 1   (write) -> ALLOW6if A == 231 (exit_group) -> ALLOW7if A == 63  (uname) -> ERRNO(EPERM)8default             -> KILL_PROCESS
এই syscall-এর হিসাব
প্রোগ্রাম ডাকল
seccomp_data
filter-এর রায়
ফল
  1. দরজায় প্রবেশ। প্রোগ্রাম write ডাকল; CPU kernel mode-এ ঢুকল। আসল কাজের আগে kernel seccomp_data ভরে filter চালায়। প্রথম দুই লাইন আর্কিটেকচার দেখে: x86-64 — ঠিক আছে, পরের লাইনে যাওয়া।
  2. ALLOW. লাইন ৩-এ syscall নম্বর পড়া হলো: nr = 1, অর্থাৎ write. লাইন ৪ মেলেনি, লাইন ৫ মিলল — রায় ALLOW. এবার kernel আসল sys_write চালাল, ফেরত দিল ২ (লেখা বাইট)। প্রোগ্রাম কিছুই টের পেল না।
  3. ERRNO. uname-এর নম্বর ৬৩. লাইন ৪, ৫, ৬ মেলেনি, লাইন ৭ মিলল — রায় ERRNO(EPERM). Kernel আসল sys_uname কোড ছুঁয়েও দেখল না; প্রোগ্রাম পেল −1 আর errno = 1 (EPERM). প্রোগ্রাম চাইলে এই ব্যর্থতা সামলে চলতে পারে।
  4. Default KILL. open() আসলে glibc-তে openat syscall (নম্বর ২৫৭)। তালিকার কোনো লাইন মেলেনি, তাই শেষ লাইনের default — KILL_PROCESS. প্রসেস সাথে সাথে শেষ; বাবা waitpid-এ দেখবে signal ৩১ (SIGSYS). এটিই allowlist-এর শক্তি: যা জানা নেই, তা নিষেধ।
  5. আর্কিটেকচারের ফাঁদ। x86-64 প্রসেসও int 0x80 দিয়ে পুরোনো ৩২-bit দরজায় syscall করতে পারে, যেখানে নম্বরগুলো আলাদা (৩২-bit-এ ৪ = write, ৬৩ = dup2). লাইন ২ না থাকলে ভুল নম্বর মিলিয়ে ভুল রায় হতো। তাই প্রতিটি সঠিক filter আগে arch যাচাই করে — এখানে KILL.

এবার আসল কোডে একই allowlist — strict mode-এর মতোই কয়েকটি syscall, কিন্তু filter দিয়ে, তাই এই container-এর ভেতরেও চলে (filter-এর ওপর filter বসানো যায়):

#include <stdio.h>
#include <string.h>
#include <stddef.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <sys/syscall.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#define ALLOW(n) BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_##n, 0, 1), \
                 BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)
int main(void) {
    struct sock_filter f[] = {           /* allowlist: like strict mode */
        BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)),
        ALLOW(read), ALLOW(write), ALLOW(exit), ALLOW(exit_group),
        BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),  /* default */
    };
    struct sock_fprog prog = { sizeof f / sizeof f[0], f };
    fflush(stdout);                      /* empty the buffer before fork */
    pid_t pid = fork();
    if (pid == 0) {
        prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
        if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) != 0) _exit(1);
        write(STDOUT_FILENO, "child: filter on, write() allowed\n", 34);
        open("/etc/hostname", O_RDONLY);             /* openat: not in list */
        write(STDOUT_FILENO, "child: never printed\n", 21);
        _exit(0);
    }
    int st;  waitpid(pid, &st, 0);
    if (WIFSIGNALED(st))
        printf("parent: child killed by signal %d (%s)\n", WTERMSIG(st), strsignal(WTERMSIG(st)));
    else
        printf("parent: child exited, code %d\n", WEXITSTATUS(st));
}

একটি আসল রানের আউটপুট:

child: filter on, write() allowed
parent: child killed by signal 31 (Bad system call)

struct sock_filter-এর প্রতিটি উপাদান একটি BPF নির্দেশ: BPF_LD লাইনটি seccomp_data থেকে syscall নম্বর A register-এ আনে; ALLOW(n) macro দুটি নির্দেশ বানায় — “A যদি __NR_n হয় তবে পরের নির্দেশে (ALLOW), নইলে একটি লাফ দিয়ে পরের পরীক্ষায়”। সন্তান write() করতে পারল, কিন্তু openat তালিকায় নেই, তাই default KILL_PROCESS — বাবা দেখল signal ৩১, strsignal-এর ভাষায় “Bad system call”. Strict mode-এর SIGKILL (৯)-এর সাথে পার্থক্যটি খেয়াল করুন। ছোট রাখতে এখানে arch যাচাই বাদ দেওয়া হয়েছে — বাস্তব filter-এ সেটি অবশ্যই থাকবে, ঠিক পরের প্রোগ্রামের মতো। PR_SET_NO_NEW_PRIVS কেন লাগল, তা-ও পরের অংশে।

৪ · ERRNO, no_new_privs আর বাস্তবে seccomp বসানো

সব নিষেধ মৃত্যুদণ্ড হওয়ার দরকার নেই। অনেক প্রোগ্রাম কোনো syscall ব্যর্থ হলে বিকল্প পথ নেয় — তাই container runtime-গুলো সাধারণত নিষিদ্ধ syscall-এ EPERM ফেরত দেয়। নিচের প্রোগ্রাম শুধু একটি syscall (uname) আটকায়, বাকি সব চলতে দেয়, আর এবার আর্কিটেকচারও যাচাই করে:

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stddef.h>
#include <unistd.h>
#include <sys/prctl.h>
#include <sys/utsname.h>
#include <sys/syscall.h>
#include <linux/audit.h>
#include <linux/filter.h>
#include <linux/seccomp.h>

int main(void) {
    struct sock_filter f[] = {           /* a tiny BPF program */
        BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, arch)),
        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_X86_64, 1, 0),
        BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS),   /* wrong arch */
        BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr)),
        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_uname, 0, 1),
        BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | EPERM),  /* uname */
        BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),          /* the rest */
    };
    struct sock_fprog prog = { sizeof f / sizeof f[0], f };
    struct utsname u;
    printf("before: uname() = %d, sysname = %s\n", uname(&u), u.sysname);
    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) { perror("no_new_privs"); return 1; }
    if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) != 0) { perror("seccomp"); return 1; }
    int r = uname(&u);
    int e = errno;
    printf("after:  uname() = %d, errno = %d (%s)\n", r, e, strerror(e));
    printf("after:  getpid() still allowed: %s\n", getpid() > 0 ? "yes" : "no");
    return 0;
}

একটি আসল রানের আউটপুট:

before: uname() = 0, sysname = Linux
after:  uname() = -1, errno = 1 (Operation not permitted)
after:  getpid() still allowed: yes

Filter বসানোর আগে uname() সফল (০)। পরে একই কল −1 আর errno = 1 (EPERM) — kernel-এর আসল uname কোড চলেইনি, filter মাঝপথে উত্তর দিয়েছে। getpid() আর printf-এর ভেতরের write আগের মতোই চলছে, কারণ default রায় ALLOW. এই ধরনের “শুধু কয়েকটি নিষেধ” তালিকাকে বলে denylist; এটি সহজ, কিন্তু দুর্বল — নতুন kernel-এ নতুন syscall এলে সেটি আপনাআপনি অনুমোদিত হয়ে যায়। নিরাপত্তার জন্য ভালো অভ্যাস allowlist: যা লাগে তা ALLOW, বাকি সব ERRNO বা KILL. errno আলাদা ভ্যারিয়েবলে আগে রাখা হয়েছে, কারণ পরের printf নিজেই errno বদলাতে পারে।

no_new_privs — কেন filter বসানোর আগে লাগে

Filter বসানোর আগে দুই প্রোগ্রামেই prctl(PR_SET_NO_NEW_PRIVS, 1) ডাকা হয়েছে। এটি প্রসেসে একটি পতাকা বসায়: এই প্রসেস আর তার উত্তরসূরিরা execve দিয়ে আর কখনো নতুন ক্ষমতা পাবে না — setuid বিট (পর্ব ৪৭) বা ফাইলের capability আর কাজ করবে না। পতাকাটি একবার বসালে আর নামানো যায় না। কেন seccomp-এর জন্য এটি দরকার? না থাকলে একজন সাধারণ ব্যবহারকারী filter দিয়ে কোনো setuid-root প্রোগ্রামের syscall-এ ভুয়া ফল দিয়ে তাকে বিভ্রান্ত করতে পারতেন। তাই kernel-এর নিয়ম: CAP_SYS_ADMIN ক্ষমতা না থাকলে seccomp filter বসাতে হলে আগে no_new_privs লাগবে। এটি Linux 3.5-এ seccomp-bpf-এর সাথেই এসেছে, আর এখন systemd বা Docker-এ আলাদা বিকল্প হিসেবেও পাওয়া যায়।

বাস্তবে কেউ হাতে BPF লেখে না

উপরের মতো কাঁচা BPF শেখার জন্য ভালো, কিন্তু বাস্তবে ব্যবহার হয় libseccomp লাইব্রেরি বা তৈরি করা profile. আপনার নিজের সার্ভারে সবচেয়ে সহজ রক্ষাকবচ সম্ভবত systemd-এর sandbox বিকল্পগুলো — একটি সার্ভিস ফাইলে কয়েকটি লাইন:

[Service]
ExecStart=/usr/local/bin/myapp
# (systemd allows comments only on their own lines, never after a value)
# never run a web app as root
User=myapp
# the no_new_privs flag
NoNewPrivileges=yes
# seccomp allowlist of common syscall groups, plus the arch check from above
SystemCallFilter=@system-service
SystemCallArchitectures=native
# keep only "bind port < 1024" in the bounding set
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
# /usr, /etc read-only; /home invisible; its own /tmp; one writable folder
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
ReadWritePaths=/var/lib/myapp

তারপর systemd-analyze security myapp.service চালালে systemd প্রতিটি বিকল্প দেখে একটি “exposure” নম্বর দেয় — কোন সুরক্ষা বাকি আছে, তার তালিকাসহ। Container-এর জগতে Docker নিজের ডিফল্ট seccomp profile (পর্ব ৫-এ উল্লেখ করা) প্রতিটি container-এ বসায়: একটি allowlist, যার বাইরের কয়েক ডজন ঝুঁকিপূর্ণ syscall (যেমন kernel module লোড, ঘড়ি বদলানো) EPERM পায়। --security-opt seccomp=profile.json দিয়ে নিজের profile দেওয়া যায়। উল্টো দিকে, --privileged বা seccomp=unconfined এই সুরক্ষা প্রায় পুরোটাই তুলে দেয় — এগুলো দেখলে আগে প্রশ্ন করুন, সত্যিই কি দরকার।

seccomp-এর সীমা: filter শুধু syscall নম্বর আর argument-এর কাঁচা মান দেখে; pointer অনুসরণ করে মেমরি পড়তে পারে না। তাই “openat চলবে, কিন্তু শুধু /etc/myapp-এর ফাইলে” — এমন নিয়ম seccomp দিয়ে লেখা যায় না; path-এর স্ট্রিং থাকে user মেমরিতে, যা filter চলার পরেও বদলে যেতে পারে। ফাইল-স্তরের নিয়মের জন্য লাগে LSM — SELinux, AppArmor বা Landlock, যা kernel নিজে path বা label খুঁজে বের করার পরে যাচাই করে।

৫ · আরও ইট — capability, namespace, Landlock আর chroot-এর সীমা

Linux capabilities. পুরোনো Unix-এ ক্ষমতা ছিল দুই রকম: root (সব) আর বাকিরা (প্রায় কিছু না)। Linux 2.2 (১৯৯৯) থেকে root-এর ক্ষমতা প্রায় ৪০টি টুকরোয় ভাগ করা: CAP_NET_BIND_SERVICE (১০২৪-এর নিচের port-এ বসা), CAP_NET_RAW (কাঁচা প্যাকেট, যেমন ping), CAP_SYS_TIME (ঘড়ি বদলানো), CAP_SYS_MODULE (kernel module লোড), CAP_SYS_ADMIN (এত বিচিত্র কাজের অনুমতি যে kernel ডেভেলপাররাই একে ঠাট্টা করে “নতুন root” বলেন)। একটি ওয়েব সার্ভারের ৮০ নম্বর port-এ বসতে শুধু প্রথমটি লাগে; বাকি সব ছেঁটে ফেললে সার্ভার আক্রান্ত হলেও আক্রমণকারী module লোড বা ঘড়ি বদলাতে পারবে না। প্রতিটি প্রসেসের কয়েকটি capability-সেট থাকে — সবচেয়ে গুরুত্বপূর্ণ effective (এখন যা খাটে, CapEff) আর bounding (ভবিষ্যতে সর্বোচ্চ যা পাওয়া সম্ভব, CapBnd). এগুলো একটি ৬৪-bit সংখ্যার bit হিসেবে লেখা হয়: bit n চালু মানে capability নম্বর n আছে। (পর্ব ৪৬-এর access matrix-এর capability list থেকে এটি আলাদা জিনিস।)

Namespace. seccomp ঠিক করে প্রসেস কী করতে পারবে; namespace ঠিক করে সে কী দেখতে পাবে। PID namespace-এ প্রসেস শুধু নিজের দলের প্রসেস দেখে, mount namespace-এ নিজের file system-এর দৃশ্য, network namespace-এ নিজের নেটওয়ার্ক, user namespace-এ নিজের UID-জগৎ। Container এই namespace আর cgroup দিয়েই তৈরি — বিস্তারিত পর্ব ৫১-এ।

Landlock. SELinux বা AppArmor-এর নীতি লেখেন সিস্টেম প্রশাসক, root হিসেবে। কিন্তু একটি সাধারণ প্রোগ্রাম যদি নিজেই বলতে চায় “আমি শুধু এই দুটি ফোল্ডারে হাত দেব”? Linux 5.13 (২০২১)-এ যুক্ত Landlock ঠিক এটি দেয় — অধিকারবিহীন (unprivileged) প্রসেসের নিজের জন্য ফাইল-প্রবেশের sandbox, root ছাড়াই। পরের সংস্করণগুলোতে আরও নিয়ম যোগ হয়েছে (যেমন TCP port-এ bind/connect নিয়ন্ত্রণ), তাই প্রোগ্রাম আগে kernel-কে জিজ্ঞেস করে কোন “ABI সংস্করণ” সমর্থিত। ব্যবহারের ধাপ তিনটি:

/* 1. which kinds of access will this ruleset govern? */
struct landlock_ruleset_attr attr = {
    .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_WRITE_FILE /* | ... */ };
int rs = syscall(SYS_landlock_create_ruleset, &attr, sizeof attr, 0);

/* 2. add one rule: reading is allowed beneath /usr/share/myapp */
struct landlock_path_beneath_attr pb = {
    .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE,
    .parent_fd = open("/usr/share/myapp", O_PATH | O_CLOEXEC) };
syscall(SYS_landlock_add_rule, rs, LANDLOCK_RULE_PATH_BENEATH, &pb, 0);

/* 3. lock myself in (no_new_privs first, just like seccomp) */
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
syscall(SYS_landlock_restrict_self, rs, 0);   /* irreversible, inherited */

chroot কেন নিরাপত্তার সীমানা নয়। chroot("/srv/jail") প্রসেসকে বলে “এখন থেকে / মানে /srv/jail”. পুরোনো অনেক গাইডে একে “chroot jail” বলা হয়, যেন এটি কারাগার। আসলে এটি শুধু path খোঁজার শুরুর বিন্দু বদলায়, আর কিছু না। প্রসেস তখনো একই UID, একই capability, একই PID আর নেটওয়ার্ক-জগৎ, আর সব syscall হাতে পায়। ফলে root ক্ষমতা থাকলে chroot-এর বাইরে বেরোনোর একাধিক সুপরিচিত পথ আছে; root ছাড়াও chroot-এর ভেতরের প্রসেস বাইরের প্রসেসকে signal পাঠাতে বা নেটওয়ার্কে যেতে পারে। Linux-এর নিজের chroot(2) man পাতা স্পষ্ট বলে, এটি কোনো নিরাপত্তা-উদ্দেশ্যে তৈরি নয় — না প্রসেসকে পুরো sandbox করতে, না file system-এর syscall সীমিত করতে। প্রতিরক্ষার সঠিক পথ: mount namespace + pivot_root (container যেভাবে করে), root-ক্ষমতা ছেড়ে দেওয়া, তার ওপর seccomp আর LSM.

এবার এই পাতার রানার-কন্টেইনারের নিজের sandbox-অবস্থা দেখা যাক — শুধু-পড়ার কমান্ড, কিছু বদলায় না:

echo "== seccomp, no_new_privs and capabilities of this shell =="
grep -E '^(CapEff|CapBnd|NoNewPrivs|Seccomp|Seccomp_filters):' /proc/self/status
echo "== which LSMs are active? =="
if [ -r /sys/kernel/security/lsm ]; then
    cat /sys/kernel/security/lsm; echo
else
    echo "/sys/kernel/security/lsm: not visible here (common inside a container)"
fi
echo "AppArmor enabled : $(cat /sys/module/apparmor/parameters/enabled 2>/dev/null || echo unknown)"
echo "SELinux fs found : $([ -d /sys/fs/selinux ] && echo yes || echo no)"
echo "my AppArmor label: $(cat /proc/self/attr/apparmor/current 2>/dev/null || echo none)"
echo "== the bounding set, decoded =="
capsh --decode="$(awk '/^CapBnd/ {print $2}' /proc/self/status)"

একটি আসল রানের আউটপুট:

== seccomp, no_new_privs and capabilities of this shell ==
CapEff:	0000000000000000
CapBnd:	00000000800405fb
NoNewPrivs:	0
Seccomp:	2
Seccomp_filters:	1
== which LSMs are active? ==
/sys/kernel/security/lsm: not visible here (common inside a container)
AppArmor enabled : Y
SELinux fs found : no
my AppArmor label: crun (unconfined)
== the bounding set, decoded ==
0x00000000800405fb=cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_sys_chroot,cap_setfcap

লাইন ধরে পড়ুন। Seccomp: 2 মানে filter mode (০ = বন্ধ, ১ = strict), আর একটি filter বসানো — container runtime-এর; এজন্যই আগের strict-mode প্রোগ্রাম EINVAL পেয়েছিল। CapEff শূন্য, কারণ shell চলছে সাধারণ ব্যবহারকারী wandbox হিসেবে। CapBnd-এ ১১টি bit চালু — runtime বাকি সব capability আগেই ছেঁটে দিয়েছে; cap_sys_admin বা cap_sys_module নেই। হিসাব মিলিয়ে দেখুন: 0x5fb = bit ০, ১, ৩–৮, ১০; 0x40000 = bit ১৮ (sys_chroot); 0x80000000 = bit ৩১ (setfcap). Container-এর ভেতরে securityfs দেখা যাচ্ছে না, কিন্তু AppArmor module চালু (Y), আর আমাদের প্রসেসের label crun (unconfined) — অর্থাৎ crun নামের একটি AppArmor profile, “unconfined” মোডে। সম্ভবত host-টি AppArmor-ওয়ালা একটি Ubuntu-ধরনের সিস্টেম; container-এর ভেতর থেকে এর বেশি নিশ্চিত বলা যায় না। নিজের সাধারণ Linux ডেস্কটপে cat /sys/kernel/security/lsm পড়া গেলে সেখানে lockdown,capability,landlock,yama,apparmor বা …,selinux,bpf-এর মতো তালিকা দেখবেন — distro-ভেদে আলাদা।

৬ · DAC বনাম MAC — মালিক ঠিক করবেন, না নীতি?

পর্ব ৪৭-এর rwx অনুমতি হলো DAC (Discretionary Access Control): ফাইলের মালিক নিজের বিবেচনায় ঠিক করেন কে পড়বে, কে লিখবে। chmod 644 দিলে সবাই পড়তে পারে — মালিকের ইচ্ছা। সমস্যা দুটি। এক, মালিকের নামে চলা যেকোনো প্রোগ্রাম মালিকের সব ক্ষমতা পায়; আক্রান্ত PDF reader-ও chmod চালাতে পারে। দুই, root সব DAC নিয়মের ঊর্ধ্বে। MAC (Mandatory Access Control)-এ সিদ্ধান্ত নেয় সিস্টেমের কেন্দ্রীয় নীতি, যা প্রশাসক লোড করেন আর kernel প্রয়োগ করে; ফাইলের মালিক তা ঢিলা করতে পারেন না, আর ভালোভাবে নকশা করা নীতিতে root-এর প্রসেসও নিজের নির্দিষ্ট গণ্ডির বাইরে যেতে পারে না।

একটি সরকারি অফিসের ফাইল ক্যাবিনেটের কথা ভাবুন। DAC হলো: প্রতিটি কর্মকর্তা নিজের ড্রয়ারের চাবি যাকে খুশি দিতে পারেন। MAC হলো: ফাইলের ওপর লাল “গোপনীয়” সিল, আর অফিসের নিয়মে এই সিলের ফাইল কেবল নির্দিষ্ট ছাড়পত্রের লোকই খুলতে পারেন — ফাইল যাঁর টেবিলেই থাকুক, তিনি চাইলেও পিয়নকে দেখাতে পারবেন না। নিয়মটি ব্যক্তির ইচ্ছার ওপর নয়, প্রতিষ্ঠানের ওপর।

Linux-এ দুটি একসাথে চলে: kernel আগে DAC যাচাই করে, DAC অনুমতি দিলে তারপর LSM (Linux Security Modules)-এর hook-এ MAC নীতি যাচাই হয়। দুটিতেই “হ্যাঁ” লাগবে। MAC কখনো DAC-এর নিষেধ উল্টে দেয় না — শুধু বাড়তি নিষেধ যোগ করে।

Bell–LaPadula — গোপনীয়তার মডেল

১৯৭৩ সালে MITRE-এর David Bell আর Leonard LaPadula মার্কিন প্রতিরক্ষা বিভাগের প্রয়োজনে একটি গাণিতিক মডেল দেন — Bell–LaPadula মডেল — যার লক্ষ্য একটাই: গোপন তথ্য যেন নিচের স্তরে চুঁইয়ে না পড়ে। প্রতিটি ব্যবহারকারী বা প্রসেস (subject)-এর একটি ছাড়পত্র-স্তর, আর প্রতিটি ফাইল (object)-এর একটি শ্রেণি: Unclassified < Confidential < Secret < Top Secret. দুটি নিয়ম:

  • Simple security property — “no read up”: নিজের স্তরের চেয়ে উঁচু শ্রেণির ফাইল পড়া যাবে না।
  • ★-property — “no write down”: নিজের স্তরের চেয়ে নিচু শ্রেণির ফাইলে লেখা যাবে না।

দ্বিতীয় নিয়মটিই চতুর। Secret স্তরের একজন অফিসার Secret ফাইল পড়তে পারেন — কিন্তু তিনি (বা তাঁর নামে চলা কোনো ক্ষতিকর প্রোগ্রাম) যদি সেই তথ্য একটি Unclassified ফাইলে লিখে দেন, সবাই পড়ে ফেলবে। No write down এই ফাঁস বন্ধ করে।

Biba — অখণ্ডতার মডেল

১৯৭৭ সালে Kenneth Biba উল্টো প্রশ্ন করেন: তথ্য ফাঁস নয়, বিশ্বস্ত তথ্য যেন অবিশ্বস্ত উৎস দিয়ে নষ্ট না হয়। Biba-র নিয়ম Bell–LaPadula-র ঠিক উল্টো: “no read down” (নিচু-অখণ্ডতার, যেমন ইন্টারনেট থেকে আসা, ডেটা পড়ে উঁচু-অখণ্ডতার প্রসেস নিজেকে দূষিত করবে না) আর “no write up” (নিচু-অখণ্ডতার প্রসেস সিস্টেম ফাইলে লিখতে পারবে না)। Windows-এর integrity level-এর ডিফল্ট নিয়ম (Low প্রসেস Medium ফাইলে লিখতে পারে না) Biba-র “no write up”-এর একটি সরল প্রয়োগ।

উদাহরণ: Secret স্তরের প্রসেসTop Secret ফাইলSecret ফাইলConfidential ফাইল
Bell–LaPadula (গোপনীয়তা)পড়া ✗, লেখা ✓পড়া ✓, লেখা ✓পড়া ✓, লেখা ✗
Biba (একই স্তরকে অখণ্ডতা ধরে)পড়া ✓, লেখা ✗পড়া ✓, লেখা ✓পড়া ✗, লেখা ✓

দুটি মডেল একসাথে খাটালে প্রসেস কেবল নিজের স্তরেই পড়তে-লিখতে পারে — নিরাপদ, কিন্তু অচল। তাই বাস্তব সিস্টেম মডেলগুলোর অংশবিশেষ নেয়। SELinux-এর MLS নীতি Bell–LaPadula-ধরনের স্তর দিতে পারে, কিন্তু সাধারণ সার্ভারে যে অংশটি সবচেয়ে বেশি কাজে আসে তা হলো type enforcement — পরের অংশ।

৭ · SELinux — label, type enforcement আর denial পড়া

SELinux তৈরি করে মার্কিন NSA, ২০০০ সালে মুক্ত সফটওয়্যার হিসেবে প্রকাশ করে। একে kernel-এ আনার আলোচনা থেকেই জন্ম নেয় LSM কাঠামো, যা Linux 2.6 (২০০৩)-এ যুক্ত হয়। আজ Fedora, RHEL, CentOS Stream, Rocky/AlmaLinux আর Android-এ এটি ডিফল্টে চালু।

সবকিছুর একটি label. SELinux-এ প্রতিটি প্রসেস আর প্রতিটি ফাইল, socket, port-এর একটি security context থাকে, চার ভাগে: user:role:type:level. যেমন Apache ওয়েব সার্ভারের প্রসেস system_u:system_r:httpd_t:s0, আর ওয়েবসাইটের ফাইল system_u:object_r:httpd_sys_content_t:s0. প্রতিদিনের কাজে প্রায় সব সিদ্ধান্ত হয় তৃতীয় ভাগ — type — দিয়ে; প্রসেসের type-কে বলে domain. শেষ ভাগ s0 হলো MLS/MCS স্তর, যা দিয়ে একই type-এর দুটি container-কে আলাদা রাখা হয় (যেমন s0:c123,c456). Label ফাইল সিস্টেমের extended attribute-এ জমা থাকে, তাই নাম বদলালেও label ফাইলের সাথেই যায়।

Type enforcement. নীতি (policy) হলো হাজারো allow নিয়মের সংকলন, যেমন (সরলীকৃত):

allow httpd_t httpd_sys_content_t : file { read open getattr };
allow httpd_t httpd_log_t         : file { append open };

অর্থ: httpd_t domain-এর প্রসেস httpd_sys_content_t type-এর ফাইল পড়তে পারবে, লগ ফাইলে শুধু যোগ করতে পারবে। যা লেখা নেই, তা নিষেধ — default deny. ফলে Apache-তে বাগ কাজে লাগিয়ে কেউ ঢুকলেও, প্রসেস root হলেও, সে /home-এর ফাইল, /etc/shadow বা SSH key পড়তে পারে না, কারণ httpd_t-এর জন্য সেই type-এর কোনো allow নেই। ডিফল্ট “targeted” নীতিতে শুধু নেটওয়ার্ক-সার্ভিসের মতো ঝুঁকিপূর্ণ প্রোগ্রামগুলো নিজস্ব domain-এ আটকা থাকে; সাধারণ ব্যবহারকারীর প্রোগ্রাম চলে unconfined_t-এ।

তিনটি মোড. Enforcing — নীতি প্রয়োগ হয়, নিষেধ মানে EACCES. Permissive — নীতি হিসাব হয় আর প্রতিটি “নিষেধ হতো” ঘটনা লগ হয়, কিন্তু কাজটি চলতে দেওয়া হয়; শুধু সমস্যা খোঁজার জন্য। Disabled — SELinux নেই, নতুন ফাইল label-ও পায় না, তাই পরে চালু করতে পুরো ফাইল সিস্টেম আবার label করতে হয়। পরিস্থিতি দেখার শুধু-পড়ার কমান্ড (উদাহরণ আউটপুট, Fedora/RHEL-ধরনের সিস্টেম):

$ getenforce
Enforcing
$ sestatus | head -5
SELinux status:                 enabled
SELinuxfs mount:                /sys/fs/selinux
SELinux root directory:         /etc/selinux
Loaded policy name:             targeted
Current mode:                   enforcing
$ ps -eZ | grep httpd | head -1
system_u:system_r:httpd_t:s0       1234 ?        00:00:00 httpd
$ ls -Z /var/www/html/
  system_u:object_r:httpd_sys_content_t:s0 index.html
unconfined_u:object_r:user_home_t:s0       report.html

দ্বিতীয় ফাইলটির label অদ্ভুত — user_home_t, যেন সে home ফোল্ডারে আছে। খুব পরিচিত একটি ঘটনা: প্রশাসক ফাইলটি নিজের home-এ বানিয়ে mv দিয়ে এনেছেন। mv label সহ ফাইল সরায়; cp নতুন ফাইল বানায়, যা গন্তব্য ফোল্ডারের label পায়। নিচে kernel কীভাবে সিদ্ধান্ত নেয়, ধাপে ধাপে দেখুন:

  1. httpd প্রসেসhttpd_tঅনুরোধ করে
  2. DACrwx, UIDপ্রথম যাচাই
  3. SELinuxAVC + policyদ্বিতীয় যাচাই
  4. ফাইলtcontextখুলবে কি?
এই মুহূর্তে
অনুরোধ
ফাইলের type
DAC (rw-r--r--)
SELinux রায়
ফল
  1. কে চাইছে। ব্রাউজার থেকে /index.html চাওয়া হলো। httpd প্রসেসের context system_u:system_r:httpd_t:s0 — domain httpd_t. Kernel প্রতিটি সিদ্ধান্তে চারটি জিনিস দেখে: উৎসের type, লক্ষ্যের type, বস্তুর শ্রেণি (file) আর অনুমতি (read).
  2. index.html: দুই যাচাই-ই পাস। প্রথমে DAC: ফাইলটি rw-r--r--, “অন্যরা” পড়তে পারে — পাস। তারপর SELinux প্রশ্ন করে (httpd_t, httpd_sys_content_t, file, read). নীতিতে allow নিয়ম আছে — ALLOW. উত্তর ক্যাশ হয় AVC (Access Vector Cache)-এ, তাই পরের বার খোঁজা দ্রুত।
  3. report.html: DAC পাস। এই ফাইলও rw-r--r--, তাই শুধু DAC থাকলে Apache এটি পড়তে পারত। কিন্তু এর type user_home_t — mv-এর সাথে home-এর label চলে এসেছে। SELinux এবার প্রশ্ন করে (httpd_t, user_home_t, file, read).
  4. নিয়ম নেই → DENY. নীতিতে httpd_t-কে user_home_t পড়তে দেওয়ার কোনো নিয়ম নেই, আর default deny. open() পেল EACCES, ব্রাউজারে 403 Forbidden, আর audit লগে একটি AVC denial লেখা হলো — কে, কী, কোন label, কোন অনুমতি। এটিই সমস্যা খোঁজার মূল সূত্র।
  5. Permissive-এ একই হিসাব। কেউ সাময়িকভাবে setenforce 0 দিলে kernel একই সিদ্ধান্ত হিসাব করে — DENY হতো — কিন্তু ফাইল খুলতে দেয় আর লগে লেখে permissive=1. সমস্যা যে SELinux-ই, তা নিশ্চিত হয়; কিন্তু এই অবস্থায় পুরো সার্ভার অরক্ষিত। তাই পরীক্ষা শেষে সাথে সাথে setenforce 1.
  6. সঠিক সমাধান: label ঠিক করা। restorecon -v /var/www/html/report.html ফোল্ডারের নিয়ম অনুযায়ী type বদলে httpd_sys_content_t করে। এবার (httpd_t, httpd_sys_content_t, file, read) — ALLOW, 200 OK. Enforcing চালুই আছে; নীতিতে একটি নিয়মও ঢিলা করতে হয়নি।

Denial পড়া — audit2why-র কাজ

Denial-এর লগ প্রথমে ভয়ংকর দেখায়, কিন্তু এটি একটি কাঠামোবদ্ধ বাক্য। সাধারণত /var/log/audit/audit.log-এ থাকে (উদাহরণ, পড়ার সুবিধার জন্য লাইন ভাঙা):

type=AVC msg=audit(1729900000.123:456): avc:  denied  { read } for
  pid=1234 comm="httpd" name="report.html" dev="nvme0n1p3" ino=789012
  scontext=system_u:system_r:httpd_t:s0
  tcontext=unconfined_u:object_r:user_home_t:s0
  tclass=file permissive=0

পড়ার নিয়ম: { read } = কোন অনুমতি চাওয়া হয়েছিল; comm = কোন প্রোগ্রাম; scontext = কে চাইছে; tcontext = কার কাছে; tclass = বস্তুর শ্রেণি; permissive=0 = সত্যিই আটকানো হয়েছে। ausearch -m AVC -ts recent | audit2why প্রতিটি denial-এর সম্ভাব্য কারণ বলে — যেমন “Missing type enforcement (TE) allow rule”, বা কোনো boolean বন্ধ আছে (যেমন httpd_can_network_connect_db, যা চালু করলে ওয়েব সার্ভার ডেটাবেসে সংযোগ করতে পারে)। এরপর সমাধানের ক্রম মোটামুটি এরকম:

  1. Label ভুল? restorecon -Rv <path>; নতুন জায়গায় ওয়েবসাইট রাখলে স্থায়ী নিয়ম: semanage fcontext -a -t httpd_sys_content_t "/srv/site(/.*)?", তারপর restorecon. (chcon-এর পরিবর্তন পরের relabel-এ মুছে যায়।)
  2. বৈধ কাজ, কিন্তু ডিফল্টে বন্ধ? উপযুক্ত boolean খুঁজে setsebool -P <name> on.
  3. সার্ভিস অন্য port-এ? semanage port -a -t http_port_t -p tcp 9876 (port-টি আগেই অন্য type-এ থাকলে -a-এর বদলে -m).
  4. সত্যিই নতুন নিয়ম দরকার? audit2allow একটি নীতি-module প্রস্তাব করতে পারে — কিন্তু অন্ধভাবে বসালে “যা ভেঙেছিল, সব অনুমতি দাও” হয়ে যায়। প্রতিটি লাইন পড়ে বুঝে তবেই।

“SELinux বন্ধ করে দিন” কেন খারাপ পরামর্শ: এটি একটি label-সমস্যা মেটাতে পুরো সার্ভারের একটি স্বাধীন সুরক্ষা-স্তর সরিয়ে দেয়। বহু বাস্তব আক্রমণে ওয়েব অ্যাপের বাগ কাজে লাগানোর পরেও SELinux আক্রমণকারীকে httpd_t-এর ছোট গণ্ডিতে আটকে রেখেছে। Disabled করলে নতুন ফাইলের label-ও তৈরি হয় না, পরে চালু করতে পুরো relabel লাগে। নতুন kernel-এ চলমান অবস্থায় SELinux বন্ধ করার সুবিধাটিই সরিয়ে দেওয়া হয়েছে। যদি একটি নির্দিষ্ট সার্ভিস নিয়ে পরীক্ষা করতেই হয়, শুধু সেই domain-কে permissive করা যায় (semanage permissive -a httpd_t), বাকি সিস্টেম enforcing রেখে — আর কাজ শেষে ফেরত।

৮ · AppArmor — path দিয়ে লেখা profile, আর দুটির তুলনা

AppArmor-এর শুরু ১৯৯০-এর দশকের শেষে Immunix কোম্পানির “SubDomain” নামে; পরে Novell/SUSE এটি এগিয়ে নেয়, আর ২০০৭ সাল থেকে Ubuntu-তে এটি ডিফল্ট। Debian, SUSE আর তাদের উত্তরসূরিরাও এটি ব্যবহার করে। Linux 2.6.36 (২০১০)-এ এটি মূল kernel-এ যুক্ত হয়। SELinux যেখানে প্রতিটি ফাইলে label লাগায়, AppArmor সেখানে প্রতিটি প্রোগ্রামের জন্য একটি profile লেখে, আর profile-এ নিয়ম লেখা হয় ফাইলের path দিয়ে। নিজের একটি প্রোগ্রামের জন্য profile দেখতে এরকম (সরলীকৃত):

#include <tunables/global>

/usr/local/bin/myapp {
  #include <abstractions/base>

  /usr/local/bin/myapp      mr,     # map + read its own binary
  /etc/myapp/**             r,      # read its config
  /var/lib/myapp/**         rw,     # its data
  network inet stream,              # TCP only
  deny /home/**             rwx,    # never touch home directories
}

প্রতিটি profile দুই মোডের একটিতে থাকে: enforce (নিয়ম প্রয়োগ, লঙ্ঘন আটকানো ও লগ) আর complain (শুধু লগ — SELinux-এর permissive-এর মতো, কিন্তু প্রতিটি profile আলাদাভাবে)। নতুন profile লেখার স্বাভাবিক পথ: complain মোডে প্রোগ্রাম চালিয়ে সব কাজ করা, লগ থেকে aa-logprof দিয়ে নিয়ম বাছাই, তারপর aa-enforce. অবস্থা দেখার কমান্ড (উদাহরণ আউটপুট, root লাগে):

$ sudo aa-status
apparmor module is loaded.
58 profiles are loaded.
52 profiles are in enforce mode.
   /usr/bin/man
   /usr/sbin/cupsd
   ...
6 profiles are in complain mode.
   ...
2 processes have profiles defined.
2 processes are in enforce mode.
   /usr/sbin/cupsd (1187)
   /usr/sbin/cups-browsed (1203)
0 processes are in complain mode.
0 processes are unconfined but have a profile defined.
$ sudo journalctl -k | grep 'apparmor="DENIED"' | tail -1
... apparmor="DENIED" operation="open" profile="/usr/local/bin/myapp"
    name="/home/rahim/notes.txt" requested_mask="r" denied_mask="r" ...

শেষ লাইনটি SELinux-এর AVC-র মতোই পড়ুন: কোন profile, কোন কাজ (open), কোন path, কোন অনুমতি। Docker-ও Ubuntu-তে প্রতিটি container-এ নিজের docker-default AppArmor profile বসায়। (আগের bash ব্লকের crun (unconfined) label-টিও এই ব্যবস্থারই চিহ্ন।)

বিষয়SELinuxAppArmor
পরিচয়ের ভিত্তিlabel (inode-এর extended attribute)path (নাম)
নীতির এককtype-এর মধ্যে allow নিয়ম, পুরো সিস্টেমের জন্যপ্রতিটি প্রোগ্রামের আলাদা profile
ডিফল্ট কোথায়Fedora, RHEL-পরিবার, AndroidUbuntu, Debian, SUSE
পরীক্ষার মোডpermissive (পুরো সিস্টেম বা একটি domain)complain (প্রতি profile)
নাম বদলানো / hard linklabel ফাইলের সাথে থাকে — নাম যা-ই হোকনিয়ম নতুন নামের ওপর খাটে, তাই profile-এ সাবধানে নিয়ম লিখতে হয়
স্তরভিত্তিক MLSসমর্থন করেনেই
শেখাকঠিন, কিন্তু খুব সূক্ষ্ম নিয়ন্ত্রণসহজে পড়া যায়, ছোট শুরু করা সহজ

বাস্তবে বড় কথা হলো নিজের distro যেটি দেয়, সেটি চালু রাখা আর তার লগ পড়তে শেখা। একই kernel-এ capability, Yama, Landlock, lockdown-এর মতো ছোট LSM পাশাপাশি চলে, আর SELinux বা AppArmor-এর মতো বড় মডিউলের একটি সাধারণত প্রধান থাকে।

৯ · Secure Boot আর measured boot গভীরে — key, shim, TPM আর attestation

এ পর্যন্ত যত sandbox দেখলেন, সবই kernel-এর ওপর ভরসা করে। কিন্তু kernel নিজেই যদি বদলানো হয়? পর্ব ৭-এ দেখেছেন Secure Boot বুটের প্রতিটি ধাপের স্বাক্ষর যাচাই করে একটি chain of trust বানায়। এবার দেখব key-গুলো কে রাখে, কে কাকে বদলাতে পারে, Linux কীভাবে এতে ঢোকে, আর “যাচাই” ছাড়াও “মাপা” কেন দরকার।

চার স্তরের key-ব্যবস্থা

Variableকী থাকেকে বদলাতে পারেসাধারণ PC-তে
PK (Platform Key)একটিমাত্র key — যন্ত্রের মালিকপুরোনো PK-র স্বাক্ষরেPC নির্মাতার key
KEK (Key Exchange Key)db/dbx হালনাগাদের অনুমতিপ্রাপ্তদের keyPK-র স্বাক্ষরেনির্মাতা + Microsoft-এর KEK CA
db (signature database)অনুমোদিত সার্টিফিকেট বা hashKEK-এর স্বাক্ষরেWindows-এর CA, তৃতীয় পক্ষের জন্য Microsoft UEFI CA, নির্মাতার CA
dbx (forbidden)বাতিল সার্টিফিকেট বা hashKEK-এর স্বাক্ষরেOS আপডেটের সাথে নিয়মিত বাড়ে

PK না থাকলে firmware থাকে “Setup Mode”-এ — তখন যে-কেউ key বসাতে পারে; PK বসালে “User Mode”, আর তখন প্রতিটি পরিবর্তনে উপরের স্তরের স্বাক্ষর লাগে। তাই ক্ষতিকর প্রোগ্রাম OS থেকে চুপচাপ নিজের key db-তে ঢোকাতে পারে না। অভিজ্ঞ মালিক চাইলে firmware-এর মেনু থেকে Setup Mode-এ নিয়ে পুরো key-শিকল নিজের key দিয়ে বদলাতে পারেন (Linux-এ sbctl-এর মতো tool দিয়ে)। তবে সাবধান: অনেক graphics কার্ডের firmware (option ROM) Microsoft-এর key-তে স্বাক্ষরিত, তাই সেই key সরালে কিছু কম্পিউটারে স্ক্রিনই আসে না — এটি কেবল নিজের হার্ডওয়্যার ভালো করে জেনে, ব্যাকআপ রেখে করার কাজ।

Linux-এর পথ: shim, MOK আর স্বাক্ষরিত module

shim Microsoft-এর UEFI CA দিয়ে স্বাক্ষরিত, আর ভেতরে distro-র নিজের সার্টিফিকেট বহন করে। এর সাথে আছে MOK (Machine Owner Key) তালিকা: মালিকের নিজের key, যা firmware-এর NVRAM-এ এমনভাবে জমা থাকে যে OS চালু হওয়ার পরে সরাসরি বদলানো যায় না। নিজে বানানো driver (যেমন DKMS দিয়ে তৈরি কোনো Wi-Fi বা graphics module) চালাতে চাইলে পথটি এরকম: একটি key জোড়া বানিয়ে module স্বাক্ষর করা; mokutil --import key.der দিয়ে একটি পাসওয়ার্ডসহ অনুরোধ রাখা; রিবুটে নীল রঙের MokManager স্ক্রিনে শারীরিকভাবে উপস্থিত মালিক সেই পাসওয়ার্ড দিয়ে নিশ্চিত করেন। এই শেষ ধাপটিই নিরাপত্তা — দূর থেকে চলা কোনো প্রোগ্রাম নিজে নিজে key যোগ করতে পারে না।

Kernel-এ module signing চালু থাকলে প্রতিটি .ko ফাইলের শেষে একটি স্বাক্ষর থাকে, আর kernel তার বিশ্বস্ত key-তালিকা (kernel-এ বসানো key, আর db/MOK থেকে আসা key) দিয়ে যাচাই করে। স্বাক্ষর না মিললে modprobe জানায় “Key was rejected by service”. Secure Boot চালু থাকলে অনেক distro kernel-এর lockdown মোডও চালু করে: root-ও তখন স্বাক্ষরবিহীন module, /dev/mem-এ লেখা বা স্বাক্ষরবিহীন kernel-এ kexec করতে পারে না। যুক্তিটি সরল — বুটে kernel যাচাই করে লাভ কী, যদি চলার পরে root যেকোনো কোড kernel-এ ঢোকাতে পারে?

বাতিল করার কাজ (revocation)-এ একটি ব্যবহারিক সমস্যা ছিল: dbx-এর জায়গা firmware-এর ছোট NVRAM-এ সীমিত। ২০২০ সালের BootHole-এর পর অসংখ্য পুরোনো GRUB-এর hash বাতিল করতে গিয়ে এটি স্পষ্ট হয়। তাই ২০২১ সালে shim-এ SBAT যুক্ত হয়: প্রতিটি bootloader-এ একটি “generation” নম্বর লেখা থাকে, আর একটি ছোট variable-এ সর্বনিম্ন গ্রহণযোগ্য generation; একটি সংখ্যা বাড়িয়েই পুরোনো সব সংস্করণ একসাথে বাতিল করা যায়। নিচে পুরো শিকলটি দেখুন:

  1. FirmwarePK · KEK · db · dbxশিকলের মূল
  2. shimshimx64.efidistro cert + MOK
  3. GRUBgrubx64.efiSBAT যাচাই
  4. Kernelvmlinuzlockdown চালু
  5. Module*.koস্বাক্ষরিত?
এই ধাপে
কে যাচাই করছে
কাকে
কোন key-তে
ফল
  1. শিকলের মূল: firmware-এর key. NVRAM-এ PK (নির্মাতা), KEK (নির্মাতা + Microsoft), db (অনুমোদিত CA), dbx (বাতিল)। PK বসানো, তাই যন্ত্রটি User Mode-এ: db বদলাতে KEK-এর স্বাক্ষর লাগে, KEK বদলাতে PK-র। Firmware নিজে flash চিপে, নির্মাতার হার্ডওয়্যার-সুরক্ষায়।
  2. Firmware → shim. ESP থেকে shimx64.efi পড়া হলো। এর স্বাক্ষর Microsoft UEFI CA-র, যা db-তে আছে; hash dbx-এ নেই — পাস, চলে। Measured boot চালু থাকলে এই মুহূর্তে shim-এর hash TPM-এর PCR-এ যোগ হয় (নিচে দেখবেন)।
  3. shim → GRUB. shim নিজের ভেতরের distro সার্টিফিকেট দিয়ে grubx64.efi যাচাই করে, সাথে SBAT generation — বাতিল সংস্করণের চেয়ে নতুন কি না। দুটোই ঠিক — পাস।
  4. GRUB → kernel. GRUB kernel আনে, কিন্তু যাচাইয়ের জন্য shim-কে জিজ্ঞেস করে। vmlinuz distro-র key-তে স্বাক্ষরিত — পাস। Kernel জেগে দেখে Secure Boot চালু, তাই অনেক distro-তে lockdown (integrity) মোড চালু হয়।
  5. Kernel → module. মালিক নিজে DKMS দিয়ে একটি driver বানিয়েছেন আর MOK দিয়ে স্বাক্ষর করেছেন; MokManager-এ key আগেই নিশ্চিত করা। Kernel-এর বিশ্বস্ত তালিকায় MOK আছে — module লোড হয়। স্বাক্ষর না থাকলে “Key was rejected by service”.
  6. বাতিল: dbx / SBAT. কেউ একটি পুরোনো বুট USB থেকে চালাতে চাইলেন, যার GRUB পরে দুর্বল প্রমাণিত হয়েছে। স্বাক্ষর বৈধ, কিন্তু তার hash dbx-এ বা generation SBAT-এর সীমার নিচে — প্রত্যাখ্যাত, “Security Violation”. সমাধান Secure Boot বন্ধ করা নয়, হালনাগাদ বুট মিডিয়া বানানো।

Measured boot আর TPM — যাচাইয়ের পাশাপাশি হিসাব রাখা

Secure Boot একটি দারোয়ান: স্বাক্ষর মিললে ঢুকতে দেয়, নইলে আটকায় — কিন্তু কে ঢুকল, তার খাতা রাখে না। Measured boot হলো সেই খাতা। প্রতিটি ধাপ পরের ধাপকে চালানোর আগে তার hash নেয় আর TPM (Trusted Platform Module) চিপের একটি PCR (Platform Configuration Register)-এ “extend” করে: PCRনতুন = SHA-256(PCRপুরোনো ‖ পরিমাপ)। PCR-এ সরাসরি কোনো মান লেখা যায় না, আর রিবুট ছাড়া শূন্যও করা যায় না। ফলে শেষ মান একটি আঙুলের ছাপ — কোন কোড, কোন ক্রমে চলেছে। নিয়মিত ব্যবহৃত কয়েকটি PCR: PCR 0 (firmware-এর কোড), PCR 4 (bootloader-এর কোড), PCR 7 (Secure Boot-এর অবস্থা আর কোন key-তে যাচাই হলো)। প্রতিটি পরিমাপ একটি event log-এও লেখা থাকে, যা পরে আবার হিসাব করে PCR-এর সাথে মেলানো যায়।

একটি খেলনা উদাহরণ — ফাইলের বদলে তিনটি ছোট টেক্সট: shim, grub আর kernel; বদলানো বুটে grub-এর জায়গায় এক অক্ষর আলাদা grud. প্রতিটির SHA-256 নিয়ে PCR-এ extend করা হয়েছে (পুরো ৩২ বাইটের মান জুড়ে), টেবিলে শুধু প্রথম ৮টি hex অঙ্ক (একটি scratch স্ক্রিপ্টে হিসাব করা):

ধাপপরিমাপ = H(টেক্সট)PCR (ঠিক বুট)PCR (GRUB বদলানো)
শুরু—00000000…00000000…
shim04e666aa…9cd29f15…9cd29f15…
GRUBf78c9c9d… / বদলানো (grud): 369fd448…02bb2d4e…2cd53d31…
kernel6923dd1b…00b2e760…023b818a…

GRUB-এর একটি অক্ষর (বাইট) বদলালেই সেই ধাপ থেকে পরের সব PCR আলাদা হয়ে যায় — kernel হুবহু এক হলেও। এই মাপ তিনভাবে কাজে লাগে:

  • Sealing: TPM একটি গোপন key (যেমন ডিস্ক-এনক্রিপশনের key) এই শর্তে রাখে — “PCR 7-এর মান এটি হলে তবেই ছাড়বে”। বুট-শিকল বদলালে PCR বদলায়, TPM key ছাড়ে না। Windows-এর BitLocker TPM দিয়ে ঠিক এভাবে ডিস্ক খোলে; তাই firmware আপডেট, মাদারবোর্ড বদলানো বা Secure Boot সেটিং বদলানোর পর প্রায়ই ৪৮ অঙ্কের recovery key চায়। Linux-এ একই কাজ systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=7-এর মতো কমান্ডে।
  • Remote attestation: দূরের একটি সার্ভার একটি এলোমেলো সংখ্যা (nonce) পাঠায়; TPM নিজের attestation key দিয়ে “PCR-এর মান + nonce” স্বাক্ষর করে (একে বলে quote). সার্ভার event log আবার হিসাব করে মিলিয়ে দেখে যন্ত্রটি জানা-ভালো অবস্থায় বুট হয়েছে কি না — যেমন অফিসের নেটওয়ার্কে ঢোকার আগে, বা ক্লাউডের সার্ভারে।
  • তদন্ত: কোনো অস্বাভাবিকতা ধরা পড়লে event log থেকে বোঝা যায় কোন ধাপে পরিমাপ বদলেছে।

TPM-এর আরও কিছু কাজ: হার্ডওয়্যার এলোমেলো-সংখ্যা উৎপাদন, এমন key রাখা যা কখনো চিপের বাইরে খোলা অবস্থায় আসে না, আর পিন বারবার ভুল দিলে সাময়িক তালা (dictionary-attack সুরক্ষা)। TPM আলাদা চিপ হতে পারে, বা CPU-র firmware-এর ভেতরে (fTPM). Windows 11 ইনস্টলের শর্তে TPM 2.0 থাকা এর গুরুত্ব সাধারণ ব্যবহারকারীর কাছেও পৌঁছে দিয়েছে। ঝুঁকি বেশি এমন ল্যাপটপে শুধু-TPM-এর বদলে TPM + PIN ব্যবহার ভালো অভ্যাস — তখন key ছাড়তে মালিকের উপস্থিতিও লাগে।

ফোনে: Android-এর Verified Boot partition-গুলোর hash স্বাক্ষরিত vbmeta-তে রাখে, dm-verity দিয়ে চলার সময় যাচাই করে আর পুরোনো দুর্বল সংস্করণে ফেরা ঠেকায়। iPhone-এ Boot ROM থেকে kernel পর্যন্ত প্রতিটি ধাপ Apple-এর key-তে যাচাই হয়। খুঁটিনাটি নির্মাতা ও সংস্করণভেদে আলাদা।

আরও গভীরে — Secure Boot-এর সার্টিফিকেটেরও মেয়াদ আছে। বহু বছর ধরে প্রায় সব PC-র db আর KEK-এ ২০১১ সালে বানানো Microsoft-এর সার্টিফিকেট ছিল, যেগুলোর মেয়াদ ২০২৬ সালে ফুরোয়; তাই Microsoft আর PC নির্মাতারা ২০২৩ সালের নতুন সার্টিফিকেট firmware আর OS আপডেটের মাধ্যমে ছড়াচ্ছেন। Firmware সাধারণত বুটের সময় তারিখ যাচাই করে না, তাই পুরোনো স্বাক্ষর হঠাৎ অচল হয় না — কিন্তু নতুন bootloader স্বাক্ষর আর নতুন dbx হালনাগাদের জন্য নতুন key লাগে, আর হালনাগাদ না পাওয়া যন্ত্র পিছিয়ে পড়ে। Linux kernel-এর ভেতরে seccomp filter চলে kernel-এর BPF interpreter বা JIT-এ; Linux 5.11 থেকে kernel নিয়মিত syscall-গুলোর ফল ক্যাশ করে, যাতে প্রতিটি কলে পুরো filter চালাতে না হয়।

পরীক্ষা ও ইন্টারভিউতে এই বিষয়ে প্রায়ই যা জিজ্ঞেস করা হয়:

  • DAC আর MAC-এর পার্থক্য? — DAC-এ মালিক অনুমতি ঠিক করেন (Unix rwx, ACL); MAC-এ কেন্দ্রীয় নীতি, মালিক বা root বদলাতে পারেন না (SELinux, AppArmor).
  • Bell–LaPadula-র দুটি নিয়ম? — no read up (simple security), no write down (★-property) — গোপনীয়তার জন্য; Biba উল্টো — no read down, no write up — অখণ্ডতার জন্য।
  • seccomp কী, আর strict ও filter mode-এর পার্থক্য? — syscall সীমিত করার ব্যবস্থা; strict-এ শুধু read/write/exit/sigreturn, বাকিতে SIGKILL; filter-এ BPF প্রোগ্রাম প্রতি syscall-এ ALLOW/ERRNO/TRAP/LOG/KILL রায় দেয়।
  • chroot কি sandbox? — না; শুধু path-এর মূল বদলায়, UID, capability, syscall, PID বা নেটওয়ার্ক আলাদা করে না।
  • Secure Boot আর measured boot-এর পার্থক্য? — Secure Boot স্বাক্ষর যাচাই করে আটকায়; measured boot প্রতিটি ধাপের hash TPM-এর PCR-এ extend করে, যা দিয়ে key seal আর remote attestation হয়।

১০ · AI-এর যুগে নিরাপত্তা-পরামর্শ — দ্রুত “সমাধান”-এর দাম

নিরাপত্তা-ব্যবস্থা মাঝে মাঝে কাজ আটকায় — ওয়েবসাইটে 403, container-এ “Operation not permitted”, নতুন driver লোড হয় না। তখন AI-কে জিজ্ঞেস করলে প্রায়ই সবচেয়ে ছোট উত্তরটি আসে: সুরক্ষাটি বন্ধ করুন। উত্তরটি “কাজ করে” — যেমন দরজার তালা খুলে রাখলে চাবি হারানোর সমস্যা মেটে। AI আপনার লগ দেখে না, আপনার distro জানে না, আর পুরোনো ফোরাম-পোস্টের অভ্যাস নকল করে।

AI-এর সাধারণ পরামর্শসমস্যাভালো পথ
setenforce 0 বা SELinux disabledপুরো সার্ভারের MAC-স্তর হারায়AVC পড়ুন; restorecon, boolean, semanage
audit2allow -M fix && semodule -i fix.pp অন্ধভাবেযা আটকেছিল, সব অনুমতি — আক্রমণওপ্রস্তাবিত প্রতিটি নিয়ম পড়ে বুঝুন
docker run --privileged, seccomp=unconfinedseccomp, capability, AppArmor প্রায় সব উঠে যায়শুধু দরকারি --cap-add বা নিজের seccomp profile
chmod 777 / সব কিছু sudo দিয়েDAC-ও শেষ; MAC-এর সমস্যা এতে মেটেও নাসঠিক মালিক আর ন্যূনতম অনুমতি (পর্ব ৪৭)
Ubuntu-তে SELinux-এর কমান্ড, Fedora-তে AppArmor-এরভুল distro-র নির্দেশ/sys/kernel/security/lsm দেখে নিশ্চিত হোন
“chroot jail নিরাপদ”, পুরোনো syscall নম্বরপুরোনো বা ভুল তথ্য; নম্বর আর্কিটেকচারভেদে আলাদাman পাতা, kernel ডকুমেন্টেশন, __NR_* macro

AI-কে প্রশ্ন করার আগে লগ আর সিস্টেমের তথ্য জোগাড় করুন, তারপর এই কাঠামোয় জিজ্ঞেস করুন। শেষ শর্তটি খেয়াল করুন — সুরক্ষা বন্ধ করা উত্তর হিসেবে গ্রহণযোগ্য নয়।

আমার Rocky Linux 9 সার্ভারে httpd /srv/site থেকে পাতা দেখাতে পারছে না, ব্রাউজারে 403 আসছে। - getenforce: Enforcing - ls -Z /srv/site/index.html: unconfined_u:object_r:var_t:s0 - ausearch -m AVC -ts recent থেকে একটি লাইন: (এখানে পুরো লাইনটি দিলাম) ১. এই AVC লাইনটি ভেঙে সহজ বাংলায় বলো — কে, কী, কোথায় চাইছিল। ২. সম্ভাব্য কারণ আর আগে শুধু-পড়ার যাচাইয়ের কমান্ড দাও। ৩. সমাধান দাও SELinux enforcing রেখেই — setenforce 0, SELinux disabled বা অন্ধ audit2allow চলবে না। ৪. প্রতিটি কমান্ড কী স্থায়ীভাবে বদলায় আর ভুল হলে কীভাবে ফেরাব, তা লেখো।

AI-এর নিরাপত্তা-পরামর্শ প্রয়োগের আগে — চেকলিস্ট

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।

প্র ০১বাগ ঠিক করাই যদি আসল কাজ হয়, তাহলে ব্রাউজার নির্মাতারা sandbox-এ এত বিনিয়োগ করলেন কেন?

২০০৮ সালে Chrome যখন আসে, তখনকার প্রধান ব্রাউজারগুলো মূলত এক প্রসেসে চলত: একটি ওয়েবপেজের ভুল কোড পুরো ব্রাউজার ফেলে দিত, আর renderer-এর একটি নিরাপত্তা-বাগ মানে ব্যবহারকারীর নামে চলা পুরো প্রোগ্রামের নিয়ন্ত্রণ। Chrome-এর দল একটি বাস্তববাদী সিদ্ধান্ত নেয়: HTML, CSS, JavaScript আর ছবির parser-এর মতো বিশাল C++ কোডে মেমরি-বাগ থাকবেই — যতই পরীক্ষা হোক। তাই প্রশ্ন বদলানো হলো: বাগ থাকলে আক্রমণকারী কতদূর যাবে?

উত্তর হিসেবে আসে broker-renderer নকশা আর OS-ভেদে sandbox: Windows-এ restricted token আর job object, macOS-এ sandbox profile, Linux-এ প্রথমে setuid sandbox ও পরে namespace আর seccomp-bpf. শেষেরটির জন্য Chrome-এর প্রয়োজনই Linux kernel-এ seccomp-bpf আনার বড় চালিকা ছিল — একটি ব্রাউজারের দরকার kernel-এর নকশা বদলে দিয়েছে। ফল দাঁড়াল এই যে ব্রাউজারকে পুরোপুরি দখল করতে এখন সাধারণত অন্তত দুটি বাগ জোড়া লাগাতে হয়: একটি renderer-এর ভেতরে ঢোকার, আর একটি sandbox থেকে বেরোনোর। নিরাপত্তা-প্রতিযোগিতাগুলোতে ব্রাউজার আক্রমণ এজন্যই বহু-ধাপের “শিকল” হয়ে উঠেছে, যা বানানো অনেক কঠিন ও ব্যয়বহুল।

২০১৮ সালে Spectre দেখাল যে একই প্রসেসের ভেতরের সীমা হার্ডওয়্যারের speculative execution-এর কারণে ফাঁস হতে পারে। জবাব আবার OS-এর সীমা: Site Isolation, যেখানে আলাদা ঠিকানা-জগৎ (পর্ব ৩২) রক্ষা করে — দাম বেশি মেমরি।

মূল উপলব্ধি: বাগ ঠিক করা আর ক্ষতির সীমা টানা প্রতিদ্বন্দ্বী নয়, পরিপূরক। বড় সিস্টেমে “বাগ থাকবে” ধরে নিয়ে সীমা টানলে প্রতিটি বাগের দাম কমে যায়, আর OS-এর যন্ত্র (প্রসেস, ঠিকানা-জগৎ, syscall filter) সেই সীমার সবচেয়ে মজবুত দেয়াল।

প্র ০২“প্রথমেই SELinux বন্ধ করুন” — এই সংস্কৃতি কীভাবে তৈরি হলো, আর দায় কি শুধু ব্যবহারকারীর?

২০০০-এর দশকের মাঝামাঝি Red Hat আর Fedora-তে SELinux ডিফল্টে চালু হলে বহু প্রশাসক প্রথমবার এমন একটি “না” শুনলেন, যার কারণ ls -l-এ দেখা যায় না। অনুমতি ঠিক, মালিক ঠিক, তবু 403. লগ ছিল দুর্বোধ্য, টুল কম, আর ইন্টারনেটের গাইডগুলো প্রথম ধাপেই লিখত “SELinux disable করুন”। একবার এটি কাজ করলে অভ্যাসে পরিণত হয়, আর পরের প্রজন্ম সেই গাইড নকল করে — আজ AI-এর উত্তরেও সেই প্রতিধ্বনি।

দায় শুধু ব্যবহারকারীর নয়। একটি নিরাপত্তা-ব্যবস্থা যদি কারণ না বলে আটকায়, মানুষ তাকে শত্রু ভাবে। এই শিক্ষা থেকে SELinux-এর চারপাশে ধীরে ধীরে সাহায্যের কাঠামো এসেছে: audit2why কারণ ব্যাখ্যা করে, setroubleshoot প্রায় মানুষের ভাষায় পরামর্শ দেয়, boolean দিয়ে সাধারণ প্রয়োজনগুলো (ওয়েব সার্ভার থেকে ডেটাবেসে সংযোগ, home ফোল্ডার থেকে পাতা দেখানো) এক লাইনে চালু করা যায়, আর একটি domain-কে আলাদা করে permissive করা যায়।

অন্যদিকে, বাস্তব ঘটনাগুলো দেখিয়েছে কেন ঝামেলা সহ্য করা মূল্যবান। Container থেকে host-এ বেরোনোর বেশ কিছু প্রকাশ্য ত্রুটির বিশ্লেষণে দেখা গেছে, SELinux চালু থাকা সিস্টেমে আক্রমণ সীমিত হয়েছে বা আটকে গেছে, কারণ container-এর প্রসেস তার label-এর বাইরের host ফাইল ছুঁতে পারেনি। প্রতিটি ক্ষেত্রে খুঁটিনাটি আলাদা, কিন্তু নীতিটি একই — একটি স্বাধীন স্তর।

মূল উপলব্ধি: নিরাপত্তা-যন্ত্রের সাফল্য শুধু কতটা আটকায় তা দিয়ে নয়, মানুষ তাকে চালু রাখে কি না তা দিয়েও মাপা হয়। ব্যাখ্যাযোগ্য denial, সহজ সঠিক-সমাধান আর ভালো ডিফল্ট — এগুলো না থাকলে মানুষ বন্ধ করার পথ খুঁজবেই।

প্র ০৩Label না path — SELinux আর AppArmor-এর দার্শনিক পার্থক্য কী, আর Linux কেন দুটোকেই জায়গা দিল?

২০০১ সালের দিকে NSA যখন SELinux-কে মূল Linux kernel-এ আনতে চাইছিল, Linus Torvalds একটি নির্দিষ্ট নিরাপত্তা-মডেলকে kernel-এ স্থায়ী করতে চাননি — নিরাপত্তা-মডেল নিয়ে বিশেষজ্ঞদের মধ্যেই মতভেদ ছিল। তাঁর পরামর্শে তৈরি হয় একটি সাধারণ কাঠামো, Linux Security Modules: kernel-এর গুরুত্বপূর্ণ জায়গায় (ফাইল খোলা, প্রসেস তৈরি, socket) hook, যেখানে যেকোনো নিরাপত্তা-মডিউল নিজের সিদ্ধান্ত দিতে পারে। LSM Linux 2.6-এ আসে, আর তার ওপর SELinux, পরে AppArmor, Smack, TOMOYO ও আরও অনেকে।

SELinux-এর দর্শন: পরিচয় বস্তুর নিজের — inode-এর label. একটি ফাইলের নাম বদলান, hard link বানান, অন্য mount-এ দেখুন — label একই, তাই সিদ্ধান্তও একই। এটি সূক্ষ্ম ও ফাঁকহীন, কিন্তু প্রতিটি ফাইলে সঠিক label রাখার দায় আসে, আর mv-এর মতো সাধারণ কাজে ভুল label ঢুকে পড়ে। AppArmor-এর দর্শন: প্রশাসকেরা path দিয়েই ভাবেন — “এই প্রোগ্রাম /etc/myapp পড়বে” — তাই নিয়মও সেভাবে লেখা হোক। এতে profile পড়া ও লেখা সহজ, নতুন প্রোগ্রামের জন্য দ্রুত নীতি বানানো যায়। সমালোচকদের যুক্তি ছিল যে একই ফাইলের একাধিক path থাকতে পারে, তাই নামভিত্তিক নিয়মে সাবধানতা লাগে; সমর্থকদের যুক্তি, ব্যবহারযোগ্যতাই বেশি সিস্টেমে সুরক্ষা চালু রাখে।

শেষে LSM-এর নমনীয়তাই মীমাংসা দিল: distro নিজের প্রয়োজনে বাছে।

মূল উপলব্ধি: যেখানে সঠিক নকশা নিয়ে বিশেষজ্ঞরাও একমত নন, সেখানে একটি ভালো কাঠামো বানানো একটি “সঠিক” উত্তর চাপিয়ে দেওয়ার চেয়ে টেকসই। policy আর mechanism আলাদা রাখার নীতি এখানে পুরো একটি শিল্পকে জায়গা দিয়েছে।

প্র ০৪Bell–LaPadula গাণিতিকভাবে নিখুঁত হলেও বাস্তবের ব্যাংক বা অফিসে কেন সরাসরি চলে না?

Bell–LaPadula তৈরি হয়েছিল ১৯৭০-এর দশকে সামরিক তথ্য-ব্যবস্থার জন্য, যেখানে একটাই চিন্তা: গোপন তথ্য ফাঁস না হওয়া। মডেলটি প্রমাণ করে যে নিয়ম দুটি মানলে কোনো ধারাবাহিক কাজের শেষে উঁচু স্তরের তথ্য নিচু স্তরের বস্তুতে পৌঁছায় না। ১৯৮০-এর দশকে মার্কিন প্রতিরক্ষা বিভাগের “Orange Book” (TCSEC) নামের মূল্যায়ন-মানদণ্ডে উঁচু শ্রেণির সিস্টেমের জন্য এ ধরনের mandatory নিয়ন্ত্রণ শর্ত হয়।

কিন্তু বাস্তবে কয়েকটি সমস্যা দেখা দিল। প্রথমত, no write down মানে একজন উঁচু স্তরের কর্মকর্তা নিচের স্তরে কোনো নির্দেশই লিখে পাঠাতে পারেন না — তাই “trusted subject” নামে নিয়মের ব্যতিক্রম লাগে, যারা জেনে-বুঝে তথ্য নামায় (declassify). দ্বিতীয়ত, তথ্য ফাঁস শুধু ফাইলে লিখে হয় না: একটি প্রসেস CPU কতক্ষণ ব্যস্ত রাখল বা কোন ফাইল আছে কি না, তা দিয়েও সংকেত পাঠানো যায় — একে বলে covert channel, যা মডেলের বাইরে। তৃতীয়ত, ব্যবসায় সবচেয়ে বড় ভয় প্রায়ই ফাঁস নয়, জালিয়াতি: একজন ক্যাশিয়ার হিসাবের খাতা বদলে দিলেন। এজন্য ১৯৭৭ সালের Biba অখণ্ডতার মডেল, আর ১৯৮৭ সালে Clark–Wilson মডেল — যা “শুধু অনুমোদিত লেনদেন-প্রক্রিয়া দিয়েই ডেটা বদলানো যাবে” আর “দায়িত্ব ভাগ” (একজন প্রস্তাব করেন, আরেকজন অনুমোদন) ধারণা আনে — বাণিজ্যিক জগতে বেশি স্বাভাবিক।

ব্যাংকের লেনদেনে “maker” আর “checker” দুইজন লাগার রীতি এই দায়িত্ব-ভাগেরই রূপ।

মূল উপলব্ধি: নিরাপত্তা-মডেল কোন প্রশ্নের উত্তর দেয়, তা আগে জানুন — গোপনীয়তা, অখণ্ডতা না প্রাপ্যতা। একটি প্রশ্নে নিখুঁত মডেল অন্য প্রশ্নে অচল হতে পারে; বাস্তব সিস্টেম কয়েকটি মডেলের অংশ জুড়ে বানানো।

প্র ০৫TPM আর remote attestation — ব্যবহারকারীর সুরক্ষা, নাকি দূর থেকে যন্ত্র নিয়ন্ত্রণের হাতিয়ার?

২০০০-এর দশকের শুরুতে “Trusted Computing” নিয়ে তীব্র বিতর্ক হয়। সমালোচকদের ভয় ছিল, remote attestation দিয়ে কোনো কোম্পানি দেখে নিতে পারবে আপনি তাদের অনুমোদিত সফটওয়্যার চালাচ্ছেন কি না, আর না চালালে সেবা দেবে না — অর্থাৎ আপনার কম্পিউটার আপনার বিরুদ্ধে সাক্ষী। আরেকটি ভয় গোপনীয়তার: প্রতিটি TPM-এর একটি অনন্য endorsement key থাকে, যা দিয়ে যন্ত্রটি চেনা সম্ভব। এর জবাবে মানদণ্ডে বিকল্প attestation key, গোপনীয়তা-রক্ষী সার্টিফিকেট-ব্যবস্থা আর মালিকের নিয়ন্ত্রণ যোগ হয়।

আজ TPM-এর সবচেয়ে বড় ব্যবহার অনেক সাদামাটা ও ব্যবহারকারী-বান্ধব: ল্যাপটপ চুরি হলে ডিস্কের ডেটা পড়া যায় না, অথচ মালিককে প্রতিবার লম্বা পাসওয়ার্ড দিতে হয় না। Windows 11-এর TPM 2.0 শর্ত (২০২১) নিয়ে আপত্তি মূলত ছিল পুরোনো কম্পিউটার বাদ পড়া নিয়ে, নজরদারি নিয়ে কম।

সীমাও আছে। গবেষকেরা দেখিয়েছেন, আলাদা চিপের TPM আর CPU-র মাঝের সংযোগ শারীরিকভাবে হাতে পেলে শুধু-TPM মোডে key-এর গোপনীয়তা দুর্বল হতে পারে; প্রতিরক্ষা হিসেবে TPM + PIN, বা CPU-র ভেতরের TPM, ঝুঁকিপূর্ণ ব্যবহারকারীদের জন্য সুপারিশ করা হয়। আর recovery key হারালে মালিক নিজেই নিজের ডেটা হারাতে পারেন — দোকানে মাদারবোর্ড বদলানোর পর এমন ঘটনা বিরল নয়।

মূল উপলব্ধি: পর্ব ৭-এর Secure Boot-এর মতো এখানেও প্রশ্ন একই — কে চাবি রাখে, আর কে নীতি ঠিক করে? মালিক যেখানে key, PIN আর recovery-র নিয়ন্ত্রণ রাখেন, সেখানে TPM সুরক্ষা; যেখানে অন্য কেউ attestation-এর ফল দিয়ে মালিকের পছন্দ আটকায়, সেখানে এটি নিয়ন্ত্রণ।

অনুশীলন

খাতায় হিসাব করুন, তারপর উত্তর মেলান।

  1. হাতে trace করুন: একটি x86-64 প্রসেসে দুটি filter বসানো। Filter A: arch না মিললে KILL; read, write, exit_group, getpid → ALLOW; uname → ERRNO(EPERM); বাকি সব → LOG. Filter B (পরে বসানো): getpid → ERRNO(EACCES); বাকি সব → ALLOW. এই syscall-গুলোর ফল কী: (ক) write, (খ) getpid, (গ) uname, (ঘ) openat?

    নিয়ম: দুটি filter-ই চলে, আর সবচেয়ে কড়া রায় জেতে (KILL > TRAP > ERRNO > … > LOG > ALLOW).

    (ক) write: A → ALLOW, B → ALLOW ⇒ ALLOW, স্বাভাবিকভাবে চলে। (খ) getpid: A → ALLOW, B → ERRNO(EACCES) ⇒ ERRNO: −1, errno = EACCES (১৩)। পরে বসানো filter আগেরটির অনুমতি কেড়ে নিতে পারে, কিন্তু দিতে পারে না। (গ) uname: A → ERRNO(EPERM), B → ALLOW ⇒ ERRNO(EPERM). (ঘ) openat: A → LOG, B → ALLOW ⇒ LOG — syscall চলে, ফাইল খোলে, আর audit log-এ একটি লাইন। এটিই নতুন filter পরীক্ষার কৌশল: default LOG রেখে কয়েকদিন চালিয়ে দেখা কোন syscall লাগে, তারপর default বদলে KILL বা ERRNO.

  2. হিসাব — capability-র bitmask: একটি প্রসেসের /proc/<pid>/status-এ আছে CapEff: 0000000000003400. আরেকটির CapEff: 0000000000200000. কোন কোন capability আছে? (সূত্র: NET_BIND_SERVICE = ১০, NET_ADMIN = ১২, NET_RAW = ১৩, SYS_ADMIN = ২১) কোনটি বেশি চিন্তার?

    0x3400 = 0x2000 + 0x1000 + 0x400 = ২১৩ + ২১২ + ২১০ ⇒ bit ১৩, ১২, ১০ ⇒ CAP_NET_RAW, CAP_NET_ADMIN, CAP_NET_BIND_SERVICE. নেটওয়ার্ক-সংক্রান্ত; একটি VPN বা নেটওয়ার্ক-টুলের জন্য যুক্তিসঙ্গত হতে পারে, একটি সাধারণ ওয়েব অ্যাপের জন্য NET_BIND_SERVICE-ই যথেষ্ট।

    0x200000 = ২২১ (দশমিকে ২০৯৭১৫২) ⇒ bit ২১ ⇒ CAP_SYS_ADMIN. একটিমাত্র bit, কিন্তু এটিই বেশি চিন্তার: mount, namespace-এর বহু কাজ, নানা প্রশাসনিক ioctl — এত কিছুর অনুমতি যে একে “নতুন root” বলা হয়। নিজের মেশিনে যাচাই: capsh --decode=0x3400.

  3. মডেল প্রয়োগ: স্তর: Public < Internal < Secret. প্রসেস P-এর স্তর Internal. ফাইল: notice.txt (Public), plan.doc (Internal), salary.xls (Secret). (ক) Bell–LaPadula-য় P কোন ফাইল পড়তে আর কোনটিতে লিখতে পারবে? (খ) একই স্তরকে অখণ্ডতা ধরে Biba-য়?

    (ক) Bell–LaPadula (no read up, no write down): পড়া — notice.txt ✓, plan.doc ✓, salary.xls ✗ (উঁচু)। লেখা — notice.txt ✗ (নিচু, ফাঁসের ঝুঁকি), plan.doc ✓, salary.xls ✓ (উপরে লেখা চলে, যদিও পড়া যায় না — “blind write”).

    (খ) Biba (no read down, no write up): পড়া — notice.txt ✗ (নিচু-অখণ্ডতার ডেটা প্রসেসকে দূষিত করতে পারে), plan.doc ✓, salary.xls ✓. লেখা — notice.txt ✓, plan.doc ✓, salary.xls ✗. খেয়াল করুন, দুই মডেলে শুধু নিজের স্তর (plan.doc) দুই কাজেই খোলা।

  4. কমান্ড লিখুন — 403-এর তদন্ত: একটি RHEL-পরিবারের সার্ভারে নতুন ফোল্ডার /srv/site থেকে Apache পাতা দেখাতে পারছে না; অনুমতি 755/644 ঠিক আছে। SELinux enforcing রেখে কারণ খোঁজা আর স্থায়ীভাবে ঠিক করার কমান্ডগুলো ক্রমানুসারে লিখুন।

    শুধু-পড়া যাচাই: getenforce (Enforcing কি না); ls -Zd /srv/site /srv/site/index.html (সম্ভবত var_t, httpd_sys_content_t নয়); ps -eZ | grep httpd (httpd_t); sudo ausearch -m AVC -ts recent — scontext=…httpd_t, tcontext=…var_t, { read } বা { getattr }; sudo ausearch -m AVC -ts recent | audit2why.

    স্থায়ী সমাধান: sudo semanage fcontext -a -t httpd_sys_content_t "/srv/site(/.*)?" (নীতিতে নিয়ম যোগ), তারপর sudo restorecon -Rv /srv/site (ফাইলে প্রয়োগ)। আবার ব্রাউজারে পরীক্ষা, আর ausearch-এ নতুন denial নেই কি না দেখা। যা করবেন না: setenforce 0, chmod -R 777, বা না বুঝে audit2allow-এর module.

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমএকটি প্রোগ্রাম অবিশ্বস্ত কোড চালানোর আগে নিজেকে seccomp filter-এ বন্দি করছে — ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. 📝 দরকারি syscall-এর allowlist আর default রায় দিয়ে BPF filter লেখা
  2. 🔒 prctl(PR_SET_NO_NEW_PRIVS, 1) — আর কখনো নতুন ক্ষমতা নয়
  3. 🧱 prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) দিয়ে filter বসানো
  4. 🚀 execve দিয়ে অবিশ্বস্ত প্রোগ্রাম চালানো — filter উত্তরাধিকারে যায়
  5. 🔍 প্রতিটি syscall-এ kernel filter চালিয়ে ALLOW / ERRNO / KILL রায় দেয়

Linux-এর seccomp strict mode-এ কোন syscall-গুলো অনুমোদিত, আর অন্য syscall করলে কী হয়?

  • fork, exec, wait — অন্য কিছু করলে EPERM
  • প্রোগ্রাম নিজে যা তালিকা দেয় — অন্য কিছু করলে SIGSYS ধরা যায়
  • read, write, exit, sigreturn — অন্য কিছু করলে SIGKILL
  • open, read, write, close — অন্য কিছু করলে প্রসেস থেমে থাকে

একটি seccomp-bpf filter একটি syscall-এর জন্য SECCOMP_RET_ERRNO | EPERM ফেরত দিল। কী ঘটে?

  • syscall চলেই না; প্রোগ্রাম −1 পায় আর errno হয় EPERM
  • প্রসেস সাথে সাথে SIGSYS-এ শেষ হয়
  • syscall চলে, কিন্তু audit log-এ একটি লাইন লেখা হয়
  • filter সরে যায় আর syscall আবার চেষ্টা হয়

Bell–LaPadula মডেলে Secret স্তরের একটি প্রসেস কোন কাজটি করতে পারে? (স্তর: Confidential < Secret < Top Secret)

  • Top Secret ফাইল পড়া
  • Confidential ফাইলে লেখা
  • Top Secret ফাইল পড়া আর Confidential ফাইলে লেখা
  • Confidential ফাইল পড়া আর Top Secret ফাইলে লেখা

SELinux permissive মোডে একটি প্রসেস নীতিতে অনুমোদিত নয় এমন একটি ফাইল পড়তে চাইলে কী হয়?

  • পড়া আটকানো হয়, EACCES আসে, আর AVC লগ হয়
  • পড়তে দেওয়া হয়, কিন্তু “আটকানো হতো” বলে AVC লগ হয়
  • কিছুই হিসাব হয় না, কোনো লগও নেই
  • ব্যবহারকারীর কাছে অনুমতি চাওয়া হয়

TPM-ভিত্তিক BitLocker চালু একটি ল্যাপটপে firmware আপডেট বা Secure Boot সেটিং বদলানোর পর recovery key চাওয়া হলো। সবচেয়ে সম্ভাব্য কারণ কী?

  • বুটের পরিমাপ (PCR) বদলেছে, তাই TPM seal করা key ছাড়ছে না
  • ডিস্কের এনক্রিপ্ট করা ডেটা নষ্ট হয়ে গেছে
  • TPM-এর key-এর মেয়াদ শেষ হয়েছে
  • Secure Boot নিজেই ডিস্ক এনক্রিপ্ট করে, আর এখন key বদলেছে

একটি গাইডে লেখা “অবিশ্বস্ত প্রোগ্রামকে chroot jail-এ রাখলেই নিরাপদ”। কোন মন্তব্যটি সঠিক?

  • ঠিক — chroot সব syscall আর নেটওয়ার্ক আটকে দেয়
  • ভুল — chroot শুধু path-এর মূল বদলায়; namespace, ক্ষমতা ছাঁটা, seccomp আর LSM লাগে
  • ঠিক, যদি প্রোগ্রামটি root না হয় — তখন chroot পুরো sandbox
  • ভুল — Linux থেকে chroot সরিয়ে দেওয়া হয়েছে

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. Sandbox বাগ ঠিক করে না, বাগের ফল সীমিত করে — least privilege, privilege separation আর defense in depth.
  2. seccomp: strict mode = read/write/exit/sigreturn, বাকিতে SIGKILL; filter mode = BPF প্রোগ্রাম, রায় ALLOW/ERRNO/TRAP/LOG/KILL; filter শুধু যোগ হয়, সবচেয়ে কড়া রায় জেতে; আগে no_new_privs, আর arch যাচাই।
  3. Capability root-কে টুকরো করে, namespace দৃশ্য আলাদা করে, Landlock অধিকারবিহীন প্রসেসকে ফাইল-sandbox দেয়; chroot নিরাপত্তার সীমানা নয়।
  4. DAC = মালিক ঠিক করেন, MAC = নীতি ঠিক করে; Bell–LaPadula: no read up, no write down; Biba উল্টো। SELinux = label + type enforcement (enforcing/permissive), AppArmor = path-ভিত্তিক profile (enforce/complain); denial পড়ে ঠিক করুন, বন্ধ নয়।
  5. Secure Boot: PK → KEK → db/dbx, shim + MOK, স্বাক্ষরিত kernel ও module, lockdown, SBAT; measured boot: PCR = H(PCR ‖ মাপ), TPM-এ key seal আর remote attestation.

পরের পর্বে: এক kernel-এর ভেতরে প্রসেসকে কীভাবে ঘেরাটোপে রাখা যায়, দেখলেন। কিন্তু একটি কম্পিউটারে যদি পুরো কয়েকটি OS একসাথে চালাতে হয়? পর্ব ৫০-এ virtual machine আর hypervisor — মডিউল ৮-এর শুরু।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান