পাঠ ৫৩ · ৬০-এর মধ্যে · মডিউল ১১
Home / Courses / Cybersecurity & Ethical Hacking / কন্টেইনার ও Kubernetes

কন্টেইনার ও Kubernetes সিকিউরিটি

Container & Kubernetes security
৮ মিনিট পড়া মধ্যবর্তী · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • একটি নিরাপদ কন্টেইনার ইমেজ তৈরির তিনটি মূল নিয়ম — base image, non-root, no hardcoded secrets
  • Kubernetes-এ RBAC ও নেটওয়ার্ক পলিসি কী নিয়ন্ত্রণ করে এবং কেন এগুলো গুরুত্বপূর্ণ
  • কেন সিক্রেট ম্যানেজমেন্ট টুলিং প্রয়োজন — সিক্রেট ইমেজে বেক করা কেন যথেষ্ট নিরাপদ নয়
  • একটি টয় কন্টেইনার-ইমেজ ফ্লিটে root-execution ও hardcoded-secret ফাইন্ডিংস প্রোগ্রাম্যাটিকভাবে অডিট করা

১ · নিরাপদ কন্টেইনার ইমেজের তিনটি নিয়ম

একটি কন্টেইনারContainerএকটি অ্যাপ্লিকেশন ও তার সব ডিপেন্ডেন্সি একসাথে প্যাকেজ করা একটি হালকা, পোর্টেবল ইউনিট, যা যেকোনো হোস্টে একইভাবে চলে। ইমেজ তৈরির সময় তিনটি মূল নিয়ম মেনে চলা উচিত —

  1. ন্যূনতম, বিশ্বস্ত base image — একটি ভারী, পুরনো OS ইমেজের বদলে ছোট, নিয়মিত-আপডেট হওয়া base image ব্যবহার করুন (কম প্যাকেজ = কম attack surface), এবং ডিপ্লয়মেন্টের আগে known-vulnerable ডিপেন্ডেন্সির জন্য স্ক্যান করুন (L24-এর সরাসরি সম্প্রসারণ)।
  2. Non-root এক্সিকিউশন — কন্টেইনার ডিফল্টভাবে root ইউজার হিসেবে চলে যদি স্পষ্টভাবে অন্যথা না বলা হয়। যদি অ্যাপ্লিকেশনে কোনো দুর্বলতা থাকে এবং আক্রমণকারী কোড এক্সিকিউশন পায়, root হিসেবে চলা একটি কন্টেইনার তাকে অনেক বেশি ক্ষমতা দেয় (least-privilege লঙ্ঘন, L31)।
  3. কখনো ইমেজে সিক্রেট বেক করবেন না — একটি Docker ইমেজের প্রতিটি লেয়ার আলাদাভাবে ইন্সপেক্ট ও এক্সট্র্যাক্ট করা সম্ভব, এমনকি যদি পরবর্তী লেয়ারে ফাইলটি "মুছে" ফেলা হয়। সিক্রেট সবসময় একটি ডেডিকেটেড সিক্রেট-ম্যানেজমেন্ট মেকানিজম (environment injection at runtime, বা Kubernetes Secrets/Vault-স্টাইল সিস্টেম) দিয়ে সরবরাহ করা উচিত।
কেন "ইমেজ থেকে ফাইল মুছে ফেলা" যথেষ্ট নয়

অনেকে ভাবেন একটি পরবর্তী Dockerfile ধাপে rm secret.txt চালালে সিক্রেটটি নিরাপদে মুছে যায়। বাস্তবে কন্টেইনার ইমেজ লেয়ারভিত্তিক — আগের লেয়ারে সিক্রেটটি এখনো বিদ্যমান থাকে এবং সহজ টুলিং দিয়ে বের করা যায়। একবার একটি সিক্রেট ইমেজে লেখা হলে, সেটিকে কম্প্রোমাইজড হিসেবে ধরে নেওয়াই নিরাপদ — শুধু rotate/revoke করাই সমাধান, মোছার চেষ্টা নয়।

২ · Kubernetes-নির্দিষ্ট উদ্বেগ

KubernetesKubernetesবহু কন্টেইনারকে বড় স্কেলে ডিপ্লয়, স্কেল ও ম্যানেজ করার একটি orchestration প্ল্যাটফর্ম। বড় স্কেলে কন্টেইনার পরিচালনা করে, এবং এর নিজস্ব সিকিউরিটি স্তর যোগ করে —

