Partager via


Passerelle d’entrée sécurisée pour le module complémentaire de maillage de services Istio pour Azure Kubernetes Service

L’article Déployer une entrée externe ou interne Istio décrit comment configurer une passerelle d’entrée pour exposer un service HTTP au trafic externe/interne. Cet article explique comment exposer un service HTTPS sécurisé à l’aide de TLS simple ou mutuel.

Prérequis

Remarque

Cet article fait référence à la passerelle d’entrée externe pour la démonstration. Les mêmes étapes s’appliquent à la configuration de TLS mutuel pour la passerelle d’entrée interne.

Certificats et clés client/serveur requis

Cet article nécessite plusieurs certificats et clés. Vous pouvez utiliser votre outil favori pour les créer ou utiliser les commandes openssl suivantes.

  1. Créez un certificat racine et une clé privée pour signer les certificats pour les exemples de services :

    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. Générer un nouveau certificat et une clé privée pour 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. Générer un certificat client et une clé privée :

    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
    

Configurer une passerelle d’entrée TLS

Créez un secret TLS Kubernetes pour la passerelle d’entrée ; utilisez Azure Key Vault pour héberger des certificats/clés et le module complémentaire du fournisseur de secrets Azure Key Vault pour synchroniser les secrets avec le cluster.

Configurer Azure Key Vault et synchroniser les secrets avec le cluster

  1. Créer un Azure Key Vault

    Vous avez besoin d’une ressource Azure Key Vault pour fournir le certificat et les entrées de clé au module complémentaire Istio.

    export AKV_NAME=<azure-key-vault-resource-name>  
    az keyvault create --name $AKV_NAME --resource-group $RESOURCE_GROUP --location $LOCATION
    
  2. Activez le module complémentaire Fournisseur Azure Key Vault pour le pilote CSI du magasin de secrets sur votre cluster.

    az aks enable-addons --addons azure-keyvault-secrets-provider --resource-group $RESOURCE_GROUP --name $CLUSTER
    
  3. Autorisez l’identité managée affectée par l’utilisateur du module complémentaire à accéder à la ressource Azure Key Vault à l’aide d’une stratégie d’accès. Sinon, si votre Key Vault utilise le contrôle d’accès en fonction du rôle (RBAC) Azure pour le modèle d’autorisations, suivez les instructions ici pour attribuer le rôle Azure Key Vault à l’identité managée affectée par l’utilisateur du module complémentaire.

    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. Créez des secrets dans Azure Key Vault à l’aide des certificats et des clés.

    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. Utilisez le manifeste suivant pour déployer SecretProviderClass afin de fournir les paramètres spécifiques à Azure Key Vault au pilote CSI.

    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. Utilisez le manifeste suivant pour déployer un exemple de pod. Le pilote CSI du magasin de secrets nécessite un pod pour référencer la ressource SecretProviderClass afin de garantir la synchronisation des secrets entre Azure Key Vault et le cluster.

    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
    
    • Vérifiez que le secret productpage-credential créé sur l’espace de noms aks-istio-ingress du cluster est défini dans la ressource SecretProviderClass.

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

      Exemple de sortie :

      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
      

Configurer la passerelle d’entrée et le service virtuel

Acheminer le trafic HTTPS via la passerelle d’entrée Istio vers les exemples d’applications. Utilisez le manifeste suivant pour déployer des ressources de passerelle et de service virtuel.

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

Remarque

Dans la définition de passerelle, credentialName doit correspondre à secretName dans la ressource SecretProviderClass et selector doit référencer la passerelle d’entrée externe par son étiquette, dans laquelle la clé de l’étiquette est istio et la valeur est aks-istio-ingressgateway-external. Pour la passerelle d’entrée interne, l’étiquette est istio et la valeur est aks-istio-ingressgateway-internal.

Définissez des variables d’environnement pour l’hôte et les ports d’entrée externes :

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"

Vérification

Envoyez une requête HTTPS pour accéder au service productpage via HTTPS :

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>"

Vérifiez que la page des produits de l’exemple d’application est accessible. La sortie attendue est :

<title>Simple Bookstore App</title>

Remarque

Pour configurer l’accès d’entrée HTTPS à un service HTTPS, c’est-à-dire configurer une passerelle d’entrée pour effectuer une transmission SNI au lieu de le terminaison TLS sur les requêtes entrantes, mettez à jour le mode tls dans la définition de passerelle vers PASSTHROUGH. Cela indique à la passerelle de passer le trafic d’entrée « tel quel», sans terminer TLS.

Configurer une passerelle d’entrée TLS mutuel

Étendez votre définition de passerelle pour prendre en charge le protocole TLS mutuel.

  1. Mettez à jour les informations d’identification de la passerelle d’entrée en supprimant le secret actuel et en créant un nouveau. Le serveur utilise le certificat d’autorité de certification pour vérifier ses clients, et nous devons utiliser la clé ca.crt pour contenir le certificat d’autorité de certification.

    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
    

    Utilisez le manifeste suivant pour recréer SecretProviderClass avec un certificat d’autorité de certification.

    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
    

    Utilisez le manifeste suivant pour redéployer un exemple de pod afin de synchroniser les secrets d’Azure Key Vault avec le cluster.

    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
    
    • Vérifiez le secret productpage-credential créé sur l’espace de noms aks-istio-ingressdu cluster.

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

      Exemple de sortie :

      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. Utilisez le manifeste suivant pour mettre à jour la définition de passerelle pour définir le mode TLS sur MUTUEL.

    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
    

Vérification

Essayez d’envoyer une requête HTTPS à l’aide de l’approche précédente, sans passer le certificat client, et voyez-le échouer.

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" 

Exemple de sortie :


...
* 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

Transmettez le certificat de votre client avec l’indicateur --cert et la clé privée avec l’indicateur --key à 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>"

Vérifiez que la page des produits de l’exemple d’application est accessible. La sortie attendue est :

<title>Simple Bookstore App</title>

Supprimer des ressources

Si vous voulez nettoyer le maillage de services Istio et les entrées (en conservant le cluster), exécutez la commande suivante :

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

Si vous voulez nettoyer toutes les ressources créées à partir des documents sur les procédures Istio, exécutez la commande suivante :

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