
Nota traducătorului.: Autorul articolului — Reuven Harrison — are peste 20 de ani de experiență în dezvoltarea de software și, în prezent, este director tehnic și cofondator al companiei Tufin, care creează soluții pentru gestionarea politicilor de securitate. Considerând politicile de rețea Kubernetes ca un instrument destul de puternic pentru segmentarea rețelei în cluster, el consideră totodată că acestea nu sunt atât de ușor de aplicat în practică. Acest material (destul de voluminos) are scopul de a îmbunătăți conștientizarea specialiștilor în acest domeniu și de a-i ajuta în crearea configurațiilor necesare.
Astăzi, multe companii aleg din ce în ce mai des Kubernetes pentru a-și rula aplicațiile. Interesul pentru acest software este atât de mare încât unii îl numesc „noua sistem de operare pentru centre de date”. Treptat, Kubernetes (sau k8s) începe să fie perceput ca o parte critică a afacerii, care necesită organizarea unor procese de afaceri mature, inclusiv asigurarea securității rețelei.
Pentru specialiștii în securitate, care se confruntă cu utilizarea Kubernetes, o adevărată descoperire ar putea fi politica de bază a acestei platforme: a permite totul.
Aceguid despre rețele va ajuta să înțelegeți structura internă a politicilor de rețea; să înțelegeți cum se deosebesc acestea de regulile pentru firewall-urile obișnuite. De asemenea, vor fi discutate câteva capcane și vor fi date recomandări care să ajute la protejarea aplicațiilor în Kubernetes.
Politicile de rețea Kubernetes
Mecanismul politicilor de rețea Kubernetes permite gestionarea interacțiunilor aplicațiilor desfășurate pe platformă la nivel de rețea (al treilea nivel în modelul OSI). Politicile de rețea nu beneficiază de unele funcții avansate ale firewall-urilor moderne, cum ar fi controlul la nivelul 7 OSI și detectarea amenințărilor, totuși, ele oferă un nivel de bază de securitate a rețelei, care reprezintă un punct de plecare decent.
Politicile de rețea controlează comunicațiile între pod-uri
Încărcările de lucru în Kubernetes sunt distribuite între pod-uri, care constau din unul sau mai multe containere desfășurate împreună. Kubernetes alocă fiecărui pod o adresă IP, disponibilă din alte pod-uri. Politicile de rețea Kubernetes stabilesc drepturi de acces pentru grupuri de pod-uri în același mod în care grupurile de securitate din cloud sunt utilizate pentru gestionarea accesului la instanțele mașinilor virtuale.
Definirea politicilor de rețea
Ca și celelalte resurse Kubernetes, politicile de rețea sunt definite în YAML. În exemplul de mai jos, aplicației balance i se oferă acces la postgres:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default.postgres
namespace: default
spec:
podSelector:
matchLabels:
app: postgres
ingress:
- from:
- podSelector:
matchLabels:
app: balance
policyTypes:
- Ingress 
(Nota traducătorului.: această captură de ecran, la fel ca toate cele similare care urmează, a fost creată nu cu instrumente native Kubernetes, ci cu ajutorul uneltelor Tufin Orca, dezvoltat de compania autorului articolului original, menționată la finalul materialului.
Pentru a defini propria politică de rețea, sunt necesare cunoștințe de bază în YAML. Acest limbaj se bazează pe indentare (care se realizează cu spații, nu cu tabulatoare). Un element cu indentare aparține celui mai apropiat element cu indentare deasupra sa. Un nou element de listă începe cu un cratimă, toate celelalte elemente având forma cheie-valoare.
După ce ați descris politica în YAML, utilizați , pentru a o crea în cluster:
kubectl create -f policy.yamlSpecificația politicii de rețea
Specificația politicii de rețea Kubernetes include patru elemente:
-
podSelector: definește pod-urile vizate de această politică (ținte) — obligatoriu; -
policyTypes: indică ce tipuri de politici sunt incluse aici: ingress și/sau egress — opțional, dar vă recomand să îl specificați clar în toate cazurile; -
ingress: definește traficul întrânc în pod-urile țintă — opțional; -
egress: definește traficul traficul din pod-urile țintă — opțional.
Un exemplu preluat de pe site-ul Kubernetes (am înlocuit role pe app), arată cum sunt utilizate toate cele patru elemente:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default
spec:
podSelector: # <<<<
matchLabels:
app: db
policyTypes: # <<<<
- Ingress
- Egress
ingress: # <<<<
- from:
- ipBlock:
cidr: 172.17.0.0/16
except:
- 172.17.1.0/24
- namespaceSelector:
matchLabels:
project: myproject
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 6379
egress: # <<<<
- to:
- ipBlock:
cidr: 10.0.0.0/24
ports:
- protocol: TCP
port: 5978 

Rețineți că nu este obligatorie includerea tuturor celor patru elemente. Numai podSelectorparametru este necesar.
Dacă omiteți policyTypes, politica va fi interpretată astfel:
- Implicit, se presupune că aceasta definește partea ingress. Dacă nu există indicații clare în politică, sistemul va considera că tot traficul este interzis.
- Comportamentul pe partea egress va fi determinat de prezența sau absența corespunzătorului parametru egress.
Pentru a evita erorile, vă recomand să indicați întotdeauna în mod explicit policyTypes.
Conform logicii prezentate mai sus, în cazul în care parametrii ingress și / sau egress sunt omisi, politica va interzice tot traficul (vezi „Regula de curățare” mai jos).
Politica implicită este de a permite
Dacă nu sunt definite politici, Kubernetes permite în mod implicit tot traficul. Toate pod-urile pot comunica liber între ele. Din punct de vedere al securității, acest lucru poate părea nelogic, dar amintiți-vă că Kubernetes a fost creat inițial de dezvoltatori pentru a facilita interacțiunea aplicațiilor. Politicile de rețea au fost adăugate ulterior.
Spațiile de nume
Spațiile de nume (Namespaces) sunt un mecanism de colaborare în Kubernetes. Ele sunt destinate izolării mediilor logice unul de celălalt, în timp ce schimbul de date între spații este permis în mod implicit.
Ca majoritatea componentelor Kubernetes, politicile de rețea trăiesc într-un anumit spațiu de nume. În blocul metadata puteți specifica exact cui aparține politica:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: my-namespace # <<<<
spec:
... Dacă spațiul de nume din metadate nu este specificat explicit, sistemul va folosi namespace-ul specificat în kubectl (implicit namespace=default):
kubectl apply -n my-namespace -f namespace.yamlVă recomand să specificați explicit namespace-ul, decât dacă elaborați o politică destinată mai multor spații de nume.
Principal element podSelector în politică va alege pod-urile din spațiul de nume căruia îi aparține politica (el nu are acces la pod-urile din alt spațiu de nume).
În mod similar, selecționerii de pod-uri în blocurile ingress și egress pot alege doar pod-uri din propriul spațiu de nume, cu excepția cazului în care nu le combinați folosind namespaceSelector (despre care vom discuta în secțiunea „Filtrare după spații de nume și pod-uri”).
Reguli de denumire a politicilor
Numele politicilor sunt unice într-un singur spațiu de nume. Nu pot exista două politici cu același nume într-un singur spațiu, dar pot exista politici cu aceleași denumiri în spații diferite. Acest lucru este convenabil când doriți să aplicați aceeași politică pe mai multe spații.
Îmi place în mod special unul dintre modurile de denumire. Acesta constă în combinarea numelui spațiului de nume cu pod-urile țintă. De exemplu:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default.postgres # <<<
namespace: default
spec:
podSelector:
matchLabels:
app: postgres
ingress:
- from:
- podSelector:
matchLabels:
app: admin
policyTypes:
- Ingress 
Etichete
La obiectele Kubernetes, cum ar fi pod-urile și spațiile de nume, pot fi atașate etichete personalizate. Etichetele (labels — sunt echivalentul etichetelor din cloud. Politicile de rețea Kubernetes folosesc etichetele pentru a selecta pod-urile, la care se aplică:
podSelector:
matchLabels:
role: db… sau spații de nume, la care se aplică. În acest exemplu, sunt selectate toate pod-urile din spațiile de nume cu etichetele corespunzătoare:
namespaceSelector:
matchLabels:
project: myproject O singură avertizare: atunci când utilizați namespaceSelector asigurați-vă că spațiile de nume selectate conțin eticheta necesară. Rețineți că spațiile de nume încorporate, cum ar fi default și kube-system, nu conțin etichete în mod default.
Pentru a adăuga o etichetă unui spațiu de nume, faceți următoarele:
kubectl label namespace default namespace=default Aici, namespace-ul din secțiune metadata trebuie să facă referire la numele efectiv al spațiului, nu la etichetă:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default # <<<
spec:
...Sursă și destinatar
Politicile pentru firewall-uri constau din reguli cu surse și destinatari. Politicile de rețea Kubernetes sunt definite pentru scopul — un set de pod-uri, la care se aplică, și stabilesc apoi reguli pentru traficul de intrare (ingress) și/sau de ieșire (egress). În exemplul nostru, scopul politicii va fi toate pod-urile din spațiul de nume default cu eticheta cu cheia app și valoarea db:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default
spec:
podSelector:
matchLabels:
app: db # <<<<
policyTypes:
- Ingress
- Egress
ingress:
- from:
- ipBlock:
cidr: 172.17.0.0/16
except:
- 172.17.1.0/24
- namespaceSelector:
matchLabels:
project: myproject
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 6379
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/24
ports:
- protocol: TCP
port: 5978 

Subsecțiune ingress în această politică deschide traficul de intrare către pod-urile țintă. Cu alte cuvinte, ingress acționează ca sursă, iar ținta este destinatarul corespunzător. Similar, egress este destinatarul, iar ținta este sursa acestuia.

Aceasta este echivalentă cu două reguli pentru firewall: Ingress → Țintă; Țintă → Egress.
Egress și DNS (important!)
Limitând traficul de ieșire, prestați o atenție specială asupra DNS-ului — Kubernetes folosește acest serviciu pentru a maparea serviciilor cu adrese IP. De exemplu, următoarea politică nu va funcționa deoarece nu ați permis aplicației balance să acceseze DNS-ul:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default.balance
namespace: default
spec:
podSelector:
matchLabels:
app: balance
egress:
- to:
- podSelector:
matchLabels:
app: postgres
policyTypes:
- Egress 
O puteți corecta deschizând accesul la serviciul DNS:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default.balance
namespace: default
spec:
podSelector:
matchLabels:
app: balance
egress:
- to:
- podSelector:
matchLabels:
app: postgres
- to: # <<<<
ports: # <<<<
- protocol: UDP # <<<<
port: 53 # <<<<
policyTypes:
- Egress 
Ultimul element to — gol și, prin urmare, selectează indirect toate pod-urile din toate spațiile de nume, permițând balance trimiterea cererilor DNS către serviciul corespunzător Kubernetes (care funcționează de obicei în spațiul kube-system).
Această abordare funcționează, totuși este excesiv permisivă și nesigură, deoarece permite direcționarea cererilor DNS în afara clusterei.
Puteți să o îmbunătățiți prin trei pași succesivi.
1. Permiteți cererile DNS doar inside clusterei, adăugând namespaceSelector:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default.balance
namespace: default
spec:
podSelector:
matchLabels:
app: balance
egress:
- to:
- podSelector:
matchLabels:
app: postgres
- to:
- namespaceSelector: {} # <<<
ports:
- protocol: UDP
port: 53
policyTypes:
- Egress 
2. Permiteți cererile DNS doar în spațiul de nume kube-system.
Pentru aceasta, trebuie adăugat un eticheta în spațiul de nume kube-system: kubectl label namespace kube-system namespace=kube-system — și specificați-l în politică folosind namespaceSelector:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default.balance
namespace: default
spec:
podSelector:
matchLabels:
app: balance
egress:
- to:
- podSelector:
matchLabels:
app: postgres
- to:
- namespaceSelector: # <<<
matchLabels: # <<<
namespace: kube-system # <<<
ports:
- protocol: UDP
port: 53
policyTypes:
- Egress 
3. Paranoicii pot merge și mai departe și pot restricționa cererile DNS la un anumit serviciu DNS în kube-system. În secțiunea „Filtrare după spații de nume și poduri” se va explica cum se poate realiza acest lucru.
O altă variantă este să permiteți DNS la nivel de spațiu de nume. În acest caz, nu va trebui să fie deschis pentru fiecare serviciu:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default.dns
namespace: default
spec:
podSelector: {} # <<<
egress:
- to:
- namespaceSelector: {}
ports:
- protocol: UDP
port: 53
policyTypes:
- Egress Gol podSelector alege toate pod-urile din spațiul de nume.

Întâlnirea inițială și ordinea regulilor
În firewall-urile obișnuite, acțiunea („Permite” sau „Blochează”) în legătură cu un pachet este determinată de prima regulă care i se aplică. În Kubernetes, ordinea politicilor nu are nicio importanță.
În mod implicit, atunci când politicile nu sunt setate, comunicațiile între pod-uri sunt permise și acestea pot schimba liber informații. Odată ce începeți să formulați politici, fiecare pod afectat de oricare dintre ele devine izolat conform disjuncției (OR logic) tuturor politicilor care l-au selectat. Pod-urile care nu sunt afectate de vreo politică rămân deschise.
Această comportare poate fi modificată folosind o regulă de curățare.
Regula de curățare („Blochează”)
Politicile firewall-urilor blochează de obicei orice trafic care nu este explicit permis.
În Kubernetes, nu există o acțiune „a bloca” (deny),dar un efect similar poate fi obținut cu o politică standard (care permite), alegând un grup gol de pod-uri sursă (ingress):
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
namespace: default
spec:
podSelector: {}
policyTypes:
- Ingress 
Această politică selectează toate pod-urile din spațiul de nume și lasă ingress nedefinit, interzicând tot traficul de intrare.
În mod similar, se poate restricționa tot traficul de ieșire din spațiul de nume:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-egress
namespace: default
spec:
podSelector: {}
policyTypes:
- Egress 
Rețineți că orice politici suplimentare care permit traficul către pod-urile din spațiul de nume vor avea prioritate asupra acestei reguli (similar cu adăugarea unei reguli de permisiune înaintea unei reguli de interdicție în configurația unui firewall).
Permite tot (Any-Any-Any-Allow)
Pentru a crea o politică "Permite tot", trebuie să completați politica de interdicție de mai sus cu un element gol ingress:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all
namespace: default
spec:
podSelector: {}
ingress: # <<<
- {} # <<<
policyTypes:
- Ingress 
Aceasta deschide accesul de la toate pod-urile din toate spațiile de nume (și toate IP-urile) către orice pod din spațiul de nume default. Un asemenea comportament este activat implicit, așa că, în general, nu este necesară definirea sa suplimentar. Cu toate acestea, uneori poate fi necesar să dezactivați temporar anumite permisiuni specifice pentru diagnosticarea problemei.
Regula poate fi restrânsă și poate permite accesul doar la un set specific de pod-uri (app:balance) din spațiul de nume default:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-to-balance
namespace: default
spec:
podSelector:
matchLabels:
app: balance
ingress:
- {}
policyTypes:
- Ingress 
Următoarea politică permite tot traficul de intrare (ingress) și ieșire (egress), inclusiv accesul către orice IP din afara cluster-ului:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all
spec:
podSelector: {}
ingress:
- {}
egress:
- {}
policyTypes:
- Ingress
- Egress 

Combinarea mai multor politici
Politicile se combină prin intermediul unei logici OR la trei niveluri; permisiunile fiecărui pod sunt stabilite conform disjuncției tuturor politicilor care îl afectează:
1. În câmpurile from și to se pot defini trei tipuri de elemente (toate acestea sunt combinate prin OR):
-
namespaceSelector— selectează întregul spațiu de nume; -
podSelector— selectează pod-urile; -
ipBlock— selectează o subrețea.
În această situație, numărul de elemente (chiar și identice) în subdiviziuni from/to nu este limitat. Toate acestea vor fi combinate printr-o logică OR.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default.postgres
namespace: default
spec:
ingress:
- from:
- podSelector:
matchLabels:
app: indexer
- podSelector:
matchLabels:
app: admin
podSelector:
matchLabels:
app: postgres
policyTypes:
- Ingress 
2. În cadrul politicii, secțiunea ingress poate avea mai multe elemente from (se unesc logic prin OR). În mod similar, secțiunea egress poate include mai multe elemente to (de asemenea se unesc prin disjuncție):
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default.postgres
namespace: default
spec:
ingress:
- from:
- podSelector:
matchLabels:
app: indexer
- from:
- podSelector:
matchLabels:
app: admin
podSelector:
matchLabels:
app: postgres
policyTypes:
- Ingress 
3. Politicile diferite sunt, de asemenea, unite logic prin OR
Dar când sunt unite, există o restricție, la care : Kubernetes poate combina politicile doar cu diferite policyTypes (Ingress sau Egress). Politicile care definesc ingress (sau egress) vor suprascrie una pe alta.
Legătura între spațiile de nume
În mod implicit, schimbul de informații între spațiile de nume este permis. Acest lucru poate fi modificat printr-o politică restrictivă, care va limita traficul de ieșire și/sau de intrare în spațiul de nume (vezi "Regula de curățare" de mai sus).
Blocând accesul într-un spațiu de nume (vezi "Regula de curățare" de mai sus), poți face excepții în politica restrictivă, permitând conexiuni dintr-un anumit spațiu de nume prin namespaceSelector:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: database.postgres
namespace: database
spec:
podSelector:
matchLabels:
app: postgres
ingress:
- from:
- namespaceSelector: # <<<
matchLabels:
namespace: default
policyTypes:
- Ingress 
Ca rezultat, toate pod-urile din spațiul de nume default vor avea acces la pod-urile postgres în spațiul de nume database. Dar ce se întâmplă dacă vrei să deschizi accesul doar la anumite pod-uri din spațiul de nume postgres Filtrarea după spațiile de nume și pod-uri default?
Kubernetes versiunea 1.11 și mai sus permite combinarea operatorilor
prin intermediul logicii AND. Iată cum arată: namespaceSelector și podSelector apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: database.postgres namespace: database spec: podSelector: matchLabels: app: postgres ingress: - from: - namespaceSelector: matchLabels: namespace: default podSelector: # <<< matchLabels: app: admin policyTypes: - Ingress
De ce este interpretat ca AND în loc de obișnuitul OR? 
nu începe cu o linie de defalcare. În YAML, aceasta înseamnă că
Rețineți că podSelector și cel care o precede podSelector se referă la același element din listă. Prin urmare, ele sunt unite logic prin AND. namespaceSelector Adăugarea unei linii de defalcare în fața
Adăugarea unei liniuțe înainte de podSelector va duce la crearea unui nou element de listă, care se va combina cu cel anterior namespaceSelector folosind operatorul logic OR.
Pentru a selecta pod-urile cu un anumit etichete în toate spațiile de nume, introduceți un vid namespaceSelector:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: database.postgres
namespace: database
spec:
podSelector:
matchLabels:
app: postgres
ingress:
- from:
- namespaceSelector: {}
podSelector:
matchLabels:
app: admin
policyTypes:
- Ingress 
Etichetele multiple sunt combinate cu AND
Regulile pentru firewall-uri cu obiecte multiple (gazde, rețele, grupuri) sunt combinate folosind operatorul logic OR. Următoarea regulă va fi aplicată dacă sursa pachetului se potrivește cu Host_1 SAU Host_2:
| Source | Destination | Service | Action |
| ----------------------------------------|
| Host_1 | Subnet_A | HTTPS | Allow |
| Host_2 | | | |
| ----------------------------------------| În schimb, în Kubernetes etichetele diferite din podSelector sau namespaceSelector sunt combinate cu AND. De exemplu, următoarea regulă va selecta pod-urile care au ambele etichete, role=db Și version=v2:
podSelector:
matchLabels:
role: db
version: v2Aceeași logică se aplică tuturor tipurilor de operatori: selecționerilor de ținte ale politicii, selecționerilor de pod-uri și selecționerilor de spații de nume.
Subrețele și adrese IP (IPBlocks)
Pentru segmentarea rețelei, firewall-urile utilizează VLAN-uri, adrese IP și subrețele.
În Kubernetes, adresele IP sunt atribuite automat pod-urilor și se pot schimba frecvent, de aceea pentru a selecta pod-urile și spațiile de nume în politicile de rețea se folosesc etichete.
Subrețele (ipBlocks) sunt utilizate în gestionarea conexiunilor externe (North-South) de intrare (ingress) sau ieșire (egress). De exemplu, această politică oferă tuturor pod-urilor din spațiul de nume default acces la serviciul DNS Google:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: egress-dns
namespace: default
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 8.8.8.8/32
ports:
- protocol: UDP
port: 53 
Selectorul gol de pod-uri în acest exemplu înseamnă „selectați toate pod-urile din spațiul de nume”.
Această politică oferă acces doar la 8.8.8.8; accesul la orice altă adresă IP este interzis. Așadar, în esență, ați blocat accesul la serviciul intern DNS Kubernetes. Dacă doriți totuși să-l deschideți, specificați acest lucru explicit.
De obicei ipBlocks și podSelectors sunt excluderi, deoarece adresele IP interne ale pod-urilor nu sunt utilizate în ipBlocks. Specificând adresele IP interne ale pod-urilor, veți permite de fapt conexiunile către/și de la poduri cu aceste adrese. În practică, nu veți ști ce adresă IP să utilizați, de aceea nu ar trebui să le folosiți pentru a alege poduri.
Ca un contraexemplu, următoarea politică include toate IP-urile și, prin urmare, permite accesul la toate celelalte poduri:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: egress-any
namespace: default
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0 
Se poate deschide accesul doar la IP-uri externe, excludând adresele IP interne ale podurilor. De exemplu, dacă subnetul podului dvs. este 10.16.0.0/14:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: egress-any
namespace: default
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 10.16.0.0/14 
Porturi și protocoale
De obicei, podurile ascultă un singur port. Aceasta înseamnă că nu trebuie să specificați numerele porturilor în politici și să lăsați totul la setările implicite. Totuși, se recomandă ca politicile să fie cât mai restrictive, așa că, în unele cazuri, este posibil să specificați totuși porturile:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default.postgres
namespace: default
spec:
ingress:
- from:
- podSelector:
matchLabels:
app: indexer
- podSelector:
matchLabels:
app: admin
ports: # <<<
- port: 443 # <<<
protocol: TCP # <<<
- port: 80 # <<<
protocol: TCP # <<<
podSelector:
matchLabels:
app: postgres
policyTypes:
- Ingress 
Observați că selectorul porturi se aplică la toate elementele din blocul to sau from, care îl conține. Pentru a specifica porturi diferite pentru diferite seturi de elemente, împărțiți ingress sau egress în mai multe subsecțiuni cu to sau from și în fiecare specificați porturile dvs.:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default.postgres
namespace: default
spec:
ingress:
- from:
- podSelector:
matchLabels:
app: indexer
ports: # <<<
- port: 443 # <<<
protocol: TCP # <<<
- from:
- podSelector:
matchLabels:
app: admin
ports: # <<<
- port: 80 # <<<
protocol: TCP # <<<
podSelector:
matchLabels:
app: postgres
policyTypes:
- Ingress 
Funcționarea porturilor implicite:
- Dacă omiteți complet definiția porturilor (
porturi), aceasta înseamnă toate protocoalele și toate porturile; - Dacă omiteți definiția protocolului (
protocol), aceasta înseamnă TCP; - Dacă omiteți definiția portului (
port), aceasta înseamnă toate porturile.
Cele mai bune practici: nu vă bazați pe valorile implicite, specificați clar ce aveți nevoie.
Vă rugăm să rețineți că este necesar să utilizați porturile pod-urilor, nu ale serviciilor (mai multe detalii în următorul paragraf).
Politicile sunt definite pentru pod-uri sau servicii?
De obicei, pod-urile din Kubernetes comunică între ele prin intermediul serviciului — un echilibrator de încărcare virtual, care redirecționează traficul către pod-urile care implementează serviciul. S-ar putea crede că politicile de rețea controlează accesul la servicii, dar nu este așa. Politicile de rețea Kubernetes funcționează cu porturile pod-urilor, nu cu cele ale serviciilor.
De exemplu, dacă un serviciu ascultă pe portul 80, dar redirecționează traficul către portul 8080 al pod-urilor sale, politica de rețea trebuie să specifice exact 8080.
Un astfel de mecanism trebuie considerat neoptimal: la schimbarea structurii interne a serviciului (porturile cărora le ascultă pod-urile), va fi necesar să actualizați politicile de rețea.
O nouă abordare arhitecturală folosind Service Mesh (de exemplu, consultați Istio mai jos — n. trad.) permite abordarea acestei probleme.
Este necesar să se definească atât Ingress, cât și Egress?
Răspunsul scurt este da, pentru ca pod-ul A să se poată conecta la pod-ul B, este necesar să i se permită să stabilească o conexiune de ieșire (pentru aceasta, trebuie configurată politica egress), iar pod-ul B trebuie să fie capabil să primească o conexiune de intrare (prin urmare, este necesară o politică ingress).
Cu toate acestea, în practică, se poate conta pe politica implicită, care permite conexiuni într-o direcție sau în ambele.
Dacă un pod-sursa este selectat de una sau mai multe egress-politici, restricțiile impuse vor fi definite de disjuncția acestora. În acest caz, va fi necesar să se permită explicit conectarea la pod-ul-destinat. Dacă pod-ul nu este selectat de nicio politică, traficul său de ieșire (egress) este permis în mod implicit.
În mod similar, soarta pod-ului-destinat, selectat de una sau mai multe ingress-politici, va fi definită de disjuncția acestora. În acest caz, trebuie permis explicit să primească trafic de la pod-ul-sursă. Dacă pod-ul nu este selectat de nicio politică, întregul trafic de intrare (ingress) pentru el este permis în mod implicit.
Consultați secțiunea „Stateful sau Stateless” mai jos.
Jurnale
Politicile de rețea Kubernetes nu pot înregistra traficul. Acest lucru îngreunează determinarea dacă o politică funcționează corect și complică foarte mult analiza în domeniul securității.
Controlul traficului către servicii externe
Politicile de rețea Kubernetes nu permit specificarea unui nume de domeniu complet (DNS) în secțiunile egress. Acest lucru conduce la dificultăți semnificative atunci când încercați să restricționați traficul către destinații externe care nu au o adresă IP fixă (precum aws.com).
Verificarea politicii
Firewall-urile vă vor avertiza sau chiar vor refuza să accepte o politică incorectă. Kubernetes efectuează, de asemenea, unele verificări. Atunci când setați o politică de rețea prin kubectl, Kubernetes poate afirma că aceasta este incorectă și se va refuza să o accepte. În alte cazuri, Kubernetes va accepta politica și va completa detaliile lipsă. Acestea pot fi vizualizate cu comanda:
kubernetes get networkpolicy -o yamlRețineți că sistemul de verificare Kubernetes nu este infailibil și poate trece cu vederea anumite tipuri de erori.
Executare
Kubernetes nu implementează politicile de rețea de unul singur, ci funcționează doar ca un gateway API, lăsând sarcina de control pe o infrastructură subiacenta numită Interface de Rețea a Containerelor (CNI). Setați politicile în clusterul Kubernetes fără a desemna un CNI corespunzător este similar cu a crea politici pe serverul de management al firewall-ului fără a le instala ulterior în firewall-uri. Trebuie să vă asigurați că aveți un CNI adecvat sau, în cazul platformelor Kubernetes gazduite în cloud, (lista furnizorilor poate fi consultată — n.r.), activând politicile de rețea, care vor seta CNI pentru dumneavoastră.
Rețineți că Kubernetes nu vă va avertiza dacă setați o politică de rețea fără un CNI auxiliar corespunzător.
Stateful sau Stateless?
Toate CNI Kubernetes cu care m-am întâlnit păstrează starea (de exemplu, Calico utilizează conntrack-ul Linux). Aceasta permite pod-ului să primească răspunsuri pentru conexiunea TCP pe care a inițiat-o, fără a fi nevoie să o stabilească din nou. Cu toate acestea, nu cunosc un standard Kubernetes care să garanteze păstrarea stării (statefulness).
Gestionarea avansată a politicilor de securitate
Iată câteva modalități de a spori eficiența implementării politicii de securitate în Kubernetes:
- Modelul arhitectural Service Mesh utilizează containere sidecar pentru a oferi telemetrie detaliată și control al traficului la nivel de servicii. Un exemplu poate fi luat din .
- Unii dintre furnizorii CNI și-au extins instrumentele astfel încât acestea să depășească politicile de rețea Kubernetes.
- asigură transparența și automatizarea politicilor de rețea Kubernetes.
Pachetul Tufin Orca gestionează politicile de rețea Kubernetes (și servește ca sursă pentru capturile de ecran menționate mai sus).
Informații suplimentare
- ;
- ;
- ;
- .
Concluzie
Politicile de rețea Kubernetes oferă un set decent de instrumente pentru segmentarea clustere, totuși, acestea sunt contraintuitive și au multe subtilități. Cred că din cauza acestei complexități, politicile multor clustere existente conțin erori. Posibile soluții pentru această problemă includ automatizarea definițiilor de politică sau utilizarea altor instrumente de segmentare.
Sper că acest ghid va ajuta la clarificarea unor întrebări și la rezolvarea problemelor cu care s-ar putea să te confrunți.
P.S. de la traducător
Citiți și în blogul nostru:
- „Înapoi la microservicii împreună cu Istio”: , , ;
- „Ghidul ilustrat pentru configurarea rețelei în Kubernetes”: , ;
- «»;
- «»;
- «».
Sursa: habr.com
