
Cílem článku je seznámit čtenáře se základy síťové interakce a řízení síťových politik v Kubernetes, stejně jako se třetím pluginem Calico, který rozšiřuje standardní možnosti. Současně budou demonstrovány pohodlí konfigurace a některé funkce na reálných příkladech z našich zkušeností s provozem.
Rychlý úvod do síťového zařízení Kubernetes
Klastrové Kubernetes si nelze představit bez sítě. Již jsme publikovali materiály o jejich základech: „“ a „».
V kontextu tohoto článku je důležité poznamenat, že za síťovou konektivitu mezi kontejnery a uzly odpovídá ne samotný K8s: k tomu se používají různé CNI pluginy (Container Networking Interface). O této koncepci jsme .
Například, nejběžnější z těchto pluginů — — zajišťuje plnou síťovou konektivitu mezi všemi uzly klastru prostřednictvím zvedání mostů na každém uzlu, přiřazující mu podsíť. Nicméně úplná a neomezená dostupnost není vždy užitečná. Aby byla zajištěna nějaká minimální izolace v klastru, je nutné zasáhnout do konfigurace firewallu. Obecně je řízení svěřeno právě tomuto CNI, což znamená, že jakékoli zásahy do iptables mohou být interpretovány nesprávně nebo ignorovány.
A „z krabice“ pro organizaci řízení síťových politik v klastru Kubernetes je poskytováno . Tento zdroj, který se vztahuje na vybrané prostory názvů, může obsahovat pravidla pro omezení přístupu mezi různými aplikacemi. Také umožňuje konfigurovat dostupnost mezi konkrétními pod a prostředími (prostory názvů) nebo bloky IP adres:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default
spec:
podSelector:
matchLabels:
role: 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: 5978Tento není zcela primitivní příklad z Může jednou provždy vzít chuť rozumět logice fungování síťových politik. Nicméně se pokusíme pochopit základní principy a metody zpracování toků provozu pomocí síťových politik…
Logicky existují 2 typy provozu: příchozí do podu (Ingress) a odchozí z něj (Egress).

Tyto 2 kategorie podle směru pohybu také tvoří politiku.
Dalším povinným atributem je selektor; ten, na koho se pravidlo vztahuje. Může to být pod (nebo skupina podů) nebo prostředí (tj. jmenný prostor). Důležitý detail: oba typy těchto objektů musí mít štítek (label v terminologii Kubernetes) — s nimiž politiky operují.
Kromě konečného počtu selektorů spojených nějakým štítkem existuje možnost napsat pravidla jako „Povolit/zakázat všechno/všem“ v různých variantách. K tomu se používají konstrukce jako:
podSelector: {}
ingress: []
policyTypes:
- Ingress— v tomto příkladu je všem podům prostředí blokován příchozí provoz. Opačného chování lze dosáhnout takovou konstrukcí:
podSelector: {}
ingress:
- {}
policyTypes:
- IngressPodobně pro odchozí:
podSelector: {}
policyTypes:
- Egress— pro jeho vypnutí. A co pro zapnutí:
podSelector: {}
egress:
- {}
policyTypes:
- EgressPokud se vrátíme k výběru CNI pluginu pro cluster, stojí za zmínku, že ne každý síťový plugin podporuje práci se NetworkPolicy.Například již zmíněný Flannel neumí konfigurovat síťové politiky, o čemž v oficiálním repozitáři. Tam je také zmíněna alternativa — Open Source projekt , který výrazně rozšiřuje standardní sadu API Kubernetes v oblasti síťových politik.

