SSD Nodes Learn 🎉 VPS $5.50/माह से
गाइड Matt Connorलेखक: Matt Connor · अपडेट किया गया: 2026-08-21

VPS पर single-node k3s cluster को सुरक्षित कैसे करें

VPS पर k3s इंस्टॉल करने के बाद पोर्ट 6443, 10250 और kubeconfig असुरक्षित रहते हैं। इस गाइड में जानें कि कैसे फायरवॉल और कॉन्फ़िगरेशन से अपने क्लस्टर को सुरक्षित बनाएं।

एक single-node k3s cluster पहले दिन क्या expose करता है

Public VPS पर एक single-node k3s cluster, one-line installer पूरा होने के अगले दिन पांच विशिष्ट स्थानों पर exposed रहता है: TCP 6443 पर Kubernetes API server, TCP 10250 पर kubelet, डिस्क पर मौजूद kubeconfig फ़ाइल, NodePort रेंज जिसे आपका firewall नहीं देख सकता, और कोई भी pod जिसे privileged या hostPath मांगने की अनुमति है। प्रत्येक का समाधान कुछ ही मिनटों में हो जाता है। यह गाइड मानती है कि k3s पहले से चल रहा है, इसलिए यदि ऐसा नहीं है, तो VPS पर single-node k3s install से शुरुआत करें और उसके बाद वापस आएं।

कुछ भी बदलने से पहले देखें कि क्या listening मोड में है।

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

एक डिफ़ॉल्ट install में 6443 (API server), 10250 (kubelet), 10256 (kube-proxy health check) और 8472/udp (flannel overlay, जो VXLAN, virtual extensible LAN का उपयोग करता है) दिखाई देते हैं। k3s डिफ़ॉल्ट रूप से 0.0.0.0 पर bind होता है, इसलिए इनमें से प्रत्येक आपके public address पर स्थित होता है, न कि केवल loopback पर।

पोर्ट 6443 पूरा क्लस्टर क्यों है

जो भी एडमिन अधिकारों के साथ पोर्ट 6443 पर ऑथेंटिकेट कर सकता है, वह पॉड बना सकता है, और एक पॉड होस्ट पर रूट हो सकता है। पोर्ट 6443 मशीन का दरवाजा है।

एक खुला 6443 तुरंत समझौता (compromise) नहीं है, क्योंकि Kubernetes पासवर्ड स्वीकार नहीं करता है। इसे क्लाइंट सर्टिफिकेट या बेयरर टोकन चाहिए। फिर भी दो बातें सच हैं।

पहली, API सर्वर कुछ अनुरोधों का उत्तर बिना किसी क्रेडेंशियल के देता है। डिफ़ॉल्ट Kubernetes RBAC (रोल-आधारित एक्सेस कंट्रोल) ग्रुप system:unauthenticated को system:public-info-viewer नामक रोल से जोड़ता है, जो /version, /healthz, /livez और /readyz की अनुमति देता है। किसी दूसरी मशीन से:

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

यह आपकी सटीक Kubernetes वर्जन जानकारी देता है, जो CVE (कॉमन वल्नेरेबिलिटीज़ एंड एक्सपोज़र) सर्च के लिए इनपुट है और यही कारण है कि एक स्कैनर तय करता है कि आपका बॉक्स दिलचस्प है। उन पाथ्स के आगे कुछ भी रिफ्यूज कर दिया जाता है, और रिफ्यूजल में आपका नाम होता है:

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 या मेश एड्रेस का उपयोग किया जाए। सर्वर सर्टिफिकेट में उस एड्रेस को लिस्ट करना होगा जिससे आप कनेक्ट करते हैं, इसलिए इसे /etc/rancher/k3s/config.yaml में SAN (सब्जेक्ट अल्टरनेटिव नेम) के रूप में जोड़ें:

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 को सर्वर को रीस्टार्ट किए बिना मौजूदा सर्वर पर इनेबल नहीं किया जा सकता है", और बदलाव से पहले लिखे गए सीक्रेट्स तब तक अपने पुराने रूप में रहते हैं जब तक आप sudo k3s secrets-encrypt reencrypt नहीं चलाते। स्पष्ट रहें कि यह क्या हासिल करता है। यह बैकअप से कॉपी की गई डेटास्टोर फाइल की सुरक्षा करता है। यह किसी ऐसे व्यक्ति के खिलाफ कुछ नहीं करता जो API सर्वर से बात कर सकता है, क्योंकि API सर्वर उन लोगों के लिए सीक्रेट्स को डिक्रिप्ट करता है जिन्हें उन्हें पढ़ने की अनुमति है। यही अंतर किसी भी सेल्फ-होस्टेड सीक्रेट स्टोर में होता है, इसीलिए Vaultwarden पर हार्डनिंग पास एन्क्रिप्शन के बजाय एडमिन टोकन और बैकअप फाइल पर समय लगाता है।

