SSD Nodes Learn 🎉 VPS $5.50/মাস থেকে
নির্দেশিকা Matt Connorদ্বারা Matt Connor · আপডেট করা হয়েছে 2026-08-21

VPS-এ k3s ক্লাস্টার সুরক্ষিত করার উপায়

একটি নতুন k3s ইনস্টলেশনে পোর্ট 6443, 10250, অনিরাপদ kubeconfig এবং উন্মুক্ত NodePort থাকে যা আপনার VPS-কে ঝুঁকিতে ফেলে। এই নির্দেশিকায় ধাপে ধাপে ক্লাস্টার হার্ডেনিংয়ের নিয়মগুলো দেখুন।

একটি সিঙ্গেল-নোড k3s ক্লাস্টার প্রথম দিনে যা উন্মুক্ত রাখে

একটি পাবলিক VPS-এ সিঙ্গেল-নোড k3s ক্লাস্টার ইনস্টল করার পরদিন থেকে পাঁচটি নির্দিষ্ট জায়গায় এটি উন্মুক্ত থাকে: TCP 6443 পোর্টে Kubernetes API সার্ভার, TCP 10250 পোর্টে kubelet, ডিস্কে থাকা kubeconfig ফাইল, আপনার ফায়ারওয়ালের অগোচরে থাকা NodePort রেঞ্জ এবং যেকোনো পড যা privileged বা hostPath এর জন্য অনুরোধ করতে পারে। প্রতিটি সমস্যার সমাধান করতে মাত্র কয়েক মিনিট সময় লাগে। এই নির্দেশিকাটি ধরে নেয় যে k3s ইতিমধ্যে চলছে; তাই যদি তা না হয়, তবে প্রথমে একটি VPS-এ সিঙ্গেল-নোড k3s ইনস্টলেশন সম্পন্ন করে এখানে ফিরে আসুন।

কোনো কিছু পরিবর্তন করার আগে দেখুন কোন পোর্টগুলো লিসেনিং অবস্থায় আছে।

sudo ss -tulpn | grep -E '6443|10250|10256|8472'

একটি ডিফল্ট ইনস্টলেশনে 6443 (API সার্ভার), 10250 (kubelet), 10256 (kube-proxy হেলথ চেক) এবং 8472/udp (flannel ওভারলে, যা VXLAN বা virtual extensible LAN ব্যবহার করে) পোর্টগুলো দেখা যায়। k3s ডিফল্টভাবে 0.0.0.0-তে বাইন্ড হয়, তাই এই পোর্টগুলোর প্রতিটি শুধুমাত্র লুপব্যাক (loopback) নয়, বরং আপনার পাবলিক আইপি ঠিকানাতেও সচল থাকে।

কেন পোর্ট 6443 পুরো ক্লাস্টারের প্রবেশদ্বার

যে কেউ অ্যাডমিন অধিকার নিয়ে পোর্ট 6443-এ প্রমাণীকরণ করতে পারলে সে পড (pod) তৈরি করতে পারে, আর একটি পড হোস্ট মেশিনে রুট (root) ব্যবহারকারীর ক্ষমতা পেতে পারে। পোর্ট 6443 হলো পুরো মেশিনের প্রবেশদ্বার।

একটি খোলা 6443 পোর্ট মানেই তাৎক্ষণিক কোনো আপস নয়, কারণ Kubernetes পাসওয়ার্ড গ্রহণ করে না। এটি ক্লায়েন্ট সার্টিফিকেট বা বিয়ারার টোকেন (bearer token) দাবি করে। তবে দুটি বিষয় সবসময় সত্য।

প্রথমত, API সার্ভার কিছু অনুরোধের ক্ষেত্রে কোনো প্রমাণীকরণ ছাড়াই উত্তর দেয়। ডিফল্ট Kubernetes RBAC (role-based access control) গ্রুপ system:unauthenticated-কে system:public-info-viewer নামক একটি রোলের সাথে যুক্ত করে, যা /version, /healthz, /livez এবং /readyz-এর অনুমতি দেয়। অন্য একটি মেশিন থেকে:

curl -sk https://YOUR_SERVER_IP:6443/version

এটি আপনার Kubernetes-এর সঠিক সংস্করণটি জানিয়ে দেয়, যা CVE (common vulnerabilities and exposures) অনুসন্ধানের ইনপুট হিসেবে কাজ করে এবং এর মাধ্যমেই একটি স্ক্যানার আপনার সার্ভারটিকে আক্রমণযোগ্য হিসেবে চিহ্নিত করে। এই পাথগুলোর বাইরের যেকোনো অনুরোধ প্রত্যাখ্যান করা হয় এবং সেই প্রত্যাখ্যানের বার্তায় আপনার পরিচয় প্রকাশ পায়:

forbidden: User "system:anonymous" cannot get path "/api"

দ্বিতীয়ত, API সার্ভারের প্রতিটি বাগ এই পোর্ট খোলা থাকা অবস্থায় দূর থেকে ব্যবহার করা সম্ভব। তাই এই অবস্থায় প্যাচিং ঐচ্ছিক বিষয় নয়, বরং বাধ্যতামূলক।

এর সহজ সমাধান হলো একটি ফায়ারওয়াল রুল। এখানে ufw-এর জন্য কেবল একটি লাইন যথেষ্ট নয়, কারণ k3s একই কার্নেলের মাধ্যমে ক্লাস্টার ট্রাফিক রাউট করে।

sudo ufw default deny incoming
sudo ufw allow OpenSSH
sudo ufw allow from 203.0.113.10 to any port 6443 proto tcp
sudo ufw allow from 10.42.0.0/16 to any
sudo ufw allow from 10.43.0.0/16 to any
sudo ufw enable