Seznamme se s Calico: teorie
Plugin Calico může být použit v integraci s Flannel (podprojekt ) nebo samostatně, pokrývající jak funkce zajištění síťové konektivity, tak možnosti řízení dostupnosti.
Jaké možnosti nabízí použití 'krabicového' řešení K8s a sady API z Calico?
Zde je, co je zabudováno do NetworkPolicy:
- politiky jsou omezeny na prostředí;
- politiky se aplikují na pody označené štítky;
- pravidla lze aplikovat na pody, prostředí nebo podsítě;
- pravidla mohou obsahovat protokoly, pojmenované nebo symbolické odkazy na porty.
A tady je, jak Calico tyto funkce rozšiřuje:
- Politiky mohou být aplikovány na jakýkoli objekt: pod, kontejner, virtuální stroj nebo rozhraní;
- Pravidla mohou obsahovat konkrétní akci (zákaz, povolení, logování);
- Jako cíl nebo zdroj pravidel může být port, rozsah portů, protokoly, HTTP- nebo ICMP-atributy, IP nebo podsíť (4. nebo 6. generace), jakékoli selektory (uzlů, hostitelů, prostředí);
- Dále lze upravit průchodnost provozu pomocí nastavení DNAT a politik pro přesměrování provozu.
První commity na GitHubu v repozitáři Calico se datují k červenci 2016, a už o rok později projekt zaujmul přední místo v organizaci síťové konektivity Kubernetes — o tom svědčí například výsledky průzkumu, :

Mnoho velkých managed řešení s K8s, jako například , , a další, začalo doporučovat jeho použití.
Co se týče výkonu, tady to vypadá skvěle. Při testování svého produktu tým vývoje Calico demonstroval astronomické výsledky, když spustil více než 50 000 kontejnerů na 500 fyzických uzlech s rychlostí vytváření 20 kontejnerů za sekundu. Problémy při škálování nebyly zaznamenány. Takové výsledky už při vyhlášení první verze. Nezávislé studie zaměřené na propustnost a objemy spotřeby zdrojů také potvrzují výkon Calico, který je prakticky neporovnáván s Flannel. :

Projekt se velmi rychle vyvíjí, podporuje práci v populárních managed řešeních K8s, OpenShift, OpenStack, a je možné použít Calico při nasazení clusteru pomocí , vyskytují se zmínky o stavbě Service Mesh sítí ( použití společně s Istio).
Praxe s Calico
V běžném případě použití vanilkového Kubernetes se instalace CNI redukuje na použití souboru calico.yaml, , pomocí kubectl apply -f.
Obvykle je aktuální verze pluginu kompatibilní s 2-3 nejnovějšími verzemi Kubernetes: práce ve starších verzích není testována a není garantována. Podle prohlášení vývojářů Calico funguje na jádře Linux nad 3.10 běžícím na CentOS 7, Ubuntu 16 nebo Debian 8, nad iptables nebo IPVS.
Izolace uvnitř prostředí
Pro lepší pochopení si představme jednoduchý případ, abychom pochopili, čím se liší síťové politiky v notaci Calico od standardních a jak přístup k sestavování pravidel usnadňuje jejich čitelnost a flexibilitu konfigurace:

V klastru jsou nasazeny 2 webové aplikace: na Node.js a PHP, přičemž jedna z nich používá Redis. Abychom zamezili přístupu k Redis z PHP, zatímco zachováme konektivitu s Node.js, stačí použít následující politiku:
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: allow-redis-nodejs
spec:
podSelector:
matchLabels:
service: redis
ingress:
- from:
- podSelector:
matchLabels:
service: nodejs
ports:
- protocol: TCP
port: 6379V podstatě jsme povolili příchozí provoz na port Redis z Node.js. A jasně jsme nezakázali nic jiného. Jakmile se objeví NetworkPolicy, všechny selektory, které jsou v něm zmíněny, začínají být izolovány, pokud není uvedeno jinak. Přičemž pravidla izolace se nevztahují na jiné objekty, které nejsou pokryty selektorem.
V příkladu se používá apiVersion Kubernetesu "out of the box", ale nic nebrání použití . Syntaxe je tam více rozvinutá, takže bude nutné přepsat pravidlo pro výše uvedený případ následujícím způsobem:
apiVersion: crd.projectcalico.org/v1
kind: NetworkPolicy
metadata:
name: allow-redis-nodejs
spec:
selector: service == 'redis'
ingress:
- action: Allow
protocol: TCP
source:
selector: service == 'nodejs'
destination:
ports:
- 6379 Výše uvedené konstrukce pro povolení nebo zakázání veškerého provozu prostřednictvím běžného NetworkPolicy API obsahují složité konstrukce se závorkami, které se těžko chápou a pamatují. V případě Calico, abychom změnili logiku fungování pravidla firewallu na opačnou, stačí změnit action: Allow na action: Deny.
Izolace podle prostředí
Nyní si představme situaci, kdy aplikace generuje obchodní metriky pro jejich shromažďování v Prometheus a další analýzu prostřednictvím Grafana. Ve výstupu mohou být citlivé údaje, které jsou z výchozího nastavení opět dostupné pro veřejné zobrazení. Zamezíme těmto údajům přístup zvenčí:

