Kommunikation zwischen GKE Enterprise-Clustern mithilfe von Anthos Service Mesh schützen und verschlüsseln

Last reviewed 2021-04-30 UTC

In dieser Anleitung wird gezeigt, wie Sie Egress- und Ingress-Gateways von Anthos Service Mesh verwenden, um Traffic zwischen Clustern mithilfe von gegenseitiger Transport Layer Security (mTLS) zu schützen. Die Anleitung richtet sich an Kubernetes-Clusteradministratoren, die für Netzwerk-, Sicherheits- und Plattformaspekte zuständig sind. Die hier erläuterten Kontrollen können insbesondere für Organisationen mit erhöhten Sicherheitsanforderungen oder für die Einhaltung gesetzlicher Vorschriften hilfreich sein. Diese Anleitung wird durch einen entsprechenden Konzeptleitfaden ergänzt.

In dieser Anleitung wird davon ausgegangen, dass Sie mit Kubernetes und Anthos Service Mesh vertraut sind.

Ziele

  • Infrastruktur mit Terraform einrichten:
    • Benutzerdefiniertes VPC-Netzwerk mit zwei privaten Subnetzen erstellen
    • Zwei Kubernetes-Cluster mit aktiviertem Anthos Service Mesh erstellen:
      • Einen GKE-Cluster
      • Einen kOps-Cluster (Kubernetes Operations), der im benutzerdefinierten VPC-Netzwerk ausgeführt wird
    • Cluster bei GKE Hub registrieren
  • MySQL-Client im GKE-Cluster bereitstellen
  • MySQL-Server in einem kOps-Cluster bereitstellen
  • Egress- und Ingress-Gateways konfigurieren, um einen Server mithilfe von mTLS bereitzustellen
  • Zugriff auf einen MySQL-Server mithilfe eines MySQL-Clients testen, der in verschiedenen Clustern oder VPCs ausgeführt wird

Kosten

In diesem Dokument verwenden Sie die folgenden kostenpflichtigen Komponenten von Google Cloud:

Mit dem Preisrechner können Sie eine Kostenschätzung für Ihre voraussichtliche Nutzung vornehmen. Neuen Google Cloud-Nutzern steht möglicherweise eine kostenlose Testversion zur Verfügung.

Nach Abschluss der in diesem Dokument beschriebenen Aufgaben können Sie weitere Kosten vermeiden, indem Sie die erstellten Ressourcen löschen. Weitere Informationen finden Sie unter Bereinigen.

Hinweis

Für diese Anleitung benötigen Sie ein Google Cloud-Projekt. Sie können ein neues Projekt erstellen oder ein vorhandenes Projekt auswählen:

  1. Rufen Sie in der Google Cloud Console die Seite für die Projektauswahl auf.

    Zur Projektauswahl

  2. Wählen Sie ein Google Cloud-Projekt aus oder erstellen Sie eines.

  3. Die Abrechnung für das Google Cloud-Projekt muss aktiviert sein.

  4. Rufen Sie in der Google Cloud Console Cloud Shell auf.

    Zu Cloud Shell

    Unten in der Google Cloud Console wird eine Cloud Shell-Sitzung mit einer Eingabeaufforderung geöffnet. Cloud Shell ist eine Shell-Umgebung, in der das Google Cloud CLI bereits installiert ist, einschließlich der Google Cloud-Befehlszeile. Das Initialisieren der Sitzung kann einige Sekunden dauern.

  5. Achten Sie darauf, dass Sie in Cloud Shell in dem Projekt arbeiten, das Sie erstellt oder ausgewählt haben:
    export PROJECT_ID=PROJECT_ID
    gcloud config set project ${PROJECT_ID}
    

    Ersetzen Sie PROJECT_ID durch Ihre Projekt-ID.

  6. Erstellen Sie eine Umgebungsvariable für die E-Mail-Adresse, die Sie für Google Cloud verwenden:
    export GOOGLE_CLOUD_EMAIL_ADDRESS=GOOGLE_CLOUD_EMAIL_ADDRESS
    

    Ersetzen Sie dabei GOOGLE_CLOUD_EMAIL_ADDRESS durch die E-Mail-Adresse, die Sie in Google Cloud nutzen.

  7. Legen Sie die Region und Zone für Ihre Compute-Ressourcen fest:
    export REGION=us-central1
    export ZONE=us-central1-b
    gcloud config set compute/region ${REGION}
    gcloud config set compute/zone ${ZONE}
    

    In dieser Anleitung wird us-central1 für die Region und us-central1-b für die Zone verwendet. Sie können in einer Region Ihrer Wahl bereitstellen.

  8. Legen Sie die erforderlichen IAM-Rollen (Identitäts- und Zugriffsverwaltung) fest. Wenn Sie Projektinhaber sind, haben Sie alle erforderlichen Berechtigungen, um die Installation auszuführen. Wenn nicht, bitten Sie Ihren Administrator, Ihnen entsprechende IAM-Rollen (Identity and Access Management) zuzuweisen. Führen Sie dazu den folgenden Befehl in Cloud Shell aus:
    ROLES=(
    'roles/container.admin' \
    'roles/gkehub.admin' \
    'roles/iam.serviceAccountAdmin' \
    'roles/iam.serviceAccountKeyAdmin' \
    'roles/resourcemanager.projectIamAdmin' \
    'roles/compute.securityAdmin' \
    'roles/compute.instanceAdmin' \
    'roles/storage.admin' \
    'roles/serviceusage.serviceUsageAdmin'
    )
    for role in "${ROLES[@]}"
    do
     gcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member "user:${GOOGLE_CLOUD_EMAIL_ADDRESS}" \
      --role="$role"
    done
    
  9. Aktivieren Sie die für die Anleitung erforderlichen APIs:
    gcloud services enable \
        anthos.googleapis.com \
        anthosgke.googleapis.com \
        anthosaudit.googleapis.com \
        compute.googleapis.com \
        container.googleapis.com \
        cloudresourcemanager.googleapis.com \
        serviceusage.googleapis.com \
        stackdriver.googleapis.com \
        monitoring.googleapis.com \
        logging.googleapis.com \
        cloudtrace.googleapis.com \
        meshca.googleapis.com \
        meshconfig.googleapis.com \
        iamcredentials.googleapis.com \
        gkeconnect.googleapis.com \
        gkehub.googleapis.com
    