पोर्ट 10250 पर kubelet का महत्व

kubelet वह agent है जो containers को start करता है। port 10250 पर इसका API pods की सूची दिखाता है और उनके भीतर commands चलाता है। यदि कोई kubelet anonymous requests स्वीकार करता है, तो वह मशीन पर मौजूद हर workload के लिए एक remote shell की तरह काम करता है।

अपना setup जाँचें:

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

एक current k3s का जवाब Unauthorized होता है, क्योंकि kubelet हर caller को authenticate और authorise करने के लिए API server से पूछता है। यदि इसके बजाय JSON pod list प्राप्त होती है, तो anonymous access चालू है। ऐसी स्थिति में, port 10250 तक पहुँच रखने वाला कोई भी व्यक्ति आपके containers से data पढ़ सकता है और उनमें commands execute कर सकता है।

इसे हर हाल में बाहर से बंद करें। single node पर kubelet का एकमात्र client उसी machine पर मौजूद control plane होता है। यह traffic loopback interface के माध्यम से आता है, जिसे ufw default रूप से स्वीकार करता है। internet से 10250 को deny करने से आपको कोई नुकसान नहीं होगा। यदि आप यहाँ किसी टूटे हुए kubectl top या ऐसे metrics-server के कारण आए हैं जो ठीक से काम नहीं कर रहा है, तो इसके कारण kubelet port 10250 errors में दिए गए हैं।

आपका kubeconfig एक cluster admin credential है

k3s, /etc/rancher/k3s/k3s.yaml को root के स्वामित्व में और 600 mode के साथ लिखता है। documentation इसके बदलने का परिणाम स्पष्ट करती है: "kubeconfig फ़ाइल root के स्वामित्व में होती है और इसे 600 के डिफ़ॉल्ट mode के साथ लिखा जाता है। mode को 644 में बदलने से host पर मौजूद अन्य unprivileged users इसे पढ़ सकेंगे।"

इसे इस तरह समझें: mode 644 हर local account को cluster administrator बना देता है। कई walkthroughs ठीक यही सुझाव देते हैं, आमतौर पर --write-kubeconfig-mode 644 के रूप में, ताकि kubectl बिना sudo के काम कर सके। यह किसी भी shell access वाले व्यक्ति को admin credential देकर काम करता है।

इसके बजाय फ़ाइल को किसी एक user के लिए copy करें।

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 के एक सदस्य के लिए client certificate रखती है, जो वह group है जिसे API server बिना शर्त अनुमति देता है, इसलिए इसके लिए RBAC नियमों की जाँच कभी नहीं की जाती है। Kubernetes में कोई certificate revocation list नहीं होती है, जिसका अर्थ है कि एक बार leak हुई copy तब तक वैध रहती है जब तक आप cluster certificate authority को rotate नहीं करते। इसे SSH private key की तरह मानें और उन accounts की संख्या कम रखें जो इसे access कर सकते हैं, जो कि VPS पर least privilege user accounts के समान तर्क है।

आपका firewall NodePort traffic को नहीं देख पाता है

एक type: NodePort service हर node के हर address पर 30000 से 32767 के बीच एक port खोलती है, जिसमें public address भी शामिल है। k3s पर एक type: LoadBalancer service इससे आगे जाती है: ServiceLB, जो कि एक bundled load balancer है, हर service के लिए kube-system में एक छोटा pod schedule करता है जो host पर सीधे service port को claim कर लेता है।

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

