다음을 통해 공유


Azure Kubernetes Service용 Istio 서비스 메시 추가 기능에 대한 보안 수신 게이트웨이

외부 또는 내부 Istio 수신 배포 문서에서는 외부/내부 트래픽에 HTTP 서비스를 노출하도록 수신 게이트웨이를 구성하는 방법을 설명합니다. 이 문서에서는 단순 또는 상호 TLS를 사용하여 보안 HTTPS 서비스를 노출하는 방법을 보여 줍니다.

필수 조건

참고 항목

이 문서에서는 데모를 위해 외부 수신 게이트웨이를 참조하며, 내부 수신 게이트웨이에 대한 상호 TLS를 구성하는 데 동일한 단계가 적용됩니다.

필수 클라이언트/서버 인증서 및 키

이 문서에는 여러 인증서와 키가 필요합니다. 즐겨 찾는 도구를 사용하여 만들거나 다음 openssl 명령을 사용할 수 있습니다.

  1. 샘플 서비스에 대한 인증서에 서명하기 위한 루트 인증서 및 프라이빗 키를 만듭니다.

    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. 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. 클라이언트 인증서 및 프라이빗 키를 생성합니다.

    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
    

TLS 수신 게이트웨이 구성

수신 게이트웨이에 대한 Kubernetes TLS 비밀을 만듭니다. Azure Key Vault를 사용하여 인증서/키를 호스트하고 Azure Key Vault 비밀 공급자 추가 기능을 사용하여 비밀을 클러스터에 동기화합니다.

Azure Key Vault 설정 및 클러스터에 비밀 동기화

  1. Azure Key Vault 만들기

    Istio 추가 기능에 인증서와 키 입력을 제공하려면 Azure Key Vault 리소스가 필요합니다.

    export AKV_NAME=<azure-key-vault-resource-name>  
    az keyvault create --name $AKV_NAME --resource-group $RESOURCE_GROUP --location $LOCATION
    
  2. 클러스터에서 비밀 저장소 CSI 드라이버용 Azure Key Vault 공급자 추가 기능을 사용하도록 설정합니다.

    az aks enable-addons --addons azure-keyvault-secrets-provider --resource-group $RESOURCE_GROUP --name $CLUSTER
    
  3. 액세스 정책을 사용하여 Azure Key Vault 리소스에 액세스할 수 있도록 추가 기능의 사용자 할당 관리 ID에 권한을 부여합니다. 또는 Key Vault가 권한 모델에 Azure RBAC를 사용하는 경우 여기에 있는 지침에 따라 추가 기능의 사용자 할당 관리 ID에 Key Vault의 Azure 역할을 할당합니다.

    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. 인증서와 키를 사용하여 Azure Key Vault에서 비밀을 만듭니다.

    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. 다음 매니페스트를 사용해 SecretProviderClass를 배포하여 CSI 드라이버에 Azure Key Vault 특정 매개 변수를 제공합니다.

    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. 다음 매니페스트를 사용하여 샘플 Pod를 배포합니다. 비밀 저장소 CSI 드라이버에는 비밀이 Azure Key Vault에서 클러스터로 동기화되도록 SecretProviderClass 리소스를 참조하는 Pod가 필요합니다.

    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
    
    • SecretProviderClass 리소스에 정의된 대로 클러스터 네임스페이스 aks-istio-ingress에서 만든 productpage-credential 비밀을 확인합니다.

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

      예제 출력:

      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
      

수신 게이트웨이 및 가상 서비스 구성

Istio 수신 게이트웨이를 통해 HTTPS 트래픽을 샘플 애플리케이션으로 라우팅합니다. 다음 매니페스트를 사용하여 게이트웨이 및 가상 서비스 리소스를 배포합니다.

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

참고 항목

게이트웨이 정의에서 credentialName은 SecretProviderClass 리소스의 secretName과 일치해야 하며, selector는 해당 레이블로 외부 수신 게이트웨이를 참조해야 합니다. 여기서 레이블의 키는 istio이고 값은 aks-istio-ingressgateway-external입니다. 내부 수신 게이트웨이의 경우 레이블은 istio이고 값은 aks-istio-ingressgateway-internal입니다.

외부 수신 호스트 및 포트에 대한 환경 변수를 설정합니다.

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"

확인

HTTPS를 통해 제품 페이지 서비스에 액세스하기 위해 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>"

샘플 애플리케이션의 제품 페이지에 액세스할 수 있는지 확인합니다. 예상 출력은 다음과 같습니다.

<title>Simple Bookstore App</title>

참고 항목

HTTPS 서비스에 대한 HTTPS 수신 액세스를 구성하려면, 즉 들어오는 요청에서 TLS 종료 대신 SNI 통과를 수행하도록 수신 게이트웨이를 구성하고 게이트웨이 정의의 tls 모드를 PASSTHROUGH로 업데이트합니다. 이렇게 하면 TLS를 종료하지 않고 수신 트래픽을 “있는 그대로” 전달하도록 게이트웨이에 지시합니다.

상호 TLS 수신 게이트웨이 구성

게이트웨이 정의를 확장하여 상호 TLS를 지원합니다.

  1. 현재 비밀을 삭제하고 새 비밀을 만들어 수신 게이트웨이 자격 증명을 업데이트합니다. 서버는 CA 인증서를 사용하여 클라이언트를 확인하고 ca.crt 키를 사용하여 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
    

    다음 매니페스트를 사용하여 CA 인증서를 통해 SecretProviderClass를 다시 만듭니다.

    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
    

    다음 매니페스트를 사용해 샘플 Pod를 다시 배포하여 Azure Key Vault에서 클러스터로 비밀을 동기화합니다.

    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
    
    • 클러스터 네임스페이스 aks-istio-ingress에서 만든 productpage-credential 비밀을 확인합니다.

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

      예제 출력:

      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. 다음 매니페스트를 사용해 게이트웨이 정의를 업데이트하여 TLS 모드를 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
    

확인

클라이언트 인증서를 전달하지 않고 이전 방식을 사용하여 HTTPS 요청을 보내려고 시도했지만 실패했습니다.

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" 

예제 출력:


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

클라이언트 인증서를 --cert 플래그 및 프라이빗 키와 함께 --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>"

샘플 애플리케이션의 제품 페이지에 액세스할 수 있는지 확인합니다. 예상 출력은 다음과 같습니다.

<title>Simple Bookstore App</title>

리소스 삭제

Istio 서비스 메시 및 수신(클러스터 뒤에 남겨짐)을 정리하려면 다음 명령을 실행합니다.

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

Istio 방법 지침 문서에서 만든 모든 리소스를 정리하려면 다음 명령을 실행합니다.

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