Umgebung vorbereiten

  1. Klonen Sie in Cloud Shell das folgende Repository:

    git clone https://github.com/GoogleCloudPlatform/anthos-service-mesh-samples
    cd anthos-service-mesh-samples/docs/mtls-egress-ingress
    
  2. Aktualisieren Sie Terraform für Ihre Umgebung. Standardmäßig ist in der Google Cloud Console Terraform 0.12 enthalten. In dieser Anleitung wird davon ausgegangen, dass Sie Terraform 0.13.5 oder höher installiert haben. Mit den folgenden Befehlen können Sie vorübergehend eine andere Version von Terraform verwenden:

    mkdir ~/bin
    curl https://releases.hashicorp.com/terraform/0.13.5/terraform_0.13.5_linux_amd64.zip -o ~/bin/terraform.zip
    unzip ~/bin/terraform.zip -d ~/bin/
    
  3. Wechseln Sie in den Unterordner terraform und initialisieren Sie Terraform:

    cd terraform/
    ~/bin/terraform init
    
  4. Erstellen Sie eine Datei terraform.tfvars anhand der zuvor erstellten Umgebungsvariablen:

    cat << EOF > terraform.tfvars
    project_id = "${PROJECT_ID}"
    region = "${REGION}"
    zones = ["${ZONE}"]
    EOF
    

    Im nächsten Schritt erstellen Sie die anfängliche Infrastruktur. Dazu erstellen Sie den Terraform-Ausführungsplan für diese Konfiguration und wenden ihn an. Mit den Skripts und Modulen in diesem Plan wird Folgendes erstellt:

    • Ein benutzerdefiniertes VPC-Netzwerk mit zwei privaten Subnetzen
    • Zwei Kubernetes-Cluster mit aktiviertem Anthos Service Mesh
    • Ein GKE-Cluster
    • Ein kOps-Cluster, der im benutzerdefinierten VPC-Netzwerk ausgeführt wird

    Mit dem Ausführungsplan werden auch Cluster für GKE Hub registriert.

  5. Führen Sie den Ausführungsplan aus:

    ~/bin/terraform plan -out mtls-terraform-plan
    ~/bin/terraform apply "mtls-terraform-plan"
    

    Die Ausgabe sieht etwa so aus:

    Apply complete! Resources: 27 added, 0 changed, 0 destroyed.
    Outputs:
    server_token = <sensitive>
    

    <sensitive> ist dabei eine Terraform-Ausgabevariable, die in der Console nicht angezeigt wird, aber abgefragt werden kann, z. B. ~/bin/terraform output server_token.

  6. Rufen Sie die Datei kubeconfig des Serverclusters aus dem Verzeichnis terraform ab. Führen Sie sie dann mit der Konfigurationsdatei client-cluster zusammen:

    cd ..
    export KUBECONFIG=client-server-kubeconfig
    cp ./terraform/server-kubeconfig $KUBECONFIG
    gcloud container clusters get-credentials client-cluster --zone ${ZONE} --project ${PROJECT_ID}
    

    Die Datei client-server-kubeconfig enthält jetzt die Konfiguration für beide Cluster. Diese können Sie mit dem folgenden Befehl prüfen:

    kubectl config view -ojson | jq -r '.clusters[].name'
    

    Die Ausgabe sieht so aus:

    gke_PROJECT_ID_us-central1-c_client-cluster
    server-cluster.k8s.local
    
  7. Rufen Sie den Kontext für die beiden Cluster zur späteren Verwendung ab:

    export CLIENT_CLUSTER=$(kubectl config view -ojson | jq -r '.clusters[].name' | grep client)
    export SERVER_CLUSTER=$(kubectl config view -ojson | jq -r '.clusters[].name' | grep server)
    echo -e "${CLIENT_CLUSTER}\n${SERVER_CLUSTER}"
    

    Die Ausgabe sieht (wieder) so aus:

    gke_PROJECT_ID_us-central1-c_client-cluster
    server-cluster.k8s.local
    

    Sie können diese Clusternamen jetzt als Kontext für weitere kubectl-Befehle verwenden.

  8. Verweisen Sie auf den Clientcluster:

    kubectl --context ${CLIENT_CLUSTER} get pods -n istio-system
    
  9. Verweisen Sie auf den Servercluster:

    kubectl --context ${SERVER_CLUSTER} get pods -n istio-system
    

Clientseite konfigurieren

Wie im Konzeptleitfaden dargestellt, müssen Sie für die Clientseite das Egress-Gateway in Anthos Service Mesh konfigurieren.