अब वह हिस्सा जो लोगों को हैरान करता है। उस port को ufw के साथ block करें, फिर भी वह जवाब देना जारी रखता है।

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

page अभी भी load होता है, क्योंकि packet का रास्ता अलग है। kube-proxy, nat table की PREROUTING chain में DNAT (destination network address translation) rules लिखता है, और PREROUTING किसी भी filtering निर्णय से पहले चलता है। destination एक pod address बन जाता है, जो host नहीं है, इसलिए kernel packet को FORWARD chain में भेज देता है और वह कभी INPUT से नहीं गुजरता। ufw के rules INPUT में होते हैं। packet कभी उनसे नहीं मिलता। jump का क्रम यहाँ देखा जा सकता है:

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

KUBE-SERVICES, PREROUTING में सबसे ऊपर स्थित होता है, और FORWARD में Kubernetes के jumps, ufw की अपनी chains के ऊपर होते हैं। यही वह mechanism है जो Docker को ufw के बावजूद ports publish करने देता है, और इसके समाधान भी वही हैं।

  • अपने provider के network firewall पर filter करें। यह machine के सामने चलता है और इसे इस बात से फर्क नहीं पड़ता कि आपका kernel traffic को कैसे route करता है।
  • NodePort और LoadBalancer का उपयोग न करें। services को ClusterIP पर रहने दें और उन तक अपने मौजूदा SSH session के जरिए kubectl port-forward से पहुँचें।
  • केवल 80 और 443 पर एक ingress expose करें, और कुछ भी नहीं।
  • kube-apiserver-arg के अंतर्गत service-node-port-range के साथ range को सीमित करें, ताकि कोई भी अनपेक्षित NodePort ऐसी जगह आए जिसे आप monitor कर रहे हों।

ufw का उपयोग करना अभी भी फायदेमंद है। यह host के लिए आने वाले traffic को नियंत्रित करता है, जैसे कि SSH और API server। यह pod traffic की निगरानी नहीं करता है, और यह उम्मीद करना कि यह ऐसा करेगा, यही कारण है कि database बाहरी रूप से पहुँच योग्य (reachable) हो जाते हैं।

hostPath या privileged वाला pod आपके VPS पर root होता है

Containers आपके kernel पर चलने वाली सामान्य प्रक्रियाएं हैं, जिनका kernel पर सीमित नियंत्रण होता है। कई pod fields इस प्रतिबंध को हटा देते हैं।

  • securityContext.privileged: true container को सभी Linux capabilities और host devices तक पहुंच प्रदान करता है।
  • hostPath host की एक directory को pod में mount करता है। यदि कोई pod / को read-write मोड में mount करता है, तो वह /root/.ssh/authorized_keys में एक key जोड़ सकता है।
  • hostPID: true container को host process namespace में डाल देता है, जहाँ PID 1 के विरुद्ध nsenter चलाने पर host shell खुल जाता है।
  • hostNetwork: true इसे host network stack पर डाल देता है, जहाँ यह host ports को bind कर सकता है और loopback पर चल रही सेवाओं तक पहुंच सकता है।

इसलिए "यहाँ pod कौन बना सकता है" का अर्थ वही है जो "इस VPS पर root कौन है"। कोई भी ServiceAccount जिसके पास किसी भी namespace में pods पर create की अनुमति है, वह root के बराबर है, जब तक कि कोई चीज़ उस pod को पहले ही अस्वीकार न कर दे।

वह चीज़ Pod Security admission है, जो API server में ही निर्मित है। इसका संक्षिप्त तरीका प्रति namespace एक label लगाना है, जिसके लिए restart की आवश्यकता नहीं होती।

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

baseline ऊपर दिए गए चारों fields को अस्वीकार कर देता है। restricted और आगे जाता है और एक non-root user, एक seccomp (secure computing mode) profile, privilege escalation पर रोक और capabilities को ALL तक सीमित करने की मांग करता है, जो कई प्रकाशित charts को तोड़ देता है। 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 इसे अस्वीकार कर देता है और बताता है कि उसने किस field को अस्वीकार किया है:

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)