শেষ দুটি রুল সরাসরি k3s ডকুমেন্টেশন থেকে নেওয়া হয়েছে। 10.42.0.0/16 হলো ডিফল্ট পড নেটওয়ার্ক এবং 10.43.0.0/16 হলো ডিফল্ট সার্ভিস নেটওয়ার্ক। এগুলো ছাড়া ufw ক্লাস্টারের অভ্যন্তরীণ ট্রাফিক ড্রপ করে দেয়, ফলে পডগুলো API সার্ভার এবং একে অপরের সাথে যোগাযোগ বিচ্ছিন্ন হয়ে পড়ে। k3s-এর উদাহরণে সব জায়গা থেকে 6443 পোর্ট ব্যবহারের অনুমতি দেওয়া হয়েছে; সেটিকে আপনার নিজস্ব আইপি অ্যাড্রেস দিয়ে প্রতিস্থাপন করাই বুদ্ধিমানের কাজ। আপনি যদি ufw-এর সাথে পরিচিত না হন, তবে VPS-এর জন্য ufw ফায়ারওয়ালের মৌলিক বিষয়গুলো পড়ে নিতে পারেন, যা এই রুলগুলোর ওপর ভিত্তি করে তৈরি ডিফল্ট পলিসিগুলো ব্যাখ্যা করে।

k3s-এর ডকুমেন্টেশনে ওভারলে পোর্ট সম্পর্কে স্পষ্টভাবে বলা হয়েছে: "নোডগুলোর VXLAN পোর্ট ইন্টারনেটে উন্মুক্ত রাখা উচিত নয়, কারণ এটি আপনার ক্লাস্টার নেটওয়ার্ককে সবার জন্য উন্মুক্ত করে দেয়।" ইনকামিং ট্রাফিকের ওপর একটি ডিফল্ট ডিনাই (deny) পলিসি পোর্টটির নাম উল্লেখ না করেই এই সমস্যার সমাধান করে।

আরও শক্তিশালী সমাধান হলো পাবলিক অ্যাড্রেসের মাধ্যমে API-তে প্রবেশ বন্ধ করে VPN বা মেশ (mesh) অ্যাড্রেস ব্যবহার করা। সার্ভার সার্টিফিকেটে অবশ্যই সেই অ্যাড্রেসটি থাকতে হবে যা দিয়ে আপনি সংযোগ স্থাপন করেন, তাই এটিকে /etc/rancher/k3s/config.yaml-এ SAN (subject alternative name) হিসেবে যোগ করুন:

tls-san:
  - 10.8.0.1
  - k3s.example.com
secrets-encryption: true
sudo systemctl restart k3s
sudo k3s secrets-encrypt status

secrets-encryption: true ডেটাস্টোরে থাকা Secret অবজেক্টগুলোকে এনক্রিপ্ট করে। k3s-এর ডকুমেন্টেশনে উল্লেখ আছে যে, "সার্ভার রিস্টার্ট না করে বিদ্যমান সার্ভারে Secrets-encryption চালু করা সম্ভব নয়", এবং এই পরিবর্তনের আগে লেখা Secret-গুলো sudo k3s secrets-encrypt reencrypt না চালানো পর্যন্ত পুরনো ফরম্যাটেই থেকে যায়। এটি কী সুবিধা দেয় তা পরিষ্কার থাকা প্রয়োজন। এটি ব্যাকআপ থেকে কপি করা ডেটাস্টোর ফাইলকে সুরক্ষা দেয়। কিন্তু যে ব্যক্তি API সার্ভারের সাথে যোগাযোগ করতে পারে, তার ক্ষেত্রে এটি কোনো সুরক্ষা দেয় না, কারণ API সার্ভার তাদের জন্যই Secret ডিক্রিপ্ট করে যাদের তা পড়ার অনুমতি আছে। একই পার্থক্য যেকোনো সেলফ-হোস্টেড সিক্রেট স্টোরের ক্ষেত্রে প্রযোজ্য, আর এ কারণেই Vaultwarden-এর নিরাপত্তা জোরদার করার প্রক্রিয়ায় এনক্রিপশনের চেয়ে অ্যাডমিন টোকেন এবং ব্যাকআপ ফাইলের সুরক্ষার ওপর বেশি গুরুত্ব দেওয়া হয়েছে।

কেন port 10250-এ kubelet গুরুত্বপূর্ণ

kubelet হলো সেই এজেন্ট যা কন্টেইনার চালু করে। port 10250-এ এর API পডগুলোর তালিকা দেখায় এবং সেগুলোর ভেতরে কমান্ড চালায়। যে kubelet বেনামী (anonymous) অনুরোধ গ্রহণ করে, তা মেশিনের প্রতিটি ওয়ার্কলোডের জন্য একটি রিমোট শেল হিসেবে কাজ করে।

আপনারটি পরীক্ষা করুন:

curl -sk https://127.0.0.1:10250/pods | head -c 60

একটি বর্তমান k3s Unauthorized উত্তর দেয়, কারণ kubelet প্রতিটি কলারকে (caller) প্রমাণীকরণ (authenticate) এবং অনুমোদন (authorise) করার জন্য API সার্ভারকে অনুরোধ করে। যদি এর পরিবর্তে JSON পড তালিকা আসে, তবে বেনামী অ্যাক্সেস চালু আছে এবং যে কেউ port 10250-এ পৌঁছাতে পারলে আপনার কন্টেইনার থেকে তথ্য পড়তে এবং কমান্ড চালাতে পারবে।

যেকোনো উপায়েই এটিকে বাইরের দিক থেকে বন্ধ করুন। একটি সিঙ্গেল নোডে kubelet-এর একমাত্র ক্লায়েন্ট হলো একই মেশিনের কন্ট্রোল প্লেন এবং সেই ট্রাফিক loopback ইন্টারফেসের মাধ্যমে প্রবেশ করে, যা ufw ডিফল্টভাবে গ্রহণ করে। ইন্টারনেট থেকে 10250 ডিনাই (deny) করলে আপনার কোনো ক্ষতি হবে না। আপনি যদি কোনো অকার্যকর kubectl top বা metrics-server যা ঠিকমতো কাজ করছে না, তার কারণে এখানে এসে থাকেন, তবে এর কারণগুলো kubelet port 10250 errors-এ সংগ্রহ করা হয়েছে।

আপনার kubeconfig হলো একটি ক্লাস্টার অ্যাডমিন ক্রেডেনশিয়াল

