Bezpieczna brama ruchu przychodzącego dla dodatku siatki usługi Istio dla usługi Azure Kubernetes Service

W artykule Deploy external or internal Istio Ingress (Wdrażanie zewnętrznego lub wewnętrznego ruchu przychodzącego) opisano sposób konfigurowania bramy ruchu przychodzącego w celu uwidocznienia usługi HTTP na ruch zewnętrzny/wewnętrzny. W tym artykule pokazano, jak uwidocznić bezpieczną usługę HTTPS przy użyciu prostego lub wzajemnego protokołu TLS.

Wymagania wstępne

Uwaga

W tym artykule opisano zewnętrzną bramę ruchu przychodzącego na potrzeby pokazu. Te same kroki dotyczą konfigurowania wzajemnego protokołu TLS dla wewnętrznej bramy ruchu przychodzącego.

Wymagane certyfikaty i klucze klienta/serwera

Ten artykuł wymaga kilku certyfikatów i kluczy. Możesz użyć ulubionego narzędzia do ich utworzenia lub użyć następujących poleceń openssl .

  1. Utwórz certyfikat główny i klucz prywatny na potrzeby podpisywania certyfikatów dla przykładowych usług:

    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. Wygeneruj certyfikat i klucz prywatny dla elementu 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. Wygeneruj certyfikat klienta i klucz prywatny:

    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
    

Konfigurowanie bramy ruchu przychodzącego TLS

Utwórz wpis tajny PROTOKOŁU TLS platformy Kubernetes dla bramy ruchu przychodzącego; Usługa Azure Key Vault umożliwia hostowanie certyfikatów/kluczy i dodatku dostawcy wpisów tajnych usługi Azure Key Vault w celu synchronizacji wpisów tajnych z klastrem.

Konfigurowanie usługi Azure Key Vault i synchronizowanie wpisów tajnych z klastrem

  1. Tworzenie usługi Azure Key Vault

    Potrzebny jest zasób usługi Azure Key Vault, aby podać dane wejściowe certyfikatu i klucza do dodatku Istio.

    export AKV_NAME=<azure-key-vault-resource-name>  
    az keyvault create --name $AKV_NAME --resource-group $RESOURCE_GROUP --location $LOCATION
    
  2. Włącz dostawcę usługi Azure Key Vault dla dodatku sterownika CSI magazynu wpisów tajnych w klastrze.

    az aks enable-addons --addons azure-keyvault-secrets-provider --resource-group $RESOURCE_GROUP --name $CLUSTER
    
  3. Autoryzuj tożsamość zarządzaną przypisaną przez użytkownika dodatku, aby uzyskać dostęp do zasobu usługi Azure Key Vault przy użyciu zasad dostępu. Alternatywnie, jeśli usługa Key Vault używa kontroli dostępu opartej na rolach platformy Azure dla modelu uprawnień, postępuj zgodnie z instrukcjami w tym miejscu , aby przypisać rolę platformy Azure usługi Key Vault dla tożsamości zarządzanej przypisanej przez użytkownika dodatku.

    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. Tworzenie wpisów tajnych w usłudze Azure Key Vault przy użyciu certyfikatów i kluczy.

    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. Użyj następującego manifestu, aby wdrożyć klasę SecretProviderClass w celu udostępnienia parametrów specyficznych dla usługi Azure Key Vault dla sterownika 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. Użyj następującego manifestu, aby wdrożyć przykładowy zasobnik. Sterownik CSI magazynu wpisów tajnych wymaga zasobnika, aby odwoływać się do zasobu SecretProviderClass w celu zapewnienia synchronizacji wpisów tajnych z usługi Azure Key Vault do klastra.

    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
    
    • Sprawdź productpage-credential wpis tajny utworzony w przestrzeni aks-istio-ingress nazw klastra zgodnie z definicją w zasobie SecretProviderClass.

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

      Przykładowe wyjście:

      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
      

Konfigurowanie bramy ruchu przychodzącego i usługi wirtualnej

Kierowanie ruchu HTTPS za pośrednictwem bramy ruchu przychodzącego Istio do przykładowych aplikacji. Użyj następującego manifestu, aby wdrożyć bramę i zasoby usługi wirtualnej.

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

Uwaga

W definicji credentialName bramy musi być zgodna z zasobem secretName SecretProviderClass i selector musi odwoływać się do zewnętrznej bramy ruchu przychodzącego według etykiety, w której klucz etykiety to istio , a wartość to aks-istio-ingressgateway-external. Dla wewnętrznej etykiety bramy ruchu przychodzącego jest istio i wartość to aks-istio-ingressgateway-internal.

Ustaw zmienne środowiskowe dla zewnętrznego hosta i portów przychodzących:

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"

Weryfikacja

Wyślij żądanie HTTPS, aby uzyskać dostęp do usługi productpage za pośrednictwem protokołu 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>"

Upewnij się, że strona produktu przykładowej aplikacji jest dostępna. Oczekiwane dane wyjściowe to:

<title>Simple Bookstore App</title>

Uwaga

Aby skonfigurować dostęp przychodzący HTTPS do usługi HTTPS, tj. skonfigurować bramę ruchu przychodzącego do wykonywania przekazywania SNI zamiast kończenia żądań przychodzących protokołu TLS, zaktualizuj tryb tls w definicji bramy na PASSTHROUGHwartość . Spowoduje to, że brama przekaże ruch przychodzący "tak, jak to jest", bez przerywania protokołu TLS.

Konfigurowanie wzajemnej bramy ruchu przychodzącego TLS

Rozszerz definicję bramy, aby obsługiwać wzajemne protokoły TLS.

  1. Zaktualizuj poświadczenia bramy ruchu przychodzącego, usuwając bieżący wpis tajny i tworząc nowy. Serwer używa certyfikatu urzędu certyfikacji do zweryfikowania swoich klientów i musimy użyć klucza ca.crt do przechowywania certyfikatu urzędu certyfikacji.

    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
    

    Użyj następującego manifestu, aby ponownie utworzyć klasę SecretProviderClass z certyfikatem urzędu certyfikacji.

    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
    

    Użyj następującego manifestu, aby ponownie wdrożyć przykładowy zasobnik, aby zsynchronizować wpisy tajne z usługi Azure Key Vault z klastrem.

    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
    
    • Sprawdź productpage-credential wpis tajny utworzony w przestrzeni nazw aks-istio-ingressklastra .

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

      Przykładowe wyjście:

      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. Użyj następującego manifestu, aby zaktualizować definicję bramy, aby ustawić tryb TLS na WARTOŚĆ 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
    

Weryfikacja

Spróbuj wysłać żądanie HTTPS przy użyciu wcześniejszego podejścia — bez przekazywania certyfikatu klienta — i zobacz, że kończy się niepowodzeniem.

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" 

Przykładowe wyjście:


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

Przekaż certyfikat klienta z flagą --cert i kluczem prywatnym z flagą --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>"

Upewnij się, że strona produktu przykładowej aplikacji jest dostępna. Oczekiwane dane wyjściowe to:

<title>Simple Bookstore App</title>

Usuwanie zasobów

Jeśli chcesz wyczyścić siatkę usługi Istio i ruch przychodzący (pozostawiając za klastrem), uruchom następujące polecenie:

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

Jeśli chcesz wyczyścić wszystkie zasoby utworzone na podstawie dokumentów z instrukcjami istio, uruchom następujące polecenie:

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