Certificate Authority Service를 사용하여 Cloud Service Mesh 인그레스 게이트웨이의 TLS 인증서 관리 자동화

이 튜토리얼에서는 플랫폼 운영자를 위해 cert-manager 도구의 Certificate Authority Service 발급기관을 사용하여 Cloud Service Mesh 인그레스 게이트웨이에 대해 TLS 인증서 관리를 자동화하는 방법을 보여줍니다. 이 인증서를 사용하면 인그레스 게이트웨이가 Virtual Private Cloud(VPC)의 클라이언트에서 시작하지만 서비스 메시 외부에 있는 HTTPS 및 기타 TLS 및 mTLS 트래픽을 종료할 수 있습니다. 이 튜토리얼에서는 사용자에게 Kubernetes 및 TLS 인증서 관련 기본 지식이 있다고 가정합니다.

소개

Cloud Service Mesh는 서비스 메시의 모든 워크로드에 대해 TLS 인증서를 프로비저닝합니다. 이러한 인증서는 서비스 메시에 있는 워크로드 사이에 암호화되고 상호 인증된 TLS(mTLS) 통신을 사용 설정합니다. 지원되는 CA 문제 중 하나이고 인증서를 서명합니다.

하지만 Cloud Service Mesh는 서비스 메시에 들어가는 트래픽에 대해 인그레스 게이트웨이로 인증서를 자동으로 프로비저닝하지 않습니다. 한 가지 일반적인 솔루션은 오픈소스 cert-manager 도구를 사용해서 인그레스 게이트웨이 인증서 관리를 자동화하는 것입니다.

cert-manager 도구는 인증 기관(CA)에 해당하는 발급기관에게서 인증서를 요청합니다. Certificate Authority Service(CA 서비스)는 자체 비공개 CA를 만들 수 있게 해주는 Google Cloud 서비스입니다. cert-manager 도구는 오픈소스 CA 서비스 외부 발급기관을 사용하여 CA 서비스에게서 인증서를 요청할 수 있습니다.

비공개 CA는 내부 네트워크 내에서 트래픽을 인증하고 암호화하는 TLS 인증서를 발급할 수 있습니다. Cloud Service Mesh 인그레스 게이트웨이는 VPC 내부에 있지만 서비스 메시 외부에 있는 클라이언트에서 수신되는 트래픽을 허용하도록 설정되는 경우가 많습니다. 내부 네트워크 트래픽의 경우 CA Service의 비공개 CA를 사용해서 인그레스 게이트웨이에 대해 인증서를 발급할 수 있습니다.

이 튜토리얼에서는 인그레스 게이트웨이에 대한 TLS 인증서 프로비저닝 및 갱신을 자동화하도록 cert-manager 도구 및 CA Service 발급기관을 설정하는 방법을 보여줍니다. cert-manager 도구는 인증서를 TLS 유형의 Kubernetes 보안 비밀 리소스로 프로비저닝합니다. cert-manager 도구는 인증서를 갱신할 때 새 인증서로 보안 비밀 리소스를 업데이트합니다. 인그레스 게이트웨이는 Envoy 프록시를 실행하고 Envoy 보안 비밀 검색 서비스(SDS)를 지원합니다. SDS는 관리자가 프로세스를 다시 시작하거나 다시 로드하지 않아도 인그레스 게이트웨이가 새 인증서를 사용할 수 있게 해줍니다.

메시에 포함된 사이드카 프록시는 CA 서비스 또는 Cloud Service Mesh 인증 기관에서 TLS 인증서를 가져올 수 있습니다. 이 튜토리얼에서는 사이드카 프록시 및 인그레스 게이트웨이 인증서에 CA 서비스를 사용합니다. 이렇게 하면 모든 TLS 인증서에 대해 하나의 루트 CA를 사용할 수 있습니다.

다음 다이어그램은 이 튜토리얼에서 프로비저닝하는 리소스를 보여줍니다. 인그레스 게이트웨이에 대해 내부 패스 스루 네트워크 부하 분산기를 프로비저닝합니다. 내부 패스 스루 네트워크 부하 분산기는 프록시가 아닙니다. 따라서 TCP 연결을 종료하거나 TLS 핸드셰이크를 수행하지 않습니다. 대신 istio-ingressgateway 배포의 포드로 연결을 라우팅합니다.

hello-example-com-credential 보안 비밀에는 인증서 및 비공개 키가 포함되어 있습니다. hello 게이트웨이는 호스트 이름이 hello.example.com인 요청에 대해 TLS 핸드셰이크를 수행하기 위해 이 인증서 및 비공개 키를 사용하도록 istio-ingressgateway 배포의 포드를 구성합니다.

CA 서비스를 통한 mTLS 관리

cert-manager 네임스페이스에서 google-cas-issuer 배포의 포드는 CA 서비스에서 만드는 CA로부터 인증서를 요청합니다. ca-service-isser 포드가 GKE용 워크로드 아이덴티티 제휴를 사용해서 Google 서비스 계정을 가장할 수 있게 해주는 Identity and Access Management 정책 바인딩을 만듭니다. 이 Google 서비스 계정에 CA 풀에서 IAM 정책 바인딩을 만들어 CA 서비스의 CA에서 인증서를 요청할 수 있는 권한을 부여합니다.

목표

비용

이 튜토리얼에서는 비용이 청구될 수 있는 다음과 같은 Google Cloud 구성요소를 사용합니다.

프로젝트 사용량을 기준으로 예상 비용을 산출하려면 가격 계산기를 사용하세요. Google Cloud를 처음 사용하는 사용자는 무료 체험판을 사용할 수 있습니다.

이 튜토리얼을 마친 후에 계속 비용이 청구되지 않도록 하려면 만든 리소스를 삭제하면 됩니다. 자세한 내용은 삭제를 참조하세요.