k3s ফাইলটি /etc/rancher/k3s/k3s.yaml-এর মালিকানায় এবং 600 মোডে লেখে। এটি পরিবর্তন করার পরিণাম সম্পর্কে ডকুমেন্টেশনে বলা হয়েছে: "kubeconfig ফাইলটির মালিক root এবং এটি ডিফল্টভাবে 600 মোডে লেখা হয়। মোড 644-এ পরিবর্তন করলে হোস্টের অন্যান্য সাধারণ ব্যবহারকারীরা এটি পড়তে পারবে।"

এটিকে এভাবে বুঝুন: 644 মোড মানে হলো প্রতিটি লোকাল অ্যাকাউন্টই এখন একজন ক্লাস্টার অ্যাডমিনিস্ট্রেটর। অনেক ওয়াকথ্রুতেই ঠিক এই পরামর্শটি দেওয়া হয়, সাধারণত --write-kubeconfig-mode 644 হিসেবে, যাতে sudo ছাড়াই kubectl কাজ করে। এটি মূলত শেল অ্যাক্সেস থাকা যেকোনো ব্যক্তিকে অ্যাডমিন ক্রেডেনশিয়াল দিয়ে দেয়।

এর পরিবর্তে ফাইলটি শুধুমাত্র একটি নির্দিষ্ট ব্যবহারকারীর কাছে কপি করুন।

mkdir -p ~/.kube
sudo install -o "$USER" -g "$USER" -m 600 /etc/rancher/k3s/k3s.yaml ~/.kube/config
kubectl get nodes

এরপর নিশ্চিত করুন যে মূল ফাইলটি এখনও সুরক্ষিত আছে:

stat -c '%a %U:%G' /etc/rancher/k3s/k3s.yaml

600 root:root হলো আপনার কাঙ্ক্ষিত উত্তর। এই ফাইলটিতে system:masters-এর একজন সদস্যের জন্য একটি ক্লায়েন্ট সার্টিফিকেট থাকে; API সার্ভার এই গ্রুপটিকে শর্তহীনভাবে অনুমতি দেয়, তাই এর জন্য কখনোই RBAC রুল যাচাই করা হয় না। Kubernetes-এ কোনো সার্টিফিকেট রিভোকেশন লিস্ট নেই, যার মানে হলো একটি ফাঁস হওয়া কপি ততক্ষণ পর্যন্ত বৈধ থাকবে যতক্ষণ না আপনি ক্লাস্টার সার্টিফিকেট অথরিটি রোটেশন করছেন। এটিকে একটি SSH প্রাইভেট কি-এর মতো বিবেচনা করুন এবং এটি অ্যাক্সেস করতে পারে এমন অ্যাকাউন্টের সংখ্যা সীমিত রাখুন। এটি VPS-এ ন্যূনতম সুবিধাপ্রাপ্ত ব্যবহারকারী অ্যাকাউন্ট ব্যবহারের যুক্তির মতোই।

আপনার ফায়ারওয়াল NodePort ট্র্যাফিক দেখতে পায় না

একটি type: NodePort সার্ভিস নোডের প্রতিটি ঠিকানায় (পাবলিক ঠিকানাসহ) 30000 থেকে 32767-এর মধ্যে একটি পোর্ট খোলে। একটি type: LoadBalancer সার্ভিস k3s-এ আরও এক ধাপ এগিয়ে যায়: ServiceLB, যা একটি বান্ডেল করা লোড ব্যালেন্সার, প্রতিটি সার্ভিসের জন্য kube-system-এ একটি ছোট পড শিডিউল করে যা সরাসরি হোস্টের সার্ভিস পোর্টটি দখল করে নেয়।

kubectl -n kube-system get pods | grep svclb
sudo ss -tulpn | grep -E ':3[0-2][0-9]{3}'

এখন সেই অংশটি যা মানুষকে অবাক করে। ufw দিয়ে সেই পোর্টটি ব্লক করলেও এটি উত্তর দেওয়া চালিয়ে যায়।

sudo ufw deny 30080/tcp
curl http://YOUR_SERVER_IP:30080

পেজটি এখনও লোড হয়, কারণ প্যাকেটটি যে পথ দিয়ে ভ্রমণ করে। kube-proxy nat টেবিলের PREROUTING চেইনে DNAT (destination network address translation) রুল লেখে, এবং কোনো ফিল্টারিং সিদ্ধান্ত নেওয়ার আগেই PREROUTING কার্যকর হয়। গন্তব্যটি তখন একটি পড অ্যাড্রেস হয়ে যায়, যা হোস্ট নয়, তাই কার্নেল প্যাকেটটিকে FORWARD চেইনের দিকে পাঠিয়ে দেয় এবং এটি কখনোই INPUT চেইনের মধ্য দিয়ে যায় না। ufw-এর রুলগুলো INPUT-এ থাকে। প্যাকেটটি কখনোই সেগুলোর মুখোমুখি হয় না। জাম্পের ক্রমটি এখানে দেখা যাচ্ছে:

sudo iptables -S PREROUTING -t nat | head
sudo iptables -S FORWARD | head

KUBE-SERVICES PREROUTING-এর শীর্ষে থাকে, এবং FORWARD-এ থাকা Kubernetes জাম্পগুলো ufw-এর নিজস্ব চেইনের উপরে অবস্থান করে। এটি সেই একই মেকানিজম যা Docker-কে ufw-এর বাইরে পোর্ট পাবলিশ করতে দেয়, এবং এর সমাধানগুলোও একই।

  • আপনার প্রোভাইডারের নেটওয়ার্ক ফায়ারওয়ালে ফিল্টার করুন। এটি মেশিনের সামনে কাজ করে এবং আপনার কার্নেল কীভাবে রাউট করছে তা নিয়ে এটি চিন্তিত নয়।
  • NodePort এবং LoadBalancer এড়িয়ে চলুন। সার্ভিসগুলোকে ClusterIP-এ রাখুন এবং আপনার বিদ্যমান SSH সেশনের মাধ্যমে kubectl port-forward ব্যবহার করে সেগুলোতে পৌঁছান।
  • 80 এবং 443 পোর্টে একটি মাত্র ইনগ্রেস এক্সপোজ করুন, অন্য কিছু নয়।
  • kube-apiserver-arg-এর অধীনে service-node-port-range ব্যবহার করে রেঞ্জটি কমিয়ে আনুন, যাতে ভুলবশত কোনো NodePort এমন কোথাও না পড়ে যা আপনি পর্যবেক্ষণ করছেন না।