RBAC (Role-Based Access Control)
Kubernetes API সার্ভার — পুরো ক্লাস্টারের কেন্দ্রীয় নিয়ন্ত্রণ বিন্দু — কার কোন cluster অপারেশন করার অনুমতি আছে তা নিয়ন্ত্রণ করে। একই least-privilege নীতি, এবার cluster-অপারেশনে প্রয়োগ।
নেটওয়ার্ক পলিসি
কোন pod কার সাথে যোগাযোগ করতে পারবে তা সীমিত করে। ডিফল্টভাবে Kubernetes-এ সব pod একে অপরের সাথে কথা বলতে পারে ("allow-all") — একটি সাধারণ ও ঝুঁকিপূর্ণ মিসকনফিগারেশন যদি স্পষ্টভাবে সীমিত না করা হয়।

লক্ষ্য করুন — উভয় ক্ষেত্রেই মূল নীতি একই যা আমরা এই মডিউল জুড়ে বারবার দেখছি: ডিফল্ট হওয়া উচিত সীমিত, প্রয়োজন অনুযায়ী স্পষ্টভাবে খোলা — বিপরীতটা নয়।

৩ · একটি কন্টেইনার ইমেজ ফ্লিট অডিট করা

নিচের toy উদাহরণে তিনটি simulated কন্টেইনার ইমেজের কনফিগারেশন আছে। audit_container() ফাংশনটি root-এ চলা এবং হার্ডকোডেড সিক্রেট থাকা — এই দুটি ফাইন্ডিংস আলাদাভাবে চেক করে।

Python
containers = {
    "web-frontend:latest": {
        "base_image": "node:20-alpine",
        "runs_as_root": False,
        "has_hardcoded_secret": False,
    },
    "legacy-api:v1": {
        "base_image": "ubuntu:18.04",
        "runs_as_root": True,
        "has_hardcoded_secret": True,
    },
    "worker-job:v3": {
        "base_image": "python:3.11-slim",
        "runs_as_root": False,
        "has_hardcoded_secret": True,
    },
}

def audit_container(config):
    findings = []
    if config["runs_as_root"]:
        findings.append("রুট (root) ইউজার হিসেবে চলছে — কম্প্রোমাইজ হলে ব্লাস্ট-র‍্যাডিয়াস বড়")
    if config["has_hardcoded_secret"]:
        findings.append("ইমেজে হার্ডকোডেড সিক্রেট — image layer থেকে বের করা সম্ভব")
    return findings

print("কন্টেইনার ইমেজ সিকিউরিটি অডিট")
print("-" * 46)
for name, config in containers.items():
    findings = audit_container(config)
    status = "ঝুঁকিপূর্ণ" if findings else "নিরাপদ"
    print(f"[{status}] {name}  (base: {config['base_image']})")
    for f in findings:
        print(f"   -> {f}")
    print()

    
লক্ষ্য করুন — legacy-api:v1-এর দুটি ফাইন্ডিংসই আছে (সবচেয়ে ঝুঁকিপূর্ণ), worker-job:v3-এর শুধু একটি, আর web-frontend:latest সম্পূর্ণ পরিষ্কার। একটি বাস্তব CI/CD পাইপলাইনে এই ধরনের অডিট স্বয়ংক্রিয়ভাবে প্রতিটি বিল্ডে চলে এবং ঝুঁকিপূর্ণ ইমেজ ডিপ্লয়মেন্ট ব্লক করতে পারে — L54-এ আমরা ঠিক এই মেকানিজম বিস্তারিত দেখব।
মূল কথা · Key takeaway

কন্টেইনার সিকিউরিটি একটি নতুন নীতি নয় — এটি এই কোর্সের পরিচিত নীতিগুলোর (least privilege, dependency scanning, secrets management) একটি নতুন প্রয়োগক্ষেত্র। যে টিম ইতিমধ্যে এই নীতিগুলো বোঝে, তার জন্য কন্টেইনার সিকিউরিটি মূলত সঠিক টুলিং ও চেকলিস্ট প্রয়োগের ব্যাপার।

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

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

প্র ০১ একটি কন্টেইনার root হিসেবে চললে ঠিক কী বাড়তি ঝুঁকি তৈরি হয়, যদি কন্টেইনারাইজেশন নিজেই একটি আইসোলেশন লেয়ার হয়?