시작하기 전에

  1. Google Cloud 콘솔에서 프로젝트 선택기 페이지로 이동한 후 프로젝트를 선택하거나 만듭니다.

  2. Google Cloud 프로젝트에 결제가 사용 설정되어 있는지 확인합니다.

  3. Google Cloud 콘솔에서 Cloud Shell로 이동합니다.

    Google Cloud 콘솔 하단에서 Cloud Shell 세션이 열리고 명령줄 프롬프트가 표시됩니다. Cloud Shell을 사용하여 튜토리얼의 모든 명령어를 실행합니다.

  4. 이 튜토리얼에 사용할 Google Cloud 콘솔 프로젝트를 설정합니다.

    gcloud config set core/project PROJECT_ID
    

    PROJECT_ID를 Cloud 프로젝트의 ID로 바꿉니다.

    Cloud Shell 승인 대화상자에서 승인을 클릭합니다. 승인을 클릭하면 Cloud Shell에서 실행하는 gcloud 명령어가 사용자 인증 정보를 사용해서 Google API로 인증을 수행할 수 있습니다.

  5. Resource Manager, GKE, GKE Hub, Cloud Service Mesh 인증 기관, CA 서비스 API를 사용 설정합니다.

    gcloud services enable \
        cloudresourcemanager.googleapis.com \
        container.googleapis.com \
        gkehub.googleapis.com \
        meshca.googleapis.com \
        privateca.googleapis.com
    

CA 서비스 구성

이 섹션에서는 CA Service에서 루트 CA 하나와 종속 CA 2개를 만듭니다. 종속 CA 하나는 인그레스 게이트웨이에 대해 인증서를 발급하고, 다른 종속 CA는 메시의 사이드카 프록시에 대해 인증서를 발급합니다.

이 튜토리얼에서는 편의상 GKE 클러스터와 루트 및 종속 CA에 동일한 프로젝트를 사용합니다. 사용자의 고유 환경에서는 GKE 클러스터 및 CA에 대해 서로 다른 프로젝트를 사용할 수 있습니다.

  1. Cloud Shell에서 루트 CA에 사용할 CA 풀을 만듭니다.

    gcloud privateca pools create ROOT_CA_POOL \
        --location CA_LOCATION \
        --tier enterprise
    
    • ROOT_CA_POOL은 CA 풀의 이름입니다. 예: root-ca-pool-tutorial
    • CA_LOCATION은 CA 풀의 위치입니다. 예를 들면 us-central1입니다.

    사용 가능한 CA 서비스 위치를 나열하려면 gcloud privateca locations list 명령어를 실행하면 됩니다.

  2. 루트 CA를 만들고 사용 설정합니다.

    gcloud privateca roots create ROOT_CA \
        --auto-enable \
        --key-algorithm ec-p384-sha384 \
        --location CA_LOCATION \
        --pool ROOT_CA_POOL \
        --subject "CN=Example Root CA, O=Example Organization" \
        --use-preset-profile root_unconstrained
    
    • ROOT_CA는 루트 CA에 사용하려는 이름입니다. root-ca-tutorial).
  3. 인그레스 게이트웨이에 인증서를 발급하는 종속 CA에 사용할 CA 풀을 만듭니다.

    gcloud privateca pools create SUBORDINATE_CA_POOL_GATEWAYS \
        --location CA_LOCATION \
        --tier devops
    
    • SUBORDINATE_CA_POOL_GATEWAYS는 CA 풀의 이름입니다. subordinate-ca-mtls-pool-gateways-tutorial).
  4. 인그레스 게이트웨이에 인증서를 발급하는 종속 CA를 만들고 사용 설정합니다.

    gcloud privateca subordinates create SUBORDINATE_CA_GATEWAYS \
        --auto-enable \
        --issuer-location CA_LOCATION \
        --issuer-pool ROOT_CA_POOL \
        --key-algorithm ec-p256-sha256 \
        --location CA_LOCATION \
        --pool SUBORDINATE_CA_POOL_GATEWAYS \
        --subject "CN=Example Gateway mTLS CA, O=Example Organization" \
        --use-preset-profile subordinate_mtls_pathlen_0
    
    • SUBORDINATE_CA_GATEWAYS는 종속 CA에 사용하려는 이름입니다. 예를 들면 subordinate-ca-mtls-gateways-tutorial입니다.
    • --use-preset-profile 플래그는 종속 mTLS 인증서 프로필을 사용하도록 종속 CA를 구성합니다. 이 프로필은 종속 CA가 mTLS용으로 클라이언트 및 서버 TLS 인증서를 모두 발급하도록 허용합니다.

    mTLS 대신 단순 TLS를 인그레스 게이트웨이에 사용하려는 경우에는 종속 CA가 서버 TLS 인증서만 발급하면 됩니다. 이 경우에는 대신 종속 서버 TLS(subordinate_server_tls_pathlen_0) 인증서 프로필을 사용할 수 있습니다.

  5. 인증서 발급 정책을 만듭니다.

    cat << EOF > policy.yaml
    baselineValues:
      keyUsage:
        baseKeyUsage:
          digitalSignature: true
          keyEncipherment: true
        extendedKeyUsage:
          serverAuth: true
          clientAuth: true
      caOptions:
        isCa: false
    identityConstraints:
      allowSubjectPassthrough: false
      allowSubjectAltNamesPassthrough: true
      celExpression:
        expression: subject_alt_names.all(san, san.type == URI && san.value.startsWith("spiffe://PROJECT_ID.svc.id.goog/ns/") )
    EOF
    

    이 발급 정책은 CA가 메시에 있는 워크로드에 대해서만 인증서를 발급하도록 제한합니다.

  6. 메시에 있는 사이드카 프록시에 인증서를 발급하는 종속 CA에 사용할 CA 풀을 만듭니다. 발급 정책을 CA 풀에 적용합니다.

    gcloud privateca pools create SUBORDINATE_CA_POOL_SIDECARS \
     --issuance-policy policy.yaml \
     --location CA_LOCATION \
     --tier devops
    
    • SUBORDINATE_CA_POOL_SIDECARS는 CA 풀의 이름입니다. subordinate-ca-mtls-pool-sidecars-tutorial).
  7. 메시에 있는 사이드카 프록시에 인증서를 발급하는 종속 CA를 만들고 사용 설정합니다.

    gcloud privateca subordinates create SUBORDINATE_CA_SIDECARS \
        --auto-enable \
        --issuer-location CA_LOCATION \
        --issuer-pool ROOT_CA_POOL \
        --key-algorithm ec-p256-sha256 \
        --location CA_LOCATION \
        --pool SUBORDINATE_CA_POOL_SIDECARS \
        --subject "CN=Example Sidecar mTLS CA, O=Example Organization" \
        --use-preset-profile subordinate_mtls_pathlen_0
    
    • SUBORDINATE_CA_GATEWAYS는 종속 CA에 사용하려는 이름입니다. 예: subordinate-ca-mtls-sidecars-tutorial