ufw এখনও ব্যবহার করা যুক্তিযুক্ত। এটি হোস্টের উদ্দেশ্যে আসা ট্র্যাফিক নিয়ন্ত্রণ করে, যেমন SSH এবং API সার্ভার। এটি কেবল পড ট্র্যাফিক নিয়ন্ত্রণ করে না, এবং এটি করবে বলে আশা করাই হলো সেই কারণ যার ফলে একটি ডাটাবেস সবার কাছে উন্মুক্ত হয়ে পড়ে।

hostPath বা privileged যুক্ত একটি pod আপনার VPS-এ root হিসেবে কাজ করে

কন্টেইনারগুলো আপনার কার্নেলের সাধারণ প্রসেস, যার কার্নেল দেখার ক্ষমতা সীমিত। বেশ কিছু pod ফিল্ড এই সীমাবদ্ধতাকে শিথিল করে দেয়।

  • securityContext.privileged: true কন্টেইনারকে সমস্ত Linux capability এবং হোস্ট ডিভাইসের অ্যাক্সেস দেয়।
  • hostPath হোস্টের একটি ডিরেক্টরিকে pod-এর ভেতর মাউন্ট করে। যে pod /-কে read-write মোডে মাউন্ট করে, সেটি /root/.ssh/authorized_keys-এ একটি কি (key) যুক্ত করতে পারে।
  • hostPID: true কন্টেইনারকে হোস্টের প্রসেস নেমস্পেসে নিয়ে যায়, যেখানে PID 1-এর বিরুদ্ধে nsenter চালালে হোস্টের শেল পাওয়া যায়।
  • hostNetwork: true এটিকে হোস্টের নেটওয়ার্ক স্ট্যাকে যুক্ত করে, যেখানে এটি হোস্টের পোর্ট বাইন্ড করতে পারে এবং loopback-এ থাকা সার্ভিসগুলোতে পৌঁছাতে পারে।

তাই "এখানে কে pod তৈরি করতে পারে" প্রশ্নটির উত্তর হলো "এই VPS-এ কে root"। যেকোনো ServiceAccount যার যেকোনো নেমস্পেসে pod তৈরির জন্য create ক্ষমতা আছে, সে root-এর সমতুল্য, যদি না অন্য কোনো ব্যবস্থা সেই pod-কে শুরুতেই প্রত্যাখ্যান করে।

সেই ব্যবস্থাটি হলো Pod Security admission, যা API server-এর ভেতরেই থাকে। সহজ কথায়, এটি প্রতিটি নেমস্পেসের জন্য একটি লেবেল এবং এর জন্য কোনো রিস্টার্টের প্রয়োজন হয় না।

kubectl label namespace default \
  pod-security.kubernetes.io/enforce=baseline \
  pod-security.kubernetes.io/enforce-version=latest \
  pod-security.kubernetes.io/warn=restricted

baseline উপরের চারটি ফিল্ডকেই প্রত্যাখ্যান করে। restricted আরও কঠোর এবং এটি non-root ইউজার, একটি seccomp (secure computing mode) প্রোফাইল, কোনো privilege escalation না থাকা এবং ALL-এ capability কমিয়ে আনার দাবি করে, যা অনেক প্রকাশিত চার্টকে অকার্যকর করে দেয়। baseline এনফোর্স করার সময় restricted সম্পর্কে শুধু সতর্কবার্তা রাখলে আপনি বুঝতে পারবেন যে কোনো কিছু কার্যকর করার আগে কী কী ভেঙে যেতে পারে।

এটি কাজ করছে কি না তা যাচাই করুন:

kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: pstest
spec:
  containers:
  - name: app
    image: busybox
    command: ["sleep", "60"]
    securityContext:
      privileged: true
EOF

API server এটি প্রত্যাখ্যান করবে এবং কোন ফিল্ডের কারণে তা প্রত্যাখ্যান করেছে তা জানিয়ে দেবে:

Error from server (Forbidden): error when creating "STDIN": pods "pstest" is forbidden: violates PodSecurity "baseline:latest": privileged (container "app" must not set securityContext.privileged=true)

প্রতিটি নেমস্পেসে আলাদা লেবেল না দিয়ে পুরো ক্লাস্টারের জন্য ডিফল্ট সেটিংস করতে চাইলে, k3s /var/lib/rancher/k3s/server/psa.yaml-এ একটি admission কনফিগারেশন ফাইলের কথা উল্লেখ করে:

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
  configuration:
    apiVersion: pod-security.admission.config.k8s.io/v1beta1
    kind: PodSecurityConfiguration
    defaults:
      enforce: "baseline"
      enforce-version: "latest"
      warn: "restricted"
      warn-version: "latest"
    exemptions:
      namespaces: [kube-system]

/etc/rancher/k3s/config.yaml-এ API server-কে এই ফাইলের দিকে নির্দেশ করুন, তারপর k3s রিস্টার্ট করুন:

kube-apiserver-arg:
  - 'admission-control-config-file=/var/lib/rancher/k3s/server/psa.yaml'

kube-system ছাড় (exemption) ঐচ্ছিক নয়। k3s-এর নিজস্ব ServiceLB pod-গুলো হোস্ট পোর্ট দাবি করে, যা baseline নিষিদ্ধ করে। তাই তালিকা থেকে kube-system বাদ দিলে, পরবর্তীবার যখন এই pod-গুলো পুনরায় তৈরি হবে, তখন সেগুলো প্রত্যাখ্যাত হবে। admission পরিবর্তনের পর k3s রিস্টার্ট করার সময় দ্বিতীয় একটি SSH সেশন খোলা রাখুন।

