Udostępnij przez


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

W artykule Wdrażanie zewnętrznej lub wewnętrznej bramy Istio Ingress opisano sposób konfigurowania bramy Ingress w celu udostępnienia usługi HTTP dla ruchu zewnętrznego/wewnętrznego. 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 wejściowej TLS

Utwórz tajny TLS w Kubernetes dla bramy wejściowej; użyj Azure Key Vault do hostowania certyfikatów/kluczy i dodatek Azure Key Vault Secrets Provider do synchronizacji tajemnic z klastrem.

Skonfiguruj Azure Key Vault i zsynchronizuj sekrety 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 dodatek dostawcy Azure Key Vault dla sterownika CSI magazynu tajemnic w klastrze.

    az aks enable-addons --addons azure-keyvault-secrets-provider --resource-group $RESOURCE_GROUP --name $CLUSTER
    
  3. Jeśli Key Vault używa Azure RBAC do modelu uprawnień, postępuj zgodnie z podanymi instrukcjami, aby przydzielić rolę użytkownika do zarządzania tajemnicami w Key Vault dla tożsamości zarządzanej użytkownika dodatku. Alternatywnie, jeśli magazyn kluczy korzysta z modelu uprawnień zasad dostępu magazynu, autoryzuj przypisaną przez użytkownika, zarządzaną tożsamość dodatku w celu uzyskania dostępu do zasobu usługi Azure Key Vault przy użyciu zasad dostępu.

    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 tajemnic 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: kubernetes.io/tls
        data:
        - objectName: test-productpage-bookinfo-key
          key: tls.key
        - objectName: test-productpage-bookinfo-crt
          key: tls.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"
        tenantId: $TENANT_ID
    EOF
    

    Alternatywnie, aby odwołać się do typu obiektu certyfikatu bezpośrednio z usługi Azure Key Vault, użyj następującego manifestu w celu wdrożenia klasy SecretProviderClass. W tym przykładzie test-productpage-bookinfo-cert-pxf jest nazwą obiektu certyfikatu w usłudze Azure Key Vault. Aby uzyskać więcej informacji, zobacz sekcję Uzyskiwanie certyfikatów i kluczy .

    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: kubernetes.io/tls
        data:
        - objectName: test-productpage-bookinfo-key
          key: tls.key
        - objectName: test-productpage-bookinfo-crt
          key: tls.crt
      parameters:
        useVMManagedIdentity: "true"
        userAssignedIdentityID: $CLIENT_ID 
        keyvaultName: $AKV_NAME
        cloudName: ""
        objects:  |
          array:
            - |
              objectName: test-productpage-bookinfo-cert-pfx  #certificate object name from keyvault
              objectType: secret
              objectAlias: "test-productpage-bookinfo-key"
            - |
              objectName: test-productpage-bookinfo-cert-pfx #certificate object name from keyvault
              objectType: cert
              objectAlias: "test-productpage-bookinfo-crt"
        tenantId: $TENANT_ID
    EOF
    
  6. Użyj następującego manifestu, aby wdrożyć przykładowy pod. 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 wejściowej i usługi wirtualnej

Kierowanie ruchu HTTPS przez bramę ingress 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 bramy credentialName musi być zgodna z zasobem secretName SecretProviderClass i selector musi odwoływać się do zewnętrznej bramy wejściowej, oznaczonej etykietą, w której klucz etykiety to istio, a wartość to aks-istio-ingressgateway-external. Dla wewnętrznej bramy wejściowej etykieta to istio, a 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 HTTPS do usługi HTTPS, czyli ustawić bramę, aby wykonywała przekazywanie SNI zamiast kończenia TLS dla żądań przychodzących, zaktualizuj tryb TLS w definicji bramy na PASSTHROUGH. Spowoduje to, że brama przepuści ruch przychodzący, jak jest, bez zakończenia protokołu TLS.

Konfigurowanie mutualnie uwierzytelnianej bramy TLS dla ruchu przychodzącego

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

  1. Zaktualizuj poświadczenia bramy wejściowej, usuwając bieżący sekret 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 do ponownego wdrożenia przykładowego podu w celu zsynchronizowania tajnych danych 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 tajemnicę utworzoną w przestrzeni nazw aks-istio-ingress.

      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 i ustawić tryb TLS na WZAJEMNE.

    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 oraz klucz prywatny z flagą --key do polecenia 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ług Istio i wejścia (pozostawiając klaster bez zmian), 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

Dalsze kroki

  • Konfigurowanie ingressu dla dodatku Istio service mesh przy użyciu API bramy Kubernetes