Calico voor netwerken in Kubernetes: introductie en een beetje ervaring

Calico voor netwerken in Kubernetes: introductie en een beetje ervaring

Het doel van het artikel is om de lezer kennis te laten maken met de basisprincipes van netwerken en het beheren van netwerkbeleid in Kubernetes, evenals met de Calico-plug-in van derden die de standaardmogelijkheden uitbreidt. Gaandeweg zullen het configuratiegemak en enkele functies worden gedemonstreerd aan de hand van echte voorbeelden uit onze operationele ervaring.

Een korte introductie tot het Kubernetes-netwerkapparaat

Een Kubernetes-cluster is niet denkbaar zonder netwerk. We hebben al materiaal gepubliceerd over de basisprincipes: “Een geïllustreerde gids voor netwerken in Kubernetes"En"Een inleiding tot Kubernetes-netwerkbeleid voor beveiligingsprofessionals.

In de context van dit artikel is het belangrijk op te merken dat K8s zelf niet verantwoordelijk is voor de netwerkconnectiviteit tussen containers en knooppunten: hiervoor zijn verschillende CNI-plug-ins (Containernetwerkinterface). Meer over dit concept wij dat hebben ze mij ook verteld.

De meest voorkomende van deze plug-ins is bijvoorbeeld Flanel — biedt volledige netwerkconnectiviteit tussen alle clusterknooppunten door bruggen op elk knooppunt te plaatsen en er een subnet aan toe te wijzen. Volledige en ongereguleerde toegankelijkheid is echter niet altijd gunstig. Om enige vorm van minimale isolatie in het cluster te bieden, is het noodzakelijk om in te grijpen in de configuratie van de firewall. In het algemene geval wordt het onder de controle van dezelfde CNI geplaatst, wat de reden is dat eventuele tussenkomsten van derden in iptables verkeerd kunnen worden geïnterpreteerd of helemaal kunnen worden genegeerd.

En er wordt “out of the box” geboden voor het organiseren van netwerkbeleidsbeheer in een Kubernetes-cluster Netwerkbeleid-API. Deze bron, verdeeld over geselecteerde naamruimten, kan regels bevatten om de toegang van de ene applicatie naar de andere te differentiëren. Hiermee kunt u ook de toegankelijkheid configureren tussen specifieke pods, omgevingen (naamruimten) of blokken IP-adressen:

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: 5978

Dit is niet het meest primitieve voorbeeld hiervan officiële documentatie kan voor eens en voor altijd de wens ontmoedigen om de logica te begrijpen van hoe netwerkbeleid werkt. We zullen echter nog steeds proberen de basisprincipes en methoden voor het verwerken van verkeersstromen te begrijpen met behulp van netwerkbeleid...

Het is logisch dat er 2 soorten verkeer zijn: de pod binnenkomen (Ingress) en eruit gaan (Egress).

Calico voor netwerken in Kubernetes: introductie en een beetje ervaring

Eigenlijk is de politiek verdeeld in deze twee categorieën, gebaseerd op de richting van de beweging.

Het volgende vereiste attribuut is een selector; degene op wie de regel van toepassing is. Dit kan een pod (of een groep pods) of een omgeving (dat wil zeggen een naamruimte) zijn. Een belangrijk detail: beide typen van deze objecten moeten een label bevatten (label in Kubernetes-terminologie) – dit zijn degenen waarmee politici werken.

Naast een eindig aantal selectors verenigd door een soort label, is het mogelijk om regels als “Alles/iedereen toestaan/weigeren” in verschillende variaties te schrijven. Voor dit doel worden constructies van het formulier gebruikt:

  podSelector: {}
  ingress: []
  policyTypes:
  - Ingress

— in dit voorbeeld worden alle pods in de omgeving geblokkeerd voor inkomend verkeer. Het tegenovergestelde gedrag kan worden bereikt met de volgende constructie:

  podSelector: {}
  ingress:
  - {}
  policyTypes:
  - Ingress

Hetzelfde geldt voor uitgaand:

  podSelector: {}
  policyTypes:
  - Egress

- om het uit te schakelen. En dit is wat je moet opnemen:

  podSelector: {}
  egress:
  - {}
  policyTypes:
  - Egress

