পাঠ ২৬ · ৫৭-এর মধ্যে · মডিউল ৬
Home / Courses / Cloud Computing & DevOps / রেজিস্ট্রি ও স্ক্যানিং

কন্টেইনার রেজিস্ট্রি ও ইমেজ সিকিউরিটি স্ক্যানিং

Container registries & image security scanning
৭ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

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

  • কন্টেইনার রেজিস্ট্রি কী এবং পাবলিক বনাম প্রাইভেট রেজিস্ট্রির পার্থক্য
  • ইমেজ ট্যাগিং কনভেনশন এবং কেন latest ট্যাগ প্রোডাকশনে বিপজ্জনক
  • ইমেজ ভালনারেবিলিটি স্ক্যানিং কীভাবে কাজ করে এবং CI/CD পাইপলাইনে এটি কেন স্বয়ংক্রিয় গেট হওয়া উচিত
  • Python দিয়ে একটি সিমুলেটেড ইমেজ স্ক্যানার ও latest-ট্যাগ চেকার তৈরি করা

১ · কন্টেইনার রেজিস্ট্রি

কন্টেইনার রেজিস্ট্রিContainer Registryকন্টেইনার ইমেজ সংরক্ষণ ও বিতরণ করার একটি রিপোজিটরি — পাবলিক (সবার জন্য উন্মুক্ত) বা প্রাইভেট (একটি নির্দিষ্ট প্রতিষ্ঠানের জন্য সীমাবদ্ধ) হতে পারে। L23-এ যেমন দেখা গেছে, একটি ইমেজ বিল্ড করার পর সেটিকে বিতরণ করতে হয় যাতে বিভিন্ন পরিবেশ (স্টেজিং, প্রোডাকশন) সেই একই ইমেজ পুল করতে পারে — রেজিস্ট্রি এই বিতরণ প্রক্রিয়ার কেন্দ্রীয় জায়গা। পাবলিক রেজিস্ট্রি (যেমন Docker Hub-স্টাইল সার্ভিস) ওপেন-সোর্স/সাধারণ ইমেজের জন্য ব্যবহৃত হয়, আর প্রাইভেট রেজিস্ট্রি একটি প্রতিষ্ঠানের নিজস্ব মালিকানাধীন অ্যাপ্লিকেশন ইমেজের জন্য (অ্যাক্সেস-নিয়ন্ত্রিত)।

২ · ইমেজ ট্যাগিং — এবং latest-এর ঝুঁকি

প্রতিটি ইমেজ একটি ট্যাগ দিয়ে চিহ্নিত হয় (যেমন myapp:1.2.0) — ভার্সন নির্দিষ্ট করার জন্য। latest ট্যাগ (যদি স্পষ্টভাবে ট্যাগ না দেওয়া হয়, ডিফল্টভাবে ব্যবহৃত হয়) সুবিধাজনক মনে হতে পারে, কিন্তু এটি একটি চলমান টার্গেট — আজ latest যা নির্দেশ করছে, কাল অন্য কিছু নির্দেশ করতে পারে (কেউ একটি নতুন ইমেজ বিল্ড করে পুশ করলেই)।

কেন এটি প্রোডাকশনে বিপজ্জনক

যদি প্রোডাকশন কনফিগারেশন myapp:latest নির্দেশ করে, তাহলে ঠিক কোন সংস্করণ আসলে চলছে তার কোনো গ্যারান্টি নেই — দুটি ভিন্ন সময়ে ডিপ্লয় করলে দুটি ভিন্ন প্রকৃত ইমেজ চলে যেতে পারে, যদিও কনফিগারেশন ফাইলে কিছুই বদলায়নি। এটি L20-এর idempotency ও L18-এর টার্ফফর্ম স্টেট ধারণা লঙ্ঘন করে — একই কনফিগ থেকে একই ফলাফল আসার নিশ্চয়তা থাকে না। বেস্ট প্র্যাকটিস: সবসময় একটি নির্দিষ্ট, অপরিবর্তনীয় ভার্সন ট্যাগ ডিপ্লয় করুন (myapp:1.2.0), কখনো latest নয়।

