Puerta de enlace de entrada segura para el complemento de malla de servicio Istio para Azure Kubernetes Service
En el artículo Entradas externas o internas de Azure Kubernetes Service (AKS) para el despliegue del complemento de malla de servicios Istio se describe cómo configurar una puerta de enlace de entrada para exponer un servicio HTTP al tráfico externo o interno. En este artículo se muestra cómo usar TLS simple o mutua para exponer un servicio HTTPS.
Requisitos previos
Habilitación del complemento Istio en el clúster según la documentación
Implementación de una puerta de enlace de entrada de Istio externa según la documentación
Nota:
En este artículo se hace referencia a la puerta de enlace de entrada externa a modo de demostración, pero se aplicarán los mismos pasos para configurar TLS mutua para la puerta de enlace de entrada interna.
Claves y certificados cliente y servidor necesarios
En este artículo se requieren varios certificados y claves. Para crearlos puede usar su herramienta favorita, o bien puede usar los siguientes comandos openssl.
Cree un certificado raíz y una clave privada para firmar los certificados de los servicios de ejemplo:
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
Genere un certificado y una clave privada para
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
Generar una clave privada y un certificado de cliente:
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
Configuración de una puerta de enlace de entrada TLS
Cree un secreto de TLS de Kubernetes para la puerta de enlace de entrada; use Azure Key Vault para hospedar certificados o claves y claves, y el complemento Proveedor de secretos de Azure Key Vault para sincronizar secretos con el clúster.
Configuración de Azure Key Vault y sincronización de secretos con el clúster
Creación de Azure Key Vault
Necesita un recurso de Azure Key Vault para proporcionar el certificado y las entradas de clave al complemento Istio.
export AKV_NAME=<azure-key-vault-resource-name> az keyvault create --name $AKV_NAME --resource-group $RESOURCE_GROUP --location $LOCATION
Habilite el complemento del proveedor de Azure Key Vault para el controlador CSI del almacén de secretos en su clúster.
az aks enable-addons --addons azure-keyvault-secrets-provider --resource-group $RESOURCE_GROUP --name $CLUSTER
Autorice a la identidad administrada asignada por el usuario del complemento a acceder al recurso de Azure Key Vault mediante una directiva de acceso. Como alternativa, si Key Vault usa Azure RBAC para el modelo de permisos, siga las instrucciones que encontrará aquí para asignar un rol de Azure de Key Vault para la identidad administrada asignada por el usuario del complemento.
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
Cree secretos en Azure Key Vault mediante los certificados y la clave.
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
Use el siguiente manifiesto para implementar SecretProviderClass, con el fin de proporcionar parámetros específicos de Azure Key Vault al controlador 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
Use el siguiente manifiesto para implementar un pod de ejemplo. El controlador CSI del almacén de secretos requiere un pod para hacer referencia al recurso SecretProviderClass, con el fin de asegurarse de que se sincronizan los secretos entre Azure Key Vault y el clúster.
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
Compruebe el secreto
productpage-credential
creado en el espacio de nombresaks-istio-ingress
del clúster como se define en el recurso SecretProviderClass.kubectl describe secret/productpage-credential -n aks-istio-ingress
Ejemplo:
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
Configuración de una puerta de enlace de entrada y de un servicio virtual
Enrute el tráfico HTTPS a las aplicaciones de ejemplo a través de la puerta de enlace de entrada de Istio. Use el siguiente manifiesto para implementar recursos de la puerta de enlace y del servicio virtual.
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:
En la definición de puerta de enlace, credentialName
debe coincidir con secretName
en el recurso SecretProviderClass y selector
debe hacer referencia a la puerta de enlace de entrada externa por su etiqueta, en la que la clave de la etiqueta es istio
y el valor es aks-istio-ingressgateway-external
. En el caso de la puerta de enlace de entrada interna la etiqueta es istio
y el valor es aks-istio-ingressgateway-internal
.
Configura las variables de entorno para el host y los puertos de entrada externos:
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"
Comprobación
Envíe una solicitud HTTPS para acceder al servicio productpage a través de 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>"
Confirma que la página del producto de la aplicación de ejemplo es accesible. El resultado esperado es:
<title>Simple Bookstore App</title>
Nota:
Para configurar el acceso de entrada de HTTPS a un servicio HTTPS, es decir, para configurar una puerta de enlace de entrada para que realice un paso a través de SNI, en lugar de una terminación TLS en las solicitudes entrantes, actualice el modo tls en la definición de puerta de enlace a PASSTHROUGH
. Esto indica a la puerta de enlace que pase el tráfico de entrada "tal como está", sin terminar TLS.
Configuración de una puerta de enlace de entrada TLS mutua
Amplíe la definición de la puerta de enlace para admitir TLS mutua.
Actualice la credencial de la puerta de enlace de entrada. Para ello, debe eliminar el secreto actual y crear otro. El servidor usa el certificado de CA para comprobar sus clientes y debemos usar la clave ca.crt para albergar el certificado de 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
Use el siguiente manifiesto para volver a crear SecretProviderClass con el certificado de 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
Use el siguiente manifiesto para volver a implementar el pod de ejemplo, con el fin de sincronizar secretos de Azure Key Vault con el clúster.
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
Compruebe el secreto
productpage-credential
creado en el espacio de nombresaks-istio-ingress
del clúster.kubectl describe secret/productpage-credential -n aks-istio-ingress
Ejemplo:
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
Use el siguiente manifiesto para actualizar la definición de la puerta de enlace para establecer el modo TLS en MÚTUA.
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
Comprobación
Intente enviar una solicitud HTTPS mediante el enfoque anterior (sin pasar el certificado de cliente) y verá que se produce un error.
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"
Ejemplo:
...
* 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
Pase el certificado del cliente con la marca --cert
y la clave privada con la marca --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>"
Confirma que la página del producto de la aplicación de ejemplo es accesible. El resultado esperado es:
<title>Simple Bookstore App</title>
Eliminar recursos
Si quieres limpiar la malla de servicio de Istio y la entrada (dejando atrás el clúster), ejecuta el siguiente comando:
az aks mesh disable --resource-group ${RESOURCE_GROUP} --name ${CLUSTER}
Si quieres limpiar todos los recursos creados a partir de los documentos de instrucciones paso a paso de Istio, ejecuta el siguiente comando:
az group delete --name ${RESOURCE_GROUP} --yes --no-wait
Azure Kubernetes Service