En introduktion till Kubernetes nätverkspolicyer för säkerhetspersonal
Notera. transl.: Författaren till artikeln, Reuven Harrison, har över 20 års erfarenhet av mjukvaruutveckling, och är idag CTO och medgrundare av Tufin, ett företag som skapar lösningar för säkerhetspolicyhantering. Samtidigt som han ser Kubernetes nätverkspolicyer som ett ganska kraftfullt verktyg för nätverkssegmentering i ett kluster, menar han också att de inte är så lätta att implementera i praktiken. Detta material (ganska omfattande) är avsett att förbättra specialisternas medvetenhet om detta problem och hjälpa dem att skapa de nödvändiga konfigurationerna.
Idag är det många företag som i allt högre grad väljer Kubernetes för att köra sina applikationer. Intresset för denna programvara är så stort att vissa kallar Kubernetes "det nya operativsystemet för datacentret." Gradvis börjar Kubernetes (eller k8s) att uppfattas som en kritisk del av verksamheten, vilket kräver organisation av mogna affärsprocesser, inklusive nätverkssäkerhet.
För säkerhetspersonal som är förbryllad över att arbeta med Kubernetes kan den verkliga avslöjandet vara plattformens standardpolicy: tillåt allt.
Den här guiden hjälper dig att förstå den interna strukturen för nätverkspolicyer; förstå hur de skiljer sig från reglerna för vanliga brandväggar. Den kommer också att täcka några fallgropar och ge rekommendationer för att säkra applikationer på Kubernetes.
Kubernetes nätverkspolicyer
Kubernetes nätverkspolicymekanism låter dig hantera interaktionen mellan applikationer som distribueras på plattformen i nätverkslagret (det tredje i OSI-modellen). Nätverkspolicyer saknar några av de avancerade funktionerna i moderna brandväggar, såsom OSI Layer 7-tillämpning och hotdetektion, men de ger en grundläggande nivå av nätverkssäkerhet som är en bra utgångspunkt.
Nätverkspolicyer styr kommunikation mellan poddar
Arbetsbelastningar i Kubernetes är fördelade över pods, som består av en eller flera behållare som distribueras tillsammans. Kubernetes tilldelar varje pod en IP-adress som är tillgänglig från andra pods. Kubernetes nätverkspolicyer anger åtkomsträttigheter för grupper av pods på samma sätt som säkerhetsgrupper i molnet används för att kontrollera åtkomst till virtuella maskininstanser.
Definiera nätverkspolicyer
Precis som andra Kubernetes-resurser specificeras nätverkspolicyer i YAML. I exemplet nedan, applikationen balance tillgång till postgres:
(Notera. transl.: den här skärmdumpen, liksom alla efterföljande liknande, skapades inte med inbyggda Kubernetes-verktyg, utan med verktyget Tufin Orca, som utvecklades av företaget till författaren till den ursprungliga artikeln och som nämns i slutet av materialet.)
För att definiera din egen nätverkspolicy behöver du grundläggande kunskaper om YAML. Det här språket är baserat på indrag (specificeras av blanksteg snarare än tabbar). Ett indraget element tillhör det närmaste indragna elementet ovanför det. Ett nytt listelement börjar med ett bindestreck, alla andra element har formen nyckelvärde.
Efter att ha beskrivit policyn i YAML, använd kubectlför att skapa det i klustret:
kubectl create -f policy.yaml
Nätverkspolicyspecifikation
Kubernetes nätverkspolicyspecifikation innehåller fyra element:
podSelector: definierar de pods som påverkas av denna policy (mål) - krävs;
policyTypes: indikerar vilka typer av policyer som ingår i detta: inträde och/eller utträde - valfritt, men jag rekommenderar att du uttryckligen specificerar det i alla fall;
ingress: definierar tillåtna inkommande trafik till målpods - valfritt;
egress: definierar tillåtna utgående trafik från målpods är valfritt.
Exempel taget från Kubernetes webbplats (jag ersatte role på app), visar hur alla fyra elementen används:
Observera att alla fyra delarna inte behöver inkluderas. Det är bara obligatoriskt podSelector, kan andra parametrar användas efter önskemål.
Om du utelämnar policyTypes, kommer policyn att tolkas enligt följande:
Som standard antas det att den definierar ingångssidan. Om policyn inte uttryckligen anger detta, kommer systemet att anta att all trafik är förbjuden.
Beteendet på utgångssidan kommer att bestämmas av närvaron eller frånvaron av motsvarande utgångsparameter.
För att undvika misstag rekommenderar jag gör det alltid tydligt policyTypes.
Enligt ovanstående logik, om parametrarna ingress och / eller egress utelämnas, kommer policyn att neka all trafik (se "Stripningsregel" nedan).
Standardpolicyn är Tillåt
Om inga policyer har definierats tillåter Kubernetes all trafik som standard. Alla poddar kan fritt utbyta information sinsemellan. Detta kan verka kontraintuitivt ur ett säkerhetsperspektiv, men kom ihåg att Kubernetes ursprungligen designades av utvecklare för att möjliggöra applikationskompatibilitet. Nätverkspolicyer lades till senare.
Namnutrymmen
Namnutrymmen är Kubernetes samarbetsmekanism. De är utformade för att isolera logiska miljöer från varandra, medan kommunikation mellan utrymmen är tillåten som standard.
Liksom de flesta Kubernetes-komponenter finns nätverkspolicyer i ett specifikt namnområde. I blocket metadata du kan ange vilket utrymme policyn tillhör:
Om namnutrymmet inte är explicit specificerat i metadatan kommer systemet att använda namnområdet som anges i kubectl (som standard namespace=default):
kubectl apply -n my-namespace -f namespace.yaml
jag rekomenderar ange namnutrymme uttryckligen, såvida du inte skriver en policy som är inriktad på flera namnområden samtidigt.
Primär elementet podSelector i policyn kommer att välja poddar från namnområdet som policyn tillhör (den nekas åtkomst till pods från ett annat namnområde).
På samma sätt, podSelectors i in- och utgångsblock kan bara välja poddar från sitt eget namnområde, såvida du inte kombinerar dem med namespaceSelector (detta kommer att diskuteras i avsnittet "Filtrera efter namnutrymmen och poddar").
Regler för policynamn
Policynamn är unika inom samma namnområde. Det kan inte finnas två policyer med samma namn i samma utrymme, men det kan finnas policyer med samma namn i olika utrymmen. Detta är användbart när du vill använda samma policy igen på flera utrymmen.
Jag gillar särskilt en av namngivningsmetoderna. Det består av att kombinera namnområdets namn med målpodarna. Till exempel:
Du kan bifoga anpassade etiketter till Kubernetes-objekt, till exempel poddar och namnområden. Etiketter (etiketter - taggar) är motsvarigheten till taggar i molnet. Kubernetes nätverkspolicyer använder etiketter för att välja baljorsom de gäller för:
podSelector:
matchLabels:
role: db
… eller namnrymdersom de gäller. Det här exemplet väljer alla poddar i namnutrymmen med motsvarande etiketter:
En försiktighet: vid användning namespaceSelectorse till att namnrymden du väljer innehåller rätt etikett. Var medveten om att inbyggda namnrymder som t.ex default и kube-system, som standard inte innehåller etiketter.
Du kan lägga till en etikett till ett utrymme så här:
kubectl label namespace default namespace=default
Samtidigt namnutrymme i avsnittet metadata bör referera till det faktiska utrymmets namn, inte etiketten:
Brandväggspolicyer består av regler med källor och destinationer. Kubernetes nätverkspolicyer definieras för ett mål - en uppsättning pods som de gäller för - och sätter sedan regler för inkommande och/eller utgående trafik. I vårt exempel kommer policyns mål att vara alla poddar i namnområdet default med etikett med nyckel app och mening db:
Underavsnitt ingress i den här policyn öppnar inkommande trafik till målpodarna. Ingress är med andra ord källan och målet är motsvarande destination. På samma sätt är utgången destinationen och målet är dess källa.
Detta motsvarar två brandväggsregler: Ingress → Target; Mål → Utgång.
Egress och DNS (viktigt!)
Genom att begränsa utgående trafik, ägna särskild uppmärksamhet åt DNS - Kubernetes använder den här tjänsten för att mappa tjänster till IP-adresser. Till exempel kommer följande policy inte att fungera eftersom du inte har tillåtit applikationen balance åtkomst till DNS:
Sista elementet to är tom, och därför väljer den indirekt alla poddar i alla namnutrymmen, tillåter balance skicka DNS-frågor till lämplig Kubernetes-tjänst (körs vanligtvis i utrymmet kube-system).
Detta tillvägagångssätt fungerar, hur det än är alltför tillåtande och osäker, eftersom det tillåter att DNS-frågor riktas utanför klustret.
Du kan förbättra det i tre på varandra följande steg.
1. Tillåt endast DNS-frågor inom kluster genom att lägga till namespaceSelector:
2. Tillåt endast DNS-frågor inom namnområdet kube-system.
För att göra detta måste du lägga till en etikett i namnområdet kube-system: kubectl label namespace kube-system namespace=kube-system - och skriv ner det i policy med hjälp av namespaceSelector:
3. Paranoida människor kan gå ännu längre och begränsa DNS-förfrågningar till en specifik DNS-tjänst i kube-system. Avsnittet "Filtrera efter namnrymder OCH poddar" kommer att berätta hur du uppnår detta.
Ett annat alternativ är att lösa DNS på namnområdesnivå. I det här fallet behöver den inte öppnas för varje tjänst:
Tömma podSelector väljer alla poddar i namnområdet.
Första match och regelordning
I konventionella brandväggar bestäms åtgärden (tillåt eller neka) på ett paket av den första regeln som det uppfyller. I Kubernetes spelar ordningen på policyerna ingen roll.
Som standard, när inga policyer är inställda, tillåts kommunikation mellan poddar och de kan fritt utbyta information. När du väl börjar formulera policyer, blir varje pod som påverkas av minst en av dem isolerad enligt disjunktionen (logiskt ELLER) för alla policyer som valde den. Pods som inte påverkas av någon policy förblir öppna.
Du kan ändra detta beteende med hjälp av en strippningsregel.
Avlägsningsregel ("Neka")
Brandväggspolicyer nekar vanligtvis all trafik som inte är uttryckligen tillåten.
Det finns ingen förnekaråtgärd i Kubernetes, dock kan en liknande effekt uppnås med en vanlig (tillåtande) policy genom att välja en tom grupp av källpoddar (ingång):
Observera att alla ytterligare policyer som tillåter trafik till pods i namnområdet kommer att ha företräde framför denna regel (liknar att lägga till en tillåt-regel före en neka-regel i en brandväggskonfiguration).
Tillåt allt (Any-Any-Any-Allow)
För att skapa en Allow All-policy måste du komplettera Neka-policyn ovan med ett tomt element ingress:
Den tillåter åtkomst från alla poddar i alla namnutrymmen (och alla IP) till valfri pod i namnområdet default. Detta beteende är aktiverat som standard, så det behöver vanligtvis inte definieras ytterligare. Men ibland kan du behöva tillfälligt inaktivera vissa specifika behörigheter för att diagnostisera problemet.
Regeln kan begränsas för att endast tillåta åtkomst till en specifik uppsättning kapslar (app:balance) i namnutrymmet default:
2. Inuti policysektionen ingress kan ha många element from (kombinerat med logiskt ELLER). Likaså avsnitt egress kan innehålla många element to (även kombinerat med disjunktion):
3. Olika policyer kombineras också med logiska ELLER
Men när man kombinerar dem finns det en begränsning för vilken påpekadeChris Cooney: Kubernetes kan bara kombinera policyer med olika policyTypes (Ingress eller Egress). Policyer som definierar ingång (eller utgående) kommer att skriva över varandra.
Relation mellan namnutrymmen
Som standard är informationsdelning mellan namnområden tillåten. Detta kan ändras genom att använda en neka-policy som begränsar utgående och/eller inkommande trafik till namnområdet (se "Strippningsregel" ovan).
När du har blockerat åtkomst till ett namnområde (se "Strippningsregeln" ovan) kan du göra undantag från den nekande policyn genom att tillåta anslutningar från ett specifikt namnområde med namespaceSelector:
Som ett resultat, alla poddar i namnområdet default kommer att ha tillgång till poddar postgres i namnutrymmet database. Men vad händer om du vill öppna åtkomst till postgres endast specifika poddar i namnområdet default?
Filtrera efter namnrymder och poddar
Kubernetes version 1.11 och högre låter dig kombinera operatörer namespaceSelector и podSelector med logiskt AND. Det ser ut så här:
Varför tolkas detta som OCH istället för det vanliga ELLER?
Observera att podSelector börjar inte med ett bindestreck. I YAML betyder detta det podSelector och står framför honom namespaceSelector hänvisa till samma listelement. Därför kombineras de med logiska OCH.
Lägger till ett bindestreck innan podSelector kommer att resultera i uppkomsten av ett nytt listelement, som kommer att kombineras med det föregående namespaceSelector med logiskt ELLER.
För att välja pods med en specifik etikett i alla namnutrymmen, ange tomt namespaceSelector:
Regler för en brandvägg med flera objekt (värdar, nätverk, grupper) kombineras med logiskt ELLER. Följande regel fungerar om paketkällan matchar Host_1 ELLER Host_2:
Tvärtom, i Kubernetes de olika etiketterna i podSelector eller namespaceSelector kombineras med logiska OCH. Till exempel kommer följande regel att välja pods som har båda etiketterna, role=db И version=v2:
podSelector:
matchLabels:
role: db
version: v2
Samma logik gäller för alla typer av operatörer: policymålväljare, podväljare och namnområdesväljare.
Subnät och IP-adresser (IPBlocks)
Brandväggar använder VLAN, IP-adresser och subnät för att segmentera ett nätverk.
I Kubernetes tilldelas IP-adresser till pods automatiskt och kan ändras ofta, så etiketter används för att välja pods och namnområden i nätverkspolicyer.
Subnät (ipBlocks) används vid hantering av inkommande (inkommande) eller utgående (utgående) externa (nord-sydlig) anslutningar. Till exempel öppnas den här policyn för alla poddar från namnområdet default åtkomst till Googles DNS-tjänst:
Den tomma podväljaren i det här exemplet betyder "välj alla pods i namnområdet."
Denna policy tillåter endast åtkomst till 8.8.8.8; åtkomst till någon annan IP är förbjuden. Så i huvudsak har du blockerat åtkomsten till den interna Kubernetes DNS-tjänsten. Om du fortfarande vill öppna den, ange detta uttryckligen.
Vanligtvis ipBlocks и podSelectors utesluter varandra, eftersom de interna IP-adresserna för pods inte används i ipBlocks. Genom att indikera interna IP-pods, kommer du faktiskt att tillåta anslutningar till/från pods med dessa adresser. I praktiken vet du inte vilken IP-adress du ska använda, varför de inte bör användas för att välja pods.
Som ett motexempel inkluderar följande policy alla IP:er och tillåter därför åtkomst till alla andra poddar:
Vanligtvis lyssnar pods på en port. Det betyder att du helt enkelt inte kan ange portnummer i policyer och lämna allt som standard. Det rekommenderas dock att göra policyer så restriktiva som möjligt, så i vissa fall kan du fortfarande ange portar:
Observera att väljaren ports gäller alla element i blocket to eller from, vilket innehåller. För att ange olika portar för olika uppsättningar av element, dela ingress eller egress i flera underavdelningar med to eller from och i varje register dina portar:
Om du utelämnar portdefinitionen helt (ports), detta betyder alla protokoll och alla portar;
Om du utelämnar protokolldefinitionen (protocol), detta betyder TCP;
Om du utelämnar portdefinitionen (port), detta betyder alla portar.
Bästa praxis: Lita inte på standardvärden, ange vad du behöver uttryckligen.
Observera att du måste använda pod-portar, inte serviceportar (mer om detta i nästa stycke).
Finns policyer definierade för pods eller tjänster?
Vanligtvis har pods i Kubernetes åtkomst till varandra via en tjänst - en virtuell lastbalanserare som omdirigerar trafik till podarna som implementerar tjänsten. Du kanske tror att nätverkspolicyer styr åtkomst till tjänster, men så är inte fallet. Kubernetes nätverkspolicyer fungerar på pod-portar, inte serviceportar.
Till exempel, om en tjänst lyssnar på port 80, men omdirigerar trafik till port 8080 av sina pods, måste du ange exakt 8080 i nätverkspolicyn.
En sådan mekanism bör anses vara suboptimal: om tjänstens interna struktur (portarna som poddarna lyssnar på) ändras, måste nätverkspolicyerna uppdateras.
Nytt arkitektoniskt tillvägagångssätt med Service Mesh (se till exempel om Istio nedan - ca översättning) låter dig hantera detta problem.
Är det nödvändigt att registrera både Ingress och Egress?
Det korta svaret är ja, för att pod A ska kunna kommunicera med pod B måste den vara tillåten att skapa en utgående anslutning (för detta måste du konfigurera en utgående policy), och pod B måste kunna acceptera en inkommande anslutning ( för detta behöver du följaktligen en ingresspolicy).
Men i praktiken kan du lita på standardpolicyn för att tillåta anslutningar i en eller båda riktningarna.
Om någon pod-källa kommer att väljas av en eller flera utträde-politiker, de restriktioner som åläggs det kommer att bestämmas av deras disjunktion. I det här fallet måste du uttryckligen tillåta anslutning till podden -till adressaten. Om en pod inte väljs av någon policy, tillåts dess utgående (utgående) trafik som standard.
På samma sätt är poddens ödeadressat, vald av en eller flera inträde-politiker, kommer att bestämmas av deras disjunktion. I det här fallet måste du uttryckligen tillåta att den tar emot trafik från källpodden. Om en pod inte väljs av någon policy, tillåts all inkommande trafik för den som standard.
Se Stateful eller Stateless nedan.
Loggar
Kubernetes nätverkspolicyer kan inte logga trafik. Detta gör det svårt att avgöra om en policy fungerar som avsett och komplicerar säkerhetsanalysen avsevärt.
Styrning av trafik till externa tjänster
Kubernetes nätverkspolicyer tillåter dig inte att ange ett fullständigt kvalificerat domännamn (DNS) i utgående sektioner. Detta faktum leder till betydande olägenheter när man försöker begränsa trafik till externa destinationer som inte har en fast IP-adress (som aws.com).
Policykontroll
Brandväggar kommer att varna dig eller till och med vägra acceptera fel policy. Kubernetes gör också viss verifiering. När du ställer in en nätverkspolicy genom kubectl kan Kubernetes förklara att den är felaktig och vägra acceptera den. I andra fall kommer Kubernetes att ta policyn och fylla i den med de uppgifter som saknas. De kan ses med kommandot:
kubernetes get networkpolicy <policy-name> -o yaml
Tänk på att Kubernetes valideringssystem inte är ofelbart och kan missa vissa typer av fel.
Utförande
Kubernetes implementerar inte nätverkspolicyer själv, utan är bara en API-gateway som delegerar bördan av kontroll till ett underliggande system som kallas Container Networking Interface (CNI). Att ställa in policyer på ett Kubernetes-kluster utan att tilldela lämplig CNI är detsamma som att skapa policyer på en brandväggshanteringsserver utan att sedan installera dem på brandväggar. Det är upp till dig att se till att du har en anständig CNI eller, i fallet med Kubernetes-plattformar, värd i molnet (du kan se listan över leverantörer här - cirka. trans.), aktivera nätverkspolicyer som ställer in CNI för dig.
Observera att Kubernetes inte kommer att varna dig om du ställer in en nätverkspolicy utan lämplig hjälp-CNI.
Statlig eller statslös?
Alla Kubernetes CNI:er jag har stött på är tillståndsbestämda (till exempel använder Calico Linux conntrack). Detta gör att podden kan ta emot svar på TCP-anslutningen den initierade utan att behöva återupprätta den. Jag är dock inte medveten om en Kubernetes-standard som skulle garantera status.
Avancerad säkerhetspolicyhantering
Här är några sätt att förbättra efterlevnaden av säkerhetspolicyn i Kubernetes:
Det arkitektoniska mönstret för Service Mesh använder sidovagnscontainrar för att tillhandahålla detaljerad telemetri och trafikkontroll på servicenivå. Som exempel kan vi ta Samma.
Några av CNI-leverantörerna har utökat sina verktyg till att gå längre än Kubernetes nätverkspolicyer.
Tufin späckhuggare Ger synlighet och automatisering av Kubernetes nätverkspolicyer.
Tufin Orca-paketet hanterar Kubernetes nätverkspolicyer (och är källan till skärmdumparna ovan).
Kubernetes nätverkspolicyer erbjuder en bra uppsättning verktyg för att segmentera kluster, men de är inte intuitiva och har många finesser. På grund av denna komplexitet tror jag att många befintliga klusterpolicyer är buggiga. Möjliga lösningar på detta problem inkluderar automatisering av policydefinitioner eller användning av andra segmenteringsverktyg.
Jag hoppas att den här guiden hjälper dig att reda ut några frågor och lösa problem du kan stöta på.