৩ · ইমেজ সিকিউরিটি স্ক্যানিং

একটি কন্টেইনার ইমেজ তার বেস OS থেকে শুরু করে প্রতিটি ইনস্টল করা লাইব্রেরি/প্যাকেজ পর্যন্ত অনেক স্তরের সফটওয়্যার ধারণ করে — এবং যেকোনো একটি স্তরে একটি পরিচিত দুর্বলতা (CVECommon Vulnerabilities and Exposuresপরিচিত, ডকুমেন্টেড সফটওয়্যার দুর্বলতার একটি স্ট্যান্ডার্ড আইডেন্টিফায়ার — Cybersecurity কোর্সের CVE/CVSS পাঠে বিস্তারিত।) থাকলে পুরো চলমান কন্টেইনারটিই ঝুঁকিতে পড়ে। ইমেজ সিকিউরিটি স্ক্যানিং একটি ইমেজের প্রতিটি লেয়ারে ইনস্টল করা প্যাকেজের তালিকা বের করে এবং সেগুলোকে পরিচিত-দুর্বল প্যাকেজের একটি আপডেটেড ডেটাবেসের সাথে ক্রস-রেফারেন্স করে।

এই স্ক্যানিং কোনো ঐচ্ছিক বা ম্যানুয়াল "মাঝেমধ্যে করি" ধাপ হওয়া উচিত নয় — বরং CI/CD পাইপলাইনে একটি স্বয়ংক্রিয় গেট হওয়া উচিত (M8-এ বিস্তারিত): একটি ইমেজে যদি একটি নির্দিষ্ট সিভিয়ারিটির উপরে কোনো দুর্বলতা পাওয়া যায়, পাইপলাইন সেই ইমেজকে ডিপ্লয় হতে বাধা দেয় (L33-এর "fail fast" নীতির প্রয়োগ, সিকিউরিটির প্রেক্ষাপটে)।

Python
# ইমেজ ভালনারেবিলিটি স্ক্যান ও latest-ট্যাগ চেকার সিমুলেশন
image_layers = {
    "base_os_layer": ["libc6=2.31", "openssl=1.1.1n", "curl=7.68.0"],
    "runtime_layer": ["python3.12", "pip=23.0"],
    "app_layer": ["requests=2.25.0", "flask=2.0.1"],
}

# পরিচিত দুর্বল প্যাকেজের একটি ইলাস্ট্রেটিভ লুকআপ (বাস্তব CVE ডেটাবেস নয়, শুধু সিমুলেশন)
known_vulnerable = {
    "openssl=1.1.1n": "CVE-2022-XXXX (মাঝারি সিভিয়ারিটি)",
    "requests=2.25.0": "CVE-2023-YYYY (উচ্চ সিভিয়ারিটি)",
}


def scan_image(layers, vulnerable_db):
    findings = []
    for layer_name, packages in layers.items():
        for pkg in packages:
            if pkg in vulnerable_db:
                findings.append((layer_name, pkg, vulnerable_db[pkg]))
    return findings


def check_latest_tag(image_ref):
    """image_ref যেমন 'myapp:1.2.0' বা 'myapp:latest' বা ট্যাগবিহীন 'myapp' (ডিফল্টভাবে latest)।"""
    tag = image_ref.split(":")[1] if ":" in image_ref else "latest"
    return tag == "latest"


print("== ইমেজ ভালনারেবিলিটি স্ক্যান ==")
findings = scan_image(image_layers, known_vulnerable)
if findings:
    for layer_name, pkg, cve in findings:
        print(f"  [দুর্বলতা পাওয়া গেছে] লেয়ার={layer_name:14} প্যাকেজ={pkg:20} {cve}")
else:
    print("  কোনো পরিচিত দুর্বলতা পাওয়া যায়নি।")