प्रत्येक namespace पर label लगाने के बजाय cluster-स्तर पर default सेट करने के लिए, k3s /var/lib/rancher/k3s/server/psa.yaml पर एक admission configuration file का दस्तावेजीकरण करता है:

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]

API server को /etc/rancher/k3s/config.yaml में इसकी ओर इंगित करें, फिर k3s को restart करें:

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

kube-system छूट वैकल्पिक नहीं है। k3s के अपने ServiceLB pods host ports का दावा करते हैं, जिसे baseline प्रतिबंधित करता है, इसलिए सूची से kube-system को हटाने का मतलब है कि अगली बार जब उन्हें recreate किया जाएगा, तो वे pods अस्वीकार कर दिए जाएंगे। admission में बदलाव के बाद k3s को restart करते समय एक दूसरा SSH session खुला रखें।

यहाँ एक अतिरिक्त जानकारी: k3s एक network policy controller के साथ आता है और इसे default रूप से सक्षम रखता है, इसलिए NetworkPolicy objects इस cluster पर बिना कुछ अतिरिक्त इंस्टॉल किए प्रभावी हो जाते हैं। यह हर Kubernetes distribution के लिए सच नहीं है, और यह एक compromised pod को बाकी cluster तक पहुँचने से रोकने का मुख्य साधन है।

उन बंडल किए गए घटकों को हटाएँ जिनका आप उपयोग नहीं करते हैं

Installer कई add-ons deploy करता है। प्रत्येक add-on एक listener जोड़ता है और patch करने के लिए एक अतिरिक्त घटक बढ़ाता है। --disable इन मानों को स्वीकार करता है: coredns, servicelb, traefik, local-storage, metrics-server, runtimes

coredns को बनाए रखें। इसके बिना cluster में कोई भी नाम resolve नहीं होता है। बाकी विकल्प हैं। /etc/rancher/k3s/config.yaml में:

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

k3s आपके द्वारा disable किए गए घटकों को हटा देता है, इसलिए traefik pods और svclb- pods अपने आप हट जाते हैं। पहले परिणामों को समझ लें। servicelb के हट जाने पर, प्रत्येक type: LoadBalancer service हमेशा के लिए <pending> पर ही रहती है, क्योंकि उसे कोई address assign नहीं करता है। traefik के हट जाने पर कोई ingress controller नहीं रहता है, इसलिए Ingress objects पूरी तरह से निष्क्रिय हो जाते हैं। जब आप किसी अन्य तरीके से traffic serve करते हैं, जैसे कि host पर reverse proxy का उपयोग करके, तब इन्हें disable करें, और जब आप इनका उपयोग कर रहे हों तो इन्हें न छेड़ें। disable-helm-controller: true उस controller को हटा देता है जो HelmChart resources को monitor करता है; यदि आप स्वयं helm चलाते हैं, तो यह एक privileged घटक है जिसका आप उपयोग नहीं कर रहे होते हैं।

डिफ़ॉल्ट ServiceAccount टोकन की automounting को बंद करना

जब तक आप अन्यथा निर्दिष्ट नहीं करते, प्रत्येक pod को /var/run/secrets/kubernetes.io/serviceaccount/token पर एक ServiceAccount टोकन मिलता है। default ServiceAccount के पास कोई RBAC अनुमतियाँ नहीं होती हैं, इसलिए केवल टोकन से बहुत कुछ हासिल नहीं होता है। एक compromised container के भीतर हमलावर को यह एक वैध क्रेडेंशियल और एक पहुँच योग्य API सर्वर प्रदान करता है, जो अधिकांश क्लस्टर एस्केलेशन लेखों का पहला चरण है।

k3s हार्डनिंग गाइड इसे प्रति namespace बंद कर देती है:

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}'

जाँचें कि यह लागू हुआ या नहीं। pod को शुरू होने के लिए पहले कुछ सेकंड का समय दें।

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