কন্টেইনার আইসোলেশন নিখুঁত নয় — কন্টেইনার-এস্কেপ দুর্বলতা (হোস্ট কার্নেলে একটি বাগ ব্যবহার করে কন্টেইনার থেকে হোস্টে বেরিয়ে আসা) মাঝেমধ্যে আবিষ্কৃত হয়। যদি একটি কন্টেইনার root হিসেবে চলে এবং এমন একটি এস্কেপ ঘটে, আক্রমণকারী হোস্টেও root অ্যাক্সেস পেয়ে যেতে পারে। non-root এক্সিকিউশন এই সম্ভাব্য এস্কেপের প্রভাব সীমিত রাখে — defense in depth-এর একটি উদাহরণ।

প্র ০২ কেন "ডিফল্ট-allow-all" pod নেটওয়ার্কিং একটি মিসকনফিগারেশন হিসেবে বিবেচিত হয়, যদিও এটি Kubernetes-এর ডিফল্ট আচরণ?

একটি ডিফল্ট আচরণ "নিরাপদ ডিফল্ট" না-ও হতে পারে। ডিফল্ট-allow-all মানে যদি একটি pod কম্প্রোমাইজ হয়, আক্রমণকারী স্বাধীনভাবে ক্লাস্টারের যেকোনো অন্য pod-এর সাথে যোগাযোগ করতে পারে (lateral movement) — যা L09-এর MITM ধারণার মতো, একবার ভেতরে ঢুকলে চলাচলের কোনো বাধা নেই। এক্সপ্লিসিট নেটওয়ার্ক পলিসি এই lateral movement-কে নির্দিষ্ট, প্রয়োজনীয় পাথে সীমিত রাখে — least-privilege নীতির নেটওয়ার্ক-স্তরের প্রয়োগ।

প্র ০৩ একটি ইমেজে হার্ডকোডেড সিক্রেট পাওয়া গেলে, শুধু ইমেজটি রিবিল্ড করে সিক্রেট মুছে দেওয়াই কেন যথেষ্ট নয়?

কারণ সিক্রেটটি ইতিমধ্যে পুরনো ইমেজ লেয়ারে (এবং সম্ভবত container registry-তে, বা কোনো ব্যাকআপে) স্থায়ীভাবে বিদ্যমান — এটি "প্রকাশিত" হিসেবে ধরে নিতে হবে। সঠিক ফিক্স হলো সেই নির্দিষ্ট সিক্রেটটি অবিলম্বে rotate/revoke করা (একটি নতুন কী ইস্যু করে পুরনোটি অকার্যকর করে দেওয়া) — শুধু ইমেজ রিবিল্ড করা পুরনো, potentially-leaked সিক্রেটটিকে valid রেখে দেয়।

অনুশীলন

  1. চিন্তা করুন: উপরের কোড সেলে containers-এ একটি নতুন ইমেজ যোগ করুন যা runs_as_root=True কিন্তু has_hardcoded_secret=False। Run চাপার আগে অনুমান করুন এর status কী দেখাবে।

    এটি "ঝুঁকিপূর্ণ" হিসেবে চিহ্নিত হবে, কিন্তু শুধুমাত্র একটি finding নিয়ে (root-execution) — সিক্রেট-সংক্রান্ত finding আসবে না। এটি দেখায় যে audit_container() প্রতিটি ঝুঁকিকে independent-ভাবে চেক করে, একটি সামগ্রিক "সবকিছু ঠিক আছে কি না" বুলিয়ানের বদলে — যা একটি বাস্তব অডিট রিপোর্টে বেশি কার্যকর কারণ এটি precisely জানায় কোন সমস্যাটি ঠিক করতে হবে।

  2. পরীক্ষা করুন: audit_container()-এ একটি তৃতীয় চেক যোগ করুন যা base_image-এ "latest" ট্যাগ ছাড়া অন্য কিছু আছে কি না তা নয় বরং base image পুরনো একটি নির্দিষ্ট তালিকায় (যেমন "ubuntu:18.04") আছে কি না তা flag করে (outdated base image detection)।

    একটি সম্ভাব্য সমাধান: একটি outdated_base_images = {"ubuntu:18.04", "python:3.6-slim"} সেট তৈরি করে, if config["base_image"] in outdated_base_images: findings.append(...) যোগ করুন। এই প্যাটার্নটি ঠিক L24-এর dependency-vulnerability lookup-এর মতো — একটি known-risky তালিকার বিপরীতে cross-reference করা।

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

আগের পাঠ
IAM ও ক্লাউড মিসকনফিগারেশন