Freigeben über


Sicheres Eingangsgateway für das Istio-Dienstgitter-Add-On für Azure Kubernetes Service

Im ArtikelBereitstellen eines externen oder internen Istio-Eingangs wird beschrieben, wie Sie ein Eingangsgateway konfigurieren, sodass ein HTTP-Dienst für externen/internen Datenverkehr verfügbar gemacht wird. In diesem Artikel wird gezeigt, wie Sie einen sicheren HTTPS-Dienst mit einfachem TLS oder mTLS (mutual TLS) verfügbar machen.

Voraussetzungen

Hinweis

Dieser Artikel bezieht sich zu Demonstrationszwecken auf das externe Eingangsgateway. Die gleichen Schritte gelten für die Konfiguration von mTLS für ein internes Eingangsgateway.

Erforderliche Client-/Serverzertifikate und Schlüssel

Für diesen Artikel sind mehrere Zertifikate und Schlüssel erforderlich. Sie können sie mit Ihrem bevorzugten Tool erstellen oder hierzu die folgenden openssl-Befehle verwenden.

  1. Erstellen Sie ein Stammzertifikat und einen privaten Schlüssel zum Signieren der Zertifikate für Beispieldienste:

    mkdir bookinfo_certs
    openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=bookinfo Inc./CN=bookinfo.com' -keyout bookinfo_certs/bookinfo.com.key -out bookinfo_certs/bookinfo.com.crt
    
  2. Generieren Sie ein Zertifikat und einen privaten Schlüssel für productpage.bookinfo.com:

    openssl req -out bookinfo_certs/productpage.bookinfo.com.csr -newkey rsa:2048 -nodes -keyout bookinfo_certs/productpage.bookinfo.com.key -subj "/CN=productpage.bookinfo.com/O=product organization"
    openssl x509 -req -sha256 -days 365 -CA bookinfo_certs/bookinfo.com.crt -CAkey bookinfo_certs/bookinfo.com.key -set_serial 0 -in bookinfo_certs/productpage.bookinfo.com.csr -out bookinfo_certs/productpage.bookinfo.com.crt
    
  3. Generieren Sie ein Clientzertifikat und einen privaten Schlüssel:

    openssl req -out bookinfo_certs/client.bookinfo.com.csr -newkey rsa:2048 -nodes -keyout bookinfo_certs/client.bookinfo.com.key -subj "/CN=client.bookinfo.com/O=client organization"
    openssl x509 -req -sha256 -days 365 -CA bookinfo_certs/bookinfo.com.crt -CAkey bookinfo_certs/bookinfo.com.key -set_serial 1 -in bookinfo_certs/client.bookinfo.com.csr -out bookinfo_certs/client.bookinfo.com.crt
    

Konfigurieren eines TLS-Eingangsgateways

Erstellen Sie ein Kubernetes-TLS-Geheimnis für das Eingangsgateway. Verwenden Sie Azure Key Vault als Host für Zertifikate/Schlüssel und das Azure Key Vault Secrets Provider-Add-On, um geheime Schlüssel mit dem Cluster zu synchronisieren.