path हट चुका है, इसलिए ls, No such file or directory प्रिंट करता है। जिस workload को वास्तव में API एक्सेस की आवश्यकता होती है, वह अपने स्वयं के pod spec में automountServiceAccountToken: true सेट करता है, इसलिए कुछ भी स्थायी रूप से लॉक नहीं होता है। यह अपने आप में एक छोटी जीत है। बड़ी जीत यह है कि किसी workload को वास्तविक अनुमतियों वाला ServiceAccount न दिया जाए, और आप देख सकते हैं कि वर्तमान में एक टोकन का क्या मूल्य है:

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

Resource limits, ताकि एक pod पूरे cluster को down न कर सके

एक single node पर control plane और आपके workloads एक ही kernel और memory pool का उपयोग करते हैं। जो pod memory leak करता है, वह हमेशा अकेले समाप्त नहीं होता। Kernel का OOM (out of memory) killer बड़े processes को प्राथमिकता देते हुए अपना शिकार चुनता है, और k3s एक बड़ा व लंबे समय तक चलने वाला process है, इसलिए pod के बजाय पूरा cluster ही बंद हो सकता है। इसके बाद आपके workloads को restart करने वाला कोई नहीं बचता, क्योंकि जो component workloads को restart करता है, वही मर चुका होता है।

एक LimitRange उन pods के लिए limits तय करता है जिनमें कोई limit set नहीं की गई है:

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

एक ResourceQuota पूरे namespace द्वारा उपयोग की जा सकने वाली कुल क्षमता को सीमित करता है:

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'

इनका अंतर दो स्थानों पर दिखाई देता है। अपनी limit पार करने के कारण kill हुआ container kubectl describe pod में Last State के अंतर्गत Reason: OOMKilled रिपोर्ट करता है, और node का बाकी हिस्सा चलता रहता है। जिस node की memory पूरी तरह समाप्त हो जाती है, वह dmesg में Killed process लाइन छोड़ता है और आमतौर पर अपने पड़ोसियों को भी साथ ले डूबता है। पहला मामला आपकी limit का सही ढंग से काम करना है। दूसरा वह स्थिति है जिसे रोकने के लिए limits मौजूद हैं।

CI और k3s क्लस्टर में निर्धारित समय पर मैनिफेस्ट स्कैन करें

स्कैनिंग दो स्थानों पर की जानी चाहिए, क्योंकि वे अलग-अलग समस्याओं को पकड़ते हैं। सबसे पहले Trivy इंस्टॉल करें। यह प्रोजेक्ट हर release के साथ एक Debian पैकेज प्रदान करता है। अगस्त 2026 में 0.74.0 संस्करण नवीनतम था, इसलिए इसे ऑटोमेशन में पिन करने से पहले नए संस्करण के लिए releases पेज देखें।

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) जॉब को विफल कर देता है। प्रत्येक परिणाम विफल होने वाले फील्ड और उसकी गंभीरता (severity) को दर्शाता है, इसलिए कोई भी privileged: true जिसे आप कमिट नहीं करना चाहते थे, वह API सर्वर तक पहुँचने के बजाय बिल्ड को ही विफल कर देगा। जिस समस्या को आपने स्वीकार करने का निर्णय लिया है, उसे एक .trivyignore फाइल में दर्ज करें, जो उस निर्णय को git में संबंधित मैनिफेस्ट के साथ सुरक्षित रखती है।

दूसरा स्थान चल रहा क्लस्टर है, जिसे एक शेड्यूल पर स्कैन किया जाता है।

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

उस कमांड के बारे में एक ईमानदारी भरा नोट। trivy k8s एक नोड कलेक्टर पॉड तैनात करता है जिसे नोड-स्तरीय सेटिंग्स की जांच करने के लिए होस्ट एक्सेस की आवश्यकता होती है। ऐसे क्लस्टर पर जहाँ आपने अभी-अभी privileged पॉड्स को अस्वीकार करना शुरू किया है, इस पर ध्यान देना आवश्यक है, न कि इसे नजरअंदाज करना। 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 करें। एक स्कैनर जिसे privileged होना आवश्यक है, वह privileged वर्कलोड पर प्रतिबंध लगाने से रोकने का कोई कारण नहीं है।