print("\n== ডিপ্লয়মেন্ট রেফারেন্স চেক ==")
deployment_refs = ["myapp:1.2.0", "myapp:latest", "myapp"]
for ref in deployment_refs:
    flagged = check_latest_tag(ref)
    status = "ব্যর্থ — 'latest' ট্যাগ ব্যবহৃত!" if flagged else "পাস — নির্দিষ্ট ভার্সন ট্যাগ"
    print(f"  {ref:14}{status}")

    
লক্ষ্য করুন — স্ক্যানার দুটি দুর্বলতা খুঁজে পেয়েছে (openssl ও requests), এবং ট্যাগ চেকার দুটি ডিপ্লয়মেন্ট রেফারেন্সকে "ব্যর্থ" চিহ্নিত করেছে — myapp:latest স্পষ্টভাবে, আর ট্যাগবিহীন myapp-ও, কারণ ট্যাগ না দিলে Docker ডিফল্টভাবে latest ধরে নেয়। এই দুটি চেকই বাস্তবে একটি CI/CD পাইপলাইনে (M8) একত্রে একটি একক "প্রি-ডিপ্লয় গেট" হিসেবে চলে।
মূল কথা · Key takeaway

রেজিস্ট্রি ইমেজ সংরক্ষণ ও বিতরণ করে, নির্দিষ্ট ভার্সন ট্যাগ ব্যবহার idempotent, পূর্বাভাসযোগ্য ডিপ্লয়মেন্ট নিশ্চিত করে, এবং স্বয়ংক্রিয় ইমেজ স্ক্যানিং একটি পরিচিত দুর্বলতাযুক্ত ইমেজকে প্রোডাকশনে পৌঁছানোর আগেই আটকে দেয়। এই তিনটি প্র্যাকটিস একসাথে M6-এর কন্টেইনারাইজেশন মডিউলকে একটি নিরাপদ, নির্ভরযোগ্য ভিত্তিতে পরিণত করে — যার উপর M7-এর Kubernetes অর্কেস্ট্রেশন দাঁড়াবে।

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

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

প্র ০১ একটি টিম যুক্তি দেয় "আমরা সবসময় সর্বশেষ সংস্করণ চাই, তাই latest ট্যাগ ব্যবহার করাই সহজ ও যুক্তিসঙ্গত" — এই যুক্তির সমস্যা কোথায়?

"সর্বশেষ সংস্করণ চাওয়া" এবং "কোন নির্দিষ্ট সংস্করণ চলছে তা জানা" — দুটো ভিন্ন জিনিস। latest ট্যাগ দিয়ে আপনি সবসময় সর্বশেষ পাবেন ঠিকই, কিন্তু কোনো সমস্যা হলে (একটি ইনসিডেন্ট, একটি রিগ্রেশন) ঠিক কোন কোড চলছিল তা নিশ্চিতভাবে জানার কোনো উপায় থাকে না, এবং রোলব্যাক করাও কঠিন হয় (আগের latest-টা এখন আর কোথাও নির্দিষ্টভাবে চিহ্নিত নেই)। সঠিক পদ্ধতি হলো CI/CD পাইপলাইন স্বয়ংক্রিয়ভাবে নতুন নির্দিষ্ট ভার্সন ট্যাগ তৈরি করবে এবং ডিপ্লয়মেন্ট কনফিগ সেই নির্দিষ্ট ট্যাগ আপডেট করবে — "latest" এর সুবিধা না হারিয়েই পূর্ণ ট্রেসেবিলিটি বজায় থাকে।

প্র ০২ ইমেজ স্ক্যানিং যদি শুধু একটি ম্যানুয়াল, "মাঝে মাঝে করি" ধাপ হয় (স্বয়ংক্রিয় CI/CD গেট না হয়ে), তাহলে বাস্তবে কী ঘটতে পারে?