Einrichten von Azure Key Vault und Synchronisieren geheimer Schlüssel mit dem Cluster

  1. Erstellen eines Azure Key Vault

    Sie benötigen eine Azure Key Vault-Ressource, um das Zertifikat und die Schlüsseleingaben für das Istio-Add-On bereitstellen zu können.

    export AKV_NAME=<azure-key-vault-resource-name>  
    az keyvault create --name $AKV_NAME --resource-group $RESOURCE_GROUP --location $LOCATION
    
  2. Aktivieren Sie das Add-On Azure Key Vault-Anbieter für den Geheimnisspeicher-CSI-Treiber in Ihrem Cluster.

    az aks enable-addons --addons azure-keyvault-secrets-provider --resource-group $RESOURCE_GROUP --name $CLUSTER
    
  3. Autorisieren Sie die benutzerseitig zugewiesene verwaltete Identität des Add-Ons mittels Zugriffsrichtlinie zum Zugriff auf die Azure Key Vault-Ressource. Wenn Ihr Key Vault Azure als Berechtigungsmodell RBAC verwendet, folgen Sie den hier aufgeführten Anweisungen, um der benutzerseitig zugewiesenen verwalteten Identität des Add-Ons eine Azure-Rolle von Key Vault zuzuweisen.

    OBJECT_ID=$(az aks show --resource-group $RESOURCE_GROUP --name $CLUSTER --query 'addonProfiles.azureKeyvaultSecretsProvider.identity.objectId' -o tsv | tr -d '\r')
    CLIENT_ID=$(az aks show --resource-group $RESOURCE_GROUP --name $CLUSTER --query 'addonProfiles.azureKeyvaultSecretsProvider.identity.clientId')
    TENANT_ID=$(az keyvault show --resource-group $RESOURCE_GROUP --name $AKV_NAME --query 'properties.tenantId')
    
    az keyvault set-policy --name $AKV_NAME --object-id $OBJECT_ID --secret-permissions get list
    
  4. Erstellen Sie mithilfe der Zertifikate und der Schlüssel Geheimnisse in Azure Key Vault.

    az keyvault secret set --vault-name $AKV_NAME --name test-productpage-bookinfo-key --file bookinfo_certs/productpage.bookinfo.com.key
    az keyvault secret set --vault-name $AKV_NAME --name test-productpage-bookinfo-crt --file bookinfo_certs/productpage.bookinfo.com.crt
    az keyvault secret set --vault-name $AKV_NAME --name test-bookinfo-crt --file bookinfo_certs/bookinfo.com.crt
    
  5. Verwenden Sie das folgende Manifest, um SecretProviderClass bereitzustellen, um für Azure Key Vault spezifische Parameter für den CSI-Treiber bereitzustellen.

    cat <<EOF | kubectl apply -f -
    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
      name: productpage-credential-spc
      namespace: aks-istio-ingress
    spec:
      provider: azure
      secretObjects:
      - secretName: productpage-credential
        type: tls
        data:
        - objectName: test-productpage-bookinfo-key
          key: key
        - objectName: test-productpage-bookinfo-crt
          key: cert
      parameters:
        useVMManagedIdentity: "true"
        userAssignedIdentityID: $CLIENT_ID 
        keyvaultName: $AKV_NAME
        cloudName: ""
        objects:  |
          array:
            - |
              objectName: test-productpage-bookinfo-key
              objectType: secret
              objectAlias: "test-productpage-bookinfo-key"
            - |
              objectName: test-productpage-bookinfo-crt
              objectType: secret
              objectAlias: "test-productpage-bookinfo-crt"
        tenantId: $TENANT_ID
    EOF
    
  6. Verwenden Sie das folgende Manifest, um einen Beispielpod bereitzustellen. Der CSI-Treiber für den Geheimnisspeicher erfordert, dass ein Pod auf die SecretProviderClass-Ressource verweist, um die Synchronisierung der Geheimnisse von Azure Key Vault mit dem Cluster sicherzustellen.

    cat <<EOF | kubectl apply -f -
    apiVersion: v1
    kind: Pod
    metadata:
      name: secrets-store-sync-productpage
      namespace: aks-istio-ingress
    spec:
      containers:
        - name: busybox
          image: mcr.microsoft.com/oss/busybox/busybox:1.33.1
          command:
            - "/bin/sleep"
            - "10"
          volumeMounts:
          - name: secrets-store01-inline
            mountPath: "/mnt/secrets-store"
            readOnly: true
      volumes:
        - name: secrets-store01-inline
          csi:
            driver: secrets-store.csi.k8s.io
            readOnly: true
            volumeAttributes:
              secretProviderClass: "productpage-credential-spc"
    EOF
    
    • Überprüfen Sie, ob das Geheimnis productpage-credential im Clusternamespace aks-istio-ingress erstellt wurde, wie in der SecretProviderClass-Ressource definiert.

      kubectl describe secret/productpage-credential -n aks-istio-ingress
      

      Beispielausgabe:

      Name:         productpage-credential
      Namespace:    aks-istio-ingress
      Labels:       secrets-store.csi.k8s.io/managed=true
      Annotations:  <none>
      
      Type:  tls
      
      Data
      ====
      cert:  1066 bytes
      key:   1704 bytes
      

Konfigurieren des Eingangsgateways und des virtuellen Diensts

Leiten Sie HTTPS-Datenverkehr über das Istio-Eingangsgateway an die Beispielanwendungen weiter. Verwenden Sie das folgende Manifest, um Gateway- und virtuelle Dienstressourcen bereitzustellen.

cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: bookinfo-gateway
spec:
  selector:
    istio: aks-istio-ingressgateway-external
  servers:
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: productpage-credential
    hosts:
    - productpage.bookinfo.com
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: productpage-vs
spec:
  hosts:
  - productpage.bookinfo.com
  gateways:
  - bookinfo-gateway
  http:
  - match:
    - uri:
        exact: /productpage
    - uri:
        prefix: /static
    - uri:
        exact: /login
    - uri:
        exact: /logout
    - uri:
        prefix: /api/v1/products
    route:
    - destination:
        port:
          number: 9080
        host: productpage