इमेज की सामग्री एक अलग प्रश्न है। trivy image ghcr.io/example/app:1.4 इमेज के अंदर पैकेज डेटाबेस को पढ़ता है और ज्ञात कमजोरियों (vulnerabilities) की सूची बनाता है, यह वही कार्य है जो आप अपने सर्वर को ज्ञात CVEs के लिए चेक करते समय करते हैं।

अब स्कैनर आउटपुट के बारे में ईमानदारी वाली बात। एक सिंगल-नोड हॉबी क्लस्टर CIS कंट्रोल्स की एक लंबी सूची में विफल हो जाएगा, और उनमें से अधिकांश विफलताएं सही होने के बावजूद आपके लिए अप्रासंगिक हैं। यह बेंचमार्क मल्टी-नोड, मल्टी-टेनेंट क्लस्टर के लिए लिखा गया है: अलग होस्ट पर etcd, मशीन से बाहर भेजे गए ऑडिट लॉग्स, एक अलग kubelet सर्टिफिकेट अथॉरिटी, और ऐसे अनुपालन (compliance) शासन के लिए एडमिशन प्लगइन्स जिनके अधीन आप नहीं हैं। k3s जानबूझकर कंट्रोल प्लेन को एक सिंगल प्रोसेस के रूप में एक कॉन्फ़िगरेशन फाइल के साथ चलाता है, इसलिए जो कंट्रोल्स kube-scheduler मैनिफेस्ट फाइल की अनुमतियों की जांच करते हैं, वे पास नहीं हो सकते, क्योंकि ऐसी कोई फाइल मौजूद ही नहीं है।

विफलताओं को इस क्रम में पढ़ें और तब रुकें जब उनका महत्व समाप्त हो जाए: /etc/rancher और /var/lib/rancher के तहत फाइल मोड और ओनरशिप, कोई भी ऐसी चीज जो anonymous या unauthenticated एक्सेस का उल्लेख करती हो, कोई भी रिपोर्ट जो किसी कंपोनेंट के 0.0.0.0 पर बाइंड होने की सूचना देती हो, और कोई भी कंटेनर जो बिना किसी कारण के UID 0 के रूप में चल रहा हो। बाकी चीजें दूसरे नोड या एक्सेस वाले दूसरे व्यक्ति के आने तक प्रतीक्षा कर सकती हैं। सौ लाइनों की ऐसी रिपोर्ट जिसे आप अनदेखा करते हैं, पांच लाइनों की उस रिपोर्ट से कम मूल्यवान है जिस पर आप कार्रवाई करते हैं।

सिंगल नोड के लिए k3s हार्डनिंग का क्रम

  1. ufw की डिफ़ॉल्ट इनकमिंग पॉलिसी को deny पर सेट करें, SSH को allow करें, अपने स्वयं के पते से 6443 को allow करें, और पॉड तथा सर्विस नेटवर्क को allow करें।
  2. kubeconfig को अपने यूजर के लिए 600 मोड में कॉपी करें, और कभी भी --write-kubeconfig-mode 644 सेट न करें।
  3. पुष्टि करें कि 10250 पर kubelet अनाम अनुरोधों (anonymous requests) को अस्वीकार करता है, और इस पोर्ट को इंटरनेट से दूर रखें।
  4. जिन बंडल किए गए घटकों का आप उपयोग नहीं करते हैं उन्हें डिसेबल करें, फिर k3s को रीस्टार्ट करें।
  5. Pod Security admission के लिए अपने नेमस्पेस को 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

क्या k3s API server को port 6443 पर इंटरनेट के लिए expose करना सुरक्षित है?