Terugkomend op de keuze voor een CNI-plug-in voor een cluster: het is vermeldenswaard dat niet elke netwerkplug-in ondersteunt NetworkPolicy. De reeds genoemde Flanel weet bijvoorbeeld niet hoe hij netwerkbeleid moet configureren, wat het wordt direct gezegd in de officiële repository. Daar wordt ook een alternatief genoemd: een Open Source-project Calico, waarmee de standaardset Kubernetes API's aanzienlijk wordt uitgebreid op het gebied van netwerkbeleid.

Calico voor netwerken in Kubernetes: introductie en een beetje ervaring

Calico leren kennen: theorie

De Calico-plug-in kan worden gebruikt in integratie met Flannel (subproject Verkoopkanaal) of onafhankelijk, met mogelijkheden voor zowel netwerkconnectiviteit als beschikbaarheidsbeheer.

Welke mogelijkheden biedt het gebruik van de K8s “boxed” oplossing en de API set van Calico?

Dit is wat er in NetworkPolicy is ingebouwd:

  • politici worden beperkt door het milieu;
  • beleid wordt toegepast op pods die zijn gemarkeerd met labels;
  • regels kunnen worden toegepast op pods, omgevingen of subnetten;
  • regels kunnen protocollen, benoemde of symbolische poortspecificaties bevatten.

Hier ziet u hoe Calico deze functies uitbreidt:

  • beleid kan op elk object worden toegepast: pod, container, virtuele machine of interface;
  • regels kunnen een specifieke actie bevatten (verbod, toestemming, loggen);
  • het doel of de bron van regels kan een poort zijn, een reeks poorten, protocollen, HTTP- of ICMP-attributen, IP of subnet (4e of 6e generatie), eventuele selectors (knooppunten, hosts, omgevingen);
  • Bovendien kunt u de doorgang van verkeer regelen met behulp van DNAT-instellingen en beleid voor het doorsturen van verkeer.

De eerste commits op GitHub in de Calico-repository dateren van juli 2016, en een jaar later nam het project een leidende positie in bij het organiseren van Kubernetes-netwerkconnectiviteit - dit blijkt bijvoorbeeld uit de onderzoeksresultaten, onder leiding van De Nieuwe Stapel:

Calico voor netwerken in Kubernetes: introductie en een beetje ervaring

Veel grote beheerde oplossingen met K8's, zoals Amazon EX, Azure-AKS, Google GKE en anderen begonnen het aan te bevelen voor gebruik.

Wat de prestaties betreft, alles is hier geweldig. Bij het testen van hun product demonstreerde het ontwikkelingsteam van Calico astronomische prestaties, waarbij meer dan 50000 containers op 500 fysieke knooppunten werden uitgevoerd met een creatiesnelheid van 20 containers per seconde. Er zijn geen problemen met de schaalvergroting vastgesteld. Dergelijke resultaten werden aangekondigd al bij de aankondiging van de eerste versie. Onafhankelijke onderzoeken die zich richten op de doorvoer en het verbruik van hulpbronnen bevestigen ook dat de prestaties van Calico bijna net zo goed zijn als die van Flannel. Bij voorbeeld:

Calico voor netwerken in Kubernetes: introductie en een beetje ervaring

Het project ontwikkelt zich zeer snel, het ondersteunt werk in populaire oplossingen beheerde K8s, OpenShift, OpenStack, het is mogelijk om Calico te gebruiken bij het inzetten van een cluster met behulp van trapzijn er verwijzingen naar de aanleg van Service Mesh-netwerken (Hier is een voorbeeld gebruikt in combinatie met Istio).

Oefen met Calico

In het algemene geval van het gebruik van vanilla Kubernetes komt het installeren van CNI neer op het gebruik van het bestand calico.yaml, gedownload van de officiële website, с омощью kubectl apply -f.

In de regel is de huidige versie van de plug-in compatibel met de nieuwste 2-3 versies van Kubernetes: de werking in oudere versies is niet getest en kan niet worden gegarandeerd. Volgens de ontwikkelaars draait Calico op Linux-kernels boven 3.10 met CentOS 7, Ubuntu 16 of Debian 8, bovenop iptables of IPVS.

