এই পর্বে যা শিখবেন
- Container-এর সীমা কোথায়, আর build, ship, run ধাপের threat map আঁকা
- Dockerfile আর Pod manifest রিভিউ করা, প্রমাণ চাওয়া আর score বের করা
- API server, etcd, RBAC আর audit log নিয়ে একজন নিরীক্ষকের প্রশ্ন
- Pod Security Standards, admission, NetworkPolicy, Secret আর ServiceAccount token-এর জায়গা চেনা
- Runtime alert থেকে শুরু করে Pod মুছে যাওয়ার আগে প্রমাণ বাঁচানো
এই পর্বের সীমা: এখানে কোনো container escape-এর কৌশল নেই। আমরা রিভিউকারী আর analyst-এর চোখে দেখব, কী ভুল হতে পারে আর কীভাবে ধরবেন। চারটি simulator ব্রাউজারের ভেতরের ছোট খেলনা ডেটায় চলে, কোনো আসল cluster ছোঁয় না। kubectl বা docker-এর উদাহরণগুলো চালানো হয়নি, শুধু নিজের অথবা লিখিত অনুমতি আছে এমন cluster-এর জন্য। অনুমতি ছাড়া অন্যের system-এ হাত দেওয়া অপরাধ হতে পারে, দেখুন পর্ব ০৬.
১ · এক kernel, অনেক ভাড়াটিয়া
একটি বহুতল ফ্ল্যাটবাড়ি ভাবুন। প্রতিটি ফ্ল্যাটের দরজা আলাদা, তালা আলাদা, ভাড়াটিয়াও আলাদা। কিন্তু পানির ট্যাংক, লিফট আর বিদ্যুতের মূল লাইন এক। ট্যাংকে ত্রুটি হলে সব ফ্ল্যাটেই সমস্যা। Container এই ফ্ল্যাটের মতো। আর hypervisor-এ চলা virtual machine অনেকটা আলাদা আলাদা বাড়ির মতো, যেখানে প্রতিটির নিজের ট্যাংক আছে।
কারিগরি ভাষায়, container কোনো ছোট কম্পিউটার নয়। এটি host-এর kernel-এ চলা সাধারণ প্রসেস। Linux namespace তার দৃশ্য আলাদা করে, আর cgroup তার সম্পদ সীমিত করে। ভেতরের গল্পটি পড়তে পারেন Operating Systems কোর্সের Container পর্বে. এখানে সেটি আবার বলব না। আমাদের প্রশ্ন একটিই: এই নকশা নিরাপত্তায় কী বদলায়?
Virtual machine বনাম container — কোথায় সীমা
Each VM has its own kernel; containers share one
প্রতিটি container kernel-এর সাথে system call দিয়ে কথা বলে। তাই kernel হলো সবার সাধারণ attack surface. এখান থেকে তিনটি বাস্তব সিদ্ধান্ত আসে:
- Node আপডেট container-এর নিরাপত্তারও অংশ। Image নতুন হলেও node-এর পুরোনো kernel থাকলে ঝুঁকি থেকে যায়। পর্ব ৩২-এর patch অগ্রাধিকার এখানেও খাটে।
- Container-এর ভেতরের root নিরীহ নয়। Container escape ঘটলে ভেতরের ক্ষমতা ক্ষতির মাত্রা ঠিক করে। তাই সীমা ভাঙা কঠিন করা আর ভাঙলেও ক্ষতি ছোট রাখা, দুটোই লাগে।
- বিশ্বাসের স্তর আলাদা হলে node আলাদা করুন। গ্রাহকের দেওয়া কোড আর পেমেন্ট সার্ভিস একই node-এ না রাখাই ভালো। Multi-tenancy-র এই সিদ্ধান্ত blast radius ঠিক করে।
Container মূলত প্যাকেজিং আর আলাদা দৃশ্যের ব্যবস্থা। নিরাপত্তার সীমা হিসেবে এটি তখনই ভালো কাজ করে, যখন least privilege-এর স্তরগুলো যোগ করা হয়। এই স্তর সাজানোই পর্ব ০৩-এর defense in depth.
২ · Threat map: build, ship, run
একটি প্যাকেট আটার পথ ভাবুন: কারখানায় তৈরি, ট্রাকে পরিবহন, দোকানে বিক্রি। কারখানায় ভেজাল মিশলে দোকানের তালা কিছু করবে না। ট্রাকে প্যাকেট বদলালে কারখানার মান যাচাই কাজে আসবে না। Container-এর জীবনও তিন ধাপের। পর্ব ০৪-এর threat modeling এই তিন ধাপে ভাগ করলে আলোচনা সহজ হয়।
Container-এর পথে কোথায় কী ঝুঁকি
Threats and controls across build, ship and run
| ধাপ | ভুল কী | প্রতিরোধ | কীভাবে জানবেন |
|---|---|---|---|
| Build | ভুল base, secret ঢুকে যাওয়া | পরিচিত base, lint, SBOM, আলাদা CI runner | Scan ফল, build log, secret scanning |
| Ship | Tag বদলে যাওয়া, চাবি ফাঁস | Digest, cosign-এর মতো স্বাক্ষর, লেখার অনুমতি সীমিত | Registry log, স্বাক্ষর যাচাইয়ের ফল |
| Run | অতি ক্ষমতা, খোলা সার্ভিস | Pod Security, RBAC, NetworkPolicy, Secret-এর সীমা | Audit log, runtime alert, flow log |
Dependency আর স্বাক্ষরের গভীর আলোচনা আছে পর্ব ৪৮-এ। Registry আর digest-এর কাজ হাতে-কলমে দেখতে পড়ুন Cloud ও DevOps কোর্সের registry পর্ব. এখানে শুধু রিভিউকারীর প্রশ্নগুলো ধরব।
৩ · Image-এর স্বাস্থ্য আর Dockerfile রিভিউ
বাজারের মাছ কেনার আগে আপনি কানকো দেখেন, গন্ধ নেন। Container image-ও চালানোর আগে যাচাই করতে হয়। ছয়টি অভ্যাস মনে রাখুন:
ছোট base
যা নেই, তাতে ত্রুটিও নেই। Distroless বা ছোট base-এ package কম, তাই scan-এর তালিকাও ছোট।
কম attack surfaceNon-root
Dockerfile-এ সংখ্যায় USER 10001. Kubernetes তখন সহজে যাচাই করতে পারে যে এটি root নয়। একে বলে non-root container.
Digest-এ বাঁধা
Tag সরে যেতে পারে। Digest হলো বিষয়বস্তুর hash, তাই একই digest মানে একই বাইট।
পুনরুৎপাদনযোগ্যLayer-এ secret নয়
পরের ধাপে মুছলেও আগের layer-এ মান থেকে যায়। Build-এর secret আলাদা mount-এ দিন।
ইতিহাস মোছে নাScan, বারবার
Image scanning শুধু build-এ নয়। আজকের পরিষ্কার image-এ কাল নতুন CVE মিলতে পারে।
জানা ত্রুটি ধরেস্বাক্ষর
স্বাক্ষর জানায় image কে বানিয়েছে আর বদলায়নি। তবে কেউ যাচাই না করলে স্বাক্ষরের মূল্য নেই। পর্ব ১১ দেখুন।
উৎসের প্রমাণএবার একটি উদাহরণ। এখানে build আর চূড়ান্ত image আলাদা, যাকে multi-stage build বলে। Package-এর token শুধু ওই একটি RUN-এর সময় পাওয়া যায়, কোনো layer-এ বসে না। ঠিকানা আর digest বানানো, আপনার নিজের মান ব্যবহার করুন।
# syntax=docker/dockerfile:1
FROM registry.example.com/base/python:3.13@sha256:50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c AS build
WORKDIR /src
COPY requirements.txt .
RUN --mount=type=secret,id=pkg_token \
PIP_INDEX_URL="https://build:$(cat /run/secrets/pkg_token)@pkg.example.com/simple" \
pip install --prefix=/install -r requirements.txt
COPY app/ app/
FROM registry.example.com/base/python-runtime:3.13@sha256:50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c
COPY --from=build /install /usr/local
COPY --from=build /src/app /app
USER 10001
ENTRYPOINT ["python", "/app/main.py"]রিভিউ চেকলিস্ট
| প্রশ্ন | লাল পতাকা | প্রমাণ চান |
|---|---|---|
| Base কোথা থেকে? | latest, অচেনা publisher | Digest, অনুমোদিত base-এর তালিকা |
শেষ USER কে? | লাইন নেই, অথবা root | সংখ্যাসূচক UID |
| Secret ঢুকতে পারে? | ENV বা ARG-এ token, COPY . | Build secret mount, .dockerignore, scan ফল |
| বাইরের কোড আসে কীভাবে? | Script নামিয়ে সরাসরি shell-এ | Checksum যাচাই, version বাঁধা |
| চূড়ান্ত image-এ কী আছে? | Compiler, shell, debug টুল | Multi-stage, SBOM |
| কে সই করল? | স্বাক্ষর নেই, যাচাই নেই | Pipeline-এর স্বাক্ষর, admission-এ যাচাই |
রিভিউয়ের একটি অংশ যন্ত্রে করা যায়। নিচের খেলনা linter শুধু text-এর নিয়ম মেলায়। BAD বদলে আবার চালান:
// Toy Dockerfile linter: text rules only, nothing is built. Edit BAD and run again.
const BAD = `FROM python:latest
ENV API_KEY=example-token
RUN curl -s https://example.com/setup.sh | sh
ADD https://example.com/app.tar.gz /app/
COPY . /app
CMD ["python", "/app/main.py"]`;
const GOOD = `FROM python:3.13-slim@sha256:aaaa
COPY app/ /app/
USER 10001
CMD ["python", "/app/main.py"]`;
function lint(text) {
const out = [], lines = text.split('\n').map(s => s.trim());
lines.forEach((l, i) => {
const add = (sev, msg) => out.push(`L${i + 1} ${sev} ${msg}`);
const from = l.match(/^FROM\s+(\S+)/i);
if (from && !from[1].includes('@sha256:')) add('MED ', 'base image not pinned by digest');
if (from && /:latest$/.test(from[1])) add('MED ', 'latest tag can change silently');
if (/^(ENV|ARG)\s+\S*(KEY|TOKEN|SECRET|PASSWORD)/i.test(l)) add('HIGH', 'secret-like value stays in image history');
if (/^RUN.*\|\s*(ba)?sh\b/i.test(l)) add('HIGH', 'remote script piped to shell, unverified');
if (/^ADD\s+https?:/i.test(l)) add('MED ', 'remote ADD has no checksum');
if (/^COPY\s+\.\s/i.test(l)) add('LOW ', 'whole context copied, check .dockerignore');
});
const users = lines.filter(l => /^USER\s/i.test(l));
const last = users.length ? users[users.length - 1].split(/\s+/)[1] : null;
if (last === null) out.push('END HIGH no USER line, runs as root by default');
else if (last === 'root' || last === '0') out.push('END HIGH final USER is root');
return out;
}
for (const [name, text] of [['BAD', BAD], ['GOOD', GOOD]]) {
const res = lint(text);
console.log(`${name}: ${res.length} finding(s)`);
res.forEach(r => console.log(' ' + r));
}BAD: 7 finding(s) এসেছে। L2 HIGH secret-like value stays in image history মানে মানটি image-এর ইতিহাসে থেকে যাবে। শেষে END HIGH no USER line, runs as root by default কোনো লাইনের নয়, পুরো ফাইলের ফল। GOOD: 0 finding(s) মানে এই কয়েকটি নিয়মে আপত্তি নেই, ফাইলটি নিরাপদ এমন প্রমাণ নয়। Linter জানে না base-এর ভেতরে কী আছে। সেটি জানতে scan আর SBOM লাগবে। আসল প্রকল্পে Hadolint-এর মতো টুল এই ধরনের কাজ করে।
৪ · Runtime: চলার সময় ক্ষমতা কমানো
পরিষ্কার image-ও চলার সময় বিপজ্জনক হতে পারে, যদি তাকে বেশি ক্ষমতা দেন। ব্যাংকের নতুন কর্মীকে ভল্টের চাবি দেওয়ার দরকার নেই। তাঁর কাজের চাবিটুকুই যথেষ্ট, এমনকি তিনি সৎ হলেও। কারণ ভুল আর চাবি চুরি দুটোই ঘটে। নিচের টেবিল প্রতিটি সেটিংয়ের সীমাও বলে দেয়।
| নিয়ন্ত্রণ | কী করে | কী থামায় না |
|---|---|---|
Capability drop: ALL | root-এর ক্ষমতার টুকরোগুলো সরায় | Application-এর নিজের ভুল |
seccomp RuntimeDefault | বিপজ্জনক ও অপ্রয়োজনীয় system call ছেঁকে দেয় | অনুমোদিত call-এর ভেতরের kernel ত্রুটি |
| AppArmor বা SELinux | ফাইল আর সম্পদে প্রোফাইলভিত্তিক সীমা | Node-এ চালু না থাকলে কিছুই নয় |
| Read-only root | Image-এর ফাইল বদলানো আটকায় | লেখার অনুমতি থাকা volume |
| Non-root | UID 0-এর সুবিধা সরায় | ওই user যা পড়তে পারে, তা ফাঁস |
allowPrivilegeEscalation: false | প্রসেস নিজের ক্ষমতা বাড়াতে পারে না | আগে থেকেই থাকা ক্ষমতা |
একটি মনে রাখার মতো তথ্য: Kubernetes-এ seccomp নিজে থেকে সবসময় চালু থাকে না। Kubelet-এ seccompDefault চালু না থাকলে আর Pod-এ না লিখলে container সাধারণত Unconfined চলে। তাই ধরে নেবেন না, লিখে দিন। Linux স্তরের ধারণা পাবেন পর্ব ৩০-এ।
তিনটি জিনিস রিভিউয়ে সরাসরি থামান: Privileged container প্রায় সব সীমা তুলে দেয়। Host mount node-এর ফাইল Pod-এর হাতে দেয়, আর readOnly থাকলেও গোপন ফাইল পড়া যেতে পারে। Docker socket mount করা মানে container runtime-এর নিয়ন্ত্রণ দেওয়া, যা কার্যত node-এর ওপর উচ্চ ক্ষমতা। “CI-তে একটু দরকার” বললে বিকল্প খুঁজুন, যেমন আলাদা build সার্ভিস। ব্যতিক্রম হলে তা লিখিত, সময়সীমাসহ আর আলাদা node-এ।
সব মিলিয়ে একটি শক্ত করা Pod দেখতে এমন। YAML-টি parser দিয়ে যাচাই করা, তবে আসল cluster-এ চালানো হয়নি। Digest বানানো। Secret বা token নেই, আর লেখার জায়গা শুধু /tmp.
apiVersion: v1
kind: Pod
metadata:
name: shop-api
namespace: shop
labels:
app: api
spec:
automountServiceAccountToken: false
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
seccompProfile:
type: RuntimeDefault
containers:
- name: api
image: registry.example.com/shop/api@sha256:50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c
ports:
- containerPort: 8080
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
memory: "256Mi"
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir:
sizeLimit: 64MisecurityContext বা security context দুই স্তরে বসে: Pod আর container. Application যদি Kubernetes API ব্যবহার না করে, automountServiceAccountToken: false token-টাই Pod-এ আনে না। limits এক Pod-এর চাপে পুরো node বসে যাওয়া কমায়। 8080 ১০২৪-এর ওপরের সাধারণ port, তাই NET_BIND_SERVICE-ও লাগেনি।
৫ · Control plane আর RBAC: cluster-এর চাবির গোছা
Kubernetes অনেক machine-এ container চালায়। তার control plane হলো অফিসের প্রধান কার্যালয়, যেখানে চাবির গোছা, হাজিরা খাতা আর সব ফাইল থাকে। এটি দখল হলে একটি Pod নয়, পুরো প্রতিষ্ঠান ঝুঁকিতে। RBAC, API server আর তিন দরজার পুরো গঠন শিখতে পড়ুন Cloud ও DevOps কোর্সের Kubernetes নিরাপত্তা পর্ব. এখানে আমরা নিরীক্ষকের প্রশ্ন সাজাব।
| অংশ | ঝুঁকি | নিরীক্ষকের প্রশ্ন |
|---|---|---|
| API server | Internet-এ খোলা, দুর্বল পরিচয় | কোন network থেকে পৌঁছানো যায়? মানুষের login-এ MFA আছে? |
| etcd | সব অবস্থা আর Secret এক জায়গায়, backup-এও | Encryption at rest চালু? Client certificate বাধ্যতামূলক? Backup কে পড়তে পারে? |
| kubelet | Node-এর API খোলা থাকলে Pod-এ পথ | Anonymous access বন্ধ? Authorization আছে? |
| RBAC | পুরোনো binding, wildcard | কার কাছে cluster-admin? শেষ পর্যালোচনা কবে? |
| Audit log | চালু নেই, বা node-এই শুধু | Policy file আছে? Log কোথায় যায়, কতদিন থাকে? |
দুটি ডিফল্ট অবাক করতে পারে। প্রথমত, Kubernetes-এর নথি অনুযায়ী API server ডিফল্টে etcd-তে resource সাধারণ text-এ রাখে। Encryption চালু করতে --encryption-provider-config দিতে হয়, আর identity provider প্রথমে থাকলে নতুন লেখা encrypt হয় না। KMS-ভিত্তিক envelope encryption ভালো পথ। দ্বিতীয়ত, --audit-policy-file ছাড়া API server audit event লেখে না। Managed cluster-এ এগুলো provider-ভেদে আলাদা, তাই shared responsibility নথি দেখে লিখিত উত্তর নিন।
RBAC-এ নাম নয়, reach দেখুন
ci-deployer নামটি নিরীহ শোনায়। কিন্তু নথি স্পষ্ট বলে, namespace-এ Pod বানানোর ক্ষমতা মানে সেখানকার Secret mount করা আর সেখানকার যেকোনো ServiceAccount হিসেবে Pod চালানোর ক্ষমতা। আবার Secret-এর শুধু list-ও মান দেখিয়ে দেয়। bind, escalate আর impersonate verb ক্ষমতা বাড়ানোর পথ হতে পারে। পর্ব ১৯ আর পর্ব ২০-এর মতোই এখানে প্রশ্ন একটি: এই পরিচয় শেষ পর্যন্ত কতদূর পৌঁছাতে পারে? RoleBinding এক namespace-এ থামে, ClusterRoleBinding থামে না।
// Toy RBAC review: which subject reaches what? In-memory roles, no cluster involved.
const ROLES = {
'pod-viewer': [{ res: ['pods'], verbs: ['get', 'list'] }],
'ci-deployer': [{ res: ['pods', 'deployments'], verbs: ['get', 'list', 'create'] }],
'secret-read': [{ res: ['secrets'], verbs: ['list'] }],
'binder': [{ res: ['clusterroles'], verbs: ['bind'] }],
'legacy-admin': [{ res: ['*'], verbs: ['*'] }],
};
const BINDINGS = [ // ns '*' stands for a ClusterRoleBinding
{ who: 'sa:shop/report', role: 'pod-viewer', ns: 'shop' },
{ who: 'sa:ci/deployer', role: 'ci-deployer', ns: 'shop' },
{ who: 'sa:ops/old-cron', role: 'legacy-admin', ns: '*' },
{ who: 'user:dev-a', role: 'secret-read', ns: 'shop' },
{ who: 'user:dev-b', role: 'binder', ns: '*' },
];
const hit = (list, v) => list.includes('*') || list.includes(v);
const mine = who => BINDINGS.filter(b => b.who === who);
const reach = (who, verb, res) => mine(who)
.filter(b => ROLES[b.role].some(r => hit(r.res, res) && hit(r.verbs, verb)))
.map(b => b.ns);
for (const who of new Set(BINDINGS.map(b => b.who))) {
const notes = [];
const wild = mine(who).some(b => ROLES[b.role]
.some(r => r.res.includes('*') || r.verbs.includes('*')));
if (wild) notes.push('CRITICAL wildcard rule');
const sec = reach(who, 'list', 'secrets');
if (sec.length) notes.push(`HIGH list secrets in ns ${sec}`);
const pods = reach(who, 'create', 'pods');
if (pods.length) notes.push(`HIGH create pods in ns ${pods}, can mount its secrets`);
const bind = reach(who, 'bind', 'clusterroles');
if (bind.length) notes.push(`HIGH bind verb in ns ${bind}, escalation path`);
console.log(`${who}: ${notes.length ? notes.length + ' finding(s)' : 'OK'}`);
notes.forEach(n => console.log(' ' + n));
}Role-এর নাম না পড়েও ঝুঁকি বেরিয়েছে। sa:ops/old-cron: 4 finding(s), কারণ * সব কিছুকে ধরে, আর ns * মানে পুরো cluster. sa:ci/deployer-এ Secret পড়ার অনুমতি লেখা নেই, তবু HIGH create pods in ns shop, can mount its secrets এসেছে। এটিই পরোক্ষ reach. sa:shop/report: OK মানে শুধু এই খেলনা নিয়মে আপত্তি নেই। আসল cluster-এ নিজের অনুমতির মধ্যে kubectl auth can-i --list --as=... দিয়ে মিলিয়ে দেখা যায়।
৬ · Pod Security Standards আর admission control
হাতে হাতে রিভিউ কয়েকটি Pod-এ চলে, হাজার Pod-এ নয়। তাই নিয়মকে দরজায় বসাতে হয়। মেট্রো স্টেশনের ব্যাগ স্ক্যানারের মতো: কেউ ভুলে গেলেও যন্ত্র থামাবে।
Pod Security Standards তিনটি স্তর ঠিক করে। আর Pod Security Admission namespace-এর label দেখে সেগুলো প্রয়োগ করে।
| স্তর | মানে | কোথায় | মূল নিয়ম |
|---|---|---|---|
| privileged | কোনো সীমা নেই | বিশ্বস্ত system টুল | নেই |
| baseline | জানা ক্ষমতা বাড়ানোর পথ বন্ধ | সাধারণ workload, শুরুর ধাপ | Privileged নয়, host namespace নয়, hostPath নয়, সীমিত capability |
| restricted | কড়া hardening | সংবেদনশীল বা বিশ্বাস কম এমন workload | Baseline-এর সাথে non-root, drop: ALL, seccomp, privilege escalation বন্ধ, সীমিত volume |
Label-এর তিন mode হলো enforce, warn আর audit. নতুন namespace-এ প্রথমে warn আর audit দিয়ে দেখা নিরাপদ, তারপর enforce. নিচের খেলনা auditor নয়টি নিয়ম মেলায়। এটি আসল PSA নয়, তার সব নিয়মও এখানে নেই।
// Toy auditor: nine restricted-style checks on in-memory pod objects, not a full PSA.
const CHECKS = [
['privileged off', (p, c) => !c.privileged],
['no host namespaces', p => !p.hostNetwork && !p.hostPID && !p.hostIPC],
['no hostPath', p => !(p.volumes || []).some(v => v.hostPath)],
['no priv escalation', (p, c) => c.allowPrivilegeEscalation === false],
['runAsNonRoot true', (p, c) => (c.runAsNonRoot ?? p.runAsNonRoot) === true],
['runAsUser not 0', (p, c) => (c.runAsUser ?? p.runAsUser) !== 0],
['seccomp set', (p, c) => ['RuntimeDefault', 'Localhost'].includes(c.seccomp ?? p.seccomp)],
['capabilities drop ALL', (p, c) => (c.drop || []).includes('ALL')],
['add only NET_BIND', (p, c) => (c.add || []).every(x => x === 'NET_BIND_SERVICE')],
];
const PODS = {
'report-worker': { runAsUser: 0, volumes: [{ hostPath: '/var/run/docker.sock' }],
containers: [{ privileged: true }] },
'plain-default': { containers: [{}] },
'shop-api': { runAsNonRoot: true, runAsUser: 10001, seccomp: 'RuntimeDefault',
containers: [{ allowPrivilegeEscalation: false, drop: ['ALL'] }] },
'two-containers': { runAsNonRoot: true, seccomp: 'RuntimeDefault', containers: [
{ allowPrivilegeEscalation: false, drop: ['ALL'] },
{ allowPrivilegeEscalation: false, drop: ['ALL'], add: ['SYS_ADMIN'] }] },
};
for (const [name, pod] of Object.entries(PODS)) {
const failed = CHECKS.filter(([, ok]) => !pod.containers.every(c => ok(pod, c)));
const score = CHECKS.length - failed.length;
console.log(`${name}: ${score}/${CHECKS.length} ${failed.length ? 'REJECT' : 'PASS'}`);
failed.forEach(([label]) => console.log(' fail: ' + label));
}plain-default: 5/9 REJECT সবচেয়ে শিক্ষণীয়। Pod-এ কিছুই বিপজ্জনক লেখা নেই, তবু restricted-এ আটকেছে, কারণ এই স্তরে নিরাপদ সেটিং লিখে দিতে হয়। report-worker: 2/9 REJECT Docker socket আর root-এর জন্য। two-containers: 8/9 REJECT দেখায়, একটি container-এর SYS_ADMIN-ই পুরো Pod আটকায়। shop-api: 9/9 PASS মানে শুধু এই নিয়মগুলো পাশ। Read-only root বা digest এই নয়টিতে নেই, আর আসল restricted স্তরও এগুলো দাবি করে না। তাই restricted পাশ মানেই পুরো নিরাপদ নয়।
এবার একজন রিভিউকারী এই report-worker Pod ধাপে ধাপে ঠিক করছেন। লক্ষ্য করুন, একবারে সব না বদলে প্রতি ধাপে পরীক্ষা করলে ভাঙা অংশ চেনা যায়।
| Score | |
| ফল | |
| বাকি ব্যর্থতা | |
| এই ধাপে বদল |
securityContext: { runAsUser: 0 } containers: - name: worker securityContext: { privileged: true } volumes: - name: docker hostPath: { path: /var/run/docker.sock }
# removed: privileged: true
containers:
- name: worker
securityContext: {}
# removed: docker socket hostPath volumes: [] # job status now comes from an internal API
securityContext: runAsNonRoot: true runAsUser: 10001
securityContext: allowPrivilegeEscalation: false capabilities: drop: ["ALL"]
securityContext:
runAsNonRoot: true
runAsUser: 10001
seccompProfile:
type: RuntimeDefault
automountServiceAccountToken: false containers: - name: worker image: registry.example.com/report@sha256:50d8... securityContext: readOnlyRootFilesystem: true volumeMounts: - { name: tmp, mountPath: /tmp }
- শুরু: ২/৯। Pod root-এ চলে, privileged, আর Docker socket mount করা। শুধু host namespace আর অতিরিক্ত capability-র দুটি নিয়ম পাশ। ব্যর্থতা ৭টি।
- Privileged সরানো: ৩/৯। রিভিউকারী মালিক দলকে জিজ্ঞেস করলেন, এটি কেন লাগে। উত্তর: শুধু socket-এর জন্য। তাই প্রথমেই এটি সরানো হলো। ব্যর্থতা ৬টি।
- Socket সরানো: ৪/৯। Worker socket দিয়ে অন্য container-এর অবস্থা পড়ত। সেই তথ্য এখন একটি ভেতরের API থেকে আসে। সবচেয়ে বড় ঝুঁকি গেল, ব্যর্থতা ৫টি।
- Non-root: ৬/৯। এক ধাপে দুটি নিয়ম পাশ হলো। আগে image-এ
/tmp-এর অনুমতি পরীক্ষা করা হয়েছে, নইলে এখানে অ্যাপ ভাঙতে পারে। ব্যর্থতা ৩টি। - Escalation আর capability: ৮/৯।
allowPrivilegeEscalation: falseআরdrop: ["ALL"]আরও দুটি নিয়ম মেটায়। অ্যাপ8080-এ শোনে, তাই কোনো capability ফেরাতে হয়নি। ব্যর্থতা ১টি। - seccomp: ৯/৯, PASS.
RuntimeDefaultযোগ হলো। এবার নয়টি নিয়মই পাশ, ব্যর্থতা ০। Staging-এ চালিয়ে দেখা হলো, কোনো system call আটকায়নি। - নিয়মের বাইরে: এখনো ৯/৯। Score বদলায়নি, তবু নিরাপত্তা বেড়েছে। Token আর আসে না, image digest-এ বাঁধা, আর root file system-এ লেখা বন্ধ। Score লক্ষ্য নয়, ঝুঁকি কমানো লক্ষ্য।
Admission control: নিজের নিয়ম
PSA-র তিনটি স্তর স্থির। “শুধু আমাদের registry-র স্বাক্ষরিত image চলবে” ধরনের নিয়মের জন্য admission control-এর webhook বা policy engine লাগে। Kyverno আর OPA Gatekeeper দুটি পরিচিত উদাহরণ, কোনোটি একমাত্র পথ নয়। ছোট নিয়মে Kubernetes-এর ValidatingAdmissionPolicy-ও চলে। সবই policy as code: নিয়ম Git-এ থাকে, রিভিউ হয়, পরীক্ষা হয়।
- অনুরোধ
POST podCI থেকে - পরিচয়, RBAC
allowকে, কী - Mutating
যোগtoken volume - Validating
নিয়মPSA, policy - etcd
createdজমা
| চেষ্টা | |
| Image | |
| securityContext | |
| সিদ্ধান্ত |
Error from server (Forbidden): pods "shop-api" is forbidden:
violates PodSecurity "restricted:latest":
allowPrivilegeEscalation != false, unrestricted capabilities,
runAsNonRoot != true, seccompProfile
admission webhook denied the request:
image must use a digest and a trusted signature
- অনুরোধ। CI-এর ServiceAccount
shopnamespace-এshop-apiPod পাঠাল। Imageapi:latest,securityContextনেই। Namespace-এ restrictedenforceচালু। - পরিচয় আর RBAC. পরিচয় মিলেছে, আর এই ServiceAccount-এর Pod বানানোর অনুমতি আছে। তাই RBAC অনুমতি দিল। মনে রাখুন, এই ধাপ Pod-এর বিষয়বস্তু দেখে না।
- Mutating admission. অনুরোধটি এখানে বদলাতে পারে। ServiceAccount admission একটি token volume যোগ করল। Mutating আগে চলে, যাতে validating ধাপ চূড়ান্ত রূপটি দেখে।
- Validating: PSA বাতিল করল। Pod restricted-এর চারটি নিয়ম মানেনি। অনুরোধ এখানেই শেষ, etcd-তে কিছু লেখা হয়নি। RBAC-এর allow এখানে কাজে আসে না।
- দ্বিতীয় চেষ্টা: policy বাতিল করল। দল
securityContextঠিক করল, তাই PSA পাশ। কিন্তু image এখনোlatest. প্রতিষ্ঠানের নিজের নিয়ম digest আর স্বাক্ষর চায়, তাই আবার DENY. - তৃতীয় চেষ্টা: ALLOW. Image এখন digest-এ বাঁধা, স্বাক্ষরও যাচাই হলো। সব দরজা পেরিয়ে Pod etcd-তে জমা হলো। ব্যর্থ দুটি চেষ্টা audit log চালু থাকলে সেখানে দেখা যাবে।
রিভিউয়ের তিনটি ফাঁক: PSA-র enforce Pod-এই খাটে, Deployment-এ নয়। তাই Deployment গৃহীত হলেও তার Pod আটকে যেতে পারে, আর warn সেই ভুল আগে দেখায়। Admission শুধু তৈরি বা বদলের সময় দেখে, আগে থেকে চলা Pod-কে নয়। আর webhook নষ্ট হলে failurePolicy অনুযায়ী হয় Pod আটকায়, নয়তো নিয়ম চুপচাপ বন্ধ হয়।
৭ · NetworkPolicy, Secret আর ServiceAccount token
NetworkPolicy: ডিফল্টে সব খোলা
একটি বড় অফিসে সব কক্ষের দরজা খোলা থাকলে, রিসেপশন পেরোনো যে কেউ হিসাব শাখায় যেতে পারেন। Kubernetes-এর network-ও ডিফল্টে এমন। কোনো policy না থাকলে Pod সবার সাথে কথা বলতে পারে। একটি Pod দখল হলে lateral movement সহজ হয়। পর্ব ২২-এর segmentation-এর ধারণা এখানে NetworkPolicy দিয়ে আসে।
নিয়ম চারটি। Policy কোনো Pod-কে বাছলে শুধু ওই দিকে সে isolated হয়। Policy একে অন্যের সাথে সংঘাত করে না, যোগ হয়। সংযোগের জন্য উৎসের egress আর গন্তব্যের ingress দুটোই লাগবে। আর এটি প্রয়োগ করে CNI plugin. Plugin সমর্থন না করলে API server policy নিয়ে নেবে, কিন্তু কিছুই আটকাবে না। তাই নিষিদ্ধ সংযোগ চেষ্টা করে নিজের cluster-এ যাচাই করুন।
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: shop
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: web-to-api
namespace: shop
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: web
ports:
- protocol: TCP
port: 8080
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: shop
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53প্রথম policy-তে খালি podSelector: {} সব Pod বাছে, আর rule না থাকায় default deny হয়। দ্বিতীয়টি একটি পথ খোলে। তৃতীয়টি না দিলে egress বন্ধ থাকায় DNS-ও ভাঙবে, যা নতুন দলের খুব সাধারণ ভুল। web থেকে api-তে পৌঁছাতে web-এর egress নিয়মও লাগবে, যা এখানে দেখানো হয়নি।
দুই দিকের নিয়ম মিলিয়ে হিসাব কেমন হয়, চালিয়ে দেখুন:
// Toy NetworkPolicy evaluator: no packets, only in-memory labels and rules.
const PODS = { web: { app: 'web' }, api: { app: 'api' }, db: { app: 'db' }, batch: { app: 'batch' } };
const POLICIES = [
{ name: 'default-deny', select: {}, types: ['ingress', 'egress'] },
{ name: 'web-out', select: { app: 'web' }, types: ['egress'],
egress: [{ peer: { app: 'api' }, port: 8080 }] },
{ name: 'api-rules', select: { app: 'api' }, types: ['ingress', 'egress'],
ingress: [{ peer: { app: 'web' }, port: 8080 }],
egress: [{ peer: { app: 'db' }, port: 5432 }] },
{ name: 'db-in', select: { app: 'db' }, types: ['ingress'],
ingress: [{ peer: { app: 'api' }, port: 5432 }] },
{ name: 'batch-out', select: { app: 'batch' }, types: ['egress'],
egress: [{ peer: { app: 'db' }, port: 5432 }] },
];
const sel = (s, pod) => Object.entries(s).every(([k, v]) => PODS[pod][k] === v);
function side(pols, pod, dir, peer, port) {
const mine = pols.filter(q => q.types.includes(dir) && sel(q.select, pod));
if (mine.length === 0) return true; // not isolated in this direction
return mine.some(q => (q[dir] || []).some(r => sel(r.peer, peer) && r.port === port));
}
const FLOWS = [['web', 'api', 8080], ['api', 'db', 5432], ['web', 'db', 5432],
['batch', 'db', 5432], ['api', 'web', 8080]];
function show(title, pols) {
console.log(title);
for (const [src, dst, port] of FLOWS) {
const out = side(pols, src, 'egress', dst, port);
const inn = side(pols, dst, 'ingress', src, port);
console.log(` ${out && inn ? 'ALLOW' : 'DENY '} ${src} -> ${dst}:${port} egress=${out} ingress=${inn}`);
}
}
show('no policy at all:', []);
show('default deny plus allow rules:', POLICIES);Policy ছাড়া পাঁচটি পথই ALLOW, এমনকি web -> db:5432. Policy-র পর শুধু দরকারি দুটি থাকে। সবচেয়ে সূক্ষ্ম লাইন DENY batch -> db:5432 egress=true ingress=false: batch-এর বের হওয়ার অনুমতি আছে, কিন্তু db-র ঢোকার নিয়মে সে নেই। এক দিক খোলা থাকলে সংযোগ হয় না। রিভিউয়ে তাই দুই দিকের নিয়মই পড়ুন।
Secret: base64 গোপনীয়তা নয়
Kubernetes Secret-এর মান base64-এ থাকে, যা যে কেউ ফেরাতে পারে। সুরক্ষা আসে চারটি স্তর থেকে: etcd encryption, RBAC-এ get, list আর watch-এর সীমা, Pod বানানোর অনুমতির সীমা, আর audit log. Environment variable-এর বদলে সীমিত অনুমতির ফাইলে দেওয়া প্রায়ই ভালো, কারণ env-এর মান debug আউটপুটে ছড়িয়ে পড়ে। বড় পরিবেশে secret vault থেকে আনা আর নিয়মিত rotation ভালো। বিস্তারিত আছে Cloud ও DevOps কোর্সের Secrets ও KMS পর্বে.
ServiceAccount: Pod-এর পরিচয়পত্র
প্রতিটি Pod একটি ServiceAccount-এর নামে চলে। না বললে নেয় namespace-এর default. আধুনিক Kubernetes-এ projected token আসে TokenRequest API থেকে: স্বল্পমেয়াদি, audience-এ বাঁধা, Pod-এর সাথে যুক্ত, আর kubelet নিজে বদলে দেয়। নিরীক্ষকের চারটি প্রশ্ন: প্রতিটি অ্যাপের কি নিজের ServiceAccount আছে? যার API লাগে না, তার automountServiceAccountToken কি false? default-এ কি কোনো role বসানো? পুরোনো মেয়াদহীন token-এর Secret পড়ে আছে কি? Cloud-এ পৌঁছাতে হলে static চাবির বদলে workload identity ভালো।
৮ · Detection আর incident response: প্রমাণ উড়ে যাওয়ার আগে
প্রতিরোধ শতভাগ হয় না। চলার সময় দেখতে হবে কী ঘটছে। আবাসিক এলাকার CCTV-র মতো, যা দরজা আটকায় না, তবে অস্বাভাবিক কিছু দেখলে জানায়।
| সন্দেহজনক ঘটনা | কোথায় দেখবেন |
|---|---|
pods/exec, নতুন ClusterRoleBinding, হঠাৎ Secret তালিকা | API server audit log |
| Container-এ shell, সংবেদনশীল ফাইল পড়া, অচেনা প্রোগ্রাম | Runtime sensor |
| অচেনা ঠিকানায় egress, CPU-তে টানা উচ্চ চাপ | CNI flow log, metric |
| নীতি ভেঙে Pod তৈরির চেষ্টা | Admission-এর log, audit log |
| Node-এর ভেতরের অস্বাভাবিক প্রসেস | EDR, auditd |
Falco runtime detection-এর একটি পরিচিত open-source উদাহরণ, যা eBPF-এর মতো পথে kernel-এর ঘটনা পড়ে। নিচের নিয়মের ধরন শিক্ষার জন্য, version-ভেদে field আর macro মিলিয়ে নিন। উৎপাদন পরিবেশে একে পর্ব ৫৬-এর মতো পরীক্ষা করে নিন, কারণ রক্ষণাবেক্ষণের shell থেকে false positive আসবে।
- rule: Shell in shop container
desc: A shell started inside a production pod
condition: >
spawned_process and container
and proc.name in (sh, bash)
and k8s.ns.name = "shop"
output: >
shell started (pod=%k8s.pod.name ns=%k8s.ns.name
cmd=%proc.cmdline user=%user.name)
priority: WARNING
tags: [container, shell]প্রমাণ কেন দ্রুত হারায়
পুরোনো server-এ disk থাকত, কাল সকালে দেখা যেত। Container-এর জগতে Pod নিজে থেকেই হারাতে পারে। Controller নষ্ট Pod সরায়, autoscaler node মুছে দেয়। পর্ব ৬৩-এর order of volatility এখানে আরও কঠিন।
| প্রমাণ | কখন হারায় | আগে থেকে কী করবেন |
|---|---|---|
| Memory | Container থামলেই | Runbook, node-এ অনুমোদিত memory সংগ্রহ |
| Container-এর লেখা ফাইল | Container সরলে | Read-only root, দ্রুত সংগ্রহ |
| Container log | Rotation, eviction, node মোছায় | Node agent দিয়ে কেন্দ্রীয় log |
| Kubernetes Event | ডিফল্টে সাধারণত প্রায় ১ ঘণ্টায় | Event-ও কেন্দ্রে পাঠান |
| Audit log | চালু না থাকলে কখনোই ছিল না | আগেই চালু, বাইরে জমা |
২ MiB/মিনিট লিখলে ইতিহাস = ৫০ ÷ ২ = ২৫ মিনিট
Kubernetes-এর নথিতে kubelet-এর ডিফল্ট containerLogMaxSize 10Mi আর containerLogMaxFiles 5. ধরা যাক একটি কাল্পনিক অ্যাপ মিনিটে ২ MiB লেখে। তাহলে node-এ মোটামুটি ২৫ মিনিটের ইতিহাস থাকে। আর kubectl logs শুধু সর্বশেষ ফাইল দেখায়, তাই সর্বোচ্চ ১০ ÷ ২ = ৫ মিনিট। আপনার গড় detection সময় (MTTD) যদি ৪০ মিনিট হয়, কেন্দ্রীয় log ছাড়া শুরুর প্রমাণ আগেই গেছে। সেজন্য পর্ব ৫৫ আর কেন্দ্রীয় logging পর্ব এখানে সরাসরি কাজে লাগে।
এবার রাতের একটি কাল্পনিক ঘটনা দেখুন। ঘড়ি simulated UTC, সব নাম আর ঠিকানা বানানো। মূল নীতিটি লক্ষ্য করুন: মোছা সবার শেষে। সাধারণ পদ্ধতি পাবেন পর্ব ৬১ আর পর্ব ৬২-এ।
| সময় (UTC) | |
| Pod | |
| Network | |
| Service traffic | |
| সংরক্ষিত প্রমাণ |
02:14:10 Warning shell started pod=shop-api-7c9d ns=shop cmd=sh user=app
kubectl get pod shop-api-7c9d -n shop -o yaml > pod.yaml kubectl logs shop-api-7c9d -n shop --all-containers \ --timestamps > logs.txt kubectl get events -n shop > events.txt sha256sum pod.yaml logs.txt events.txt > SHA256SUMS
# detach from Service and ReplicaSet, keep the pod kubectl label pod shop-api-7c9d -n shop app- quarantine=true # a deny-all NetworkPolicy selects quarantine=true kubectl cordon node-3
02:31 memory captured on node-3 by IR lead 02:38 node-3 disk snapshot created 02:40 hashes recorded in custody log
02:13:52 create pods/exec shop-api-7c9d
user=sa:ci/deployer src=203.0.113.50
02:15:07 list secrets ns=shop → 403
sa:ci/deployer: create pods in ns shop → rotate CI credentials → review secrets mountable in ns shop → add source rule for CI identity
custody log closed pod deleted after approval fleet checked: api@sha256:50d8... signature OK post-incident review scheduled
- 02:14: Alert. Production Pod-এ shell খুলেছে। প্রথম প্রলোভন হলো Pod মুছে দেওয়া। মুছবেন না। Incident খুলে সময় UTC-তে লিখতে শুরু করুন। প্রমাণ এখনো ০।
- 02:16: সহজ প্রমাণ আগে। Spec, log আর event ফাইলে নেওয়া হলো, hash-সহ। এই পড়ার কাজে Pod বদলায় না, আর কয়েক মিনিটের মধ্যেই log ঘুরে যেতে পারত।
- 02:19: মুছে নয়, আলাদা করে।
applabel সরানোয় Pod আর Service-এর traffic পায় না, Deployment একটি নতুন Pod তোলে। Quarantine policy network বন্ধ করে। সেবা চলছে, প্রমাণও আছে। - 02:31: ভঙ্গুর প্রমাণ। অনুমোদিত দায়িত্বশীল memory আর node snapshot নিলেন, chain of custody-তে লিখলেন। প্রতিটি কাজ Pod বা node-কে কিছুটা বদলায়, তাই নোট রাখা জরুরি।
- 02:48: কে করেছিল? Audit log দেখায় alert-এর ১৮ সেকেন্ড আগে
sa:ci/deployerঅচেনা ঠিকানা থেকে exec করেছে। Secret তালিকার চেষ্টা ৪০৩ পেয়েছে। তাই CI-এর credential ফাঁস এখন প্রধান ধারণা। - 03:05: কতদূর পৌঁছাতে পারত? RBAC reach দেখায়, এই পরিচয়
shop-এ Pod বানাতে পারে। তাই CI credential বদলানো হলো, আর ওই namespace-এর mount করার মতো Secret পর্যালোচনায় গেল। - 03:40: এবার মোছা। অনুমোদনের পর Pod মোছা হলো। Pod-এ বাঁধা token অচল হয়। চলমান Pod-গুলো স্বাক্ষরিত digest-এর সাথে মেলানো হলো। শেষে post-incident রিভিউ আর নতুন detection.
সীমাগুলো জানুন: Policy যোগ হওয়ায় আলাদা করা Pod-এ অন্য কোনো policy-র allow খাটলে traffic খোলা থাকতে পারে। চলমান সংযোগ কাটবে কি না, তা CNI-ভেদে আলাদা, তাই যাচাই করুন। kubectl exec বা ephemeral container Pod-এ চিহ্ন রাখে, আর ephemeral container পরে সরানো যায় না। দরকার হলে করুন, তবে লিখে রাখুন। প্রমাণ সংগ্রহ আগে থেকে অনুমোদিত runbook-এ থাকা ভালো, যা পর্ব ৬৪-এর artifact-এর সাথে মেলে।
৯ · মানদণ্ড আর প্রকাশিত ঘটনার শিক্ষা
নিজের চেকলিস্ট বানানোর আগে পরিচিত মানদণ্ড দেখুন। এগুলো নিরীক্ষার ভাষা, বিকল্প নয়। সংস্করণ বদলায়, তাই ব্যবহারের আগে মূল উৎস মিলিয়ে নিন।
- CIS Kubernetes Benchmark: সেটিং ধরে ধরে পরীক্ষা। kube-bench একটি পরিচিত টুল। Managed cluster-এ provider-ভিত্তিক সংস্করণ দেখুন।
- NSA/CISA Kubernetes Hardening Guidance: প্রথম প্রকাশ ২০২১ সালের আগস্টে, সংস্করণ 1.2 ২০২২ সালের আগস্টে। এটি supply chain, হামলাকারী আর ভেতরের ঝুঁকি, তিনটিই ধরে।
- OWASP Kubernetes Top 10: প্রকল্পের সাইটে এখন ২০২৫ সংস্করণ দেখা যায়, আগেরটি ২০২২-এর। ক্রম আর নাম দুই সংস্করণে আলাদা।
| ID | ঝুঁকি (২০২৫ তালিকা) | এই পর্বে |
|---|---|---|
| K01 | Insecure Workload Configurations | অংশ ৪, ৬ |
| K02 | Overly Permissive Authorization Configurations | অংশ ৫ |
| K03 | Secrets Management Failures | অংশ ৭ |
| K04 | Lack Of Cluster Level Policy Enforcement | অংশ ৬ |
| K05 | Missing Network Segmentation Controls | অংশ ৭ |
| K06 | Overly Exposed Kubernetes Components | অংশ ৫ |
| K07 | Misconfigured And Vulnerable Cluster Components | অংশ ৫, ৯ |
| K08 | Cluster To Cloud Lateral Movement | অংশ ৭ |
| K09 | Broken Authentication Mechanisms | অংশ ৫ |
| K10 | Inadequate Logging And Monitoring | অংশ ৫, ৮ |
প্রকাশিত ঘটনা থেকে যা শেখার
ঘটনাগুলো প্রকাশিত প্রতিবেদনের স্তরে। আমরা শুধু দেখব প্রতিরক্ষায় কী ফাঁক ছিল।
| ঘটনা (প্রতিবেদন অনুযায়ী) | ফাঁক | কী থামাতে পারত |
|---|---|---|
| Tesla, ২০১৮: RedLock জানায়, পাসওয়ার্ডহীন Kubernetes console থেকে cloud credential বেরিয়ে cryptojacking হয় | খোলা dashboard, Pod-এর কাছে static credential | পরিচয় যাচাই, private access, workload identity, CPU আর egress alert |
| Kubeflow, ২০২০: Microsoft-এর Azure Security Center দল জানায়, internet-এ খোলা dashboard থেকে কয়েক ডজন cluster-এ mining চলে | Admin UI সরাসরি খোলা, যে কেউ workload চালাতে পারত | Dashboard বাইরে না খোলা, admission-এ image-এর সীমা, audit alert |
| Runc-এর ত্রুটি CVE-2019-5736, ২০১৯: container runtime-এ গুরুতর ত্রুটি প্রকাশ | Runtime আর node-এর আপডেটে দেরির ঝুঁকি | Node patch, non-root, সীমিত ক্ষমতা, patch-এর তালিকায় runtime রাখা |
লক্ষ্য করুন, প্রথম দুটিতে জাদুর মতো কোনো ত্রুটি নেই। ভুল সেটিং আর খোলা দরজা। আরও ঘটনার ধরন পাবেন পর্ব ৬৫-এ। Cloud account-এর দিক থেকে এই আলোচনা পর্ব ৭২-এর (Cloud নিরাপত্তা) সাথে যুক্ত।
আরও গভীরে: Kernel ভাগ করার সীমা কমাতে gVisor-এর মতো user-space kernel, Kata Containers-এর মতো হালকা VM আর Pod-এর user namespace আছে। প্রতিটির compatibility আর performance-এর দাম আলাদা। Forensics-এ kubelet-এর Checkpoint API (Kubernetes ১.৩০ থেকে beta) চলমান container-এর অবস্থা সংরক্ষণ করতে পারে। তবে archive-এ প্রসেসের memory থাকে, তাই secret-এর মতোই সুরক্ষিত রাখতে হয়। Build-এর বিশ্বাসের ধাপ মাপতে SLSA দেখুন।
পরীক্ষা ও ইন্টারভিউতে:
- Container আর VM-এর নিরাপত্তা-পার্থক্য? Container host-এর kernel ভাগ করে, VM-এর নিজের kernel থাকে।
- Baseline আর restricted? Baseline জানা escalation বন্ধ করে, restricted non-root,
drop: ALL, seccomp-এর মতো কড়া সেটিং চায়। - RBAC-এ Secret পড়ার অনুমতি না দিলেও ঝুঁকি কেন? Pod বানানোর ক্ষমতা Secret mount করার পথ দেয়।
- Pod সংক্রমিত মনে হলে প্রথমে মুছবেন? না। প্রমাণ নিয়ে label আর NetworkPolicy দিয়ে আলাদা করুন।
- NetworkPolicy দিলেও traffic চলছে কেন? CNI প্রয়োগ না করতে পারে, অথবা অন্য policy-র allow যোগ হয়েছে।
১০ · AI-এর যুগে container নিরাপত্তা
AI manifest লিখতে খুব দ্রুত। সমস্যা হলো, error সারানোর সহজ পথ প্রায়ই নিরাপত্তা কমানোর পথ। Pod চালু হয়েছে দেখে খুশি হলে ক্ষতিটি চোখে পড়ে না।
| AI-এর পরামর্শ | ঝুঁকি | ঠিক পথ |
|---|---|---|
Forbidden সারাতে cluster-admin | পুরো cluster-এর ক্ষমতা | ঠিক যে verb আর resource লাগে, শুধু তা |
Permission error-এ privileged: true, chmod 777 | সীমা তুলে দেওয়া | ফাইলের owner, fsGroup, আলাদা volume |
TLS error-এ --insecure-skip-tls-verify | পরিচয় যাচাই বন্ধ | সঠিক CA দেওয়া, certificate সারানো |
latest image, ENV-এ token | অনির্দিষ্ট কোড, secret ফাঁস | Digest, secret mount |
| বানানো CVE, CIS নম্বর বা field | ভুল সিদ্ধান্ত | মূল নথি আর --dry-run=server |
| Alert দেখে “এটি নিরীহ” | আত্মবিশ্বাসী ভুল triage | Log দেখে মানুষের সিদ্ধান্ত |
AI-কে লেখক নয়, দ্বিতীয় রিভিউকারী হিসেবে ব্যবহার করুন। AI-তে পাঠানোর আগে সব আসল নাম, token আর ঠিকানা সরিয়ে নিন।
AI-এর উত্তর হাতে পেলে
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন, তারপর “→ উত্তর” চাপুন।
প্র ০১Container কি একটি নিরাপত্তা-সীমা? গ্রাহকের অচেনা কোড একই kernel-এ চালানো যায়?
উত্তর নির্ভর করে আপনি কাকে কতটা বিশ্বাস করেন তার ওপর। Unix-এর chroot এসেছিল ১৯৭৯ সালের দিকে, মূলত file system-এর দৃশ্য বদলাতে, নিরাপত্তার পূর্ণ ব্যবস্থা হিসেবে নয়। পরে Linux-এ namespace আর cgroup যুক্ত হয়, আর ২০১৩ সালে Docker এগুলোকে সহজ প্যাকেজে আনে। অর্থাৎ container-এর জনপ্রিয়তার মূল কারণ ছিল বিতরণের সুবিধা। কঠিন নিরাপত্তা-সীমা তার প্রথম লক্ষ্য ছিল না। পরে seccomp, capability আর user namespace-এর মতো স্তর যোগ হয়ে সীমাটি অনেক শক্ত হয়েছে, তবে kernel এখনো সবার সাধারণ জায়গা।
২০১৯ সালে runc-এর গুরুতর ত্রুটি CVE-2019-5736 প্রকাশ পেলে অনেক দল বুঝতে পারে, runtime নিজেও attack surface. সমাধান এসেছিল patch আর বাড়তি স্তর থেকে। একই সময়ে বড় cloud প্রতিষ্ঠানগুলো অচেনা কোড চালাতে শক্ত সীমা খুঁজছিল। ২০১৭–২০১৮ সালের দিকে Kata Containers, gVisor আর Firecracker-এর মতো প্রকল্প সামনে আসে। এদের মূল ভাবনা হলো, অচেনা কোড আর host kernel-এর মাঝে আরও একটি স্তর রাখা।
একই প্রতিষ্ঠানের নিজের বিশ্বস্ত সার্ভিসে shared kernel সাধারণত গ্রহণযোগ্য, যদি restricted সেটিং, নিয়মিত patch আর আলাদা namespace থাকে। কিন্তু একটি অনলাইন code runner, যেখানে অচেনা মানুষ কোড চালান, সেখানে সাধারণ container একা যথেষ্ট ভাবা ঝুঁকিপূর্ণ। সেখানে VM-ভিত্তিক বা sandbox runtime, আলাদা node আর কড়া egress নিয়ম ভাবা উচিত। দাম আছে: বেশি memory, শুরুতে দেরি আর কিছু application-এর সাথে অমিল। তাই সব workload-এ একই ব্যবস্থা না নিয়ে ঝুঁকি অনুযায়ী ভাগ করাই বুদ্ধিমানের কাজ।
মূল উপলব্ধি: “Container নিরাপদ কি না” প্রশ্নটি অসম্পূর্ণ। আসল প্রশ্ন, এই workload-এর বিশ্বাসের স্তর কত, আর সীমা ভাঙলে ক্ষতি কতদূর যাবে।
প্র ০২Scanner শূন্য CVE দেখালে কি image নিরাপদ?
না। Scanner image-এর package তালিকা জানা দুর্বলতার database-এর সাথে মেলায়। তাই শূন্য ফলের অর্থ, “এই scanner আজ, এই database অনুযায়ী, যা চিনতে পারল তাতে জানা সমস্যা পায়নি।” এর ভেতরে অন্তত চারটি অনুমান আছে।
প্রথমত, সময়। ২০২১ সালের ডিসেম্বরে Log4Shell (CVE-2021-44228) প্রকাশ পাওয়ার আগের দিন অনেক image-ই scan-এ পরিষ্কার ছিল। ত্রুটি আগে থেকেই ছিল, শুধু জানা ছিল না। পরদিন image একটি বাইটও না বদলে ঝুঁকিপূর্ণ হয়ে যায়। তখন যাঁদের কাছে SBOM ছিল, তাঁরা দ্রুত খুঁজে পেয়েছিলেন Log4j কোথায়। অন্যরা বহু দিন ধরে খুঁজেছেন।
দ্বিতীয়ত, দৃশ্যমানতা। Application-এর ভেতরে কপি করে রাখা library বা নিজের বানানো binary অনেক সময় package তালিকায় ওঠে না। তৃতীয়ত, ধরন। Scanner আপনার নিজের logic-এর ভুল বা দুর্বল সেটিং ধরতে পারে না। চতুর্থত, উদ্দেশ্য। ২০২৪ সালে xz-এর ঘটনা (CVE-2024-3094) দেখিয়েছে, বিশ্বস্ত প্রকল্পেও ইচ্ছাকৃত backdoor ঢুকতে পারে, আর প্রকাশের আগে কোনো database-এ তা থাকে না।
উল্টো দিকে, অনেক CVE থাকলেই image বিপজ্জনক নয়। যে কোড কখনো চলে না, সেখানকার ত্রুটির ঝুঁকি কম হতে পারে। VEX, reachability আর পর্ব ৩২-এর EPSS ও KEV অগ্রাধিকার ঠিক করতে সাহায্য করে।
রিভিউয়ে তাই “শূন্য CVE” দেখে থামবেন না। জিজ্ঞেস করুন, scan কবে হয়েছে, কোন database-এ, কোন image digest-এর ওপর, আর চলমান Pod কি সেই digest-ই ব্যবহার করছে? পুরোনো ফল দেখিয়ে নতুন image চালানো খুব সাধারণ ফাঁক। Scanner-এর ফলাফলও মানুষের পর্যালোচনা চায়, কারণ false positive আর ভুল version-মিল দুটোই ঘটে।
মূল উপলব্ধি: Scan একটি ছবি, নিরাপত্তার সনদ নয়। ছোট image, SBOM, নিয়মিত পুনরায় scan আর runtime detection মিলিয়েই আত্মবিশ্বাস আসে।
প্র ০৩সন্দেহজনক Pod মুছে নতুন Pod চালানো সহজ। তাহলে প্রমাণের জন্য অপেক্ষা কেন?
Immutable infrastructure-এর ভাবনা বলে, নষ্ট জিনিস মেরামত নয়, বদলে ফেলুন। সেবা ফেরাতে এটি চমৎকার। তাই অনেক দলের প্রথম প্রতিক্রিয়া হয় Pod মুছে দেওয়া। কিন্তু এখানে দুটি প্রশ্ন আলাদা: সেবা ফেরানো, আর কী ঘটেছিল তা জানা।
Pod মুছলে memory, লেখা ফাইল আর চলমান প্রসেস হারাতে পারে। আপনি হয়তো জানবেন না চাবিটি কীভাবে এসেছে। একই image থেকে নতুন Pod উঠলে একই ত্রুটি দিয়ে আবার একই ঘটনা ঘটতে পারে। চুরি হওয়া CI credential-ও Pod মুছলে ফেরত আসে না। ১৫ মিনিট পরে একই alert আবার বাজতে পারে, এবার প্রমাণ ছাড়া। আইনি বা চুক্তির কারণে সংরক্ষণ দরকার হলেও মূল জিনিস ততক্ষণে হারিয়ে যাবে।
অন্যদিকে অপেক্ষারও দাম আছে। Pod যদি সক্রিয়ভাবে ডেটা পাঠায়, প্রতিটি মিনিট ক্ষতি বাড়ায়। RFC 3227-এর order of volatility ভঙ্গুর প্রমাণ আগে নিতে বলে, তবে কোনো নিয়ম ব্যবসার ক্ষতি থামানোর সিদ্ধান্ত বাতিল করে না। তাই ভালো সমাধান “মুছব না রাখব” নয়, বরং তৃতীয় পথ: দ্রুত আলাদা করা। Label সরিয়ে traffic ঘোরানো, network বন্ধ করা আর নতুন Pod তোলা, এতে সেবা আর প্রমাণ দুটোই বাঁচে।
এটি রাত ২টায় প্রথমবার ভাবার বিষয় নয়। পর্ব ৬৬-এর মহড়ায় ঠিক করুন, কোন severity-তে কে মুছতে পারবেন, কে memory নেবেন, কার অনুমতি লাগবে। কোনো Pod যদি ব্যক্তিগত তথ্য ধরে, আইনি পরামর্শ আগে থেকেই নেওয়া ভালো।
মূল উপলব্ধি: Recovery আর investigation আলাদা লক্ষ্য। Quarantine দুটোকে একসাথে রাখার উপায়, আর সিদ্ধান্তটি আগে থেকে লিখে রাখাই আসল প্রস্তুতি।
প্র ০৪RBAC-এ Secret পড়ার অনুমতি নেই, তবু কেন একজন ডেভেলপার Secret-এর কাছে পৌঁছাতে পারেন?
কারণ Kubernetes-এ অনুমতিগুলো আলাদা দ্বীপ নয়, একটি graph-এর মতো। create pods শুধু Pod তৈরির অনুমতি মনে হয়। কিন্তু Pod সেই namespace-এর Secret mount করতে পারে, আর সেখানকার যেকোনো ServiceAccount বেছে নিতে পারে। তাই নথিতেই একে পরোক্ষ access হিসেবে সাবধান করা হয়েছে। অর্থাৎ role-এ লেখা নেই, তবু পথ আছে।
একই ধরনের ভাবনা নিরাপত্তায় আগেও দেখা গেছে। পর্ব ৩১-এর Active Directory-তে একজন সাধারণ user-ও group, delegation আর session-এর শিকলে অনেক বড় ক্ষমতার কাছে পৌঁছাতে পারেন। তাই সেখানে tiering এসেছে। Cloud IAM-এও একই গল্প: একটি role বদলানোর অধিকার অনেক সময় admin হওয়ার সমান। শিক্ষা একটিই, তালিকা নয়, পথ দেখুন।
নিরীক্ষায় এর অর্থ কী? প্রথমত, পরিচয় ধরে উত্তর চান: এই ServiceAccount শেষ পর্যন্ত কী করতে পারে? দ্বিতীয়ত, bind, escalate, impersonate, node-এর proxy আর PersistentVolume তৈরির মতো অনুমতিকে সাধারণ নয়, বিশেষ অনুমতি ধরুন। তৃতীয়ত, শুধু RBAC দিয়ে সব বন্ধ হয় না। Pod Security আর admission ঠিক করে দেয়, কোন Pod বানানো যাবে। অনুমতি আর admission একসাথে পড়লেই আসল সীমা বোঝা যায়।
একটি বাস্তব নকশা হলো, সংবেদনশীল Secret-কে সাধারণ দলের namespace-এ না রাখা। ডেভেলপার যেখানে Pod বানাতে পারেন, সেখানে production-এর ডেটাবেসের চাবি থাকলে RBAC-এর সূক্ষ্ম নিয়ম খুব কম কাজে আসে। Namespace-এর সীমাকেই বিশ্বাসের সীমা ধরে নকশা করুন। নিয়মিত পর্যালোচনায় অব্যবহৃত binding সরান, কারণ পুরোনো অনুমতি চুপচাপ পড়ে থাকে আর কেউ তার খোঁজ রাখে না।
মূল উপলব্ধি: Least privilege মানে কম লাইন লেখা নয়। প্রতিটি পরিচয়ের শেষ reach ছোট রাখা।
প্র ০৫Kubernetes-এর network ডিফল্টে খোলা কেন? প্রথম থেকেই default deny হলে ভালো হতো না?
Kubernetes-এর network মডেলের মূল প্রতিশ্রুতি হলো, প্রতিটি Pod-এর একটি নিজস্ব IP থাকবে, আর Pod-গুলো NAT ছাড়াই একে অপরের সাথে কথা বলতে পারবে। এর লক্ষ্য ছিল application-কে সহজে সরানো। পুরোনো VM-এর application সামান্য বদলে চলবে, port নিয়ে জটিল হিসাব লাগবে না। ডেভেলপারের অভিজ্ঞতার দিক থেকে এটি ছিল বড় জয়। Cluster-এর ভেতরকে একটি বিশ্বস্ত জায়গা ধরে নেওয়ার পুরোনো ভাবনাও এর সাথে মানিয়ে যেত।
নিরাপত্তার নিয়ম এসেছে আলাদা স্তর হিসেবে। NetworkPolicy API ২০১৭ সালের দিকে স্থিতিশীল হয়, আর তার প্রয়োগ রাখা হয় CNI plugin-এর হাতে। ফলে Kubernetes বিভিন্ন network পরিবেশে চলতে পেরেছে, কিন্তু দাম হিসেবে নীরব ব্যর্থতার সম্ভাবনা থেকে গেছে। Plugin প্রয়োগ না করলে policy শুধু একটি কাগজ।
Default deny শুরু থেকে থাকলে কী হতো? প্রতিটি নতুন সার্ভিসে প্রথমে সংযোগ ভাঙত, DNS পর্যন্ত। শেখার সিঁড়ি খাড়া হতো, আর চালু করতে ক্লান্ত দল হয়তো “সব খোলা” নিয়ম বসিয়ে দিত। এটি নিরাপত্তায় একটি পুরোনো সত্য: কঠিন default মানুষকে ফাঁকি শেখায়। আবার নরম default-এর ক্ষতিও আসল। Pod-এর flat network-এ একটি দখল হওয়া সার্ভিস পুরো namespace, এমনকি cloud metadata-র দিকেও হাত বাড়াতে পারে।
বাস্তব পথ হলো ধাপে ধাপে এগোনো। প্রথমে flow log দেখে কে কার সাথে কথা বলে তা জানুন। তারপর সংবেদনশীল namespace-এ default deny দিন, আর template-এ অনুমোদিত নিয়ম যোগ করুন। IP আর label ছাড়িয়ে পরিচয়ের দিকে গেলে আসে mTLS আর service mesh, যা পর্ব ৭৪-এর zero trust ভাবনার কাছাকাছি।
মূল উপলব্ধি: নিরাপত্তার default একটি সামাজিক সিদ্ধান্তও। ভালো নকশা নিরাপদ পথকে সহজ পথ বানায়।
অনুশীলন
সব কাজ এই পাতায় বা খাতায় করা যায়। আসল cluster লাগবে না।
-
Log-এর হিসাব: একটি কাল্পনিক অ্যাপ মিনিটে ৬ MiB log লেখে। Kubelet-এর ডিফল্ট ১০ MiB × ৫টি ফাইল। (ক) Node-এ মোটামুটি কত সময়ের log থাকে? (খ)
kubectl logs-এ সর্বোচ্চ কতটুকু? (গ) Alert দেরিতে ১৫ মিনিট পরে এলে আর log কোথাও পাঠানো না হলে শুরুর কত MiB হারিয়েছে?(ক) মোট ১০ × ৫ = ৫০ MiB. ৫০ ÷ ৬ ≈ ৮.৩৩ মিনিট, অর্থাৎ প্রায় ৮ মিনিট ২০ সেকেন্ড।
(খ) সর্বশেষ ফাইল ১০ MiB পর্যন্ত, তাই ১০ ÷ ৬ ≈ ১.৬৭ মিনিট, প্রায় ১ মিনিট ৪০ সেকেন্ড।
(গ) ১৫ মিনিটে লেখা হয়েছে ৯০ MiB. ধরে রাখা যায় ৫০ MiB. তাই প্রায় ৪০ MiB, অর্থাৎ শুরুর প্রায় ৬ মিনিট ৪০ সেকেন্ডের log নেই। সমাধান হলো node agent দিয়ে কেন্দ্রীয় জায়গায় পাঠানো, শুধু rotation-এর আকার বাড়ানো নয়।
-
Manifest রিভিউ: একটি কাল্পনিক Pod-এ আছে
hostNetwork: true, imagetools:latest,runAsUser: 0,hostPath: /var/run/docker.sock, কোনোresourcesনেই, আরdefaultServiceAccount. মালিক বলছেন, “এটি শুধু ভেতরের রিপোর্ট টুল।” আপনার রিভিউ মন্তব্য কী হবে?আটকানোর কারণ: Docker socket আর
hostNetworkPod-কে node-এর পরিবেশের কাছে নিয়ে যায়, আর root তার সাথে যোগ হয়ে ক্ষতি বাড়ায়। “ভেতরের টুল” কোনো সীমা নয়, কারণ ভেতরের টুল দখল হলেও একই ক্ষমতা পাওয়া যায়।পরিবর্তন: Socket-এর বদলে যা দরকার তার API,
hostNetworkসরানো, non-root, restricted-এর সেটিং, digest-এ বাঁধা image, memory limit, নিজস্ব ServiceAccount, আর API না লাগলেautomountServiceAccountToken: false.ব্যতিক্রম লাগলে: ব্যবসায়িক কারণ, মালিক, মেয়াদ, আলাদা node, আর কঠোর detection লিখিতভাবে। মন্তব্যে “নিষিদ্ধ” না লিখে ঝুঁকি আর বিকল্প দুটোই দিন।
-
ফল অনুমান করুন: Pod
a,b,c. একটিই NetworkPolicy, যা শুধুb-কে বাছে, ধরন শুধু ingress, আরaথেকে port 80 অনুমতি দেয়। CNI প্রয়োগ করে। এই চারটি সংযোগের ফল কী:a → b:80,a → b:443,c → b:80,b → a:80?a → b:80ALLOW:a-র egress isolated নয়,b-র ingress নিয়ম মেলে।a → b:443DENY: port মেলে না।c → b:80DENY: উৎস মেলে না।b → a:80ALLOW: Policy-তে Egress নেই, তাইb-র বের হওয়া বন্ধ নয়, আরaingress-এ isolated নয়। অনেকেই এটি ভুল করেন:b-কে “আটকানো” হয়েছে শুধু ঢোকার দিকে। Simulator-এরPOLICIESআরFLOWSবদলে মিলিয়ে দেখতে পারেন। -
Simulator সারান: ওপরের
rbac_reach.js-এsa:ops/old-cronশুধুopsnamespace-এ Pod দেখবে।BINDINGS-এর কোন লাইন কীভাবে বদলাবেন, আর আউটপুটে ওই পরিচয়ের জন্য কী আসবে? আসল প্রকল্পে এই বদলের আগে কী যাচাই করবেন?লাইনটি হবে
{ who: 'sa:ops/old-cron', role: 'pod-viewer', ns: 'ops' }. আউটপুটে আসবেsa:ops/old-cron: OK, কারণpod-viewer-এ wildcard, Secret, Pod তৈরি বাbindনেই।আসল প্রকল্পে আগে audit log থেকে দেখুন এই পরিচয় সত্যিই কোন verb আর resource ব্যবহার করে, নিজের পরিবেশে
can-iদিয়ে নতুন অনুমতি পরীক্ষা করুন, আর পরের নির্ধারিত cron চলা পর্যন্ত নজর রাখুন। না হলে মাসে একবার চলা কাজ হঠাৎ ভাঙতে পারে।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমএকটি Pod তৈরির অনুরোধের ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- Authentication: অনুরোধকারী কে
- Authorization: RBAC-এ অনুমতি আছে কি না
- Mutating admission: Pod-এ কিছু যোগ বা বদল
- Schema যাচাই: গঠন ঠিক আছে কি না
- Validating admission: নিয়ম মানছে কি না
- etcd-তে জমা হওয়া
একই node-এর container আর virtual machine-এর সীমার মূল পার্থক্য কোনটি?
- Container-এ network থাকে না
- Container host-এর kernel ভাগ করে, VM-এর নিজের kernel থাকে
- Container সবসময় root-এ চলে
- VM-এ কখনো নিরাপত্তা-ত্রুটি হয় না
একটি Dockerfile-এ এক ধাপে token ফাইল কপি করে পরের ধাপে rm দিয়ে মোছা হলো। Token কি চূড়ান্ত image-এ নিরাপদভাবে নেই?
- হ্যাঁ, মুছলেই সব জায়গা থেকে চলে যায়
- হ্যাঁ, যদি image-এর tag পরিবর্তন করা হয়
- না, আগের layer-এ token থেকে যায়, তাই build secret mount ব্যবহার করতে হয়
- না, তবে non-root চালালেই সমস্যা মিটে যায়
একটি ServiceAccount-এর RBAC-এ Secret-এর কোনো verb নেই, কিন্তু namespace-এ create pods আছে। রিভিউকারী কেন আপত্তি করবেন?
- Pod বানালে CPU খরচ বাড়ে
- Create verb আপনাআপনি deny নিয়ম তৈরি করে
- Secret base64 হওয়ায় সব Pod-এর log-এ ছাপা হয়
- Pod ওই namespace-এর Secret mount করে পরোক্ষে সেগুলোর কাছে পৌঁছাতে পারে
একটি namespace-এ শুধু default-deny ingress আর egress আছে, এবং Pod db-র ingress-এ Pod api-কে port 5432-এ অনুমতি দেওয়া হলো। Pod api-র জন্য egress নিয়ম নেই, কিন্তু CNI প্রয়োগ করে। api থেকে db:5432 সংযোগের ফল কী?
- Deny, কারণ api-র egress অনুমতি নেই
- Allow, কারণ db-র ingress মিলেছে
- Allow, কারণ শেষ নিয়ম আগের নিয়মকে বাতিল করে
- API server সংঘাতের ত্রুটি দেখাবে
Kubernetes API server ডিফল্ট সেটিংয়ে Secret etcd-তে কীভাবে রাখে, আর encryption-এর জন্য কী লাগে?
- ডিফল্টে KMS দিয়ে encrypt করা থাকে
- ডিফল্টে সাধারণ রূপে থাকে, encryption-এর জন্য encryption configuration দিতে হয়
- RBAC চালু করলেই etcd-তে encrypt হয়
- Pod-এ readOnlyRootFilesystem দিলে encrypt হয়
Production-এর Pod-এ সন্দেহজনক shell-এর alert এল। Pod-টি Deployment-এর অধীনে। সেবা চালু রেখে প্রমাণ বাঁচানোর সবচেয়ে ভালো প্রথম পরিকল্পনা কোনটি?
- সঙ্গে সঙ্গে Pod মুছে দেওয়া
- Pod-এর ভেতরে shell খুলে নিজে ফাইল মুছে পরিষ্কার করা
- প্রথমে spec, log আর event নিয়ে Pod-কে label আর NetworkPolicy দিয়ে আলাদা করা
- পরের দিন অফিসের সময় পর্যন্ত অপেক্ষা করা
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন, অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- Container host-এর kernel ভাগ করে। তাই node আপডেট, least privilege আর বিশ্বাসভিত্তিক আলাদা করা জরুরি।
- ঝুঁকি ভাবুন build, ship, run ধরে: ছোট base, non-root, digest, layer-এ secret নয়, scan, স্বাক্ষর।
- Runtime-এ privileged, hostPath আর Docker socket নয়; capability বাদ, seccomp আর read-only root.
- Cluster-এ আসল reach দেখুন, etcd encryption আর audit log যাচাই করুন, PSA, admission আর default deny NetworkPolicy বসান।
- Incident-এ আগে প্রমাণ, তারপর আলাদা করা, শেষে মোছা। কেন্দ্রীয় log আর অনুমোদিত runbook আগেই লাগবে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৭৪ · Zero trust architecture — NIST SP 800-207 থেকে বাস্তবায়নপরবর্তী পর্বপরিচয়ভিত্তিক প্রবেশ আর ধাপে ধাপে রূপান্তর।
- সাইবার নিরাপত্তা · কোর্স হোম৮০টি পর্বপুরো কোর্সের রোডম্যাপ।
- পর্ব ৪৮ · Supply chain নিরাপত্তাপুনরালোচনাDependency, SBOM, signing আর SLSA.
- পর্ব ৬৩ · Digital forensicsপুনরালোচনাChain of custody আর প্রমাণ সংরক্ষণ।
- Cloud ও DevOps · Kubernetes নিরাপত্তাপাশের ট্র্যাকRBAC, NetworkPolicy আর Pod Security-র আসল run.
- Cloud ও DevOps · Container registryপাশের ট্র্যাকDigest, scanning, SBOM আর signing.
- Operating Systems · Containerগভীরে যানNamespace, cgroup আর overlayfs-এর ভেতরের কাজ।
- Kubernetes: Pod Security Standardsমূল উৎসতিন স্তরের নির্দিষ্ট নিয়ম।
- OWASP Kubernetes Top 10মূল উৎসঝুঁকির তালিকা আর প্রতিরোধ।
- Kubernetes ব্লগ: NSA/CISA Hardening Guidanceসারসংক্ষেপনির্দেশিকার সারসংক্ষেপ আর মূল নথির পথ।