এখানে একটি বাড়তি সুবিধা হলো: k3s-এ একটি network policy controller থাকে এবং এটি ডিফল্টভাবেই চালু থাকে, তাই অতিরিক্ত কিছু ইনস্টল না করেই এই ক্লাস্টারে NetworkPolicy অবজেক্ট কার্যকর হয়। সব Kubernetes ডিস্ট্রিবিউশনে এটি থাকে না, এবং একটি compromised pod যাতে অন্য কোথাও পৌঁছাতে না পারে, তা আটকানোর জন্য এটিই সেরা টুল।

যেসব bundled component আপনি ব্যবহার করেন না সেগুলো সরিয়ে ফেলুন

ইন্সটলারটি বেশ কিছু add-on মোতায়েন করে। প্রতিটি add-on একটি listener যোগ করে এবং প্যাচ করার জন্য বাড়তি একটি বিষয় তৈরি করে। --disable এই মানগুলো গ্রহণ করে: coredns, servicelb, traefik, local-storage, metrics-server, runtimes

coredns বজায় রাখুন। এটি ছাড়া ক্লাস্টারের কোনো কিছুই নাম resolve করতে পারে না। বাকিগুলো আপনার পছন্দের ওপর নির্ভর করে। /etc/rancher/k3s/config.yaml-এ:

disable:
  - traefik
  - servicelb
disable-helm-controller: true
sudo systemctl restart k3s
kubectl get pods -A

k3s আপনার নিষ্ক্রিয় করা componentগুলো মুছে ফেলে, তাই traefik pod এবং svclb- podগুলো নিজে থেকেই অদৃশ্য হয়ে যায়। আগে এর ফলাফলগুলো জেনে নিন। servicelb না থাকলে, প্রতিটি type: LoadBalancer service চিরকাল <pending>-এ আটকে থাকে, কারণ সেগুলোকে ঠিকানা দেওয়ার মতো কেউ থাকে না। traefik না থাকলে কোনো ingress controller থাকে না, তাই Ingress objectগুলো কোনো কাজই করে না। যখন আপনি অন্য কোনো উপায়ে traffic পরিচালনা করেন, যেমন host-এ একটি reverse proxy ব্যবহার করেন, তখন এগুলো নিষ্ক্রিয় করুন; আর যখন এগুলো ব্যবহার করেন তখন এগুলোকে স্পর্শ করবেন না। disable-helm-controller: true সেই controller-টিকে সরিয়ে ফেলে যা HelmChart resource পর্যবেক্ষণ করে। আপনি যদি নিজে helm চালান, তবে এই privileged component-টি আপনার প্রয়োজন নেই।

ডিফল্ট ServiceAccount টোকেন অটোমাউন্ট বন্ধ করা

আপনি অন্য কিছু উল্লেখ না করলে প্রতিটি পড ডিফল্টভাবে /var/run/secrets/kubernetes.io/serviceaccount/token-এ একটি ServiceAccount টোকেন পায়। default ServiceAccount-এর কোনো RBAC পারমিশন থাকে না, তাই শুধুমাত্র এই টোকেন দিয়ে খুব বেশি কিছু করা সম্ভব নয়। তবে একটি compromised কন্টেইনারের ভেতরে থাকা আক্রমণকারীর জন্য এটি একটি বৈধ ক্রেডেনশিয়াল এবং API সার্ভারে প্রবেশের সুযোগ তৈরি করে দেয়, যা ক্লাস্টার এসকেলেশনের অধিকাংশ কৌশলের প্রথম ধাপ।

k3s হার্ডেনিং গাইড অনুযায়ী প্রতিটি নেমস্পেসের জন্য এটি বন্ধ রাখা হয়:

kubectl patch serviceaccount --namespace default default --patch '{"automountServiceAccountToken": false}'
kubectl patch serviceaccount --namespace kube-node-lease default --patch '{"automountServiceAccountToken": false}'
kubectl patch serviceaccount --namespace kube-public default --patch '{"automountServiceAccountToken": false}'

এটি কার্যকর হয়েছে কি না তা যাচাই করুন। পডটি চালু হওয়ার জন্য কয়েক সেকেন্ড সময় দিন।

kubectl run t --image=busybox --restart=Never --command -- sleep 30
kubectl exec t -- ls /var/run/secrets/kubernetes.io/serviceaccount

পাথটি এখন আর নেই, তাই ls কমান্ডটি No such file or directory আউটপুট দেবে। যে ওয়ার্কলোডগুলোর সত্যিকার অর্থেই API অ্যাক্সেস প্রয়োজন, তারা তাদের নিজস্ব পড স্পেসিফিকেশনে automountServiceAccountToken: true সেট করে নিতে পারে, ফলে কোনো কিছুই স্থায়ীভাবে বন্ধ হয়ে যায় না। এটি নিজেই একটি ছোট জয়। বড় সুবিধাটি হলো কোনো ওয়ার্কলোডকে অপ্রয়োজনীয় পারমিশনসহ ServiceAccount না দেওয়া। একটি টোকেনের বর্তমান ভ্যালু কত তা আপনি এভাবে দেখতে পারেন:

kubectl auth can-i --list --as=system:serviceaccount:default:default

রিসোর্স লিমিট, যাতে একটি পড পুরো ক্লাস্টারকে অচল করে দিতে না পারে

একটি সিঙ্গেল নোডে কন্ট্রোল প্লেন এবং আপনার ওয়ার্কলোডগুলো একই কার্নেল ও মেমোরি পুল শেয়ার করে। মেমোরি লিক হওয়া কোনো পড সবসময় একা মারা যায় না। কার্নেলের OOM (আউট অফ মেমোরি) কিলার বড় প্রসেসগুলোর ওপর ভিত্তি করে তার শিকার বেছে নেয়, আর k3s একটি বড় এবং দীর্ঘস্থায়ী প্রসেস। ফলে পডের পরিবর্তে পুরো ক্লাস্টারই বন্ধ হয়ে যেতে পারে। তখন আপনার ওয়ার্কলোডগুলো আর রিস্টার্ট হয় না, কারণ যে সার্ভিসটি ওয়ার্কলোড রিস্টার্ট করার কথা, সেটিই মারা গেছে।