EOF

Hinweis

In der Gatewaydefinition muss credentialName dem Parameter secretName SecretProviderClass-Ressource entsprechen, und selector muss über die Beschriftung auf das externe Eingangsgateway verweisen, in dem der Schlüssel der Bezeichnung istio und der Wert aks-istio-ingressgateway-external lautet. Die Bezeichnung des internen Eingangsgateways lautet istio und der Wert aks-istio-ingressgateway-internal.

Legen Sie Umgebungsvariablen für den Host und die Ports des externen Eingangsgateways fest:

export INGRESS_HOST_EXTERNAL=$(kubectl -n aks-istio-ingress get service aks-istio-ingressgateway-external -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
export SECURE_INGRESS_PORT_EXTERNAL=$(kubectl -n aks-istio-ingress get service aks-istio-ingressgateway-external -o jsonpath='{.spec.ports[?(@.name=="https")].port}')
export SECURE_GATEWAY_URL_EXTERNAL=$INGRESS_HOST_EXTERNAL:$SECURE_INGRESS_PORT_EXTERNAL

echo "https://$SECURE_GATEWAY_URL_EXTERNAL/productpage"

Überprüfung

Senden Sie eine HTTPS-Anforderung, um über HTTPS auf den Productpage-Dienst zuzugreifen:

curl -s -HHost:productpage.bookinfo.com --resolve "productpage.bookinfo.com:$SECURE_INGRESS_PORT_EXTERNAL:$INGRESS_HOST_EXTERNAL" --cacert bookinfo_certs/bookinfo.com.crt "https://productpage.bookinfo.com:$SECURE_INGRESS_PORT_EXTERNAL/productpage" | grep -o "<title>.*</title>"

Vergewissern Sie sich, dass auf die Produktseite der Beispielanwendung zugegriffen werden kann. Die erwartete Ausgabe lautet:

<title>Simple Bookstore App</title>

Hinweis

Um den HTTPS-Eingangszugriff mit einem HTTPS-Dienst zu konfigurieren, d. h. ein Ingress-Gateway so zu konfigurieren, dass bei eingehenden Anforderungen ein SNI-Passthrough anstelle eines TLS-Abschlusses durchgeführt wird, ändern bzw. aktualisieren Sie den TLS-Modus in der Gatewaydefinition in PASSTHROUGH. Dadurch wird das Gateway angewiesen, den eingehenden Datenverkehr unverändert weiterzuleiten, ohne TLS zu beenden.

Konfigurieren eines mTLS-Eingangsgateways

Erweitern Sie Ihre Gatewaydefinition, sodass mTLS unterstützt wird.

  1. Aktualisieren Sie die Anmeldeinformationen des Eingangsgateways, indem Sie den aktuellen geheimen Schlüssel löschen und einen neuen erstellen. Der Server verwendet das Zertifizierungsstellenzertifikat, um seine Clients zu überprüfen, und wir müssen den Schlüssel ca.crt verwenden, um das Zertifizierungsstellenzertifikat zu speichern.

    kubectl delete secretproviderclass productpage-credential-spc -n aks-istio-ingress
    kubectl delete secret/productpage-credential -n aks-istio-ingress
    kubectl delete pod/secrets-store-sync-productpage -n aks-istio-ingress
    

    Verwenden Sie das folgende Manifest, um SecretProviderClass mit dem Zertifizierungsstellenzertifikat neu zu erstellen.

    cat <<EOF | kubectl apply -f -
    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
      name: productpage-credential-spc
      namespace: aks-istio-ingress
    spec:
      provider: azure
      secretObjects:
      - secretName: productpage-credential
        type: opaque
        data:
        - objectName: test-productpage-bookinfo-key
          key: tls.key
        - objectName: test-productpage-bookinfo-crt
          key: tls.crt
        - objectName: test-bookinfo-crt
          key: ca.crt
      parameters:
        useVMManagedIdentity: "true"
        userAssignedIdentityID: $CLIENT_ID 
        keyvaultName: $AKV_NAME
        cloudName: ""
        objects:  |
          array:
            - |
              objectName: test-productpage-bookinfo-key
              objectType: secret
              objectAlias: "test-productpage-bookinfo-key"
            - |
              objectName: test-productpage-bookinfo-crt
              objectType: secret
              objectAlias: "test-productpage-bookinfo-crt"
            - |
              objectName: test-bookinfo-crt
              objectType: secret
              objectAlias: "test-bookinfo-crt"
        tenantId: $TENANT_ID
    EOF
    

    Verwenden Sie das folgende Manifest, um den Beispielpod erneut bereitzustellen, sodass geheime Schlüssel aus Azure Key Vault mit dem Cluster synchronisiert werden.

    cat <<EOF | kubectl apply -f -
    apiVersion: v1
    kind: Pod
    metadata:
      name: secrets-store-sync-productpage
      namespace: aks-istio-ingress
    spec:
      containers:
        - name: busybox
          image: registry.k8s.io/e2e-test-images/busybox:1.29-4
          command:
            - "/bin/sleep"
            - "10"
          volumeMounts:
          - name: secrets-store01-inline
            mountPath: "/mnt/secrets-store"
            readOnly: true
      volumes:
        - name: secrets-store01-inline
          csi:
            driver: secrets-store.csi.k8s.io
            readOnly: true
            volumeAttributes:
              secretProviderClass: "productpage-credential-spc"
    EOF
    
    • Überprüfen Sie den geheimen Schlüssel productpage-credential, der im Clusternamespace aks-istio-ingress erstellt wurde.

      kubectl describe secret/productpage-credential -n aks-istio-ingress
      

      Beispielausgabe:

      Name:         productpage-credential
      Namespace:    aks-istio-ingress
      Labels:       secrets-store.csi.k8s.io/managed=true
      Annotations:  <none>
      
      Type:  opaque
      
      Data
      ====
      ca.crt:   1188 bytes
      tls.crt:  1066 bytes
      tls.key:  1704 bytes
      
  2. Verwenden Sie das folgende Manifest, um die Gatewaydefinition zu aktualisieren, um den TLS-Modus auf MUTUAL festzulegen.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: Gateway
    metadata:
      name: bookinfo-gateway
    spec:
      selector:
        istio: aks-istio-ingressgateway-external # use istio default ingress gateway
      servers:
      - port:
          number: 443
          name: https
          protocol: HTTPS
        tls:
          mode: MUTUAL
          credentialName: productpage-credential # must be the same as secret
        hosts:
        - productpage.bookinfo.com
    EOF
    

Überprüfung

Versuchen Sie, HTTPS-Anforderung mithilfe des vorherigen Ansatzes – ohne das Clientzertifikat zu übergeben – zu senden. Sie werden dann feststellen, dass die Anforderung fehlschlägt.

curl -v -HHost:productpage.bookinfo.com --resolve "productpage.bookinfo.com:$SECURE_INGRESS_PORT_EXTERNAL:$INGRESS_HOST_EXTERNAL" --cacert bookinfo_certs/bookinfo.com.crt "https://productpage.bookinfo.com:$SECURE_INGRESS_PORT_EXTERNAL/productpage" 

Beispielausgabe:


...
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS alert, unknown (628):
* OpenSSL SSL_read: error:0A00045C:SSL routines::tlsv13 alert certificate required, errno 0
* Failed receiving HTTP2 data
* OpenSSL SSL_write: SSL_ERROR_ZERO_RETURN, errno 0
* Failed sending HTTP2 data
* Connection #0 to host productpage.bookinfo.com left intact
curl: (56) OpenSSL SSL_read: error:0A00045C:SSL routines::tlsv13 alert certificate required, errno 0

Übergeben Sie das Zertifikat Ihres Clients mit dem Flag --cert und den privaten Schlüssel mit dem Flag --key an curl.

curl -s -HHost:productpage.bookinfo.com --resolve "productpage.bookinfo.com:$SECURE_INGRESS_PORT_EXTERNAL:$INGRESS_HOST_EXTERNAL" --cacert bookinfo_certs/bookinfo.com.crt --cert bookinfo_certs/client.bookinfo.com.crt --key bookinfo_certs/client.bookinfo.com.key "https://productpage.bookinfo.com:$SECURE_INGRESS_PORT_EXTERNAL/productpage" | grep -o "<title>.*</title>"

Vergewissern Sie sich, dass auf die Produktseite der Beispielanwendung zugegriffen werden kann. Die erwartete Ausgabe lautet:

<title>Simple Bookstore App</title>

Löschen von Ressourcen

Wenn Sie das Istio-Dienstnetz und die Eingangsgateways bereinigen (und den Cluster zurücklassen) möchten, führen Sie den folgenden Befehl aus:

az aks mesh disable --resource-group ${RESOURCE_GROUP} --name ${CLUSTER}

Wenn Sie alle Ressourcen bereinigen möchten, die in den Istio-Anleitungsdokumenten erstellt wurden, führen Sie den folgenden Befehl aus:

az group delete --name ${RESOURCE_GROUP} --yes --no-wait