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
Activer le module complémentaire Istio sur le cluster comme indiqué dans la documentation
Déployer une passerelle d’entrée Istio externe comme indiqué dans la documentation
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.
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
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
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
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
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
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
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
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
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 nomsaks-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.
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 nomsaks-istio-ingress
du 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
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
Azure Kubernetes Service