একটি LimitRange সেই পডগুলোর জন্য লিমিট নির্ধারণ করে দেয় যেগুলোতে কোনো লিমিট সেট করা নেই:

apiVersion: v1
kind: LimitRange
metadata:
  name: defaults
  namespace: default
spec:
  limits:
  - type: Container
    default:
      cpu: 500m
      memory: 512Mi
    defaultRequest:
      cpu: 50m
      memory: 128Mi

একটি ResourceQuota পুরো নেমস্পেসের জন্য রিসোর্স ব্যবহারের সীমা নির্ধারণ করে:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: cap
  namespace: default
spec:
  hard:
    limits.cpu: "3"
    limits.memory: 3Gi
    pods: "20"

এরপর /etc/rancher/k3s/config.yaml-এ k3s-এর জন্য জায়গা সংরক্ষিত রাখুন:

kubelet-arg:
  - 'system-reserved=cpu=250m,memory=512Mi'

এই পার্থক্যের প্রভাব দুটি জায়গায় দেখা যায়। কোনো কন্টেইনার তার নিজস্ব লিমিট অতিক্রম করার কারণে কিল হলে, kubectl describe pod-এ Last State-এর অধীনে Reason: OOMKilled রিপোর্ট করে এবং নোডের বাকি অংশ সচল থাকে। কিন্তু পুরো নোডের মেমোরি শেষ হয়ে গেলে dmesg-এ একটি Killed process লাইন দেখা যায় এবং সাধারণত এটি আশেপাশের সার্ভিসগুলোকেও অচল করে দেয়। প্রথম ঘটনাটি প্রমাণ করে যে আপনার লিমিট সঠিকভাবে কাজ করছে। দ্বিতীয় ঘটনাটি হলো সেই পরিস্থিতি, যা প্রতিরোধের জন্যই লিমিট ব্যবহার করা হয়।

CI এবং k3s ক্লাস্টারে নির্ধারিত সময়সূচী অনুযায়ী ম্যানিফেস্ট স্ক্যান করা

স্ক্যানিং দুটি জায়গায় করা প্রয়োজন, কারণ তারা ভিন্ন ভিন্ন সমস্যা শনাক্ত করে। প্রথমে Trivy ইনস্টল করুন। এই প্রজেক্টটি প্রতিটি রিলিজের সাথে একটি Debian প্যাকেজ প্রদান করে। 2026 সালের আগস্ট মাসে 0.74.0 সংস্করণটি বর্তমান ছিল, তাই অটোমেশনে এটি যুক্ত করার আগে নতুন কোনো সংস্করণ এসেছে কি না তা রিলিজ পেজে যাচাই করে নিন।

sudo apt-get install -y wget
wget -q https://github.com/aquasecurity/trivy/releases/download/v0.74.0/trivy_0.74.0_Linux-64bit.deb
sudo dpkg -i trivy_0.74.0_Linux-64bit.deb
trivy --version

প্রথম জায়গাটি হলো আপনার ম্যানিফেস্টসমূহ, ক্লাস্টারে পৌঁছানোর আগেই এগুলো স্ক্যান করতে হবে।

trivy fs --scanners misconfig --severity HIGH,CRITICAL --exit-code 1 ./deploy

--exit-code 1 কোনো ত্রুটি পাওয়া গেলে CI (continuous integration) জবটি ব্যর্থ করে দেয়। প্রতিটি ফলাফলে ব্যর্থ ফিল্ড এবং এর তীব্রতা উল্লেখ থাকে, তাই আপনি ভুলবশত কোনো privileged: true কমিট করলে তা API সার্ভারে পৌঁছানোর আগেই বিল্ডটি ব্যর্থ হয়ে যাবে। কোনো ত্রুটি যদি আপনি গ্রহণ করার সিদ্ধান্ত নেন, তবে তা একটি .trivyignore ফাইলে রাখুন। এতে ওই ম্যানিফেস্টের পাশেই git-এ আপনার সিদ্ধান্তের রেকর্ড থেকে যাবে।

দ্বিতীয় জায়গাটি হলো চলমান ক্লাস্টার, যেখানে নির্ধারিত সময়সূচী অনুযায়ী স্ক্যান করতে হবে।

trivy k8s --compliance=k8s-cis-1.23 --report summary

এই কমান্ডটি সম্পর্কে একটি সততার খাতিরে সতর্কতা: trivy k8s একটি নোড কালেক্টর পড ডেপ্লয় করে, যার নোড-লেভেল সেটিংস পরীক্ষা করার জন্য হোস্ট অ্যাক্সেসের প্রয়োজন হয়। যে ক্লাস্টারে আপনি সবেমাত্র প্রিভিলেজড পড বর্জন করা শুরু করেছেন, সেখানে এটি এড়িয়ে না গিয়ে বিষয়টি গুরুত্বের সাথে বিবেচনা করা উচিত। trivy k8s --report summary --disable-node-collector ব্যবহার করলে কালেক্টরটি বাদ পড়ে যাবে এবং এর সাথে নোড-লেভেল চেকগুলোও আর হবে না।

kube-bench হোস্ট সাইডটি কভার করে: ফাইলের অনুমতি এবং প্রসেস ফ্ল্যাগ, যা CIS (Center for Internet Security) বেঞ্চমার্কের বেশিরভাগ অংশ জুড়ে থাকে। এটি একটি k3s প্রোফাইল প্রদান করে। আপস্ট্রিম Job ম্যানিফেস্টটি নিন এবং সেটিকে আপনার প্রয়োজন অনুযায়ী পরিবর্তন করুন।

curl -sfL -o kube-bench-job.yaml https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yaml

কন্টেইনার কমান্ডটি পরিবর্তন করে ["kube-bench", "--benchmark", "k3s-cis-1.7"] করুন এবং /etc/kubernetes/var/lib/etcd মাউন্টগুলোকে /etc/rancher/var/lib/rancher দিয়ে প্রতিস্থাপন করুন, কারণ k3s এই পথেই তার ফাইলগুলো সংরক্ষণ করে। এরপর:

