Gateway di ingresso sicuro per il componente aggiuntivo mesh del servizio Istio per il servizio Azure Kubernetes
L'articolo Distribuire l'ingresso Istio esterno o interno descrive come configurare un gateway di ingresso per esporre un servizio HTTP al traffico esterno/interno. Il presente articolo illustra come esporre un servizio HTTPS sicuro usando TLS semplice o reciproco.
Prerequisiti
Abilitare il componente aggiuntivo Istio nel cluster come descrottp nella documentazione
Distribuire un gateway di ingresso Istio esterno come descritto nella documentazione
Nota
Questo articolo si riferisce al gateway di ingresso esterno per la dimostrazione, si applicano gli stessi passaggi per la configurazione di TLS reciproco per il gateway di ingresso interno.
Certificati e chiavi client/server necessari
Questo articolo richiede l'impiego di vari certificati e chiavi. È possibile usare lo strumento preferito per crearli oppure utilizzare i seguenti comandi openssl.
Creare un certificato radice e una chiave privata per firmare i certificati per i servizi di esempio:
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
Generare un certificato e una chiave privata per
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
Generare un certificato client e una chiave privata:
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
Configurare un gateway di ingresso TLS
Creare un segreto TLS Kubernetes per il gateway in ingresso; usare Azure Key Vault per ospitare certificati/chiavi e il componente aggiuntivo Provider di segreti di Azure Key Vault per sincronizzare i segreti nel cluster.
Configurare Azure Key Vault e sincronizzare i segreti nel cluster
Creare un Azure Key Vault
È necessaria una risorsa di Azure Key Vault per fornire il certificato e gli input della chiave al componente aggiuntivo Istio.
export AKV_NAME=<azure-key-vault-resource-name> az keyvault create --name $AKV_NAME --resource-group $RESOURCE_GROUP --location $LOCATION
Abilitare il componente aggiuntivo sul cluster provider di Azure Key Vault per il driver CSI dell'archivio segreto.
az aks enable-addons --addons azure-keyvault-secrets-provider --resource-group $RESOURCE_GROUP --name $CLUSTER
Autorizzare l'identità gestita assegnata dall'utente del componente aggiuntivo per accedere alla risorsa di Azure Key Vault mediante i criteri di accesso. In alternativa, se Key Vault utilizza il controllo degli accessi in base al ruolo di Azure per il modello di autorizzazioni, seguire le istruzioni riportate qui per assegnare un ruolo di Azure di Key Vault per l'identità gestita assegnata dall'utente del componente aggiuntivo.
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
Creare segreti in Azure Key Vault utilizzando i certificati e le chiavi.
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
Usare il manifesto seguente per distribuire SecretProviderClass per fornire parametri specifici di Azure Key Vault al driver 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
Utilizzare il manifesto seguente per distribuire un pod di esempio. Il driver CSI dell'archivio segreti richiede un pod per fare riferimento alla risorsa SecretProviderClass per garantire la sincronizzazione dei segreti da Azure Key Vault al 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
Verificare il segreto
productpage-credential
creato nello spazio dei nomiaks-istio-ingress
del cluster, come definito nella risorsa SecretProviderClass.kubectl describe secret/productpage-credential -n aks-istio-ingress
Output di esempio:
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
Configurare il gateway in ingresso e il servizio virtuale
Instradare il traffico HTTPS tramite il gateway di ingresso Istio alle applicazioni di esempio. Applicare il manifesto seguente per distribuire le risorse del gateway e del servizio virtuale.
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
Nota
Nella definizione del gateway, credentialName
deve corrispondere a secretName
nella risorsa SecretProviderClass e selector
deve fare riferimento al gateway di ingresso esterno in base alla relativa etichetta, in cui la chiave dell'etichetta è istio
e il valore è aks-istio-ingressgateway-external
. Per l'etichetta gateway di ingresso interna è istio
e il valore è aks-istio-ingressgateway-internal
.
Impostare le variabili di ambiente per l'host e le porte in ingresso esterni:
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"
Verifica
Inviare una richiesta HTTPS per accedere al servizio productpage tramite 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>"
Verificare che la pagina del prodotto dell'applicazione di esempio sia accessibile. L'output previsto è il seguente:
<title>Simple Bookstore App</title>
Nota
Per configurare l'accesso in ingresso HTTPS a un servizio HTTPS, ad esempio per impostare un gateway di ingresso per eseguire il pass-through SNI anziché la terminazione TLS nelle richieste in ingresso, aggiornare a PASSTHROUGH
la modalità tls nella definizione del gateway. Ciò indica al gateway di passare il traffico in ingresso "così come è", senza terminare TLS.
Configurare un gateway di ingresso TLS reciproco
Estendere la definizione del gateway per supportare il TLS reciproco.
Aggiornare le credenziali del gateway in ingresso eliminando il segreto corrente e creandone uno nuovo. Il server utilizza il certificato della CA per verificare i client ed è necessario usare la chiave ca.crt per conservare il certificato della CA.
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
Usare il manifesto seguente per ricreare SecretProviderClass con il certificato della CA.
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
Utilizzare il manifesto seguente per ridistribuire il pod di esempio per sincronizzare i segreti da Azure Key Vault al 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
Verificare il segreto
productpage-credential
creato nello spazio dei nomiaks-istio-ingress
del cluster.kubectl describe secret/productpage-credential -n aks-istio-ingress
Output di esempio:
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
Usare il manifesto seguente per aggiornare la definizione del gateway per impostare la modalità TLS su MUTUAL.
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
Verifica
Tentare di inviare una richiesta HTTPS usando l'approccio precedente, senza passare il certificato client, e verificarne l'esito negativo.
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"
Output di esempio:
...
* 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
Passare il certificato del client con il flag --cert
e la chiave privata con il flag --key
a 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>"
Verificare che la pagina del prodotto dell'applicazione di esempio sia accessibile. L'output previsto è il seguente:
<title>Simple Bookstore App</title>
Eliminare risorse
Se si vuole pulire la mesh del servizio Istio e i dati in ingresso (lasciando il cluster), eseguire il comando seguente:
az aks mesh disable --resource-group ${RESOURCE_GROUP} --name ${CLUSTER}
Se si desidera pulire tutte le risorse create dai documenti di istruzioni di Istio, eseguire il comando seguente:
az group delete --name ${RESOURCE_GROUP} --yes --no-wait
Azure Kubernetes Service