Prometheus je obvykle umístěn v samostatném servisním prostředí — v příkladu to bude namespace následujícího typu:
apiVersion: v1
kind: Namespace
metadata:
labels:
module: prometheus
name: kube-prometheus Pole metadata.labels se zde ukázalo jako nezbytné. Jak bylo již dříve zmíněno, namespaceSelector (jako i podSelector) operuje s labely. Proto, aby bylo možné získávat metriky ze všech podů na určitém portu, bude nutné přidat nějakou značku (nebo použít stávající), a poté aplikovat konfiguraci, jako je:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-metrics-prom
spec:
podSelector: {}
ingress:
- from:
- namespaceSelector:
matchLabels:
module: prometheus
ports:
- protocol: TCP
port: 9100A v případě použití politik Calico bude syntaxe následující:
apiVersion: crd.projectcalico.org/v1
kind: NetworkPolicy
metadata:
name: allow-metrics-prom
spec:
ingress:
- action: Allow
protocol: TCP
source:
namespaceSelector: module == 'prometheus'
destination:
ports:
- 9100Obecně platí, že přidáváním podobných politik podle konkrétních potřeb lze ochránit před zlomyslným nebo náhodným zásahem do činnosti aplikací v klastru.
Nejlepší praxí, podle tvůrců Calico, je přístup 'Zakažte vše a zřetelně otevřete potřebné', který je zdokumentován v (podobného přístupu se drží i ostatní – konkrétně v ).
Použití dalších objektů Calico
Připomínám, že prostřednictvím rozšířeného API Calico lze regulovat dostupnost uzlů, aniž by byla omezena pouze na pody. V následujícím příkladu pomocí GlobalNetworkPolicy se uzavírá možnost průchodu ICMP dotazů v klastru (například pingi z podu na uzel, mezi pody nebo z uzlu na IP podu):
apiVersion: crd.projectcalico.org/v1
kind: GlobalNetworkPolicy
metadata:
name: block-icmp
spec:
order: 200
selector: all()
types:
- Ingress
- Egress
ingress:
- action: Deny
protocol: ICMP
egress:
- action: Deny
protocol: ICMP V uvedeném případě zůstává možnost uzlům klastru 'dostoupit' si mezi sebou přes ICMP. A tento problém se řeší prostředky GlobalNetworkPolicy, aplikovanými na entitu HostEndpoint:
apiVersion: crd.projectcalico.org/v1
kind: GlobalNetworkPolicy
metadata:
name: deny-icmp-kube-02
spec:
selector: "role == 'k8s-node'"
order: 0
ingress:
- action: Allow
protocol: ICMP
egress:
- action: Allow
protocol: ICMP
---
apiVersion: crd.projectcalico.org/v1
kind: HostEndpoint
metadata:
name: kube-02-eth0
labels:
role: k8s-node
spec:
interfaceName: eth0
node: kube-02
expectedIPs: ["192.168.2.2"]Případ s VPN
Nakonec přináším zcela reálný příklad použití funkcí Calico pro případ okoloklasterní interakce, kdy standardní sadu politik nestačí. Pro přístup k webové aplikaci klienty se používá VPN tunel, a tento přístup je přísně kontrolován a omezen konkrétním seznamem povolených služeb:

