Partekatu honen bidez:


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

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.

  1. 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
    
  2. 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
    
  3. 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

  1. 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
    
  2. 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
    
  3. 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
    
  4. 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
    
  5. 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
    
  6. 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 nombres aks-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.

  1. 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 nombres aks-istio-ingressdel 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
      
  2. 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