☸️ धडा 10 — Kubernetes RBAC: फक्त त्यांच्यावर लिहिलेल्याच खोल्या उघडणारी keycards
📍 तुम्ही इथे आहात: 16 पैकी धडा 10 · मागील: lesson-09-encryption · पुढील: lesson-11-k8s-secrets-network
📦 या ब्रँचमध्ये काय आहे
धडे 01–09, आणि पहिले Kubernetes नियंत्रण: RBAC (role-based access control).
Kubernetes API कडे येणारी प्रत्येक request — व्यक्तीकडून, CI कडून, pod मधल्या app कडून — ती
Roles आणि bindings विरुद्ध तपासली जाते. तुम्ही ServiceAccounts, Role विरुद्ध
ClusterRole, RoleBinding विरुद्ध ClusterRoleBinding, RBAC additive का आहे आणि default ने
नकार का देते, आणि kubectl auth can-i ने permission कशी test करायची हे शिकता.
- sec/cloud.py —
rbac_can(bindings, roles, subject, verb, resource, namespace) - sec/demo.py —
python3 sec/demo.py k8s_rbac(ते वापरत असलेलेROLESआणिBINDINGSk8s_rbac()च्या सुरुवातीला आहेत) - sec/test_sec.py — check
L10
🧒 5 वर्षांच्या मुलाला समजावल्यासारखे
शाळेच्या परिसरात अनेक खोल्या आहेत. तिथे काम करणाऱ्या प्रत्येकाला एक keycard 🪪 मिळते.
- पहिल्या दिवशी keycard काहीच उघडत नाही. अजिबात काहीच नाही.
- सुरक्षा कार्यालय एका कागदावर नियम लिहिते: "reader नियम: सूचना-खोली आणि वर्ग-यादी खोलीत पाहू शकतो." "deployer नियम: वेळापत्रकाचा फलक बदलू शकतो." तो कागद म्हणजे Role.
- मग दीपिका नोंदवहीत लिहिते: "निकाल-मदतनीसचे keycard → reader नियम → फक्त School इमारतीत." ती ओळ म्हणजे RoleBinding.
म्हणून निकाल-मदतनीस School इमारतीच्या सूचना-खोलीत पाहू शकते. ती passwords असलेली तिजोरी उघडू शकते का? नाही — नोंदवहीतली कोणतीही ओळ तसे सांगत नाही. ती मुख्य कार्यालयाच्या इमारतीतल्या सूचना-खोलीत पाहू शकते का? नाही — तिची ओळ फक्त School इमारत म्हणते.
काही नियम प्रत्येक इमारतीसाठी असतात (परिसराचा नकाशा). ते कागद म्हणजे ClusterRoles, आणि एखाद्याला प्रत्येक इमारतीत असा नियम देणारी ओळ म्हणजे ClusterRoleBinding. दीपिका अशा ओळी फार कमी लिहिते.
आणि आणखी एक नियम: ज्या मदतनीसला keycard ची कधीच गरज नाही, तिने ते बाळगूच नये. ते हरवले तर कोणीही ते वापरू शकत नाही.
🗺️ आकृती
flowchart LR
sa1["🤖 ServiceAccount<br/>results-api"] -->|"RoleBinding (ns school)"| r1["📜 Role read-grades<br/>get, list · pods, configmaps"]
sa2["🤖 ServiceAccount<br/>ci"] -->|"RoleBinding (ns school)"| r2["📜 Role deployer<br/>get … patch · deployments, services"]
g["👥 group auditors"] -->|"ClusterRoleBinding (all namespaces)"| cr["📜 ClusterRole view"]
api{"☸️ API server<br/>any rule allows? yes → allow<br/>none → 403 (deny by default)"}
r1 --> api
r2 --> api
cr --> api
x["❌ results-api get secrets → denied<br/>❌ ci update deployments in kube-system → denied"]
🗺️ काढलेली आकृती + एक lab: https://school-edh.pages.dev/security/lesson-diagrams.html#l10
❓ काय
- Subject — कोण विचारत आहे: user (तुमच्या identity provider कडून किंवा certificate मधून), एखादा group, किंवा ServiceAccount.
- ServiceAccount — cluster मधल्या software ची ओळख, एका namespace च्या आत.
Pod एका ServiceAccount म्हणून चालतो (तुम्ही निवडले नाही तर
default). Pod ला एक अल्पायुषी, audience-bound token file म्हणून mount करून मिळतो, आणि तो API ला call करायला त्याचा वापर करतो. automountServiceAccountToken: false— तो token mount करू नका. बहुतेक apps कधीच Kubernetes API ला call करत नाहीत, म्हणून त्यांनी असा token बाळगू नये जो attacker बिघडलेल्या pod मधून चोरू शकेल.- Role — एका namespace च्या आतल्या नियमांची यादी: कोणत्या resources वर (
pods,deployments,secrets…) कोणती verbs (get,list,watch,create,update,patch,delete), हवे असल्यास फक्त नाव दिलेल्या objects वर (resourceNames). - ClusterRole — तसेच, पण namespace शी बांधलेले नाही. Cluster-व्यापी गोष्टींसाठी
(nodes, namespaces) किंवा पुन्हा वापरता येणारा नियमसंच म्हणून वापरा. Built-in उदाहरणे:
view,edit,admin,cluster-admin. - RoleBinding — Role (किंवा ClusterRole) subjects ना एका namespace मध्ये देते.
viewClusterRole RoleBinding ने bind केल्यास "view" फक्त त्या namespace मध्ये मिळते. - ClusterRoleBinding — ClusterRole subjects ना प्रत्येक namespace मध्ये देते. खूप ताकदवान; यादी छोटी आणि तपासलेली ठेवा.
- Additive, default ने नकार — RBAC मध्ये deny नियमच नाहीत. Subject च्या कोणत्याही binding ने
परवानगी दिली तर request allowed; नाहीतर denied (
403 Forbidden). दुसऱ्या नियमाने permission "काढून घेता" येत नाही — ती देणारे binding तुम्ही काढून टाकता. - दिसतात त्यापेक्षा जास्त किमतीची verbs:
secretsवरचेlistकिंवाwatchफक्त नावे नाही, तर प्रत्येक Secret ची सामग्री परत करते;- एखाद्या namespace मध्ये
podsवरचेcreateएखाद्याला असा pod चालवू देते जो त्या namespace चे कोणतेही Secret किंवा ServiceAccount mount करतो; escalate,bindआणिimpersonatesubject ला जास्त permissions देऊ किंवा उसन्या घेऊ देतात;*असलेले काहीही.
kubectl auth can-i— API server ला विचारते "याला परवानगी मिळेल का?", स्वतःसाठी किंवा,--asसह, दुसऱ्या subject साठी.--listएखादा subject करू शकतो ते सगळे दाखवते.
🤔 का
कारण Kubernetes API म्हणजेच cluster. जो pods तयार करू शकतो, Secrets वाचू शकतो किंवा
Deployments बदलू शकतो, तो cluster चालवू शकेल असा कोणताही code चालवू शकतो आणि त्यातला कोणताही password वाचू शकतो. Apps आणि
CI pipelines हा आत शिरण्याचा सर्वात नेहमीचा मार्ग आहे: एका web app मधला bug attacker ला त्या pod चा
ServiceAccount token देतो. तो ServiceAccount cluster-admin ला bound असेल — एक नेहमीचा shortcut
— तर एक web bug म्हणजे पूर्ण cluster. प्रत्येक app साठी एक छोटा ServiceAccount, फक्त त्याच्या स्वतःच्या namespace मध्ये
लागेल तेवढ्यालाच bound, असला तर तोच bug छोटाच राहतो.
🔧 कसे (या repo मध्ये)
sec/cloud.py मधले rbac_can(bindings, roles, subject, verb, resource, namespace)
bindings मधून फिरते. त्या namespace मधल्या (किंवा namespace "*" असलेल्या, जी model ची
ClusterRoleBinding लिहिण्याची पद्धत आहे) त्या subject च्या प्रत्येक binding साठी,
ते bound role चा प्रत्येक नियम पाहते. Verb आणि resource दोन्ही एखाद्या नियमात असतील, तर ते
(True, "<role> allows <verb> <resource>") परत करते. कोणताही नियम जुळला नाही, तर ते
(False, "no binding allows it (RBAC denies by default)") परत करते. तपासायला कोणताही deny नियम नाही
— अगदी खऱ्या RBAC सारखे.
sec/demo.py मधले k8s_rbac() दोन roles (read-grades, deployer)
आणि दोन bindings (sa:results-api, sa:ci, दोन्ही namespace school मध्ये) वापरते.
🧪 करून पाहा
python3 sec/demo.py k8s_rbac
python3 - <<'EOF'
import sys; sys.path.insert(0, "sec"); from cloud import rbac_can
from demo import ROLES, BINDINGS
roles = {**ROLES, "secret-reader": [dict(verbs={"get"}, resources={"secrets"})],
"view-all": [dict(verbs={"get", "list", "watch"}, resources={"pods", "services"})]}
bindings = BINDINGS + [dict(subject="sa:results-api", role="secret-reader", namespace="school"),
dict(subject="group:auditors", role="view-all", namespace="*")]
for s, v, r, ns in (("sa:results-api", "get", "secrets", "school"), # new RoleBinding
("sa:results-api", "list", "secrets", "school"), # only 'get' was granted
("sa:results-api", "get", "secrets", "kube-system"), # a RoleBinding stays in its namespace
("sa:results-api", "delete", "pods", "school"), # nobody granted delete
("group:auditors", "list", "pods", "kube-system"), # a ClusterRoleBinding reaches every namespace
("sa:notices-api", "get", "pods", "school")): # no binding at all
print(f"{s:<16} {v:<6} {r:<8} in {ns:<11} → {rbac_can(bindings, roles, s, v, r, ns)}")
EOF
python3 sec/test_sec.py
✅ तपासा — तुम्हाला काय दिसायला हवे
k8s_rbac हे छापते:
═══ k8s_rbac ═══
── who may do what in the cluster (RBAC denies by default)
sa:results-api get pods in school → (True, 'read-grades allows get pods')
sa:results-api get secrets in school → (False, 'no binding allows it (RBAC denies by default)')
sa:ci update deployments in school → (True, 'deployer allows update deployments')
sa:ci update deployments in kube-system → (False, 'no binding allows it (RBAC denies by default)')
one ServiceAccount per app · no cluster-admin for apps or CI · automountServiceAccountToken: false when unused
✅ done — every door checked
तुमचा snippet हे छापतो:
sa:results-api get secrets in school → (True, 'secret-reader allows get secrets')
sa:results-api list secrets in school → (False, 'no binding allows it (RBAC denies by default)')
sa:results-api get secrets in kube-system → (False, 'no binding allows it (RBAC denies by default)')
sa:results-api delete pods in school → (False, 'no binding allows it (RBAC denies by default)')
group:auditors list pods in kube-system → (True, 'view-all allows list pods')
sa:notices-api get pods in school → (False, 'no binding allows it (RBAC denies by default)')
Tests मध्ये ✅ L10 Kubernetes RBAC denies what no binding allows आहे आणि शेवट
16/16 passed ने होतो.
🏁 तुम्ही आत्ताच काय सिद्ध केले
Permissions फक्त बेरीज होतात: नव्या binding ने get secrets दिले, आणि बाकी काहीच बदलले नाही.
list हे वेगळे verb आहे, म्हणून ते denied राहिले. Binding school मध्ये राहते, म्हणून
kube-system बंदच राहिले. कोणतेही binding नसलेला subject — sa:notices-api — काहीच
करू शकत नाही. आणि एका cluster-व्यापी binding ने (group:auditors) असा namespace गाठला ज्याचे नाव
कोणीच घेतले नव्हते — म्हणूनच ClusterRoleBindings ना सगळ्यात जास्त काळजी लागते. खऱ्या
cluster मध्ये तुम्ही secret-reader सुद्धा resourceNames ने एकाच Secret पुरते मर्यादित कराल.
⚠️ नेहमीच्या चुका
- "चालावे म्हणून" app च्या ServiceAccount ला किंवा CI ला
cluster-adminbind करणे - प्रत्येक pod namespace च्या
defaultServiceAccount म्हणून चालणे, आणिdefaultला जादा हक्क जोडणे — आता प्रत्येक pod कडे ते हक्क आहेत - API ला कधीच call न करणाऱ्या pods मध्ये mount केलेला ServiceAccount token
- Role मध्ये
verbs: ["*"]आणिresources: ["*"] - app ला एकच Secret लागत असताना
list secretsदेणे (get+resourceNamesवापरा) create podsहे जवळजवळ "या namespace मधले प्रत्येक Secret वाचा" सारखेच आहे हे विसरणे- RoleBinding पुरेसे असताना ClusterRoleBinding
- पुन्हा कधीच न पाहणे: bindings साचत जातात, आणि
old-dashboardअजूनहीadminला bound का आहे हे कोणालाच माहीत नसते
🏭 प्रत्यक्ष वापरात
खऱ्या account वर — प्रत्येक app साठी एक ServiceAccount, token mount न करता, एक नाव दिलेले Secret आणि एक ConfigMap वाचू शकणारा Role, आणि app च्या namespace मध्ये एक RoleBinding:
apiVersion: v1
kind: ServiceAccount
metadata: { name: results-api, namespace: school }
automountServiceAccountToken: false
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata: { name: results-api, namespace: school }
rules:
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["results-settings"]
verbs: ["get"]
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["results-db"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata: { name: results-api, namespace: school }
subjects:
- kind: ServiceAccount
name: results-api
namespace: school
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: results-api
Deployment मध्ये ServiceAccount निवडा आणि app API ला call करत नसेल तर token बंद ठेवा:
spec:
template:
spec:
serviceAccountName: results-api
automountServiceAccountToken: false
CI built-in edit ClusterRole सह एकाच namespace मध्ये deploy करते, जो
RoleBinding ने bound आहे (म्हणजे "edit" फक्त school मध्ये लागू होते):
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata: { name: ci-deployer, namespace: school }
subjects:
- kind: ServiceAccount
name: ci
namespace: ci-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: edit
विश्वास ठेवण्याआधी test करा — आणि जे denied व्हायला हवे तेसुद्धा test करा:
kubectl auth can-i get secrets/results-db -n school --as=system:serviceaccount:school:results-api # yes
kubectl auth can-i list secrets -n school --as=system:serviceaccount:school:results-api # no
kubectl auth can-i create deployments -n kube-system --as=system:serviceaccount:ci-system:ci # no
kubectl auth can-i --list -n school --as=system:serviceaccount:school:results-api
धोकादायक bindings शोधा — cluster-व्यापी cluster-admin कोणाकडे आहे:
kubectl get clusterrolebindings -o json \
| jq -r '.items[] | select(.roleRef.name=="cluster-admin") | .metadata.name + " → " + ([.subjects[]? | .kind + ":" + .name] | join(", "))'
EKS वर, IAM मधली माणसे आणि roles access entries आणि access policies (किंवा तुम्ही स्वतः bind केलेले Kubernetes groups) मधून cluster मध्ये येतात, आणि pods ना AWS permissions EKS Pod Identity किंवा IRSA मधून मिळतात — हा एक वेगळा, AWS-बाजूचा least privilege (IAM शाळा त्याबद्दल खोलवर शिकवते).
🏭 प्रत्यक्ष वापरात हे का महत्त्वाचे: API server चा audit log चालू करा (EKS वर:
auditसाठी control plane logging) आणि403responses आणि प्रत्येक नवे ClusterRoleBinding तपासा. App कडून आलेला 403 बहुधा bug असतो; एकाच ServiceAccount कडून आलेले भरपूर 403 बहुधा कोणीतरी दारे चाचपून पाहत असल्याचे लक्षण असते.
⏭️ पुढे
Secret कोण वाचू शकतो हे RBAC ठरवते. पण Secret च्या आत काय असते, आणि network वरून pod पर्यंत कोण पोहोचू शकतो? पुढे: Secrets फक्त base64 असतात, आणि pods सगळ्यांशी बोलतात — जोपर्यंत तुम्ही वेगळे सांगत नाही.
git checkout lesson-11-k8s-secrets-network