Google Kubernetes Engine 클러스터 만들기

  1. Cloud Shell에서 GKE 클러스터를 만듭니다.

    gcloud container clusters create CLUSTER_NAME \
        --enable-ip-alias \
        --num-nodes 4 \
        --release-channel regular \
        --scopes cloud-platform \
        --workload-pool PROJECT_ID.svc.id.goog \
        --zone ZONE
    

    CLUSTER_NAME을 클러스터에 사용할 이름으로 바꿉니다. 예를 들면 asm-ingress-cert-manager-ca-service입니다.

    ZONE을 클러스터에 사용할 영역으로 바꿉니다. 예를 들면 us-central1-f입니다.

    이 명령어에 관해서는 다음 사항에 유의하세요.

    • --release-channel 플래그는 클러스터에 대해 GKE 출시 채널을 선택합니다.
    • cert-manager 도구의 Cloud Service Mesh 및 CA 서비스 발급기관 모두 클러스터 노드에 cloud-platform 범위를 설정해야 합니다.
    • --workload-pool 인수는 CA 서비스 발급기관 Kubernetes 서비스 계정이 Google 서비스 계정을 가장하도록 허용하는 GKE용 워크로드 아이덴티티 제휴를 사용 설정합니다. 이러한 가장에 따라 Google 서비스 계정에 대해 키 파일을 다운로드하지 않아도 CA 서비스 발급기관 포드가 CA Service API에 액세스할 수 있습니다.
  2. 사용자 계정에 클러스터 관리자 권한을 부여합니다.

    kubectl create clusterrolebinding cluster-admin-binding \
        --clusterrole cluster-admin \
        --user $(gcloud config get-value core/account)
    

    Cloud Service Mesh에 대해 역할 기반 액세스 제어(RBAC) 규칙을 만들고 cert-manager 도구를 설치하려면 Kubernetes cluster-admin ClusterRole에서 제공되는 권한이 필요합니다.

Anthos Service Mesh 제어 영역 설치

이 튜토리얼에서는 모든 리소스가 하나의 프로젝트에 포함된 상태로 Google Cloud에서 GKE 클러스터에 대해 관리형 Cloud Service Mesh를 설치합니다. 사용자의 고유 환경에서는 관리형 Cloud Service Mesh 또는 클러스터 내 컨트롤 플레인 을 사용하여 이 문서에 설명된 솔루션을 적용할 수 있습니다.