In diesem Abschnitt konfigurieren Sie Anthos Service Mesh-Elemente, um externen Traffic anhand seines Ursprungs zu ermitteln und um ein benutzerdefiniertes Zertifikat zum Verschlüsseln der Kommunikation zu nutzen. Außerdem soll nur dieser Traffic an sein Ziel (die MySQL-DB in einem Container) weitergeleitet werden. In der Regel verwenden Sie dazu einen Dienst in Kubernetes. In diesem Fall müssen Sie den Traffic innerhalb der Mesh-Kommunikation erfassen. Zum Erfassen des Traffics verwenden Sie Istio-Elemente für das Erstellen einer speziellen Definition des Dienstes. Sie definieren dabei die folgenden Elemente:

  • Egress-Gateway
  • Diensteintrag
  • Virtueller Dienst
  • TLS-Zertifikate (als Secret)
  • Zielregeln
  1. Rufen Sie in Cloud Shell die serverseitige IP-Adresse des Ingress-Gateways ab. Fragen Sie dazu die Load-Balancer-IP-Adresse des istio-ingressgateway-Dienstes mit dem Kontext der Serverseite ab (zuvor erstellter $SERVER_CLUSTER):

    INGRESS_HOST=$(kubectl -n istio-system --context ${SERVER_CLUSTER} get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    

    Da der INGRESS_HOST nur der IP-Adressteil Ihres Hosts ist, müssen Sie einen vollständig qualifizierten Domainnamen (FQDN) erstellen. Dieser Schritt ist notwendig, da die Zertifikate für die korrekte Funktionsweise einen Domainnamen erfordern.

    In dieser Anleitung verwenden Sie den Platzhalter-DNS-Dienst nip.io, um einen FQDN für die Eingangs-IP-Adresse zu erstellen. Mit diesem Dienst können Sie den FQDN anlegen, ohne eine Domain zu haben.

  2. Speichern Sie die URL des FQDN-Dienstes in einer Umgebungsvariablen:

    export SERVICE_URL="${INGRESS_HOST}.nip.io"
    

    Nachdem Sie SERVICE_URL als FQDN definiert haben, können Sie mit der Definition des Istio-Teils des Clientclusters beginnen.

Egress-Gateway erstellen

Sie beginnen mit dem Erstellen des Egress-Gateways für die Überwachung auf Traffic, der für den externen Dienst bestimmt ist.

Egress-Gateway zur Überwachung auf den für den externen Dienst vorgesehenen Traffic

  1. Erstellen Sie in Cloud Shell die folgende YAML-Datei und nennen Sie sie client-egress-gateway.yaml:

    cat <<EOF > client-egress-gateway.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
     name: istio-egressgateway-mysql
    spec:
     selector:
       istio: egressgateway
     servers:
     - port:
         number: 15443
         name: tls
         protocol: TLS
       hosts:
       - $SERVICE_URL
       tls:
         mode: ISTIO_MUTUAL
    EOF
    
  2. Wenden Sie die obige YAML-Datei auf den Clientcluster an:

    kubectl --context ${CLIENT_CLUSTER} apply -f client-egress-gateway.yaml
    

    Achten Sie auf die Ports. Sie haben hier den default-Port für den Switch für ausgehende Server verwendet, nämlich 15443. Wenn Sie einen anderen Port verwenden möchten, müssen Sie das service-Objekt des Egress-Gateways bearbeiten und zu Ihren benutzerdefinierten Ports hinzufügen.

    Der hosts-Switch definiert den Endpunkt, zu dem der Traffic geleitet werden soll.

Diensteintrag definieren

Als Nächstes legen Sie für das Service Mesh den externen Dienst fest. Istio hat eine eigene Registry, in der Dienstendpunkte für das Mesh gespeichert sind. Wenn Istio zusätzlich zu Kubernetes installiert ist, werden die im Cluster definierten Dienste der Istio-Registry automatisch hinzugefügt. Mit der Definition des Diensteintrags fügen Sie der Istio-Registry einen neuen Endpunkt hinzu, wie im folgenden Diagramm dargestellt:

Hinzufügen eines Endpunkts zur Istio-Registry mithilfe einer Definition für den Diensteintrag

  1. Erstellen Sie in Cloud Shell die folgende YAML-Datei und nennen Sie sie client-service-entry.yaml:

    cat <<EOF > client-service-entry.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: ServiceEntry
    metadata:
     name: mysql-external
    spec:
     hosts:
       - $SERVICE_URL
     location: MESH_EXTERNAL
     ports:
       - number: 3306
         name: tcp
         protocol: TCP
       - number: 13306
         name: tls
         protocol: TLS
     resolution: DNS
     endpoints:
       - address: $SERVICE_URL
         ports:
           tls: 13306
    EOF
    
  2. Wenden Sie die obige YAML-Datei auf den Clientcluster an:

    kubectl --context ${CLIENT_CLUSTER} apply -f client-service-entry.yaml
    

    Die Clientdienstdefinition in dieser YAML-Datei legt für den Dienst fest, welche Art von Traffic erwartet wird (MySQL L4 – Netzwerkebene 4 über Port 3306). Außerdem definieren Sie die Kommunikation als „Mesh-extern“. Im Abschnitt „Endpunkte“ legen Sie fest, dass der Traffic zu der zuvor festgelegten FQDN-Adresse $SERVICE_URL geleitet werden soll, die dem Ingress-Gateway auf dem Servercluster zugeordnet (kOps) ist.

Virtuellen Dienst definieren

Ein virtueller Dienst besteht aus einer Reihe von Traffic-Routingregeln, die angewendet werden sollen, wenn ein Host adressiert wird. Mit jeder Routingregel werden Kriterien zum Abgleich für den Traffic eines bestimmten Protokolls definiert. Wenn der Traffic der Regel entspricht, wird er an einen benannten Zieldienst (oder an eine Untergruppe oder Version davon) gesendet, der in der Registry definiert ist. Weitere Informationen finden Sie in der Istio-Dokumentation.

Definition eines virtuellen Dienstes, der für Istio festlegt, wie das Routing für Traffic zum externen Dienst angewendet werden soll

Die Definition des virtuellen Dienstes legt für Istio fest, wie das Routing für den Traffic zum externen Dienst angewendet werden soll. Mit der folgenden Definition legen Sie für das Mesh fest, den Traffic vom Client an das Egress-Gateway an Port 15443 weiterzuleiten. Leiten Sie dann den Traffic vom Egress-Gateway zum Host $SERVICE_URL an Port 13306 weiter. Dieser wird durch Ihr serverseitiges Ingress-Gateway überwacht.

  1. Erstellen Sie die folgende YAML-Datei und nennen Sie sie client-virtual-service.yaml:

    cat <<EOF > client-virtual-service.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
     name: direct-mysql-through-egress-gateway
    spec:
     hosts:
       - $SERVICE_URL
     gateways:
       - istio-egressgateway-mysql
       - mesh
     tcp:
       - match:
           - gateways:
               - mesh
             port: 3306
         route:
           - destination:
               host: istio-egressgateway.istio-system.svc.cluster.local
               subset: mysql
               port:
                 number: 15443
             weight: 100
       - match:
           - gateways:
               - istio-egressgateway-mysql
             port: 15443
         route:
           - destination:
               host: $SERVICE_URL
               port:
                 number: 13306
             weight: 100
    EOF
    
  2. Wenden Sie die YAML-Definition auf den Clientcluster an:

    kubectl --context ${CLIENT_CLUSTER} apply -f client-virtual-service.yaml
    

    Sie können festlegen, auf welche Gateways die Konfiguration angewendet werden soll. Dazu bearbeiten Sie in der YAML-Datei den Switch gateways.

    Der wesentliche Teil in dieser Definition ist die Verwendung des reservierten Begriffs mesh, der alle Sidecars im Mesh impliziert. Wird dieses Feld weggelassen, wird gemäß der Istio-Dokumentation das Standard-Gateway (Mesh) verwendet, um die Regel auf alle Sidecars im Mesh anzuwenden. Wenn Sie eine Liste mit Gatewaynamen bereitstellen, gelten die Regeln nur für die Gateways. Wenn Sie die Regeln auf Gateways und Sidecars anwenden möchten, geben Sie mesh als einen Gatewaynamen an.

Im nächsten Abschnitt definieren Sie, wie der Traffic verarbeitet wird, der den Client-Produktions-Proxy match.gateways.mesh verlässt. Außerdem definieren Sie mit dem Switch match.gateways.istio-egressgateway-mysql wie Traffic vom Egress-Gateway an den externen Dienst weitergeleitet wird.

Zielregel definieren (vom Client zum Egress-Gateway)

Nachdem Sie definiert haben, wie Traffic an den externen Dienst weitergeleitet wird, müssen Sie die Trafficrichtlinien festlegen. Der oben definierte virtuelle Dienst verarbeitet zwei Routingfälle gleichzeitig. In einem Fall wird Traffic vom Sidecar-Proxy zum Egress-Gateway und im anderen Fall Traffic vom Egress-Gateway zum externen Dienst verarbeitet.

Damit diese Fälle mit den Zielregeln abgeglichen werden, benötigen Sie zwei separate Regeln. Das folgende Diagramm zeigt die erste Regel, mit der Traffic vom Proxy zum Egress-Gateway verarbeitet wird Mit dieser Definition legen Sie für Anthos Service Mesh fest, dass seine Standardzertifikate für die mTLS-Kommunikation verwendet werden sollen.

Zielregel zur Definition, wie Traffic vom Sidecar-Proxy zum Egress-Gateway verarbeitet wird

  1. Erstellen Sie in Cloud Shell die folgende YAML-Datei und nennen Sie sie client-destination-rule-to-egress-gateway.yaml:

    cat <<EOF > client-destination-rule-to-egress-gateway.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
      name: egressgateway-for-mysql
    spec:
      host: istio-egressgateway.istio-system.svc.cluster.local
      subsets:
        - name: mysql
          trafficPolicy:
            loadBalancer:
              simple: ROUND_ROBIN
            portLevelSettings:
              - port:
                  number: 15443
                tls:
                  mode: ISTIO_MUTUAL
                  sni: $SERVICE_URL
    EOF
    
  2. Wenden Sie die obige YAML-Definition auf den Clientcluster an:

    kubectl --context ${CLIENT_CLUSTER} apply -f client-destination-rule-to-egress-gateway.yaml
    

    In obiger YAML-Datei haben Sie mit dem Switch hosts definiert, wie Traffic vom Client-Proxy zum Egress-Gateway weitergeleitet wird. Außerdem haben Sie das Mesh so konfiguriert, dass ISTIO_MUTUAL auf Port 15443 verwendet wird. Dies ist einer der Ports, der automatisch am Egress-Gateway geöffnet wird.

Zielregel erstellen (vom Egress-Gateway zum externen Dienst)

Das folgende Diagramm zeigt die zweite Zielregel, die für das Mesh festlegt, wie Traffic vom Egress-Gateway zum externen Dienst verarbeitet werden soll.

Zweite Zielregel zur Definition, wie Traffic vom Egress-Gateway zum externen Dienst verarbeitet wird

Sie müssen für das Mesh festlegen, dass für die gegenseitige TLS-Kommunikation mit dem externen Dienst Ihre eingefügten Zertifikate verwendet werden sollen.

  1. Erstellen Sie die Zertifikate in Cloud Shell im Verzeichnis anthos-service-mesh-samples/docs/mtls-egress-ingress:

     ./create-keys.sh
    

    Achten Sie darauf, ein Passwort anzugeben, wenn das Skript dazu auffordert.

  2. Kopieren Sie die generierten Dateien in das aktuelle Verzeichnis:

    cp ./certs/2_intermediate/certs/ca-chain.cert.pem ca-chain.cert.pem
    cp ./certs/4_client/private/$SERVICE_URL.key.pem client-$SERVICE_URL.key.pem
    cp ./certs/4_client/certs/$SERVICE_URL.cert.pem client-$SERVICE_URL.cert.pem
    
  3. Erstellen Sie ein Kubernetes-Secret, das die Zertifikate speichert, damit später im Gateway darauf verwiesen werden kann:

     kubectl --context ${CLIENT_CLUSTER} create secret -n istio-system \
      generic client-credential \
      --from-file=tls.key=client-$SERVICE_URL.key.pem \
      --from-file=tls.crt=client-$SERVICE_URL.cert.pem \
      --from-file=ca.crt=ca-chain.cert.pem
    

    Mit dem obigen Befehl werden dem Secret die folgenden Zertifikatsdateien hinzugefügt:

    client-$SERVICE_URL.key.pem
    client-$SERVICE_URL.cert.pem
    ca-chain.cert.pem
    

    Im Folgenden wird die aktuelle Best Practice zur Verteilung von Zertifikaten durch Nutzung des Secret Discovery Service (SDS) anstelle von Dateibereitstellungen angewendet. Durch diese Vorgehensweise wird ein Neustart der Pods nach dem Hinzufügen eines neuen Zertifikats vermieden. Ab Istio 1.8/1.9 benötigen Sie mit dieser Methode keine Lesezugriffsrechte (RBAC) mehr für das Secret der Gateways.

  4. Fügen Sie die Zertifikate zur DestinationRule hinzu und benennen Sie sie mit client-destination-rule-to-external-service.yaml:

    cat <<EOF > client-destination-rule-to-external-service.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
     name: originate-mtls-for-mysql
    spec:
     host: $SERVICE_URL
     trafficPolicy:
       loadBalancer:
         simple: ROUND_ROBIN
       portLevelSettings:
       - port:
           number: 13306
         tls:
           mode: MUTUAL
           credentialName: client-credential
           sni: $SERVICE_URL
    EOF
    
  5. Wenden Sie die obige YAML-Definition auf den Clientcluster an:

    kubectl --context ${CLIENT_CLUSTER} apply -f client-destination-rule-to-external-service.yaml
    

    Diese Regel ist nur wirksam, wenn Sie das Secret zuvor erstellt haben. Das Secret sorgt dafür, dass die Zertifikate zur mTLS-Verschlüsselung vom Egress-Gateway zum externen Endpunkt verwendet werden.

Nachdem Sie diese Schritte ausgeführt haben, ist die clientseitige Einrichtung abgeschlossen wie im folgenden Diagramm dargestellt.

Endgültige clientseitige Einrichtung

Serverseite konfigurieren

Wie im Konzeptleitfaden erläutert müssen Sie für den Server das Ingress-Gateway in Anthos Service Mesh konfigurieren. Bevor Sie die erforderlichen Dateien erstellen, sollten Sie prüfen, welche Komponenten erforderlich sind, um den Serverteil der Lösung umzusetzen.

Im Wesentlichen möchten Sie eingehenden Traffic anhand seines Ursprungs und des Zertifikats ermitteln. Sie möchten auch nur diesen Traffic an sein Ziel weiterleiten, Ihre MySQL-DB in einem Container. Normalerweise verwenden Sie dazu einen Dienst in Kubernetes. In diesem Beispiel ermitteln Sie dagegen den eingehenden Traffic innerhalb der Mesh-Kommunikation. Daher benötigen Sie eine spezielle Definition für einen Dienst, der Folgendes enthält:

  • TLS-Zertifikate (als Secret)
  • Ingress-Gateway
  • Virtueller Dienst

Secret für das Ingress-Gateway erstellen

Zum Schutz der Kommunikation für das Ingress-Gateway müssen Sie die gleichen Zertifikate wie für das Egress-Gateway verwenden. Speichern Sie dazu die Zertifikate in einem Secret und definieren Sie dieses Secret mit dem Ingress-Gatewayobjekt.

  1. Kopieren Sie in Cloud Shell die generierten Dateien in den Speicherort, an dem Sie den nächsten Befehl ausführen:

    cp ./certs/3_application/private/$SERVICE_URL.key.pem server-$SERVICE_URL.key.pem
    cp ./certs/3_application/certs/$SERVICE_URL.cert.pem server-$SERVICE_URL.cert.pem
    
  2. Erstellen Sie das Server-Secret:

    kubectl --context ${SERVER_CLUSTER} create secret -n istio-system \
        generic mysql-credential \
        --from-file=tls.key=server-$SERVICE_URL.key.pem \
        --from-file=tls.crt=server-$SERVICE_URL.cert.pem \
        --from-file=ca.crt=ca-chain.cert.pem
    

    Sie haben jetzt dem Secret die folgenden Zertifikatsdateien hinzugefügt:

    server-$SERVICE_URL.key.pem
    server-$SERVICE_URL.cert.pem
    ca-chain.cert.pem
    

Ingress-Gateway definieren

Um Traffic vom clientseitigen Cluster zu empfangen, müssen Sie ein Ingress-Gateway angeben, das die TLS-Kommunikation mithilfe der Zertifikate entschlüsseln und prüfen kann.

Definition eines Ingress-Gateways zur Prüfung des Traffics auf Sicherheits- und Weiterleitungskriterien

Dieses Diagramm zeigt, wo sich das Ingress-Gateway in Ihrem Cluster befindet. Der Traffic wird weitergeleitet und geprüft, ob er den Sicherheits- und Weiterleitungskriterien entspricht.

  1. Verwenden Sie in Cloud Shell die folgende YAML-Datei und nennen Sie sie server-ingress-gatway.yaml:

    cat <<EOF > server-ingress-gatway.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
     name: gateway-mysql
    spec:
     selector:
       istio: ingressgateway # Istio default gateway implementation
     servers:
     - port:
         number: 13306
         name: tls-mysql
         protocol: TLS
       tls:
         mode: MUTUAL
         credentialName: mysql-credential
       hosts:
       - "$SERVICE_URL"
    EOF
    
  2. Wenden Sie die obige YAML-Definition auf den Clientcluster an:

    kubectl --context ${SERVER_CLUSTER} apply -f server-ingress-gatway.yaml
    

    Beachten Sie den Abschnitt tls:, der besonders wichtig ist. In diesem Abschnitt definieren Sie die Verwendung von mTLS. Damit dies tatsächlich wie gewünscht funktioniert, müssen Sie das von Ihnen erstellte Secret mit den Zertifikaten bereitstellen.

  3. Aktivieren Sie Port 13306 durch Patchen des Dienstes für eingehenden Traffic. Zum Aktivieren dieses Ports erstellen Sie die folgende JSON-Datei und nennen Sie sie gateway-patch.json:

    cat <<EOF > gateway-patch.json
    [{
      "op": "add",
      "path": "/spec/ports/0",
      "value": {
        "name": "tls-mysql",
        "protocol": "TCP",
        "targetPort": 13306,
        "port": 13306
      }
    }]
    EOF
    
  4. Wenden Sie den Patch auf den Gatewaydienst an:

    kubectl --context ${SERVER_CLUSTER} -n istio-system patch --type=json svc istio-ingressgateway -p "$(cat gateway-patch.json)"
    

Nachdem Sie den Port des Ingress-Gateways geöffnet haben, müssen Sie von Ihrem neuen Ingress-Gateway eingehenden Traffic abrufen und an Ihren Datenbank-Pod weiterleiten. Dazu verwenden Sie ein internes Mesh-Objekt, den virtuellen Dienst.

Virtuellen Dienst definieren

Wie bereits erwähnt, ist der virtuelle Dienst eine Definition von Trafficmustern zum Abgleich, die den Traffic innerhalb des Mesh bestimmen. Sie müssen den Traffic von Ihrem Ingress-Gateway an Ihren MySQL-DB-Dienst korrekt ermitteln und weiterleiten, wie im folgenden Diagramm dargestellt.

Ermittlung und Weiterleitung von Traffic vom Ingress-Gateway zum MySQL-DB-Dienst

  1. Erstellen Sie in Cloud Shell die folgende YAML-Datei und nennen Sie sie server-virtual-service.yaml:

    cat <<EOF > server-virtual-service.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
     name: mysql-virtual-service
    spec:
     hosts:
       - "$SERVICE_URL"
     gateways:
       - gateway-mysql
     tcp:
       - route:
         - destination:
             port:
               number: 3306
             host: mysql.default.svc.cluster.local
    EOF
    

    Es ist erforderlich, dass dieser virtuelle Dienst auf das Ingress-Gateway verweist, von dem der Traffic stammt. Der Name des Gateways lautet gateway-mysql.

    Da dieser virtuelle Dienst auf L4 angewendet wird, müssen Sie das Flag tcp zur Beschreibung Ihres MySQL-Traffics angeben. Dieses Flag legt für das Mesh grundsätzlich fest, dass für diesen Traffic L4 verwendet wird.

    Sie haben vielleicht schon gesehen, dass der Dienst für eingehenden Traffic Port 13306 für die Weiterleitung des Traffics nutzt. Der virtuelle Dienst übernimmt diesen Port und setzt ihn auf 3306 zurück.

    Zum Abschluss leiten Sie Traffic an den MySQL-Server im Kubernetes-Cluster des Servers weiter. In diesem Beispiel überwacht der Server den Standard-MySQL-Port 3306.

  2. Wenden Sie die YAML-Definition auf den Servercluster an:

    kubectl --context ${SERVER_CLUSTER} apply -f server-virtual-service.yaml
    

Diese beiden Definitionen entschlüsseln die mTLS-gekapselte MySQL-Clientanfrage und leiten sie an die MySQL-Datenbank im Mesh weiter.

Beachten Sie, dass die interne Mesh-Weiterleitung ebenfalls mit Verschlüsselung ausgeführt wird. In diesem Fall basiert die Verschlüsselung aber auf den internen Mesh-Zertifikaten. Die mTLS-Beendigung erfolgt am Gateway.

Jetzt haben Sie eine vollständig und gegenseitig verschlüsselte Möglichkeit der Kommunikation mit Ihrem MySQL-Server. Diese Art der Verschlüsselung ist für den MySQL-Client und -Server transparent, sodass keine Anwendungsänderungen erforderlich sind. Dies vereinfacht Aufgaben wie das Ändern oder Rotieren von Zertifikaten. Außerdem kann diese Art der Kommunikation für viele unterschiedliche Szenarien verwendet werden.

Einrichtung testen

Nachdem Sie die client- und serverseitige Einrichtung vorgenommen haben, können Sie diese testen.

Trafficweiterleitung von Client zu Server testen

Nun muss Traffic vom Client zum Server generiert werden. Sie möchten dem Trafficfluss folgen und dafür sorgen, dass der Traffic wie gewünscht weitergeleitet und verschlüsselt bzw. entschlüsselt wird.

  1. Stellen Sie in Cloud Shell den MySQL-Server im Servercluster bereit:

    kubectl --context ${SERVER_CLUSTER} apply -f server/mysql-server/mysql.yaml
    
  2. Starten Sie einen MySQL-Client im Clientcluster:

    kubectl run --context ${CLIENT_CLUSTER} --env=SERVICE_URL=$SERVICE_URL -it --image=mysql:5.6 mysql-client-1 --restart=Never -- /bin/bash
    

    Wenn der Container gestartet wurde, wird eine Shell angezeigt, die so aussehen sollte:

    root@mysql-client-1:/#
    
  3. Stellen Sie eine Verbindung zu Ihrem MySQL-Server her:

    mysql -pyougottoknowme -h $SERVICE_URL
    
  4. Verwenden Sie die folgenden Befehle, um eine Datenbank und eine Tabelle hinzuzufügen:

    CREATE DATABASE test_encrypted_connection;
    USE test_encrypted_connection;
    CREATE TABLE message (id INT, content VARCHAR(20));
    INSERT INTO message (id,content) VALUES(1,"Crypto Hi");
    
  5. Nachdem Sie die Verbindung hergestellt und die Tabelle hinzugefügt haben, beenden Sie die MySQL-Verbindung und den Pod:

    exit
    exit
    

    Sie müssen dafür „exit“ zweimal eingeben: einmal, um die DB-Verbindung zu beenden, und dann noch einmal, um den Pod zu beenden. Wenn der Pod nach dem Beenden nicht mehr reagiert, drücken Sie Strg+C, um die Bash-Shell zu schließen.

Mit diesen Schritten sollte eine aussagekräftige Logging-Ausgabe generiert werden, die Sie dann weiter analysieren können.

Im nächsten Abschnitt prüfen Sie, ob der clientseitige Traffic über den Proxy und das Egress-Gateway weitergeleitet wurde. Außerdem testen Sie, ob der Traffic beim Server über das Ingress-Gateway eingeht.

Clientseitigen Proxy und Egress-Gateway testen

  1. Prüfen Sie in Cloud Shell auf dem clientseitigen Proxy, ob die Istio-Proxy-Logs angezeigt werden:

    kubectl --context ${CLIENT_CLUSTER} logs -l run=mysql-client-1 -c istio-proxy -f
    

    Die Debug-Ausgabe sieht dann ungefähr so aus:

    [2021-02-10T21:19:08.292Z] "- - -" 0 - "-" "-" 176 115 10 - "-" "-" "-" "-" "192.168.1.4:15443" outbound|15443|mysql|istio-egressgateway.istio-system.svc.cluster.local 192.168.1.12:58614 34.66.165.46:3306 192.168.1.12:39642 - -
    

    Drücken Sie Strg+C, um die Logausgabe zu beenden.

    In diesem Logeintrag sehen Sie, dass der Client die Anfrage an den Server an der IP-Adresse 34.66.165.46 an Port 3306 sendet. Die Anfrage wird weitergeleitet (outbound) zum istio-egressgateway, das die IP-Adresse 192.168.1.4 an Port 15443 überwacht. Sie haben diese Weiterleitung in Ihrem virtuellen Dienst (client-virtual-service.yaml) definiert.

  2. Prüfen Sie die Proxy-Logs des Egress-Gateways:

    kubectl --context ${CLIENT_CLUSTER} logs -n istio-system -l app=istio-egressgateway -f
    

    Die Ausgabe sieht in etwa so aus:

    [2021-02-10T21:19:08.292Z] "- - -" 0 - "-" "-" 176 115 19 - "-" "-" "-" "-" "34.66.165.46:13306" outbound|13306||34.66.165.46.nip.io 192.168.1.4:53542 192.168.1.4:15443 192.168.1.12:58614 34.66.165.46.nip.io -
    

    Drücken Sie Strg+C, um die Logausgabe zu beenden.

    In diesem Logeintrag können Sie feststellen, dass die Clientanfrage, die an das istio-egressgateway weitergeleitet wurde, das die IP-Adresse 192.168.1.4 an Port 15443 überwacht, an außerhalb des Service Mesh zu dem externen Dienst weitergeleitet wurde, der die IP-Adresse 34.66.165.46 an Port 13306. überwacht. Sie haben diese Weiterleitung im zweiten Teil Ihres virtuellen Dienstes definiert (client-virtual-service.yaml).

Serverseitiges Ingress-Gateway testen

  1. Rufen Sie in Cloud Shell auf der Serverseite die Proxy-Logs des Ingress-Gateways auf:

    kubectl --context ${SERVER_CLUSTER} logs -n istio-system -l app=istio-ingressgateway -f
    

    Die Ausgabe im Log sieht in etwa so aus:

    [2021-02-10T21:22:27.381Z] "- - -" 0 - "-" "-" 0 78 5 - "-" "-" "-" "-" "100.96.4.8:3306" outbound|3306||mysql.default.svc.cluster.local 100.96.1.3:55730 100.96.1.3:13306 100.96.1.1:42244 34.66.165.46.nip.io -
    

    Drücken Sie Strg+C, um die Logausgabe zu beenden.

    In diesem Logeintrag können Sie feststellen, dass die Anfrage des externen Clients, die an das istio-ingressgateway weitergeleitet wurde, das die IP-Adresse 34.66.165.46 an Port 13306 überwacht, zum MySQL-Dienst innerhalb des Mesh weitergeleitet wurde, der mit dem Dienstnamen mysql.default.svc.cluster.local an Port 3306. identifiziert wurde. Sie haben diese Weiterleitung im Ingress-Gateway (server-ingress-gateway.yaml) definiert.

  2. Prüfen Sie für den MySQL-Server die Istio-Proxy-Logs:

    kubectl --context ${SERVER_CLUSTER} logs -l app=mysql -c istio-proxy -f
    

    Die Ausgabe sieht dann ungefähr so aus:

    [2021-02-10T21:22:27.382Z] "- - -" 0 - "-" "-" 1555 1471 4 - "-" "-" "-" "-" "127.0.0.1:3306" inbound|3306|mysql|mysql.default.svc.cluster.local 127.0.0.1:45894 100.96.4.8:3306 100.96.1.3:55730 outbound_.3306_._.mysql.default.svc.cluster.local -
    

    Drücken Sie Strg+C, um die Logausgabe zu beenden.

    In diesem Logeintrag ist der eingehende Aufruf an den MySQL-Datenbankserver aufgeführt, der die IP-Adresse 100.96.4.8 an Port 3306 überwacht. Der Aufruf stammt vom Pod für eingehenden Traffic mit der IP-Adresse 100.96.1.3.

    Weitere Informationen zum Logging-Format von Envoy finden Sie unter Zugriffslogs von Envoy abrufen.

  3. Testen Sie Ihre Datenbank, um die generierte Eingabe zu prüfen:

    MYSQL=$(kubectl --context ${SERVER_CLUSTER} get pods -n default | tail -n 1 | awk '{print $1}')
    kubectl --context ${SERVER_CLUSTER} exec $MYSQL -ti -- /bin/bash
    
  4. Prüfen Sie die erstellte Datenbank:

    mysql -pyougottoknowme
    USE test_encrypted_connection;
    SELECT * from message;
    

    Die Ausgabe sieht etwa so aus:

    +------+-----------+
    | id   | content   |
    +------+-----------+
    |    1 | Crypto Hi |
    +------+-----------+
    1 row in set (0.00 sec)
    
  5. Verlassen Sie die MySQL-Datenbank:

    exit
    exit
    

    Sie müssen exit zweimal eingeben – einmal, um die DB-Verbindung zu beenden, und dann noch einmal, um den Pod zu beenden.

Zugriff nach Entfernen der Zertifikate testen

Nachdem Sie nun getestet und geprüft haben, ob der Zugriff über die eingefügten Zertifikate funktioniert, testen Sie nun das Gegenteil: Was geschieht, wenn Sie das Egress-Gateway und das Zertifikat weglassen? Dieser Test wird auch als negativer Test bezeichnet.

Sie können diesen Test durch Starten eines anderen Pods in einem Namespace ausführen, bei dem der Proxy nicht auf der jeweiligen Seite eingefügt wurde.

  1. Erstellen Sie in Cloud Shell einen neuen Namespace.

    kubectl --context ${CLIENT_CLUSTER} create ns unencrypted
    
  2. Erstellen Sie einen Pod und starten Sie eine interaktive Shell im Container:

    kubectl --context ${CLIENT_CLUSTER} run -it --image=mysql:5.6 \
    mysql-client-2 --env=SERVICE_URL=$SERVICE_URL \
    -n unencrypted --restart=Never -- /bin/bash
    
  3. Versuchen Sie, nach dem Start der interaktiven Shell eine Verbindung zur Datenbank herzustellen:

    mysql -pyougottoknowme -h $SERVICE_URL
    

    Nach 30 Sekunden wird eine Ausgabe ähnlich der folgenden dargestellt:

    Warning: Using a password on the command line interface can be insecure.
    ERROR 2003 (HY000): Can't connect to MySQL server on '104.154.164.12.nip.io' (110)
    

    Diese Warnung ist erwartungsgemäß, da dieser Pod das Egress-Gateway umgeht und versucht, das Ingress-Gateway ($SERVICE_URL) direkt über das Internet zu erreichen.

  4. Versuchen Sie, die Dienst-IP-Adresse aufzulösen:

    resolveip $SERVICE_URL
    

    Die entsprechende Ausgabe sieht etwa so aus: Ihre IP-Adresse lautet aber anders.

    IP address of 104.154.164.12.nip.io is 104.154.164.12
    

    Dies zeigt, dass der FQDN aufgelöst werden kann und die fehlgeschlagene Verbindung tatsächlich auf das Fehlen eines eingefügten Zertifikats zurückzuführen ist.

  5. Beenden Sie die MySQL-Verbindung und den MySQL-Server-Pod:

    exit
    exit
    

Genauere Untersuchung

In dieser Anleitung wird nicht behandelt, dass Konfigurationen für ausgehenden Traffic normalerweise zu einer anderen Rolle oder Organisation in Ihrem Unternehmen gehören, da sie im Namespace istio-system gehostet werden. Konfigurieren Sie Kubernetes RBAC-Berechtigungen so, dass nur Netzwerkadministratoren die in dieser Anleitung beschriebenen Ressourcen direkt erstellen und ändern können.

Nachdem Sie nun wissen, wie Sie ein Service Mesh verwenden, um eine sichere Kommunikation zu gewährleisten, sollten Sie dies mit einer Anwendung testen, die Daten sicher austauschen muss und bei der Sie die Verschlüsselung bis zur Zertifikatsebene steuern möchten. Für den Einstieg können Sie Anthos Service Mesh installieren.

Verwenden Sie zwei GKE-Cluster und kombinieren Sie diese mithilfe der Methode dieser Anleitung. Diese Methode funktioniert auf der GKE Enterprise-Plattform auch zwischen zwei ausländischen Kubernetes-Clustern.

Service Meshes sind eine hervorragende Möglichkeit, den Schutz innerhalb des Clusters sowie bei externen Diensten zu erhöhen. Ein letzter Anwendungsfall für den Test ist ein mTLS-Endpunkt, der kein zweiter Kubernetes-Cluster, sondern ein Drittanbieter ist (z. B. ein Zahlungsdienstleister) ist.

Bereinigen

Um zu vermeiden, dass Ihrem Google Cloud-Konto die in dieser Anleitung verwendeten Ressourcen in Rechnung gestellt werden, können Sie Ihr Projekt löschen.

Projekt löschen

  1. Wechseln Sie in der Google Cloud Console zur Seite Ressourcen verwalten.

    Zur Seite „Ressourcen verwalten“

  2. Wählen Sie in der Projektliste das Projekt aus, das Sie löschen möchten, und klicken Sie dann auf Löschen.
  3. Geben Sie im Dialogfeld die Projekt-ID ein und klicken Sie auf Shut down (Beenden), um das Projekt zu löschen.

Nächste Schritte