সময়ের চাপে বা ভুলে যাওয়ার কারণে স্ক্যান স্কিপ হয়ে যেতে পারে — বিশেষ করে জরুরি একটি হটফিক্স দ্রুত ডিপ্লয় করার সময়, যখন "একটু পরে স্ক্যান করব" বলে এড়িয়ে যাওয়া সহজ। যেহেতু নতুন CVE প্রতিদিনই আবিষ্কৃত হয়, একটি পুরনো ইমেজ যা আগে "ক্লিন" ছিল তা আজ একটি নতুন আবিষ্কৃত দুর্বলতার কারণে আর ক্লিন নাও থাকতে পারে — তাই প্রতিটি বিল্ড/ডিপ্লয়ে স্বয়ংক্রিয়ভাবে পুনরায় স্ক্যান করা আবশ্যক, মানুষের মনে রাখার উপর নির্ভর করা যায় না।

প্র ০৩ একটি প্রাইভেট কন্টেইনার রেজিস্ট্রি ব্যবহার করার নিরাপত্তাগত সুবিধা কী, একটি সম্পূর্ণ পাবলিক রেজিস্ট্রির তুলনায় (L16-এর অ্যাক্সেস-নিয়ন্ত্রণ নীতির সাথে সম্পর্ক)?

একটি প্রাইভেট রেজিস্ট্রি অ্যাক্সেস-নিয়ন্ত্রিত — শুধু অনুমোদিত ব্যবহারকারী/সার্ভিসই ইমেজ পুল/পুশ করতে পারে (L16-এর least-privilege নীতির সরাসরি প্রয়োগ, এখানে নেটওয়ার্ক পোর্টের বদলে ইমেজ অ্যাক্সেসে)। একটি প্রতিষ্ঠানের মালিকানাধীন অ্যাপ্লিকেশন কোড (যা ইমেজের ভেতরে বান্ডল থাকে) পাবলিক রেজিস্ট্রিতে রাখলে যে কেউ সেই ইমেজ ডাউনলোড করে ভেতরের কোড বিশ্লেষণ করতে পারতো — প্রাইভেট রেজিস্ট্রি এই এক্সপোজার সম্পূর্ণ এড়িয়ে যায়।

অনুশীলন

  1. চিন্তা করুন: একটি ওপেন-সোর্স লাইব্রেরির জন্য কি পাবলিক নাকি প্রাইভেট রেজিস্ট্রি বেশি যুক্তিসঙ্গত? কেন?

    পাবলিক রেজিস্ট্রি — যেহেতু ওপেন-সোর্স প্রকল্পের লক্ষ্যই হলো সর্বোচ্চ সংখ্যক ব্যবহারকারীর কাছে সহজে পৌঁছানো, এবং কোড ইতিমধ্যেই পাবলিকলি উপলব্ধ (সোর্স রিপোজিটরিতে), তাই ইমেজ প্রাইভেট রাখার কোনো নিরাপত্তাগত সুবিধা নেই — বরং পাবলিক রেজিস্ট্রি ব্যবহারকারীদের জন্য সহজলভ্যতা বাড়ায়।

  2. পরীক্ষা করুন: উপরের কোড সেলে known_vulnerable-এ "python3.12"-এর জন্য একটি নতুন এন্ট্রি যোগ করুন এবং scan_image চালিয়ে দেখুন এটি runtime_layer-এ শনাক্ত হয় কিনা।

    হ্যাঁ — যেহেতু scan_image ফাংশন প্রতিটি লেয়ারের প্রতিটি প্যাকেজ পরীক্ষা করে (নির্দিষ্ট কোনো লেয়ারে সীমাবদ্ধ নয়), "python3.12" এখন known_vulnerable-এ থাকায় এটি runtime_layer-এ শনাক্ত হয়ে findings তালিকায় যোগ হবে — দেখায় যে স্ক্যানার বেস OS, রানটাইম বা অ্যাপ — যেকোনো লেয়ারের দুর্বলতা সমানভাবে ধরতে পারে।

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

আগের পাঠ
মাল্টি-স্টেজ বিল্ড ও ইমেজ অপ্টিমাইজেশন