Azure Kubernetes Service용 Istio 서비스 메시 추가 기능에 대한 보안 수신 게이트웨이
외부 또는 내부 Istio 수신 배포 문서에서는 외부/내부 트래픽에 HTTP 서비스를 노출하도록 수신 게이트웨이를 구성하는 방법을 설명합니다. 이 문서에서는 단순 또는 상호 TLS를 사용하여 보안 HTTPS 서비스를 노출하는 방법을 보여 줍니다.
필수 조건
참고 항목
이 문서에서는 데모를 위해 외부 수신 게이트웨이를 참조하며, 내부 수신 게이트웨이에 대한 상호 TLS를 구성하는 데 동일한 단계가 적용됩니다.
필수 클라이언트/서버 인증서 및 키
이 문서에는 여러 인증서와 키가 필요합니다. 즐겨 찾는 도구를 사용하여 만들거나 다음 openssl 명령을 사용할 수 있습니다.
샘플 서비스에 대한 인증서에 서명하기 위한 루트 인증서 및 프라이빗 키를 만듭니다.
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
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
클라이언트 인증서 및 프라이빗 키를 생성합니다.
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 설정 및 클러스터에 비밀 동기화
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
클러스터에서 비밀 저장소 CSI 드라이버용 Azure Key Vault 공급자 추가 기능을 사용하도록 설정합니다.
az aks enable-addons --addons azure-keyvault-secrets-provider --resource-group $RESOURCE_GROUP --name $CLUSTER
액세스 정책을 사용하여 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
인증서와 키를 사용하여 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
다음 매니페스트를 사용해 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
다음 매니페스트를 사용하여 샘플 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를 지원합니다.
현재 비밀을 삭제하고 새 비밀을 만들어 수신 게이트웨이 자격 증명을 업데이트합니다. 서버는 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
다음 매니페스트를 사용해 게이트웨이 정의를 업데이트하여 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
Azure Kubernetes Service