ЁЯХ╕я╕П рдзрдбрд╛ 11 тАФ Kubernetes secrets рдЖрдгрд┐ network policies: рдЖрд░рд╢рд╛рддрд▓реЗ рдЙрд▓рдЯреЗ рдЕрдХреНрд╖рд░ рдореНрд╣рдгрдЬреЗ рдХреБрд▓реВрдк рдирд╛рд╣реА
ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 16 рдкреИрдХреА рдзрдбрд╛ 11 ┬╖ рдорд╛рдЧреАрд▓: lesson-10-k8s-rbac ┬╖ рдкреБрдвреАрд▓: lesson-12-admission
ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ
рдзрдбреЗ 01тАУ10, рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ рдирд╡реНрдпрд╛ cluster рдордзреНрдпреЗ рдЕрд╕рдгрд╛рд░реА рджреЛрди рдЖрд╢реНрдЪрд░реНрдпреЗ. рдкрд╣рд┐рд▓реЗ: Kubernetes Secret рдлрдХреНрдд base64 рдЕрд╕рддреЗ тАФ рдПрдХ encoding, encryption рдирд╛рд╣реА тАФ рдЬреЛрдкрд░реНрдпрдВрдд рддреБрдореНрд╣реА encryption at rest рдЪрд╛рд▓реВ рдХрд░рдд рдирд╛рд╣реА рдХрд┐рдВрд╡рд╛ рдЦрд░реЗ рдЧреБрдкрд┐рдд external store рдордзреНрдпреЗ рдареЗрд╡рдд рдирд╛рд╣реА. рджреБрд╕рд░реЗ: рдкреНрд░рддреНрдпреЗрдХ pod рдЗрддрд░ рдкреНрд░рддреНрдпреЗрдХ pod рд╢реА рдмреЛрд▓реВ рд╢рдХрддреЛ, рдЬреЛрдкрд░реНрдпрдВрдд рдПрдЦрд╛рджреА NetworkPolicy рддреНрдпрд╛рд▓рд╛ select рдХрд░рдд рдирд╛рд╣реА тАФ рдЖрдгрд┐ рддреЗрд╣реА рдлрдХреНрдд cluster рдЪрд╛ network plugin (CNI) policies рд▓рд╛рдЧреВ рдХрд░рдд рдЕрд╕реЗрд▓ рддрд░рдЪ. рддреБрдореНрд╣реА рджреЛрдиреНрд╣реА рд╢рд┐рдХрддрд╛, рдЖрдгрд┐ default-deny pattern рд╕реБрджреНрдзрд╛.
- sec/cloud.py тАФ
netpol_allows(policies, src_labels, dst_labels, port) - sec/demo.py тАФ
python3 sec/demo.py k8s_network - sec/test_sec.py тАФ check
L11
ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ
рдЧреБрдкреНрдд рдкрд╛рдХреАрдЯ. рдРрд╢реНрд╡рд░реНрдпрд╛ database рдЪрд╛ password "SECRET" рдЕрд╕реЗ рд▓рд┐рд╣рд┐рд▓реЗрд▓реНрдпрд╛ рдкрд╛рдХрд┐рдЯрд╛рдд рдареЗрд╡рддреЗ. рдкрдг рдЖрдд password рдХреБрд▓реВрдкрдмрдВрдж рдирд╛рд╣реА тАФ рддреЛ рдлрдХреНрдд рдЖрд░рд╢рд╛рддрд▓реНрдпрд╛ рдЙрд▓рдЯреНрдпрд╛ рдЕрдХреНрд╖рд░рд╛рдВрдд рд▓рд┐рд╣рд┐рд▓реЗрд▓рд╛ рдЖрд╣реЗ. рдЬреНрдпрд╛рдЪреНрдпрд╛рдХрдбреЗ рдЖрд░рд╕рд╛ рдЖрд╣реЗ (рдЖрдгрд┐ рдЖрд░рд╕рд╛ рд╕рдЧрд│реНрдпрд╛рдВрдХрдбреЗрдЪ рдЕрд╕рддреЛ) рддреЛ рд╡рд╛рдЪреВ рд╢рдХрддреЛ. рд╣реЗрдЪ base64. рддреЗ рдЦрд░реЛрдЦрд░ рдЧреБрдкреНрдд рдХрд░рд╛рдпрдЪреЗ рдЕрд╕реЗрд▓, рддрд░ рдХрд╛рд░реНрдпрд╛рд▓рдпрд╛рдиреЗ рдЪрд╛рд╡реА-рддрд┐рдЬреЛрд░реАрддрд▓реНрдпрд╛ рдЪрд╛рд╡реАрдиреЗ рдкрд╛рдХрд┐рдЯрд╛рд▓рд╛ рдХреБрд▓реВрдк рд▓рд╛рд╡рд╛рдпрд▓рд╛ рд╣рд╡реЗ (KMS рд╕рд╣ encryption at rest), рдХрд┐рдВрд╡рд╛ password рдЪрд╛рд╡реА-рддрд┐рдЬреЛрд░реАрддрдЪ рдареЗрд╡реВрди рдЧрд░рдЬ рдЕрд╕реЗрд▓ рддреЗрд╡реНрд╣рд╛рдЪ рдЖрдгрд╛рдпрд▓рд╛ рд╣рд╡рд╛ (external secret store).
рд╡рд▒реНрд╣рд╛рдВрдбреЗ. рд╢рд╛рд│реЗрдЪреНрдпрд╛ рдирд╡реНрдпрд╛ рдЗрдорд╛рд░рддреАрдд рдкреНрд░рддреНрдпреЗрдХ рджрд╛рд░ рдЙрдШрдбреЗ рдЕрд╕рддреЗ. рдХреЛрдгрддрд╛рд╣реА рд╡рд┐рджреНрдпрд╛рд░реНрдереА рд╢рд┐рдХреНрд╖рдХ-рдЦреЛрд▓реАрдд, server room рдордзреНрдпреЗ, рд╕реНрд╡рдпрдВрдкрд╛рдХрдШрд░рд╛рдд рдЬрд╛рдК рд╢рдХрддреЛ. рдХрд╛рд░реНрдпрд╛рд▓рдп рдПрдЦрд╛рджреНрдпрд╛ рджрд╛рд░рд╛рд╡рд░ рдпрд╛рджреА рдШреЗрдКрди рдкрд╣рд╛рд░реЗрдХрд░реА рдареЗрд╡реВ рд╢рдХрддреЗ: "рдлрдХреНрдд рдирд┐рдХрд╛рд▓-рдорджрддрдиреАрд╕, рдЖрдгрд┐ рдлрдХреНрдд 5432 рдХреНрд░рдорд╛рдВрдХрд╛рдЪреНрдпрд╛ рдмрд╛рдЬреВрдЪреНрдпрд╛ рджрд╛рд░рд╛рддреВрди."
рдкрд╣рд╛рд░реЗрдХрд▒реНрдпрд╛рдВрдмрджреНрджрд▓ рджреЛрди рдирд┐рдпрдо:
- рдкрд╣рд╛рд░реЗрдХрд░реА рдирд╕рд▓реЗрд▓реЗ рджрд╛рд░ рд╕рдЧрд│реНрдпрд╛рдВрд╕рд╛рдареА рдЙрдШрдбреЗрдЪ рд░рд╛рд╣рддреЗ. рдореНрд╣рдгреВрди рдХрд╛рд░реНрдпрд╛рд▓рдп рд╕реБрд░реБрд╡рд╛рддреАрд▓рд╛ рдкреНрд░рддреНрдпреЗрдХ рджрд╛рд░рд╛рд╡рд░ рд░рд┐рдХрд╛рдореА рдпрд╛рджреА тАФ "рдХреЛрдгреАрдЪ рдирд╛рд╣реА" тАФ рдШреЗрдКрди рдкрд╣рд╛рд░реЗрдХрд░реА рдареЗрд╡рддреЗ, рдЖрдгрд┐ рдордЧ рдирд╛рд╡реЗ рдЬреЛрдбрддреЗ. рд╣реЗрдЪ default-deny.
- рдкрд╣рд╛рд░реЗрдХрд▒реНрдпрд╛рдЪреА рдпрд╛рджреА рдлрдХреНрдд рдХрд╛рдЧрдж рдЖрд╣реЗ. рдЗрдорд╛рд░рддреАрдд рдкрд╣рд╛рд░реЗрдХрд░реА рд╕реЗрд╡рд╛рдЪ рдирд╕реЗрд▓ (policies рд▓рд╛рдЧреВ рдХрд░рдгрд╛рд░рд╛ CNI), рддрд░ рддреЛ рдХрд╛рдЧрдж рдХрд╛рд╣реАрдЪ рдХрд░рдд рдирд╛рд╣реА, рдЖрдгрд┐ рдХреЛрдгреА рддреБрдореНрд╣рд╛рд▓рд╛ рд╕рд╛рдВрдЧрддрд╣реА рдирд╛рд╣реА.
ЁЯЧ║я╕П рдЖрдХреГрддреА
flowchart LR
r["ЁЯН│ results-api"] -->|"5432 тЬУ"| pg["ЁЯУТ postgres<br/>NetworkPolicy: from app=results-api, port 5432"]
n["ЁЯУв notices-api"] -. "5432 тЬЧ" .-> pg
r -. "22 тЬЧ" .-> pg
w["ЁЯМР web (no policy selects it)"]
any["any pod"] -->|"тЬУ everything allowed<br/>until default-deny"| w
s["ЁЯФС Secret password: U3VtbWVyтАж<br/>base64 -d тЖТ readable"] --> k["ЁЯЧЭя╕П encryption at rest (KMS)<br/>or External Secrets"]
ЁЯЧ║я╕П рдХрд╛рдврд▓реЗрд▓реА рдЖрдХреГрддреА + рдПрдХ lab: https://school-edh.pages.dev/security/lesson-diagrams.html#l11
тЭУ рдХрд╛рдп
- Secret тАФ рдЫреЛрдЯреА рд╕рдВрд╡реЗрджрдирд╢реАрд▓ values (passwords, tokens,
keys) рдареЗрд╡рдгрд╛рд░рд╛ Kubernetes object. Binary data YAML рдордзреНрдпреЗ рдмрд╕рд╛рд╡рд╛ рдореНрд╣рдгреВрди values base64-encoded рдЕрд╕рддрд╛рдд. Base64 рд╣реЗ
encryption рдирд╛рд╣реА:
base64 -dрддреЗ рдЙрд▓рдЯреЗ рдХрд░рддреЗ, рдХреЛрдгрддреАрд╣реА key рд▓рд╛рдЧрдд рдирд╛рд╣реА. - Secrets рдХреБрдареЗ рд░рд╛рд╣рддрд╛рдд тАФ etcd рдордзреНрдпреЗ, cluster рдЪреНрдпрд╛ database рдордзреНрдпреЗ. Encryption at rest рдирд╕реЗрд▓, рддрд░ etcd backup рдХрд┐рдВрд╡рд╛ disk snapshot рдорд┐рд│рд╡рдгрд╛рд░рд╛ рдХреЛрдгреАрд╣реА рдкреНрд░рддреНрдпреЗрдХ Secret рд╡рд╛рдЪреВ рд╢рдХрддреЛ.
- Secrets рд╕рд╛рдареА encryption at rest тАФ API server Secrets etcd рдордзреНрдпреЗ рд▓рд┐рд╣рд┐рдгреНрдпрд╛рдЖрдзреА encrypt рдХрд░рддреЛ, рдЬреЗ EncryptionConfiguration file рдиреЗ set up рдХреЗрд▓реЗ рдЬрд╛рддреЗ. рд╕рд░реНрд╡рд╛рдд рдЪрд╛рдВрдЧрд▓рд╛ provider рдореНрд╣рдгрдЬреЗ KMS v2: cloud KMS рдордзрд▓реА key data keys рдЧреБрдВрдбрд╛рд│рддреЗ (envelope encryption, рдзрдбрд╛ 07). EKS рд╡рд░, рдирд╡реНрдпрд╛ versions рдордзреНрдпреЗ default envelope encryption рдпреЗрддреЗ, рдЖрдгрд┐ Secrets рд╕рд╛рдареА рддреБрдореНрд╣реА рддреБрдордЪреА рд╕реНрд╡рддрдГрдЪреА KMS key рдирд┐рд╡рдбреВ рд╢рдХрддрд╛, рдореНрд╣рдгрдЬреЗ рддрд┐рдЪреЗ рдирд┐рдпрдВрддреНрд░рдг рдЖрдгрд┐ audit рддреБрдордЪреНрдпрд╛рдХрдбреЗ рд░рд╛рд╣рддреЗ. рдЕрд╕реНрддрд┐рддреНрд╡рд╛рдд рдЕрд╕рд▓реЗрд▓реА Secrets рдкреБрдиреНрд╣рд╛ рд▓рд┐рд╣рд┐рд▓реА рдЧреЗрд▓реНрдпрд╛рд╡рд░рдЪ encrypt рд╣реЛрддрд╛рдд.
- Secrets рдХреЛрдг рд╡рд╛рдЪреВ рд╢рдХрддреЛ тАФ рд╣реЗ рдЕрдЬреВрдирд╣реА RBAC (рдзрдбрд╛ 10) рдард░рд╡рддреЗ.
secretsрд╡рд░get/list, рдХрд┐рдВрд╡рд╛ namespace рдордзреНрдпреЗpodsрд╡рд░create, рддреЗ рд╡рд╛рдЪрд╛рдпрд▓рд╛ рдкреБрд░реЗрд╕реЗ рдЖрд╣реЗ. API рдЬреНрдпрд╛рд▓рд╛ рд╡рд╛рдЪреВ рджреЗрддреЛ рддреНрдпрд╛рдЪреНрдпрд╛рдкрд╛рд╕реВрди encryption at rest рд╕рдВрд░рдХреНрд╖рдг рджреЗрдд рдирд╛рд╣реА. - External secret stores тАФ рдЦрд░реА value AWS Secrets Manager, SSM Parameter Store рдХрд┐рдВрд╡рд╛ HashiCorp Vault рдордзреНрдпреЗ рдареЗрд╡рд╛. External Secrets Operator рддреА Kubernetes Secret рдордзреНрдпреЗ copy рдХрд░рддреЛ рдЖрдгрд┐ refresh рдХрд░рддреЛ; Secrets Store CSI Driver рддреА file рдореНрд╣рдгреВрди mount рдХрд░рддреЛ. Rotation рдПрдХрд╛рдЪ рдард┐рдХрд╛рдгреА рд╣реЛрддреЗ.
- NetworkPolicy тАФ рддреА рдЬреНрдпрд╛ pods рдирд╛ (label рдиреЗ) select рдХрд░рддреЗ рддреНрдпрд╛рдВрдЪреНрдпрд╛рд╕рд╛рдареА рдкрд░рд╡рд╛рдирдЧреА рдЕрд╕рд▓реЗрд▓реНрдпрд╛ traffic рдЪреА рдпрд╛рджреА:
ingress(рдХреЛрдг рдЖрдд рдпреЗрдК рд╢рдХрддреЛ) рдЖрдгрд┐egress(рддреЗ рдХреБрдареЗ рдЬрд╛рдК рд╢рдХрддрд╛рдд), pod labels, namespace labels рдХрд┐рдВрд╡рд╛ IP ranges, рдЖрдгрд┐ ports рдиреБрд╕рд╛рд░. - рдкреНрд░рддреНрдпреЗрдХрд╛рд▓рд╛ рдЖрд╢реНрдЪрд░реНрдп рд╡рд╛рдЯрдгрд╛рд░реЗ рджреЛрди рдирд┐рдпрдо:
- рдХреЛрдгрддреАрд╣реА policy select рди рдХрд░рдгрд╛рд░рд╛ pod рдЖрдд рдЖрдгрд┐ рдмрд╛рд╣реЗрд░ рд╕рдЧрд│реЗ traffic рдЪрд╛рд▓реВ рджреЗрддреЛ;
- рдПрдХрджрд╛ рдХреЛрдгрддреНрдпрд╛рд╣реА policy рдиреЗ pod рд▓рд╛ ingress (рдХрд┐рдВрд╡рд╛ egress) рд╕рд╛рдареА select рдХреЗрд▓реЗ, рдХреА рдлрдХреНрдд рдПрдЦрд╛рджреНрдпрд╛ policy рдиреЗ рдкрд░рд╡рд╛рдирдЧреА рджрд┐рд▓реЗрд▓реЗрдЪ рдЖрдд рдЬрд╛рддреЗ тАФ policies RBAC рд╕рд╛рд░рдЦреНрдпрд╛ рдмреЗрд░реАрдЬ рд╣реЛрддрд╛рдд; deny рдирд┐рдпрдо рдирд╕рддрд╛рдд.
- Default-deny тАФ
podSelector: {}(namespace рдордзрд▓рд╛ рдкреНрд░рддреНрдпреЗрдХ pod) рдЕрд╕рд▓реЗрд▓реА рдЖрдгрд┐ рдХреЛрдгрддрд╛рд╣реА allow рдирд┐рдпрдо рдирд╕рд▓реЗрд▓реА policy. рдордЧ рдЫреЛрдЯреНрдпрд╛ allow policies рдЬреЛрдбрд╛. DNS рд▓рдХреНрд╖рд╛рдд рдареЗрд╡рд╛: default-deny egress рдЕрд╕реЗрд▓ рддрд░ pods рдирд╛ port 53 рд╡рд░kube-dnsрдкрд░реНрдпрдВрдд рдкреЛрд╣реЛрдЪрд╛рдпрдЪреА рдкрд░рд╡рд╛рдирдЧреА рд╣рд╡реА. - CNI рдиреЗ рддреА рд▓рд╛рдЧреВ рдХрд░рд╛рдпрд▓рд╛ рд╣рд╡реА тАФ NetworkPolicy рдлрдХреНрдд рдПрдХ API object рдЖрд╣реЗ. Calico, Cilium рдЖрдгрд┐ Amazon VPC CNI (network policy рдЪрд╛рд▓реВ рдХреЗрд▓реНрдпрд╛рд╡рд░) рддреА рд▓рд╛рдЧреВ рдХрд░рддрд╛рдд. рдХрд╛рд╣реА plugins (рдЙрджрд╛рд╣рд░рдгрд╛рд░реНрде рд╕рд╛рдзреЗ Flannel) objects рд╕реНрд╡реАрдХрд╛рд░рддрд╛рдд рдЖрдгрд┐ рддреНрдпрд╛рдВрдЪреНрдпрд╛рдХрдбреЗ рджреБрд░реНрд▓рдХреНрд╖ рдХрд░рддрд╛рдд.
ЁЯдФ рдХрд╛
рдХрд╛рд░рдг рджреЛрдиреНрд╣реА defaults security рдЪреНрдпрд╛ рджреГрд╖реНрдЯреАрдиреЗ рдЪреБрдХреАрдЪреНрдпрд╛ рджрд┐рд╢реЗрд▓рд╛ рдЖрд╣реЗрдд. рдПрдХрд╛ pod рдордзреНрдпреЗ shell рдорд┐рд│рд╡рдгрд╛рд░рд╛ attacker
(рдзрдбрд╛ 01 рдордзрд▓реЗ injection, рдзрдбрд╛ 13 рдордзрд▓реА рдЕрд╕реБрд░рдХреНрд╖рд┐рдд library) рдордЧ database, cache, metrics endpoints рдЖрдгрд┐
рдЗрддрд░ teams рдЪреНрдпрд╛ services рдкрд░реНрдпрдВрдд рдкреЛрд╣реЛрдЪреВ рд╢рдХрддреЛ тАФ рдЬреЛрдкрд░реНрдпрдВрдд рдПрдЦрд╛рджреА policy рддреНрдпрд╛рд▓рд╛
рдерд╛рдВрдмрд╡рдд рдирд╛рд╣реА. рдЖрдгрд┐ рд╕реБрд░рдХреНрд╖рд┐рдд "рджрд┐рд╕рдгрд╛рд░реЗ" Secret backups, CI logs рдЖрдгрд┐ kubectl get -o yaml output рдордзреНрдпреЗ рдЕрд╢рд╛ рд╕реНрд╡рд░реВрдкрд╛рдд copy рд╣реЛрддреЗ рдЬреЗ рдХреЛрдгреАрд╣реА decode рдХрд░реВ рд╢рдХрддреЛ. Default-deny рдЖрдгрд┐ encrypted, рдмрд╛рд╣реЗрд░реВрди managed
secrets рдореБрд│реЗ рдПрдХ рдмрд┐рдШрдбрд▓реЗрд▓рд╛ pod рдлрдХреНрдд рдПрдХ рдмрд┐рдШрдбрд▓реЗрд▓рд╛ pod рд░рд╛рд╣рддреЛ тАФ рдкреВрд░реНрдг рд╢рд╛рд│рд╛ рдирд╛рд╣реА.
ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)
sec/cloud.py рдордзрд▓реЗ netpol_allows(policies, src_labels, dst_labels, port)
pod рдХрдбреЗ рдпреЗрдгрд╛рд▒реНрдпрд╛ ingress рдЪреЗ model рдХрд░рддреЗ:
- рдЬреНрдпрд╛ policies рдЪреЗ рд╕рдЧрд│реЗ
podlabels destination pod рд╢реА рдЬреБрд│рддрд╛рдд рддреНрдпрд╛ рд╢реЛрдзрд╛; - рдХреЛрдгрддреАрдЪ рддреНрдпрд╛рд▓рд╛ select рдХрд░рдд рдирд╕реЗрд▓ тЖТ
True(рд╕рдЧрд│реНрдпрд╛рдВрд╕рд╛рдареА рдЙрдШрдбрд╛); - рдирд╛рд╣реАрддрд░ тЖТ рддреЗрд╡реНрд╣рд╛рдЪ allowed рдЬреЗрд╡реНрд╣рд╛ select рдХрд░рдгрд╛рд▒реНрдпрд╛ рдПрдЦрд╛рджреНрдпрд╛ policy рдЪреЗ рд╕рдЧрд│реЗ
fromlabels source рд╢реА рдЬреБрд│рддрд╛рдд рдЖрдгрд┐ port рддреНрдпрд╛рдЪреНрдпрд╛portsрдордзреНрдпреЗ рдЕрд╕рддреЛ.
рд░рд┐рдХрд╛рдорд╛ pod={} рдкреНрд░рддреНрдпреЗрдХ pod select рдХрд░рддреЛ, рдЖрдгрд┐ ports=[] рдХрд╛рд╣реАрдЪ allow рдХрд░рдд рдирд╛рд╣реА тАФ рджреЛрдиреНрд╣реА рдорд┐рд│реВрди
default-deny. sec/demo.py рдордзрд▓реЗ k8s_network() postgres pods рд╡рд░ рдПрдХ policy
рд▓рд╛рд╡рддреЗ. Snippet рд╣реЗ рд╕реБрджреНрдзрд╛ рджрд╛рдЦрд╡рддреЛ рдХреА Secret рдЪреА value base64 рдордзреНрдпреЗ рдХрд╢реА рджрд┐рд╕рддреЗ тАФ рдзрдбрд╛ 07 рдЪреНрдпрд╛ scan рдордзрд▓рд╛
рдХрд╛рд▓реНрдкрдирд┐рдХ password рд╡рд╛рдкрд░реВрди.
ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛
python3 sec/demo.py k8s_network
python3 - <<'EOF'
import sys, base64; sys.path.insert(0, "sec"); from cloud import netpol_allows
deny_all = dict(pod={}, ports=[], **{"from": {}}) # selects every pod, allows nothing
db_rule = dict(pod={"app": "postgres"}, ports=[5432], **{"from": {"app": "results-api"}})
web_rule = dict(pod={"app": "web"}, ports=[8080], **{"from": {"app": "ingress"}})
tries = (({"app": "notices-api"}, {"app": "web"}, 8080), ({"app": "ingress"}, {"app": "web"}, 8080),
({"app": "results-api"}, {"app": "postgres"}, 5432), ({"app": "web"}, {"app": "postgres"}, 5432))
for name, pols in (("no policies", []), ("default-deny only", [deny_all]), ("default-deny + 2 allows", [deny_all, db_rule, web_rule])):
print(f"тФАтФА {name}")
for src, dst, port in tries:
print(f" {src['app']:<12} тЖТ {dst['app']}:{port:<5} {'allowed' if netpol_allows(pols, src, dst, port) else 'blocked'}")
stored = base64.b64encode(b"Summer2026!school").decode()
print(f"тФАтФА a Secret as the API server stores it: password: {stored}")
print(f" anyone who can read it runs base64 -d тЖТ {base64.b64decode(stored).decode()}")
EOF
python3 sec/test_sec.py
тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ
k8s_network рд╣реЗ рдЫрд╛рдкрддреЗ:
тХРтХРтХР k8s_network тХРтХРтХР
тФАтФА NetworkPolicy on the database pods: only results-api, only 5432
results-api тЖТ postgres:5432 allowed
notices-api тЖТ postgres:5432 blocked
results-api тЖТ postgres:22 blocked
pods with no policy selecting them accept everything тЖТ True тАФ start every namespace with default-deny
Kubernetes Secrets are only base64 тАФ enable encryption at rest, or use an external secret store
тЬЕ done тАФ every door checked
рддреБрдордЪрд╛ snippet рд╣реЗ рдЫрд╛рдкрддреЛ:
тФАтФА no policies
notices-api тЖТ web:8080 allowed
ingress тЖТ web:8080 allowed
results-api тЖТ postgres:5432 allowed
web тЖТ postgres:5432 allowed
тФАтФА default-deny only
notices-api тЖТ web:8080 blocked
ingress тЖТ web:8080 blocked
results-api тЖТ postgres:5432 blocked
web тЖТ postgres:5432 blocked
тФАтФА default-deny + 2 allows
notices-api тЖТ web:8080 blocked
ingress тЖТ web:8080 allowed
results-api тЖТ postgres:5432 allowed
web тЖТ postgres:5432 blocked
тФАтФА a Secret as the API server stores it: password: U3VtbWVyMjAyNiFzY2hvb2w=
anyone who can read it runs base64 -d тЖТ Summer2026!school
Tests рдордзреНрдпреЗ тЬЕ L11 a NetworkPolicy lets only the named source in рдЖрд╣реЗ рдЖрдгрд┐ рд╢реЗрд╡рдЯ
16/16 passed рдиреЗ рд╣реЛрддреЛ.
ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ
рдХреЛрдгрддреАрд╣реА policy рдирд╕рддрд╛рдирд╛ рдЪрд╛рд░рд╣реА connections рдЪрд╛рд▓рддрд╛рдд тАФ рдЕрдЧрджреА web тЖТ postgres рд╕реБрджреНрдзрд╛, рдЬреЗ рдХреЛрдгрд╛рд▓рд╛рдЪ
рдирдХреЛ рд╣реЛрддреЗ. рдлрдХреНрдд default-deny рд╕рдЧрд│реЗрдЪ рдмрдВрдж рдХрд░рддреЗ (рд╕реБрд░рдХреНрд╖рд┐рдд, рдкрдг app рдмрдВрдж рдкрдбрддреЗ). Default-deny
рдЖрдгрд┐ рджреЛрди рдЫреЛрдЯреЗ allow рдирд┐рдпрдо рд╢рд╛рд│реЗрд▓рд╛ рд╣рд╡реЗ рдЕрд╕рд▓реЗрд▓реЗ рдиреЗрдордХреЗ рджреЛрди рдорд╛рд░реНрдЧ рджреЗрддрд╛рдд, рдЖрдгрд┐ рдмрд╛рдХреАрдЪреЗ рджреЛрди
рдмрдВрдж рд░рд╛рд╣рддрд╛рдд. Policies рдмреЗрд░реАрдЬ рд╣реЛрддрд╛рдд: allows рдирд╛ deny "рдХрд╛рдвреВрди рдЯрд╛рдХрд╛рд╡рд╛" рд▓рд╛рдЧрд▓рд╛ рдирд╛рд╣реА. рдЖрдгрд┐
"рдЧреБрдкреНрдд" password рдПрдХрд╛ рдУрд│реАрдд, рдХреЛрдгрддреНрдпрд╛рд╣реА key рд╢рд┐рд╡рд╛рдп, base64 рдордзреВрди рдкрд░рдд рдЖрд▓рд╛.
тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛
- рдЬреНрдпрд╛ cluster рдЪрд╛ CNI policies рд▓рд╛рдЧреВ рдХрд░рдд рдирд╛рд╣реА рддреНрдпрд╛рд╡рд░ policies рд▓рд┐рд╣рд┐рдгреЗ тАФ рд╕рдЧрд│реЗ рдареАрдХ рджрд┐рд╕рддреЗ рдЖрдгрд┐ рдХрд╛рд╣реАрдЪ block рд╣реЛрдд рдирд╛рд╣реА (рдЦрд▒реНрдпрд╛ connection рдиреЗ test рдХрд░рд╛, рдЦрд╛рд▓реА рдкрд╛рд╣рд╛)
- рдлрдХреНрдд рдХрд╛рд╣реА "рдорд╣рддреНрддреНрд╡рд╛рдЪреНрдпрд╛" pods рд╕рд╛рдареА policies тАФ рдмрд╛рдХреА рдкреНрд░рддреНрдпреЗрдХ pod рд╕рдЧрд│реНрдпрд╛рдВрд╕рд╛рдареА рдЙрдШрдбрд╛ рд░рд╛рд╣рддреЛ
- DNS allow рд╢рд┐рд╡рд╛рдп default-deny egress тАФ рдкреНрд░рддреНрдпреЗрдХ lookup fail рд╣реЛрддреЛ рдЖрдгрд┐ app рдмрд┐рдШрдбрд▓реЗрд▓реЗ рджрд┐рд╕рддреЗ
- database рд╡рд░ ingress allow, рдкрдг API рд╡рд░ default-deny egress рдЖрдгрд┐ database рдХрдбреЗ рдХреЛрдгрддрд╛рд╣реА egress allow рдирд╛рд╣реА тАФ traffic рд╕рд╛рдареА рджреЛрдиреНрд╣реА рдмрд╛рдЬреВ рдЙрдШрдбреНрдпрд╛ рдЕрд╕рд╛рд╡реНрдпрд╛ рд▓рд╛рдЧрддрд╛рдд
- рдЕрд╕реЗ label рд╡рд╛рдкрд░реВрди pods select рдХрд░рдгреЗ рдЬреЗ рджреБрд╕рд▒реНрдпрд╛ team рдЪреНрдпрд╛ pods рд╡рд░рд╣реА рдЕрд╕рддреЗ (
app: api) - "рддреЗ encoded рдЖрд╣реЗрдд рдореНрд╣рдгреВрди" Git рдордзреНрдпреЗ Secrets;
kubectl get secret -o yamlticket рдордзреНрдпреЗ paste рдХрд░рдгреЗ - encryption at rest рдЪрд╛рд▓реВ рдХреЗрд▓реЗ, рдкрдг рдЬреБрдиреА Secrets рдХрдзреАрдЪ рдкреБрдиреНрд╣рд╛ рд▓рд┐рд╣рд┐рд▓реА рдирд╛рд╣реАрдд, рдореНрд╣рдгреВрди рддреА etcd рдордзреНрдпреЗ рдЕрдЬреВрдирд╣реА рд╕рд╛рдзреА (plain) рдЖрд╣реЗрдд
- RBAC рдЬреНрдпрд╛рд▓рд╛
get secretsрдХрд░реВ рджреЗрддреЗ рдЕрд╢рд╛ user рд▓рд╛ encryption at rest рдерд╛рдВрдмрд╡рддреЗ рдЕрд╕реЗ рдорд╛рдирдгреЗ тАФ рддреЗ рдерд╛рдВрдмрд╡рдд рдирд╛рд╣реА
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд
рдЦрд▒реНрдпрд╛ account рд╡рд░ тАФ namespace рд╕рд╛рдареА default-deny, рдПрдХ DNS allow, рдЖрдгрд┐ рд╢рд╛рд│реЗрд▓рд╛ рд╣рд╡реЗ рдЕрд╕рд▓реЗрд▓реЗ рджреЛрди рдорд╛рд░реНрдЧ:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: default-deny, namespace: school }
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: allow-dns, namespace: school }
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 }]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: postgres-from-results-api, namespace: school }
spec:
podSelector: { matchLabels: { app: postgres } }
policyTypes: [Ingress]
ingress:
- from: [{ podSelector: { matchLabels: { app: results-api } } }]
ports: [{ protocol: TCP, port: 5432 }]
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: results-api-to-postgres, namespace: school }
spec:
podSelector: { matchLabels: { app: results-api } }
policyTypes: [Egress]
egress:
- to: [{ podSelector: { matchLabels: { app: postgres } } }]
ports: [{ protocol: TCP, port: 5432 }]
CNI рддреА рд▓рд╛рдЧреВ рдХрд░рддреЛ рд╣реЗ рд╕рд┐рджреНрдз рдХрд░рд╛ тАФ рдЪреБрдХреАрдЪреЗ label рдЕрд╕рд▓реЗрд▓рд╛ рдПрдХ рддрд╛рддреНрдкреБрд░рддрд╛ pod fail рд╡реНрд╣рд╛рдпрд▓рд╛ рд╣рд╡рд╛, рдЖрдгрд┐ рдпреЛрдЧреНрдп label рдЕрд╕рд▓реЗрд▓рд╛ connect рд╡реНрд╣рд╛рдпрд▓рд╛ рд╣рд╡рд╛:
kubectl -n school run np-test --rm -it --restart=Never --image=busybox:1.36 \
--labels app=notices-api -- nc -zv -w 2 postgres 5432 # expect: timed out
kubectl -n school run np-test --rm -it --restart=Never --image=busybox:1.36 \
--labels app=results-api -- nc -zv -w 2 postgres 5432 # expect: open
Amazon VPC CNI рдЕрд╕рд▓реЗрд▓реНрдпрд╛ EKS рд╡рд░, add-on configuration рдордзреНрдпреЗ policy enforcement рдЪрд╛рд▓реВ рдХрд░рд╛
({"enableNetworkPolicy": "true"}), рдХрд┐рдВрд╡рд╛ Calico рдХрд┐рдВрд╡рд╛ Cilium рдЪрд╛рд▓рд╡рд╛.
Secrets рд╕рд╛рдареА encryption at rest тАФ рддреБрдореНрд╣реА рд╕реНрд╡рддрдГ рдЪрд╛рд▓рд╡рдд рдЕрд╕рд▓реЗрд▓реНрдпрд╛ cluster рд╡рд░,
--encryption-provider-config рдиреЗ API server рд▓рд╛ рджрд┐рд▓реЗрд▓реА EncryptionConfiguration:
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources: ["secrets"]
providers:
- kms:
apiVersion: v2
name: school-kms
endpoint: unix:///var/run/kmsplugin/socket.sock
timeout: 3s
- identity: {} # only so Secrets written before the change can still be read
рдордЧ рдкреНрд░рддреНрдпреЗрдХ Secret рдкреБрдиреНрд╣рд╛ рд▓рд┐рд╣рд╛, рдореНрд╣рдгрдЬреЗ рддреА encrypted рд╕реНрд╡рд░реВрдкрд╛рдд рд╕рд╛рдард╡рд▓реА рдЬрд╛рддреАрд▓:
kubectl get secrets --all-namespaces -o json | kubectl replace -f -
EKS рд╡рд░, Secrets рд╕рд╛рдареА рддреБрдордЪреА рд╕реНрд╡рддрдГрдЪреА KMS key рдирд┐рд╡рдбрд╛:
aws eks associate-encryption-config --cluster-name school \
--encryption-config '[{"resources":["secrets"],"provider":{"keyArn":"arn:aws:kms:ap-south-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab"}}]'
External Secrets Operator тАФ password AWS Secrets Manager рдордзреНрдпреЗ рд░рд╛рд╣рддреЛ; operator
Kubernetes Secret results-db рддрдпрд╛рд░ рдХрд░рддреЛ рдЖрдгрд┐ refresh рдХрд░рддреЛ:
apiVersion: external-secrets.io/v1
kind: SecretStore
metadata: { name: aws-secrets, namespace: school }
spec:
provider:
aws:
service: SecretsManager
region: ap-south-1
auth:
jwt:
serviceAccountRef: { name: external-secrets-reader }
---
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata: { name: results-db, namespace: school }
spec:
refreshInterval: 1h
secretStoreRef: { name: aws-secrets, kind: SecretStore }
target: { name: results-db }
data:
- secretKey: password
remoteRef: { key: school/results-db, property: password }
(рдЬреБрдиреНрдпрд╛ operator releases рдордзреНрдпреЗ external-secrets.io/v1beta1 рд╡рд╛рдкрд░рд▓реЗ рдЬрд╛рддреЗ; рддреБрдореНрд╣реА рдЪрд╛рд▓рд╡рдд рдЕрд╕рд▓реЗрд▓реЗ version рддрдкрд╛рд╕рд╛.)
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: namespace рдордзреНрдпреЗ
default-denypolicy рдирд╕реЗрд▓ рддреЗрд╡реНрд╣рд╛ fail рд╣реЛрдгрд╛рд░рд╛ CI check рдЬреЛрдбрд╛, рдЖрдгрд┐ Git рдордзреНрдпреЗdata:рд╕рд╣kind: Secretрд╕рд╛рдкрдбрд▓реНрдпрд╛рд╡рд░ fail рд╣реЛрдгрд╛рд░рд╛ secret scanner (рдзрдбрд╛ 07) рдЬреЛрдбрд╛. рджреЛрдиреНрд╣реА рдЪреБрдХрд╛ рдХрд░рд╛рдпрд▓рд╛ рд╕реЛрдкреНрдпрд╛ рдЖрд╣реЗрдд рдЖрдгрд┐ рдХреЛрдгреА рддреНрдпрд╛рдВрдЪрд╛ рд╡рд╛рдкрд░ рдХрд░реЗрдкрд░реНрдпрдВрдд рд╢рд╛рдВрдд рд░рд╛рд╣рддрд╛рдд.
тПня╕П рдкреБрдвреЗ
Pod рдХреЛрдг рддрдпрд╛рд░ рдХрд░реВ рд╢рдХрддреЛ рд╣реЗ RBAC рдард░рд╡рддреЗ. рддреНрдпрд╛рдЪреНрдпрд╛рдкрд░реНрдпрдВрдд рдХреЛрдг рдкреЛрд╣реЛрдЪреВ рд╢рдХрддреЛ рд╣реЗ NetworkPolicy рдард░рд╡рддреЗ. рдкрдг рдХреЛрдгрддреНрдпрд╛ рдкреНрд░рдХрд╛рд░рдЪрд╛ pod рдореБрд│рд╛рдд рдЪрд╛рд▓реВ рд╢рдХрддреЛ тАФ root рдореНрд╣рдгреВрди? privileged? host рдЪреА disk mount рдХрд░реВрди? рдкреБрдвреЗ: pod рдЪрд╛рд▓рдгреНрдпрд╛рдкреВрд░реНрд╡реАрдЪреЗ рджрд╛рд░.
git checkout lesson-12-admission