Klienti se připojují k VPN přes standardní UDP port 1194 a při připojení získávají trasy k podsítem a službám clusteru. Podsíť se přenáší celá, aby se při restartování a změnách adres neztratily služby.
Port v konfiguraci je standardní, což klade určité požadavky na proces konfigurace aplikace a její přesun do Kubernetes clusteru. Například v AWS LoadBalancer byl UDP přidán doslova koncem loňského roku v omezeném počtu regionů a NodePort nelze použít kvůli jeho směrování na všech uzlech clusteru, což znemožňuje škálování počtu instancí serveru za účelem zajištění odolnosti proti selhání. Navíc bude nutné změnit rozsah portů, který je vybírán automaticky...
Na základě zvažovaných řešení bylo vybráno následující:
- Pod’y s VPN jsou plánovány na uzel v režimu
hostNetwork, tedy na skutečné IP. - Služba je vystavena ven přes
ClusterIP. Na uzlu se fyzicky aktivuje port, který je dostupný zvenčí za určitých podmínek (například pokud je k dispozici skutečná IP adresa). - Určení uzlu, na kterém se pod aktivoval, leží za hranicemi našeho vyprávění. Řeknu pouze, že je možné pevně „připevnit“ službu k uzlu nebo napsat malou sidecar službu, která bude sledovat aktuální IP adresu VPN služby a upravovat DNS záznamy zadané klienty – na co má kdo fantazii.
Z hlediska směrování můžeme jednoznačně identifikovat klienta za VPN podle jeho IP adresy, kterou server VPN přiděluje. Níže je primitivní příklad omezení přístupu takovému klientovi k službám, ilustrace na výše zmíněném Redis:
apiVersion: crd.projectcalico.org/v1
kind: HostEndpoint
metadata:
name: vpnclient-eth0
labels:
role: vpnclient
environment: production
spec:
interfaceName: "*"
node: kube-02
expectedIPs: ["172.176.176.2"]
---
apiVersion: crd.projectcalico.org/v1
kind: GlobalNetworkPolicy
metadata:
name: vpn-rules
spec:
selector: "role == 'vpnclient'"
order: 0
applyOnForward: true
preDNAT: true
ingress:
- action: Deny
protocol: TCP
destination:
ports: [6379]
- action: Allow
protocol: UDP
destination:
ports: [53, 67]Zde je připojení na port 6379 striktně zakázáno, ale zároveň je zachována funkčnost služby DNS, jejíž provoz často trpí při sestavování pravidel. Protože, jak bylo dříve zmíněno, pokud se objeví selektor, je na něj aplikována výchozí zákazová politika, pokud není určeno jinak.
Závěry
S využitím rozšířeného API Calico lze flexibilně konfigurovat a dynamicky měnit směrování v clusteru a kolem něj. Obecně může jeho použití působit jako střílení z děla na vrabce, a implementace L3 sítě s BGP a IP-IP tunely vypadá monstrózně v jednoduché instalaci Kubernetes v ploché síti… Nicméně jinak se nástroj jeví jako zcela životaschopný a užitečný.
Izolace clusteru k zajištění bezpečnostních požadavků nemusí být vždy realizovatelná, a právě v takových případech přichází na pomoc Calico (nebo podobné řešení). Uvedené příklady v článku (s malými úpravami) se používají v několika instalacích našich klientů v AWS.
P.S.
Přečtěte si také náš blog:
- «»;
- část 1 a 2 (síťový model, overlay sítě) , ;
- «».
Zdroj: habr.com