kubectl apply -f kube-bench-job.yaml
kubectl logs -l app=kube-bench --tail=-1

লক্ষ্য করুন এই Job-টি কী: hostPID: true এবং হোস্ট ডিরেক্টরি মাউন্ট, যা ঠিক সেই পড আকৃতি যা আপনি এক সেকশন আগেই বর্জন করা শুরু করেছিলেন। এটি ছাড়প্রাপ্ত kube-system নেমস্পেসে চালান, আউটপুট পড়ুন এবং তারপর kubectl delete job kube-bench করুন। একটি স্ক্যানারকে প্রিভিলেজড হতে হয় বলেই প্রিভিলেজড ওয়ার্কলোড নিষিদ্ধ করার নীতি থেকে সরে আসা উচিত নয়।

ইমেজ কন্টেন্ট একটি আলাদা বিষয়। trivy image ghcr.io/example/app:1.4 ইমেজের ভেতরের প্যাকেজ ডেটাবেস পড়ে এবং কী কী দুর্বলতা রয়েছে তার তালিকা তৈরি করে, যা ঠিক আপনার সার্ভারে পরিচিত CVE চেক করার মতোই একটি কাজ।

এখন স্ক্যানারের আউটপুট সম্পর্কে কিছু বাস্তব কথা। একটি সিঙ্গেল-নোড হবি ক্লাস্টার CIS কন্ট্রোলের একটি দীর্ঘ তালিকায় ব্যর্থ হবে, এবং এই ব্যর্থতাগুলোর বেশিরভাগই সঠিক হলেও আপনার জন্য অপ্রাসঙ্গিক। এই বেঞ্চমার্কটি একটি মাল্টি-নোড, মাল্টি-টেন্যান্ট ক্লাস্টারের জন্য লেখা: যেখানে etcd আলাদা হোস্টে থাকে, অডিট লগ মেশিন থেকে বাইরে পাঠানো হয়, আলাদা kubelet সার্টিফিকেট অথরিটি থাকে এবং এমন কমপ্লায়েন্স রেজিম থাকে যার জন্য আপনার অ্যাডমিশন প্লাগইন প্রয়োজন। k3s ইচ্ছাকৃতভাবে কন্ট্রোল প্লেনকে একটি কনফিগ ফাইলসহ একক প্রসেস হিসেবে চালায়, তাই যে কন্ট্রোলগুলো kube-scheduler ম্যানিফেস্ট ফাইলের অনুমতি পরীক্ষা করে সেগুলো পাস করতে পারে না, কারণ এমন কোনো ফাইল সেখানে নেই।

ব্যর্থতাগুলো এই ক্রমে পড়ুন এবং যখন মনে হবে আর কোনো লাভ নেই তখন থামুন: /etc/rancher এবং /var/lib/rancher এর অধীনে ফাইলের মোড এবং মালিকানা, বেনামী বা অননুমোদিত অ্যাক্সেস সংক্রান্ত যেকোনো কিছু, 0.0.0.0 এ বাইন্ড করা কোনো কম্পোনেন্ট এবং কোনো কারণ ছাড়াই UID 0 হিসেবে চলা কন্টেইনার। বাকি বিষয়গুলো দ্বিতীয় নোড বা দ্বিতীয় কোনো ব্যবহারকারীর অ্যাক্সেস পাওয়ার আগ পর্যন্ত অপেক্ষা করতে পারে। একশ লাইনের রিপোর্ট যা আপনি উপেক্ষা করেন, তার চেয়ে পাঁচ লাইনের রিপোর্ট অনেক বেশি কার্যকর যার ওপর আপনি ব্যবস্থা নেন।

একটি সিঙ্গেল নোডের জন্য k3s হার্ডেনিং নির্দেশিকা

  1. ufw-এর ডিফল্ট incoming পলিসি deny-তে সেট করুন, SSH-এর অনুমতি দিন, আপনার নিজস্ব IP থেকে 6443 পোর্টের অ্যাক্সেস দিন এবং pod ও service নেটওয়ার্কের অনুমতি দিন।
  2. kubeconfig ফাইলটি আপনার ব্যবহারকারীর কাছে 600 মোডে কপি করুন এবং কখনোই --write-kubeconfig-mode 644 সেট করবেন না।
  3. নিশ্চিত করুন যে 10250 পোর্টে kubelet বেনামী অনুরোধ প্রত্যাখ্যান করছে এবং পোর্টটিকে ইন্টারনেটের নাগালের বাইরে রাখুন।
  4. যে bundled কম্পোনেন্টগুলো আপনি ব্যবহার করেন না সেগুলো নিষ্ক্রিয় করুন, তারপর k3s রিস্টার্ট করুন।
  5. Pod Security admission-এর জন্য আপনার namespace-গুলোকে enforce=baseline এবং warn=restricted দিয়ে লেবেল করুন।
  6. ডিফল্ট ServiceAccount টোকেন অটোমাউন্টিং বন্ধ করুন।
  7. একটি LimitRange এবং ResourceQuota যোগ করুন, এবং k3s-এর জন্য CPU ও মেমরি রিজার্ভ করুন।
  8. CI-তে trivy fs --scanners misconfig যোগ করুন এবং প্রতি মাসে একটি CIS স্ক্যান চালান।

নিচের হোস্ট মেশিনটির জন্য অন্য যেকোনো সার্ভারের মতোই যত্ন প্রয়োজন এবং k3s সেখানে বাড়তি কিছু জটিলতা যোগ করে। এটি apt-এর পরিবর্তে একটি স্ক্রিপ্ট দ্বারা ইনস্টল করা হয়েছে, তাই apt upgrade এটিকে কখনোই আপডেট করবে না। Ubuntu-তে unattended upgrades ব্যবহার করে অপারেটিং সিস্টেমকে নিজস্ব শিডিউলে প্যাচযুক্ত রাখুন এবং কাঙ্ক্ষিত চ্যানেল বা ভার্সন ব্যবহার করে ইনস্টলারটি পুনরায় চালানোর মাধ্যমে k3s-কে সতর্কতার সাথে আপগ্রেড করুন। একটি মেশিনে দুটি ভিন্ন আপডেট পাথ মনে রাখা কঠিন হতে পারে, তাই কোন কম্পোনেন্টটি কীভাবে আপডেট হবে তা লিখে রাখুন।