Isolatie binnen de omgeving

Laten we voor een algemeen begrip eens kijken naar een eenvoudig geval om te begrijpen hoe netwerkbeleid in de Calico-notatie verschilt van standaardbeleid en hoe de aanpak voor het maken van regels de leesbaarheid en configuratieflexibiliteit ervan vereenvoudigt:

Calico voor netwerken in Kubernetes: introductie en een beetje ervaring

In het cluster zijn 2 webapplicaties ingezet: in Node.js en PHP, waarvan er één gebruik maakt van Redis. Om de toegang tot Redis vanuit PHP te blokkeren, terwijl de connectiviteit met Node.js behouden blijft, past u gewoon het volgende beleid toe:

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: 6379

In wezen hebben we binnenkomend verkeer van Node.js naar de Redis-poort toegestaan. En ze verboden duidelijk niets anders. Zodra NetworkPolicy verschijnt, worden alle daarin genoemde selectors geïsoleerd, tenzij anders aangegeven. De isolatieregels zijn echter niet van toepassing op andere objecten die niet onder de selector vallen.

Het voorbeeld gebruikt apiVersion Kubernetes is out-of-the-box, maar niets weerhoudt u ervan om het te gebruiken bron met dezelfde naam uit de Calico-levering. De syntaxis daar is gedetailleerder, dus u zult de regel voor het bovenstaande geval in de volgende vorm moeten herschrijven:

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

De bovengenoemde constructies voor het toestaan ​​of weigeren van al het verkeer via de reguliere NetworkPolicy API bevatten constructies met haakjes die moeilijk te begrijpen en te onthouden zijn. In het geval van Calico hoeft u alleen maar de logica van een firewallregel in het tegenovergestelde te veranderen action: Allow op action: Deny.

Isolatie door omgeving

Stel je nu een situatie voor waarin een applicatie bedrijfsstatistieken genereert voor verzameling in Prometheus en verdere analyse met Grafana. De upload kan gevoelige gegevens bevatten, die standaard weer openbaar zichtbaar zijn. Laten we deze gegevens verbergen voor nieuwsgierige blikken:

Calico voor netwerken in Kubernetes: introductie en een beetje ervaring

Prometheus wordt in de regel in een aparte serviceomgeving geplaatst - in het voorbeeld zal het een naamruimte zijn zoals deze:

apiVersion: v1
kind: Namespace
metadata:
  labels:
    module: prometheus
  name: kube-prometheus

Veld metadata.labels dit bleek geen toeval te zijn. Zoals hierboven vermeld, namespaceSelector (Leuk vinden podSelector) werkt met labels. Om het mogelijk te maken dat metrieken van alle pods op een specifieke poort worden overgenomen, moet u daarom een ​​soort label toevoegen (of van bestaande overnemen) en vervolgens een configuratie toepassen zoals:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-metrics-prom
spec:
  podSelector: {}
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          module: prometheus
    ports:
    - protocol: TCP
      port: 9100

En als u Calico-beleid gebruikt, ziet de syntaxis er als volgt uit:

apiVersion: crd.projectcalico.org/v1
kind: NetworkPolicy
metadata:
  name: allow-metrics-prom
spec:
  ingress:
  - action: Allow
    protocol: TCP
    source:
      namespaceSelector: module == 'prometheus'
    destination:
      ports:
      - 9100

Door dit soort beleid voor specifieke behoeften toe te voegen, kunt u zich over het algemeen beschermen tegen kwaadwillige of onbedoelde interferentie in de werking van applicaties in het cluster.

De beste praktijk is volgens de makers van Calico de ‘Blokkeer alles en open expliciet wat je nodig hebt’-aanpak, gedocumenteerd in officiële documentatie (Anderen volgen een soortgelijke aanpak – in het bijzonder in reeds genoemd artikel).

Extra Calico-objecten gebruiken

Ik wil u eraan herinneren dat u via de uitgebreide set Calico API's de beschikbaarheid van knooppunten kunt regelen, en niet beperkt bent tot pods. In het volgende voorbeeld wordt gebruik gemaakt van GlobalNetworkPolicy de mogelijkheid om ICMP-verzoeken in het cluster door te geven is gesloten (bijvoorbeeld pings van een pod naar een knooppunt, tussen pods of van een knooppunt naar een IP-pod):

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