यह अपने आप में कोई खुला दरवाजा नहीं है, क्योंकि API server को client certificate या token की आवश्यकता होती है और यह बाकी सभी अनुरोधों को forbidden: User "system:anonymous" के साथ अस्वीकार कर देता है। फिर भी दो जोखिम बने रहते हैं। अज्ञात कॉल करने वाले अभी भी /version को पढ़ सकते हैं, जो एक स्कैनर को ठीक-ठीक बता देता है कि किस Kubernetes release को खोजना है। और port खुला रहने पर भविष्य की हर API server vulnerability दूर से ही सुलभ हो जाती है। सिंगल-नोड क्लस्टर पर आपके अपने kubectl के अलावा बाहर से किसी को भी 6443 की आवश्यकता नहीं होती है, इसलिए इसे केवल अपने पते से sudo ufw allow from YOUR_IP to any port 6443 proto tcp के साथ अनुमति दें और बाकी को default deny policy पर छोड़ दें।

मेरा ufw rule NodePort service को ब्लॉक क्यों नहीं करता है?

क्योंकि पैकेट कभी उस chain तक नहीं पहुँचता जहाँ आपका rule स्थित है। kube-proxy, nat table की PREROUTING chain में DNAT rules डालता है, जो सबसे पहले चलती है और destination को pod address में बदल देती है। इसके बाद पैकेट को स्थानीय रूप से डिलीवर करने के बजाय forward किया जाता है, इसलिए यह FORWARD से होकर गुजरता है और INPUT को छोड़ देता है, जहाँ ufw के rules रहते हैं। इसे sudo iptables -S PREROUTING -t nat | head के साथ सत्यापित करें। NodePorts को अपने प्रदाता के network firewall पर फ़िल्टर करें, या type: NodePort से बचें और इसके बजाय kubectl port-forward के साथ services तक पहुँचें।

क्या मुझे k3s को --write-kubeconfig-mode 644 के साथ चलाना चाहिए?

नहीं। k3s documentation स्पष्ट करता है कि यह क्या करता है: "mode को 644 में बदलने से host पर अन्य unprivileged users इसे पढ़ सकेंगे।" उस फ़ाइल में system:masters के लिए एक client certificate होता है, इसलिए mode 644 हर local account को cluster administrator बना देता है। इसके बजाय इसे sudo install -o "$USER" -g "$USER" -m 600 /etc/rancher/k3s/k3s.yaml ~/.kube/config के साथ किसी एक user पर कॉपी करें, और मूल फ़ाइल को 600 root:root पर ही रहने दें।

मुझे k3s के लिए किस kube-bench benchmark का उपयोग करना चाहिए?

k3s-cis-1.7 का उपयोग करें। kube-bench documentation में कहा गया है: "kube-bench में Rancher K3S platform के लिए benchmarks शामिल हैं। इसे चलाने के लिए आपको kube-bench command चलाते समय --benchmark k3s-cis-1.7 निर्दिष्ट करना होगा।" इसे स्पष्ट रूप से पास करें, क्योंकि auto-detection यह मान लेता है कि layout kubeadm वाला है, जबकि k3s अपनी फ़ाइलों को /etc/rancher और /var/lib/rancher के अंतर्गत रखता है। उन विफलताओं की अपेक्षा करें जो एक सिंगल नोड पर लागू नहीं होती हैं, और सबसे पहले फ़ाइल अनुमतियों (permissions) और अज्ञात पहुँच (anonymous access) पर कार्रवाई करें।

क्या Pod Security admission बंडल किए गए k3s घटकों को तोड़ देगा?

यदि आप इसे kube-system पर लागू (enforce) करते हैं तो यह ऐसा करेगा। k3s द्वारा type: LoadBalancer services के लिए बनाए गए ServiceLB pods host पर ports का दावा करते हैं, और baseline host ports को प्रतिबंधित करता है, इसलिए अगली बार जब वे pods फिर से बनाए जाते हैं तो उन्हें अस्वीकार कर दिया जाता है। admission configuration फ़ाइल में kube-system को छूट दें, या Pod Security को केवल अपने स्वामित्व वाले namespaces पर labels के रूप में लागू करें। enforce=baseline और warn=restricted के साथ शुरुआत करें ताकि आप इसे चालू करने से पहले पढ़ सकें कि restricted क्या तोड़ देगा।