FAQ

ইন্টারনেটে 6443 পোর্টে k3s API server উন্মুক্ত রাখা কি নিরাপদ?

এটি নিজে থেকেই কোনো উন্মুক্ত দরজা নয়, কারণ API server-এর জন্য একটি client certificate বা token প্রয়োজন হয় এবং অন্য যেকোনো অনুরোধ forbidden: User "system:anonymous" দিয়ে প্রত্যাখ্যান করা হয়। তবে দুটি ঝুঁকি থেকে যায়। বেনামী ব্যবহারকারীরা এখনও /version পড়তে পারে, যা একজন স্ক্যানারকে ঠিক কোন Kubernetes রিলিজটি খুঁজতে হবে তা জানিয়ে দেয়। এছাড়া, পোর্টটি খোলা থাকলে ভবিষ্যতে API server-এর যেকোনো দুর্বলতা দূর থেকেই আক্রমণযোগ্য হয়ে ওঠে। একটি সিঙ্গেল-নোড ক্লাস্টারে আপনার নিজের kubectl ছাড়া বাইরের কারো 6443 পোর্টের প্রয়োজন নেই, তাই sudo ufw allow from YOUR_IP to any port 6443 proto tcp ব্যবহার করে শুধুমাত্র আপনার অ্যাড্রেস থেকে এটি ব্যবহারের অনুমতি দিন এবং বাকি সবকিছুর জন্য ডিফল্ট deny পলিসি কার্যকর রাখুন।

আমার ufw রুল কেন NodePort সার্ভিসকে ব্লক করছে না?

কারণ প্যাকেটটি কখনোই সেই চেইনে পৌঁছায় না যেখানে আপনার রুলটি রয়েছে। kube-proxy nat টেবিলের PREROUTING চেইনে DNAT রুল বসায়, যা সবার আগে চলে এবং গন্তব্য পরিবর্তন করে একটি pod অ্যাড্রেসে পাঠিয়ে দেয়। প্যাকেটটি তখন লোকালি ডেলিভার হওয়ার পরিবর্তে ফরওয়ার্ড করা হয়, তাই এটি FORWARD চেইন দিয়ে যায় এবং INPUT চেইনকে এড়িয়ে যায়, যেখানে ufw-এর রুলগুলো থাকে। sudo iptables -S PREROUTING -t nat | head দিয়ে এটি নিশ্চিত করুন। আপনার প্রোভাইডারের নেটওয়ার্ক ফায়ারওয়ালে NodePort ফিল্টার করুন, অথবা type: NodePort এড়িয়ে চলুন এবং পরিবর্তে kubectl port-forward দিয়ে সার্ভিসগুলোতে পৌঁছান।

আমার কি --write-kubeconfig-mode 644 দিয়ে k3s চালানো উচিত?

না। k3s ডকুমেন্টেশনে স্পষ্টভাবে বলা আছে এটি কী করে: "মোড 644-এ পরিবর্তন করলে হোস্টের অন্যান্য সাধারণ ব্যবহারকারীরাও এটি পড়তে পারবে।" এই ফাইলে system:masters-এর জন্য একটি client certificate থাকে, তাই 644 মোড ব্যবহার করলে স্থানীয় প্রতিটি অ্যাকাউন্ট ক্লাস্টার অ্যাডমিনিস্ট্রেটর হয়ে যাবে। পরিবর্তে sudo install -o "$USER" -g "$USER" -m 600 /etc/rancher/k3s/k3s.yaml ~/.kube/config ব্যবহার করে এটি একটি নির্দিষ্ট ইউজারের কাছে কপি করুন এবং মূল ফাইলটি 600 root:root-এ রেখে দিন।

k3s-এর জন্য আমার কোন kube-bench বেঞ্চমার্ক ব্যবহার করা উচিত?

k3s-cis-1.7 ব্যবহার করুন। kube-bench ডকুমেন্টেশনে বলা হয়েছে: "kube-bench-এ Rancher K3S প্ল্যাটফর্মের জন্য বেঞ্চমার্ক অন্তর্ভুক্ত আছে। এটি চালানোর জন্য আপনাকে kube-bench কমান্ড দেওয়ার সময় --benchmark k3s-cis-1.7 উল্লেখ করতে হবে।" এটি স্পষ্টভাবে উল্লেখ করুন, কারণ অটো-ডিটেকশন kubeadm লেআউট ধরে নেয়, যেখানে k3s তার ফাইলগুলো /etc/rancher এবং /var/lib/rancher-এর অধীনে রাখে। এমন কিছু ব্যর্থতা আশা করতে পারেন যা একটি সিঙ্গেল নোডের ক্ষেত্রে প্রযোজ্য নয়, এবং ফাইল পারমিশন ও বেনামী অ্যাক্সেসের বিষয়গুলোতে আগে ব্যবস্থা নিন।

Pod Security admission কি k3s-এর বান্ডেল করা কম্পোনেন্টগুলোকে অকেজো করে দেবে?

যদি আপনি এটি kube-system-এ এনফোর্স করেন তবে তা হবে। k3s যে ServiceLB পডগুলো type: LoadBalancer সার্ভিসের জন্য তৈরি করে, সেগুলো হোস্টে পোর্ট দাবি করে, এবং baseline হোস্ট পোর্ট নিষিদ্ধ করে, তাই পরবর্তীবার পডগুলো পুনরায় তৈরি হওয়ার সময় সেগুলো প্রত্যাখ্যাত হয়। অ্যাডমিশন কনফিগারেশন ফাইলে kube-system-কে ছাড় দিন, অথবা শুধুমাত্র আপনার নিজের তৈরি করা নেমস্পেসগুলোতে লেবেল হিসেবে Pod Security প্রয়োগ করুন। enforce=baseline এবং warn=restricted দিয়ে শুরু করুন যাতে এটি চালু করার আগে আপনি পড়তে পারেন যে restricted কী কী অকেজো করতে পারে।