In het bovenstaande geval is het nog steeds mogelijk dat clusterknooppunten elkaar “reiken” via ICMP. En dit probleem wordt door middel opgelost GlobalNetworkPolicy, toegepast op een entiteit 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"]

Het VPN-geval

Ten slotte zal ik een heel reëel voorbeeld geven van het gebruik van Calico-functies in het geval van interactie tussen clusters, waarbij een standaardset beleid niet voldoende is. Om toegang te krijgen tot de webapplicatie gebruiken klanten een VPN-tunnel, en deze toegang wordt streng gecontroleerd en beperkt tot een specifieke lijst met services die mogen worden gebruikt:

Calico voor netwerken in Kubernetes: introductie en een beetje ervaring

Clients maken verbinding met de VPN via standaard UDP-poort 1194 en ontvangen, wanneer verbonden, routes naar de clustersubnetten van pods en services. Volledige subnetten worden gepusht om geen services te verliezen tijdens het opnieuw opstarten en adreswijzigingen.

De poort in de configuratie is standaard, wat enkele nuances oplegt aan het proces van het configureren van de applicatie en het overbrengen ervan naar het Kubernetes-cluster. In dezelfde AWS verscheen LoadBalancer voor UDP bijvoorbeeld eind vorig jaar letterlijk in een beperkte lijst met regio's, en NodePort kan niet worden gebruikt vanwege de forwarding op alle clusterknooppunten en het is onmogelijk om het aantal serverinstances te schalen voor doeleinden van fouttolerantie. Bovendien moet u het standaardpoortbereik wijzigen...

Na het zoeken naar mogelijke oplossingen is gekozen voor het volgende:

  1. Pods met VPN worden per node ingepland hostNetwork, dat wil zeggen naar het werkelijke IP-adres.
  2. De dienst wordt buiten via geplaatst ClusterIP. Op het knooppunt is fysiek een poort geïnstalleerd, die met kleine voorbehouden (voorwaardelijke aanwezigheid van een echt IP-adres) van buitenaf toegankelijk is.
  3. Het bepalen van de knoop waarop de peul opkwam valt buiten het bestek van ons verhaal. Ik zal alleen zeggen dat je de service stevig aan een knooppunt kunt 'spijkeren' of een kleine zijspanservice kunt schrijven die het huidige IP-adres van de VPN-service controleert en de DNS-records bewerkt die bij klanten zijn geregistreerd - wie maar genoeg fantasie heeft.

Vanuit een routeringsperspectief kunnen we een VPN-client op unieke wijze identificeren aan de hand van het IP-adres dat is uitgegeven door de VPN-server. Hieronder vindt u een primitief voorbeeld van het beperken van de toegang van een dergelijke klant tot diensten, geïllustreerd op de bovengenoemde 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]

Hier is verbinding maken met poort 6379 ten strengste verboden, maar tegelijkertijd blijft de werking van de DNS-service behouden, waarvan de werking vaak te lijden heeft bij het opstellen van regels. Omdat, zoals eerder vermeld, wanneer een selector verschijnt, het standaard weigeringsbeleid hierop wordt toegepast, tenzij anders aangegeven.

Resultaten van

Zo kunt u met behulp van de geavanceerde API van Calico de routing in en rond het cluster flexibel configureren en dynamisch wijzigen. Over het algemeen kan het gebruik ervan lijken op het schieten op mussen met een kanon, en het implementeren van een L3-netwerk met BGP- en IP-IP-tunnels ziet er monsterlijk uit in een eenvoudige Kubernetes-installatie op een plat netwerk... Maar verder ziet de tool er behoorlijk haalbaar en nuttig uit. .

Het isoleren van een cluster om aan de beveiligingseisen te voldoen is misschien niet altijd haalbaar, en dit is waar Calico (of een vergelijkbare oplossing) te hulp schiet. De in dit artikel gegeven voorbeelden (met kleine aanpassingen) worden gebruikt in verschillende installaties van onze klanten in AWS.

PS

Lees ook op onze blog:

Bron: www.habr.com

Voeg een reactie