Cloud Service Mesh는 여러 시나리오를 위해 다양한 설치 옵션을 제공합니다. 이 튜토리얼을 완료한 후에는 설치 가이드를 참조해서 해당 환경에 가장 적합한 옵션을 선택하는 것이 좋습니다.

  1. Cloud Shell에서 asmcli 설치 도구를 다운로드합니다.

    curl --location --output asmcli https://storage.googleapis.com/csm-artifacts/asm/asmcli_1.13
    
    chmod +x asmcli
    

    asmcli를 사용하여 Cloud Service Mesh 제어 영역을 설치합니다.

  2. Cloud Service Mesh 제어 영역 설치

    ./asmcli install \
        --ca gcp_cas \
        --ca_pool projects/PROJECT_ID/locations/CA_LOCATION/caPools/SUBORDINATE_CA_POOL_SIDECARS \
        --channel regular \
        --cluster_location ZONE \
        --cluster_name CLUSTER_NAME \
        --enable_all \
        --enable_registration \
        --fleet_id PROJECT_ID \
        --managed \
        --output_dir asm-files \
        --project_id PROJECT_ID \
        --verbose
    

    설치하려면 몇 분 정도 걸립니다. 설치가 완료되면 다음 출력이 표시됩니다.

    asmcli: Successfully installed ASM.`
    

인그레스 게이트웨이 설치

  1. Cloud Shell에서 인그레스 게이트웨이의 Kubernetes 네임스페이스를 만듭니다.

    kubectl create namespace GATEWAY_NAMESPACE
    
    • GATEWAY_NAMESPACE는 인그레스 게이트웨이에 사용하려는 네임스페이스 이름입니다. 예를 들면 istio-ingress입니다.
  2. 인그레스 게이트웨이 내부 패스 스루 네트워크 부하 분산기에 사용할 고정 내부 IP 주소를 예약합니다.

    LOAD_BALANCER_IP=$(gcloud compute addresses create \
        asm-ingress-gateway-ilb \
        --region REGION \
        --subnet default \
        --format 'value(address)')
    
    • REGION을 GKE 클러스터 노드에 사용되는 영역이 포함된 리전으로 바꿉니다. 예를 들어 클러스터에 us-central1-f 영역이 사용되는 경우 REGIONus-central1로 바꿉니다.

    이 명령어는 지정된 리전에서 기본 서브넷의 IP 주소를 예약합니다.

  3. 인그레스 게이트웨이에 대해 연산자 매니페스트를 만듭니다.

    cat << EOF > ingressgateway-operator.yaml
    apiVersion: install.istio.io/v1alpha1
    kind: IstioOperator
    metadata:
      name: ingressgateway-operator
      annotations:
        config.kubernetes.io/local-config: "true"
    spec:
      profile: empty
      revision: asm-managed
      components:
        ingressGateways:
        - name: istio-ingressgateway
          namespace: GATEWAY_NAMESPACE
          enabled: true
          k8s:
            overlays:
            - apiVersion: apps/v1
              kind: Deployment
              name: istio-ingressgateway
              patches:
              - path: spec.template.metadata.annotations
                value:
                  inject.istio.io/templates: gateway
              - path: spec.template.metadata.labels.sidecar\.istio\.io/inject
                value: "true"
              - path: spec.template.spec.containers[name:istio-proxy]
                value:
                  name: istio-proxy
                  image: auto
            service:
              loadBalancerIP: $LOAD_BALANCER_IP
            serviceAnnotations:
              networking.gke.io/load-balancer-type: Internal
              networking.gke.io/internal-load-balancer-allow-global-access: "true"
    EOF
    

    연산자 매니페스트에 관해서는 다음 사항에 유의하세요.

  4. 제어 영역을 설치할 때 asmcli 스크립트로 다운로드된 istioctl 도구 및 연산자 매니페스트를 사용하여 인그레스 게이트웨이 설치 매니페스트를 만듭니다.

    ./asm-files/istioctl manifest generate \
        --filename ingressgateway-operator.yaml \
        --output ingressgateway
    
  5. 인그레스 게이트웨이를 설치합니다.

    kubectl apply --recursive --filename ingressgateway/
    

cert-manager 도구 설치

  1. Cloud Shell에서 cert-manager 도구 설치 매니페스트를 다운로드하고 적용합니다.

    CERT_MANAGER_VERSION=v1.5.4
    
    curl --location --output cert-manager.yaml "https://github.com/jetstack/cert-manager/releases/download/${CERT_MANAGER_VERSION}/cert-manager.yaml"
    
    kubectl apply --filename cert-manager.yaml
    

    cert-manager 도구 설치는 완료하는 데 1분 정도 걸립니다.

CA Service 발급기관 컨트롤러 설치

CA Service 발급기관 컨트롤러는 cert-manager 도구가 CA Service를 사용해서 인증서를 요청할 수 있게 해줍니다. 이 컨트롤러는 cert-manager 도구 외부 발급기관 확장 메커니즘을 사용합니다.

  1. Cloud Shell에서 Google 서비스 계정을 만듭니다.

    gcloud iam service-accounts create CAS_ISSUER_GSA \
        --display-name "CA Service issuer for cert-manager"
    
    • CAS_ISSUER_GSA는 Google 서비스 계정의 이름입니다. cert-manager-ca-service-issuer).

    Certificate Authority Service 발급기관 컨트롤러는 이 Google 서비스 계정을 사용해서 Certificate Authority Service API에 인증을 수행합니다.

  2. Certificate Authority Service 발급기관 컨트롤러 Google 서비스 계정이 종속 CA를 포함하는 CA 풀에서 인증서를 요청하도록 허용하는 Identity and Access Management 정책 바인딩을 만듭니다.

    gcloud privateca pools add-iam-policy-binding SUBORDINATE_CA_POOL_GATEWAYS \
        --location CA_LOCATION \
        --member "serviceAccount:CAS_ISSUER_GSA@PROJECT_ID.iam.gserviceaccount.com" \
        --role roles/privateca.certificateRequester
    
  3. Certificate Authority Service 발급기관 컨트롤러 설치 매니페스트를 다운로드합니다.

    CAS_ISSUER_VERSION=v0.5.3
    
    curl --location --output ca-service-issuer.yaml "https://github.com/jetstack/google-cas-issuer/releases/download/${CAS_ISSUER_VERSION}/google-cas-issuer-${CAS_ISSUER_VERSION}.yaml"
    
  4. cert-manager 네임스페이스에서 ksa-google-cas-issuer Kubernetes 서비스 계정이 GKE용 워크로드 아이덴티티 제휴를 사용하여 Google 서비스 계정(GSA)을 가장하도록 허용하는 IAM 정책 바인딩을 만듭니다.

    gcloud iam service-accounts add-iam-policy-binding \
     CAS_ISSUER_GSA@PROJECT_ID.iam.gserviceaccount.com \
        --member "serviceAccount:PROJECT_ID.svc.id.goog[cert-manager/ksa-google-cas-issuer]" \
        --role roles/iam.workloadIdentityUser
    

    CA 서비스 발급기관 컨트롤러 포드는 ksa-google-cas-issuer Kubernetes 서비스 계정을 사용합니다.

  5. GKE 클러스터에 CA Service 발급기관 컨트롤러를 설치합니다.

    kubectl apply --filename ca-service-issuer.yaml
    
  6. CA 서비스 발급기관 컨트롤러 포드에 사용되는 Kubernetes 서비스 계정에 GKE용 워크로드 아이덴티티 제휴 주석 iam.gke.io/gcp-service-account를 추가합니다.

    kubectl annotate serviceaccount ksa-google-cas-issuer --namespace cert-manager \
       "iam.gke.io/gcp-service-account=CAS_ISSUER_GSA@PROJECT_ID.iam.gserviceaccount.com"
    

    이 주석은 Kubernetes 서비스 계정이 Google API에 액세스하기 위해 Google 서비스 계정을 가장할 수 있음을 GKE에 알려줍니다.

인증서 발급기관 만들기

  1. Cloud Shell에서 GoogleCASIssuer 매니페스트를 만들고 적용합니다.

    cat << EOF > gateway-cas-issuer.yaml
    apiVersion: cas-issuer.jetstack.io/v1beta1
    kind: GoogleCASIssuer
    metadata:
      name: gateway-cas-issuer
      namespace: GATEWAY_NAMESPACE
    spec:
      caPoolId: SUBORDINATE_CA_POOL_GATEWAYS
      location: CA_LOCATION
      project: PROJECT_ID
    EOF
    
    kubectl apply --filename gateway-cas-issuer.yaml
    

    발급기관은 cert-manager 도구가 인그레스 게이트웨이 네임스페이스에 있는 종속 CA 풀에서 인증서를 프로비저닝할 수 있게 해줍니다.

샘플 애플리케이션 배포

이 섹션에서는 cert-manager 도구가 CA Service 발급기관을 사용해서 CA Service로부터 인증서를 가져올 수 있는지 확인합니다. 이를 확인하기 위해 요청 라우팅 구성과 인그레스 게이트웨이에 대한 인증서가 포함된 샘플 애플리케이션을 배포합니다.

  1. Cloud Shell에서 샘플 애플리케이션 리소스에 대한 네임스페이스를 만듭니다.

    cat << EOF > sample-app-namespace.yaml
    apiVersion: v1
    kind: Namespace
    metadata:
      name: APP_NAMESPACE
      annotations:
        mesh.cloud.google.com/proxy: '{"managed":"true"}'
      labels:
        istio.io/rev: asm-managed
    EOF
    
    kubectl apply --filename sample-app-namespace.yaml
    
    • APP_NAMESPACE는 샘플 애플리케이션의 네임스페이스 이름입니다. 예를 들면 sample-app입니다.

    mesh.cloud.google.com/proxy 주석은 네임스페이스에 관리형 데이터 영역을 사용 설정합니다.

    istio.io/rev: asm-managed 라벨은 샘플 애플리케이션 네임스페이스에서 관리형 데이터 영역에 일반 출시 채널을 선택합니다. 신속 또는 정식 출시 채널을 사용하는 경우 이 라벨 값을 변경합니다.

  2. 샘플 애플리케이션에 대해 배포 리소스를 만듭니다.

    cat << EOF > deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: hello
      namespace: APP_NAMESPACE
      labels:
        app: hello
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: hello
      template:
        metadata:
          labels:
            app: hello
        spec:
          containers:
          - image: gcr.io/google-samples/hello-app:1.0
            name: hello-app
            ports:
            - containerPort: 8080
    EOF
    
    kubectl apply --filename deployment.yaml
    
  3. 샘플 애플리케이션에 대해 서비스 리소스를 만듭니다.

    cat << EOF > service.yaml
    apiVersion: v1
    kind: Service
    metadata:
      name: SERVICE_NAME
      namespace: APP_NAMESPACE
    spec:
      ports:
      - name: http-hello
        port: 8080
      selector:
        app: hello
      type: ClusterIP
    EOF
    
    kubectl apply --filename service.yaml
    
    • SERVICE_NAME은 서비스의 이름입니다. 예를 들면 hello입니다.
  4. 인증서 발급기관을 사용해서 도메인 이름 hello.example.com에 대한 인증서 리소스를 만듭니다.

    cat << EOF > certificate.yaml
    apiVersion: cert-manager.io/v1
    kind: Certificate
    metadata:
      name: hello-example-com-certificate
      namespace: GATEWAY_NAMESPACE
    spec:
      secretName: hello-example-com-credential
      commonName: hello.example.com
      dnsNames:
      - hello.example.com
      duration: 24h
      renewBefore: 8h
      issuerRef:
        group: cas-issuer.jetstack.io
        kind: GoogleCASIssuer
        name: gateway-cas-issuer
    EOF
    
    kubectl apply --filename certificate.yaml
    

    인증서 네임스페이스는 인그레스 게이트웨이 네임스페이스와 일치해야 합니다. 변경사항이 전체 서비스 메시에 영향을 줄 수 있기 때문에 일반적으로 플랫폼 관리자만 이 네임스페이스의 리소스를 변경할 수 있습니다. cert-manager 도구는 동일한 네임스페이스에서 TLS 인증서에 대해 보안 비밀 리소스를 만듭니다. 즉, 애플리케이션 관리자가 인그레스 게이트웨이 네임스페이스에 액세스할 필요가 없습니다.

    인증서에서 dnsNames 목록에 추가 호스트 이름을 추가할 수 있습니다. 이러한 호스트 이름은 인증서에 주체 대체 이름(SAN)으로 포함됩니다.

  5. 샘플 애플리케이션에 대해 게이트웨이 리소스를 만듭니다.

    cat << EOF > gateway.yaml
    apiVersion: networking.istio.io/v1beta1
    kind: Gateway
    metadata:
      name: GATEWAY_NAME
      namespace: GATEWAY_NAMESPACE
    spec:
      selector:
        istio: ingressgateway
      servers:
      - hosts:
        - APP_NAMESPACE/hello.example.com
        port:
          name: https-hello
          number: 443
          protocol: HTTPS
        tls:
          credentialName: hello-example-com-credential
          mode: MUTUAL
    EOF
    
    kubectl apply --filename gateway.yaml
    
    • GATEWAY_NAME은 게이트웨이 이름입니다. 예를 들면 hello입니다.
    • 게이트웨이의 credentialName 필드는 인증서의 secretName 필드와 일치합니다. cert-manager 도구는 CA Service의 TLS 인증서로 Kubernetes 보안 비밀을 만듭니다. 인그레스 게이트웨이는 이 인증서를 사용하여 hello.example.com 대상의 TLS 트래픽을 종료할 수 있습니다.

    게이트웨이 매니페스트는 상호 TLS(mTLS)를 지정합니다. 일반 TLS에 대해 게이트웨이를 구성하려면 게이트웨이의 TLS 모드를 대신 SIMPLE로 설정합니다.

  6. 샘플 애플리케이션에 대해 VirtualService 리소스를 만듭니다.

    cat << EOF > virtual-service.yaml
    apiVersion: networking.istio.io/v1beta1
    kind: VirtualService
    metadata:
      name: hello
      namespace: APP_NAMESPACE
    spec:
      hosts:
      - hello.example.com
      gateways:
      - GATEWAY_NAMESPACE/GATEWAY_NAME
      http:
      - route:
        - destination:
            host: SERVICE_NAME
            port:
              number: 8080
    EOF
    
    kubectl apply --filename virtual-service.yaml
    

    게이트웨이 및 VirtualService에는 서로 다른 네임스페이스가 사용됩니다. 이 공통 패턴은 게이트웨이에서 호스트 기반 라우팅 변경을 인그레스 게이트웨이 네임스페이스에서 리소스 변경 권한이 있는 플랫폼 관리자로 제한합니다.

    샘플 애플리케이션 네임스페이스에서 VirtualService 수정 권한이 있는 애플리케이션 관리자는 플랫폼 관리자의 도움 없이 URL 경로와 같은 다른 요청 필드로 라우팅을 변경할 수 있습니다.

다른 구성 옵션을 살펴보려면 인증서, 게이트웨이, VirtualService 리소스에 대한 API 문서를 참조하세요.

인그레스 게이트웨이를 통해 서비스 메시에 들어가는 트래픽에 인증 및 승인 정책을 적용할 수 있습니다. 이렇게 하려면 Istio PeerAuthenticationAuthorizationPolicy API 문서를 참조하세요.

솔루션 확인

이 섹션에서는 mTLS를 사용하여 HTTPS 요청을 서비스 메시 외부에서 샘플 애플리케이션으로 전송할 수 있는지 확인합니다. 확인을 위해 Compute Engine VM 인스턴스를 만들고 CA 서비스에서 클라이언트 TLS 인증서를 요청한 다음 이 인증서를 사용하여 샘플 애플리케이션에 대한 요청을 인증합니다.

VM 인스턴스에 대한 SSH 액세스 권한이 필요합니다. 기본 네트워크에는 SSH 액세스를 허용하는 방화벽 규칙이 포함되어 있습니다. SSH 액세스 권한이 없으면 방화벽 규칙 문서에 따라 포트 22에서 수신되는 TCP 연결을 허용하는 방화벽 규칙을 만듭니다.

  1. Cloud Shell에서 Google 서비스 계정을 만듭니다.

    gcloud iam service-accounts create CLIENT_VM_GSA \
        --display-name "CA Service tutorial VM instance service account"
    
    • CLIENT_VM_GSA는 Google 서비스 계정의 이름입니다. cas-tutorial-client).

    이 Google 서비스 계정을 Compute Engine VM 인스턴스에 할당합니다.

  2. Google 서비스 계정에 게이트웨이 종속 CA 풀에 대한 CA 서비스 인증서 요청자 역할을 부여합니다.

    gcloud privateca pools add-iam-policy-binding SUBORDINATE_CA_POOL_GATEWAYS \
        --location CA_LOCATION \
        --member "serviceAccount:CLIENT_VM_GSA@PROJECT_ID.iam.gserviceaccount.com" \
        --role roles/privateca.certificateRequester
    

    이 역할은 CA 풀에서 인증서 요청 권한을 제공합니다.

  3. GKE 클러스터와 동일한 VPC에서 Compute Engine VM 인스턴스를 만듭니다.

    gcloud compute instances create cas-tutorial-client \
        --scopes cloud-platform \
        --service-account CLIENT_VM_GSA@PROJECT_ID.iam.gserviceaccount.com \
        --zone ZONE
    

    CA 서비스 API에 액세스하려면 VM 인스턴스에 cloud-platform 범위가 필요합니다.

  4. 인그레스 게이트웨이 내부 패스 스루 네트워크 부하 분산기의 IP 주소를 파일에 저장합니다.

    kubectl get services istio-ingressgateway \
       --namespace GATEWAY_NAMESPACE \
       --output jsonpath='{.status.loadBalancer.ingress[0].ip}' > ilb-ip.txt
    
  5. 루트 CA의 공개 키 인증서를 파일에 저장합니다.

    gcloud privateca roots describe ROOT_CA \
        --location CA_LOCATION \
        --pool ROOT_CA_POOL \
        --format 'value(pemCaCertificates)' > root-ca-cert.pem
    
  6. 루트 CA 인증서 및 인그레스 게이트웨이 내부 패스 스루 네트워크 부하 분산기의 IP 주소가 포함된 파일을 VM 인스턴스에 복사합니다.

    gcloud compute scp root-ca-cert.pem ilb-ip.txt cas-tutorial-client:~ \
       --zone ZONE
    
  7. SSH를 사용하여 VM 인스턴스에 연결합니다.

    gcloud compute ssh cas-tutorial-client --zone ZONE
    

    SSH 세션에서 이 섹션의 나머지 명령어를 실행합니다.

  8. ca-certificatescoreutils 패키지를 설치하고, curl, openssl, jq 명령줄 도구를 설치합니다.

    sudo apt-get update --yes
    
    sudo apt-get install --yes ca-certificates coreutils curl jq openssl
    
  9. 클라이언트 TLS 인증서에 대해 키 쌍을 만듭니다.

    openssl genrsa -out private-key.pem 2048
    
    openssl rsa -in private-key.pem -pubout -out public-key.pem
    
  10. 메타데이터 서버를 쿼리하여 VM 인스턴스에 연결된 Google 서비스 계정 ID의 이메일 주소를 가져옵니다.

    GSA_EMAIL=$(curl --silent --header "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email)
    
  11. Certificate Authority Service API에서 클라이언트 TLS 인증서를 요청할 때 요청 본문으로 사용하는 JSON 파일을 만듭니다.

    cat << EOF > request.json
    {
      "config": {
        "publicKey": {
          "format": "PEM",
          "key": "$(base64 --wrap 0 public-key.pem)"
        },
        "subjectConfig": {
          "subject": {
            "commonName": "$(hostname --short)",
            "organization": "Example Organization"
          },
          "subjectAltName": {
            "dnsNames": [
              "$(hostname --fqdn)"
            ],
            "emailAddresses": [
              "$GSA_EMAIL"
            ]
          }
        },
        "x509Config": {
          "caOptions": {
            "isCa": false
          },
          "keyUsage": {
            "baseKeyUsage": {
              "digitalSignature": true,
              "keyEncipherment": true
            },
            "extendedKeyUsage": {
              "clientAuth": true
            }
          }
        }
      },
      "lifetime": "86400s"
    }
    EOF
    

    구성 섹션의 필드에 대해 자세히 알아보려면 CA 서비스 API 문서의 CertificateConfig 유형을 참조하세요.

  12. 메타데이터 서버에서 OAuth 2.0 액세스 토큰을 요청합니다.

    TOKEN=$(curl --silent --header "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token | jq --raw-output ".access_token")
    

    이 액세스 토큰은 VM 인스턴스에 연결된 Google 서비스 계정에 부여된 권한을 제공합니다.

  13. CA 서비스 API에서 클라이언트 TLS 인증서를 요청하고 응답 본문을 파일에 저장합니다.

    curl --silent --request POST \
        --header "Authorization: Bearer $TOKEN" \
        --header "Content-Type: application/json" \
        --data @request.json \
        --output response.json \
        "https://privateca.googleapis.com/v1/projects/PROJECT_ID/locations/CA_LOCATION/caPools/SUBORDINATE_CA_POOL_GATEWAYS/certificates"
    

    이 명령어는 액세스 토큰을 사용하여 API 요청을 인증합니다.

  14. 클라이언트 인증서 및 인증서 체인을 파일에 저장합니다.

    jq --raw-output --join-output ".pemCertificate , .pemCertificateChain[]" response.json > client-cert-chain.pem
    
  15. curl을 사용하여 VM 인스턴스에서 샘플 애플리케이션으로 HTTPS 요청을 전송합니다.

    curl --cert client-cert-chain.pem --key private-key.pem \
        --cacert root-ca-cert.pem \
        --resolve hello.example.com:443:$(cat ilb-ip.txt) \
        --silent https://hello.example.com | head -n1
    

    출력 형식은 다음과 같습니다.

    Hello, world!
    

    이 응답은 curl이 mTLS를 사용하여 HTTPS 요청을 성공적으로 전송했음을 보여줍니다. 터미널 출력에 표시되는 메시지로 샘플 애플리케이션이 응답했습니다.

    curl 명령어는 다음을 수행합니다.

    • --cert--key 플래그는 요청 인증을 위해 클라이언트 TLS 인증서 및 비공개 키를 사용하도록 curl에 지시합니다. 클라이언트 인증서 파일에는 클라이언트 인증서부터 루트 CA까지 전체 인증서 체인이 포함됩니다.

    • --cacert 플래그는 이 튜토리얼에서 만든 루트 CA 또는 해당 종속 CA 중 하나가 서버 인증서를 발급했는지 확인하도록 curl에 지시합니다.

      이 플래그를 생략하면 curl이 Debian의 ca-certificates 패키지와 같은 운영체제의 기본 CA 번들을 사용해서 서버 인증서 확인을 시도합니다. 하지만 이 튜토리얼에서 만든 루트 CA가 기본 CA 번들에 포함되지 않기 때문에 확인이 실패합니다.

    • --resolve 플래그는 포트 443에서 hello.example.com을 호스팅하기 위해 내부 패스 스루 네트워크 부하 분산기 IP 주소를 요청 대상으로 사용하도록 curl에 지시합니다.

      이 플래그를 생략하면 curl이 DNS를 사용해서 hello.example.com 호스트 이름 확인을 시도합니다. 하지만 이 호스트 이름에 대한 DNS 항목이 없기 때문에 DNS 변환이 실패합니다.

      사용자의 고유 환경에서는 내부 패스 스루 네트워크 부하 분산기 IP 주소($LOAD_BALANCER_IP)를 가리키는 DNS A 레코드를 만드는 것이 좋습니다. 레코드 관리 문서에 따라 Cloud DNS를 사용하여 이 레코드를 만듭니다.

    • --silent 플래그는 터미널 출력에 응답 다운로드 진행 상태가 보고되지 않도록 억제합니다.

    • 이 명령어는 curl 출력을 head -n1로 보냅니다. 그 결과 터미널 출력에는 응답 본문의 첫 번째 줄만 포함됩니다.

  16. SSH 세션을 종료합니다.

    exit
    

이 섹션에서는 CA Service API에서 직접 클라이언트 TLS 인증서를 요청했습니다. 클라이언트가 개별 Kubernetes 클러스터에서 다른 서비스 메시의 이그레스 게이트웨이인 경우, cert-manager 도구 및 동일한 루트 CA를 사용하는 Certificate Authority Service 발급기관을 사용하여 이그레스 게이트웨이에 클라이언트 인증서를 제공할 수 있습니다.

다른 상황에서는 Hashicorp Vault, Terraform, gcloud와 같은 도구를 사용해서 서비스 메시 외부의 워크로드에 대해 클라이언트 TLS 인증서를 요청할 수 있습니다. 자세한 내용은 샘플 솔루션에 대한 CA Service 문서 및 CA Service에 대한 gcloud 문서를 참조하세요.

(선택사항) 트러스트 저장소에 CA 인증서 추가

이 선택적인 섹션에서는 Linux Debian 배포판에 대해 신뢰할 수 있는 CA 인증서 저장소에 CA 인증서를 추가하는 방법을 보여줍니다. 이 안내는 Ubuntu와 같이 Debian에서 파생되는 배포판에도 적용됩니다.

이 저장소에 CA 인증서를 추가한다는 것은 curl, Python, Go, Ruby를 사용하여 HTTPS 요청을 전송할 때 신뢰할 수 있는 CA 인증서 위치를 지정할 필요가 없다는 것을 의미합니다.

  1. SSH를 사용하여 VM 인스턴스에 연결합니다.

    gcloud compute ssh cas-tutorial-client --zone ZONE
    

    SSH 세션에서 이 섹션의 나머지 명령어를 실행합니다.

  2. 루트 CA 인증서를 /usr/local/share/ca-certificates 디렉터리에 복사하고 파일 확장자가 .crt인지 확인합니다.

    sudo cp root-ca-cert.pem /usr/local/share/ca-certificates/cas-rootca.crt
    
  3. 모든 사용자가 루트 CA 인증서 파일을 읽을 수 있도록 파일 권한을 설정합니다.

    sudo chmod 644 /usr/local/share/ca-certificates/cas-rootca.crt
    
  4. update-ca-certificates 스크립트를 실행합니다.

    sudo update-ca-certificates
    

    이 스크립트는 /etc/ssl/certs 디렉터리의 신뢰할 수 있는 인증서 집합 및 /etc/ssl/certs/ca-certificates.crt 파일에 인증서를 추가합니다.

    출력은 다음과 같습니다.

    Updating certificates in /etc/ssl/certs...
    1 added, 0 removed; done.
    Running hooks in /etc/ca-certificates/update.d...
    done.
    
  5. curl을 사용하여 VM 인스턴스에서 샘플 애플리케이션으로 HTTPS 요청을 전송합니다.

    curl --cert client-cert-chain.pem --key private-key.pem \
       --resolve hello.example.com:443:$(cat ilb-ip.txt) \
       --silent https://hello.example.com | head -n1
    

    출력 형식은 다음과 같습니다.

    Hello, world!
    

    이 응답은 curl이 mTLS를 사용하여 HTTPS 요청을 성공적으로 전송했고 기본 CA 인증서 저장소를 사용해서 인그레스 게이트웨이에서 서버 TLS 인증서를 검증했음을 보여줍니다.

  6. SSH 세션을 종료합니다.

    exit
    

문제 해결

CA Service 발급기관 컨트롤러로 TLS 인증서 보안 비밀이 생성되지 않으면 CA 인증서 발급기관 컨트롤러의 로그를 확인합니다.

kubectl logs deployment/google-cas-issuer --namespace cert-manager

Cloud Service Mesh를 설치할 때 문제가 발생하면 asmcli 도구를 실행하여 클라우드 프로젝트 및 GKE 클러스터를 검증합니다.

이 튜토리얼에서 문제가 발생하면 다음 문서를 검토하세요.

삭제

이 튜토리얼에서 사용된 리소스 비용이 Google Cloud 계정에 청구되지 않도록 하려면 프로젝트를 삭제하거나 개별 리소스를 삭제하면 됩니다.

프로젝트 삭제

  1. Cloud Shell에서 프로젝트를 삭제합니다.

    gcloud projects delete PROJECT_ID
    

리소스 삭제

이 가이드에서 사용된 Google Cloud 프로젝트를 유지하려면 개별 리소스를 삭제합니다.

  1. Cloud Shell에서 GKE 허브에 등록된 GKE 클러스터를 해제합니다.

    gcloud container hub memberships unregister CLUSTER_NAME \
        --gke-cluster ZONE/CLUSTER_NAME
    
  2. GKE 클러스터를 삭제합니다.

    gcloud container clusters delete CLUSTER_NAME \
        --zone ZONE --async --quiet
    
  3. 종속 CA 풀에서 IAM 정책 바인딩을 삭제합니다.

    gcloud privateca pools remove-iam-policy-binding SUBORDINATE_CA_POOL_GATEWAYS \
        --location CA_LOCATION \
        --member "serviceAccount:CAS_ISSUER_GSA@PROJECT_ID.iam.gserviceaccount.com" \
        --role roles/privateca.certificateRequester
    
    gcloud privateca pools remove-iam-policy-binding SUBORDINATE_CA_POOL_GATEWAYS \
        --location CA_LOCATION \
        --member "serviceAccount:CLIENT_VM_GSA@PROJECT_ID.iam.gserviceaccount.com" \
        --role roles/privateca.certificateRequester
    
  4. 종속 CA 및 루트 CA의 삭제를 사용 중지하고 예약합니다.

    gcloud privateca subordinates disable SUBORDINATE_CA_GATEWAYS \
        --location CA_LOCATION \
        --pool SUBORDINATE_CA_POOL_GATEWAYS \
        --quiet
    
    gcloud privateca subordinates delete SUBORDINATE_CA_GATEWAYS \
        --location CA_LOCATION \
        --pool SUBORDINATE_CA_POOL_GATEWAYS \
        --ignore-active-certificates \
        --quiet
    
    gcloud privateca subordinates disable SUBORDINATE_CA_SIDECARS \
        --location CA_LOCATION \
        --pool SUBORDINATE_CA_POOL_SIDECARS \
        --quiet
    
    gcloud privateca subordinates delete SUBORDINATE_CA_SIDECARS \
        --location CA_LOCATION \
        --pool SUBORDINATE_CA_POOL_SIDECARS \
        --ignore-active-certificates \
        --quiet
    
    gcloud privateca roots disable ROOT_CA \
        --location CA_LOCATION \
        --pool ROOT_CA_POOL \
        --quiet
    
    gcloud privateca roots delete ROOT_CA \
        --location CA_LOCATION \
        --pool ROOT_CA_POOL \
        --ignore-active-certificates \
        --quiet
    
  5. CA 서비스 발급기관 컨트롤러 Google 서비스 계정의 IAM 정책 바인딩을 삭제합니다.

    gcloud iam service-accounts remove-iam-policy-binding \
        CAS_ISSUER_GSA@PROJECT_ID.iam.gserviceaccount.com \
        --member "serviceAccount:PROJECT_ID.svc.id.goog[cert-manager/ksa-google-cas-issuer]" \
        --role roles/iam.workloadIdentityUser
    
  6. Google 서비스 계정을 삭제합니다.

    gcloud iam service-accounts delete --quiet \
        CAS_ISSUER_GSA@PROJECT_ID.iam.gserviceaccount.com
    
    gcloud iam service-accounts delete --quiet \
        CLIENT_VM_GSA@PROJECT_ID.iam.gserviceaccount.com
    
  7. 예약된 부하 분산기 IP 주소를 삭제합니다.

    gcloud compute addresses delete asm-ingress-gateway-ilb \
        --region REGION --quiet
    
  8. Compute Engine VM 인스턴스를 삭제합니다.

    gcloud compute instances delete cas-tutorial-client \
        --zone ZONE --quiet
    

다음 단계