ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [IHOS 1기] 5주차 - 마이크로서비스 통신 보안
    Kubernetes 2025. 5. 11. 02:53

    9.0. 실습 환경 구성

    9.0.1. [실습 환경 구성] k8s(1.23.17) 배포 : NodePort(30000 HTTP, 30005 HTTPS)
    #
    git clone https://github.com/AcornPublishing/istio-in-action
    cd istio-in-action/book-source-code-master
    pwd # 각자 자신의 pwd 경로
    code .
    
    # 아래 extramounts 생략 시, myk8s-control-plane 컨테이너 sh/bash 진입 후 직접 git clone 가능
    kind create cluster --name myk8s --image kindest/node:v1.23.17 --config - <<EOF
    kind: Cluster
    apiVersion: kind.x-k8s.io/v1alpha4
    nodes:
    - role: control-plane
      extraPortMappings:
      - containerPort: 30000 # Sample Application (istio-ingrssgateway) HTTP
        hostPort: 30000
      - containerPort: 30001 # Prometheus
        hostPort: 30001
      - containerPort: 30002 # Grafana
        hostPort: 30002
      - containerPort: 30003 # Kiali
        hostPort: 30003
      - containerPort: 30004 # Tracing
        hostPort: 30004
      - containerPort: 30005 # Sample Application (istio-ingrssgateway) HTTPS
        hostPort: 30005
      - containerPort: 30006 # TCP Route
        hostPort: 30006
      - containerPort: 30007 # kube-ops-view
        hostPort: 30007
      extraMounts: # 해당 부분 생략 가능
      - hostPath: /Users/gasida/Downloads/istio-in-action/book-source-code-master # 각자 자신의 pwd 경로로 설정
        containerPath: /istiobook
    networking:
      podSubnet: 10.10.0.0/16
      serviceSubnet: 10.200.1.0/24
    EOF
    
    # 설치 확인
    docker ps
    
    # 노드에 기본 툴 설치
    docker exec -it myk8s-control-plane sh -c 'apt update && apt install tree psmisc lsof wget bridge-utils net-tools dnsutils tcpdump ngrep iputils-ping git vim -y'
    
    # (옵션) kube-ops-view
    helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
    helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30007 --set env.TZ="Asia/Seoul" --namespace kube-system
    kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view
    
    ## kube-ops-view 접속 URL 확인
    open "http://localhost:30007/#scale=1.5"
    open "http://localhost:30007/#scale=1.3"
    
    # (옵션) metrics-server
    helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/
    helm install metrics-server metrics-server/metrics-server --set 'args[0]=--kubelet-insecure-tls' -n kube-system
    kubectl get all -n kube-system -l app.kubernetes.io/instance=metrics-server
    
     
    9.0.2. [실습 환경 구성] istio 1.17.8 설치 - Docs , Install , prof
    # myk8s-control-plane 진입 후 설치 진행
    docker exec -it myk8s-control-plane bash
    -----------------------------------
    # (옵션) 코드 파일들 마운트 확인
    tree /istiobook/ -L 1
    혹은
    git clone ... /istiobook
    
    # istioctl 설치
    export ISTIOV=1.17.8
    echo 'export ISTIOV=1.17.8' >> /root/.bashrc
    
    curl -s -L https://istio.io/downloadIstio | ISTIO_VERSION=$ISTIOV sh -
    cp istio-$ISTIOV/bin/istioctl /usr/local/bin/istioctl
    istioctl version --remote=false
    
    # demo 프로파일 컨트롤 플레인 배포
    istioctl install --set profile=demo --set values.global.proxy.privileged=true -y
    
    # 보조 도구 설치
    kubectl apply -f istio-$ISTIOV/samples/addons
    
    # 빠져나오기
    exit
    -----------------------------------
    
    # 설치 확인 : istiod, istio-ingressgateway, crd 등
    kubectl get istiooperators -n istio-system -o yaml
    kubectl get all,svc,ep,sa,cm,secret,pdb -n istio-system
    kubectl get cm -n istio-system istio -o yaml
    kubectl get crd | grep istio.io | sort
    
    # 실습을 위한 네임스페이스 설정
    kubectl create ns istioinaction
    kubectl label namespace istioinaction istio-injection=enabled
    kubectl get ns --show-labels
    
    # istio-ingressgateway 서비스 : NodePort 변경 및 nodeport 지정 변경 , externalTrafficPolicy 설정 (ClientIP 수집)
    kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 8080, "nodePort": 30000}]}}'
    kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec": {"type": "NodePort", "ports": [{"port": 443, "targetPort": 8443, "nodePort": 30005}]}}'
    kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec":{"externalTrafficPolicy": "Local"}}'
    kubectl describe svc -n istio-system istio-ingressgateway
    
    # NodePort 변경 및 nodeport 30001~30003으로 변경 : prometheus(30001), grafana(30002), kiali(30003), tracing(30004)
    kubectl patch svc -n istio-system prometheus -p '{"spec": {"type": "NodePort", "ports": [{"port": 9090, "targetPort": 9090, "nodePort": 30001}]}}'
    kubectl patch svc -n istio-system grafana -p '{"spec": {"type": "NodePort", "ports": [{"port": 3000, "targetPort": 3000, "nodePort": 30002}]}}'
    kubectl patch svc -n istio-system kiali -p '{"spec": {"type": "NodePort", "ports": [{"port": 20001, "targetPort": 20001, "nodePort": 30003}]}}'
    kubectl patch svc -n istio-system tracing -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 16686, "nodePort": 30004}]}}'
    
    # Prometheus 접속 : envoy, istio 메트릭 확인
    open http://127.0.0.1:30001
    
    # Grafana 접속
    open http://127.0.0.1:30002
    
    # Kiali 접속 1 : NodePort
    open http://127.0.0.1:30003
    
    # (옵션) Kiali 접속 2 : Port forward
    kubectl port-forward deployment/kiali -n istio-system 20001:20001 &
    open http://127.0.0.1:20001
    
    # tracing 접속 : 예거 트레이싱 대시보드
    open http://127.0.0.1:30004
    
     

    9.1. 애플리케이션 네트워크 보안의 필요성

    참고: https://netpple.github.io/docs/istio-in-action/Istio-ch9-securing-1-overview

    들어가며 : 인증, 인가, 전송 중 데이터 암호화
    • 애플리케이션 보안이란, 인가 받지 않은 사용자가 오염시키거나 훔치거나 접근해서는 안 되는 귀중한 애플리케이션 데이터를 보호하는 데 기여하는 모든 행동을 말한다.
    • 사용자 데이터를 지키려면 다음 사항이 필요하다.
      • 리소스 접근을 허가하기 전에 사용자 인증 및 인가
      • 데이터를 요청한 클라이언트로 가면서 여러 네트워크 장치를 거쳐가는 동안 데이터 도청을 방지하는 전송 중 데이터 암호화
    👉🏻

    인증(authentication)이란 클라이언트나 서버가 자신의 정체를 입증하는 절차를 말하며, 아는 것(패스워드)이나 갖고 있는 것(장치, 인증서) 또는 자기 자신(지문 같은 고유 특성)을 이용한다. 인가(authorization)란 이미 인증된 사용자가 리소스의 생성이나 조회, 갱신, 삭제 같은 작업을 수행하는 것을 허용하거나 거부하는 절차를 말한다.

     
    9.1.1 서비스 간 인증 Service-to-service authentication - SPIFFE 프레임워크
    • 안전하려면, 서비스는 자신이 상호작용하는 서비스는 모두 인증해야 한다.
    • 다시 말해, 확인할 수 있는 ID 문서를 제시한 후에만 다른 서비스를 신뢰해야 한다.
    • 보통, 이 문서는 그 문서를 발급한 신뢰할 수 있는 제3자에게 획인한다.
    • 이번 장에서는 이스티오가 SPIFFE Secure Prduction Identity Framework For Everyone 프레임워크를 사용해 서비스들의 ID 발급을 자동화하는 방법을 다룬다.
      • Universal identity control plane for distributed systems https://spiffe.io/
      • SPIFFE and SPIRE provide strongly attested, cryptographic identities to workloads across a wide variety of platforms
      • SPIFFE is a set of open-source specifications for a framework capable of bootstrapping and issuing identity to services across heterogeneous environments and organizational boundaries. The heart of these specifications is the one that defines short lived cryptographic identity documents – called SVIDs via a simple API. Workloads can then use these identity documents when authenticating to other workloads, for example by establishing a TLS connection or by signing and verifying a JWT token.
      • https://spiffe.io/docs/latest/spiffe-about/overview/* → Implements 확인
    • 발급된 ID는 서비스들이 서로 인증하는 데 사용한다.
     
    9.1.2 최종 사용자 인증 End-user authentication - JWT 등 자격증명
    • 최종 사용자 인증은 사용자의 개인 데이터를 저장하는 애플리케이션의 핵심이다.
    • 성숙한 최종 사용자 인증 프로토콜은 여러 가지가 있지만, 대부분은 사용자를 인증 서버로 리다이렉션하는 것이 핵심이다.
    • 사용자가 인증 서버에서 로그인을 성공하면 사용자 정보를 담고 있는 자격 증명(HTTP 쿠키나 JWT 등으로 저장)을 받는다.
    • 사용자는 인증을 위해 이 자격 증명을 서비스에 제시한다.
    • 서비스는 어떤 종류든 접근을 허용하기 전에 자격 증명을 발급한 인증 서버에 자격 증명을 검증한다.
     
    9.1.3 인가 Authorization - 작업 수행 승인/거부
    • 인가는 호출자가 인증된 후 진행된다.
    • 호출자가 ‘누구’인지 서버가 식별하고 나면, 서버는 이 ID가 ‘어떤’ 작업을 수행할 수 있도록 허용돼 있는지 확인하고 그에 따라 승인하거나 거부한다.
    • 예를 들어 웹 애플리케이션에서 인가는 사용자가 리소스를 생성, 조회, 업데이트, 삭제할 수 있는지 여부를 확인하는 형식을 취한다.
    • 이스티오는 서비스 인증과 ID 모델을 기반으로 서비스 사이에 또는 최종 사용자와 서비스 사이에 세분화된 인가 기능을 제공한다.
     
    9.1.4 모놀리스와 마이크로서비스의 보안 비교 Comparison of security in monoliths and microservices
      • 마이크로서비스와 모놀리스 모두 최종 사용자 및 서비스인증인가를 구현해야 한다.
      • 그러나 마이크로서비스에는 보호해야 하는, 네트워크를 오가는 커넥션과 요청이 훨씬 더 많다.
      • 반면 모놀리스는 커넥션이 더 적고, 보통은 가상머신 혹은 물리 머신 같은 더 정적인 인프라에서 실행된다.
      • 정적인 인프라에서 실행하면 (고정) IP 주소ID 확인 근거로 심기 좋으며, 덕분에 인증용 인증서에서 흔하게 사용한다. (네트워크 방화벽 규칙에도 사용한다)
      • 그림 9.1은 IP를 신뢰의 근거로 삼기 좋은 정적 인프라를 보여준다.
    고정IP로 온프레미스에서 실행되는 모놀리스 애플리케이션고정IP로 온프레미스에서 실행되는 모놀리스 애플리케이션
     
      • 반면에 마이크로서비스는 쉽게 수백, 수천 개의 서비스로 불어나므로 정적 환경에서는 서비스를 운영할 수 없다.
      • 이런 이유로 클라우드 컴퓨팅이나 컨테이너 오케스트레이션 같은 동적 환경을 활용하는데, 여기서 서비스는 수많은 서버로 스케줄링되고 수명이 짧다.
      • 따라서 IP 주소를 사용하는 것 같은 전통적인 방법들은 ID의 근거로 미덥지 못하게 된다.
      • 설상가상으로 서비스가 반드시 같은 네트워크에서 실행되는 것도 아니며, 여러 클라우드 프로바이더에 걸쳐 있거나 심지어는 그림 9.2처럼 온프레미스에서도 실행 될 수 있다.
    클라우드와 온프레미스에서 실행되는 상호 연결이 많은 마이크로서비스클라우드와 온프레미스에서 실행되는 상호 연결이 많은 마이크로서비스
     
    • 이런 문제를 해결해 고도로 동적이고 이질적인 환경에서 ID제공하고자 이스티오SPIFFE specification 사양을 사용한다.
    • SPIFFE는 고도로 동적이고 이질적인 환경에서 워크로드에 ID를 제공하기 위한 일렬의 오픈소스 표준이다.
    • SPIFFE 처리에 대한 더 자세한 내용과 함께 이 SPIFFE가 이스티오의 ID 추정을 뒷받침하는 방법을 보려면 부록 C를 참조하자.
     
    9.1.5 이스티오가 SPIFFE를 구현하는 방법 How Istio implements SPIFFE - SVID
      • SPIFFE ID는 RFC 3986 호환 URI로, spiffe://trust-domain/path 형식으로 구성된다.
      • 여기서는 다음과 같다.
        • trust-domain 은 개인 또는 조직 같은 ID 발급자를 나타낸다.
        • path는 trust-domain 내에서 워크로드를 유일하게 식별한다.
     
      • path가 워크로드를 식별하는 자세한 방법은 정해져 있지 않아서 SPIFFE 명세 구현자가 결정할 수 있다.
      • 이스티오에서는 이 path를 특정 워크로드가 사용하는 서비스 어카운트로 채운다.
      • SPIFFE IDSVID (Spiffe Verifiable Identity Document, SPIFFE 검증할 수 있는 ID 문서) 라고도 하는 X.509 인증서인코딩되며, 이는 이스티오의 컨트롤 플레인이 워크로드마다 만들어낸다.
      • 그런 다음, 이 인증서는 전송 데이터를 암호화함으로써 서비스 간 통신의 전송을 보호하는데 사용된다.
      • 다시 말하지만, 부록 C에서 이 모든 작업이 어떻게 작동하는지를 휠씬 자세히 다룬다.
      • 이번 장에서는 이스티오의 기능으로 보안 태세를 개선하는 데 초점을 맞춘다.
       
      9.1.6 이스티오 보안 요약 Istio security in a nutshell - PeerAuthentication , RequestAuthentication , AuthorizationPolicy
        • 이스티오 보안을 이해하기 위해 이스티오가 정의한 커스텀 리소스로 프록시를 설정하는 서비스 메시 운영자의 관점으로 바꿔보자.
          • 인증에 성공하면, 프록시는 상대 peer인증서에 인코딩된 정보를 추출해 요청 인가에 사용할 수 있도록 한다.
          • 인증에 성공하면, 역시 자격 증명에 인코딩된 정보를 추출해 요청 인가에 사용할 수 있도록 한다.
          • PeerAuthentication 리소스는 서비스 간의 트래픽을 인증하도록 프록시를 설정한다. The PeerAuthentication resource configures the proxy to authenticate service-to-service traffic.
          • RequestAuthentication 리소스는 프록시가 최종 사용자의 자격 증명을 발급 서버에 확인해 인증하도록 설정한다. The RequestAuthentication resource configures the proxy to authenticate end-user credentials against the servers that issued them.
          • AuthorizationPolicy 리소스는 앞선 두 리소스에 따라 추출한 정보를 토대로 프록시가 요청을 인가하거나 거부하도록 구성한다. The AuthorizationPolicy resource configures the proxy to authorize or reject requests by making decisions based on the data extracted by the previous two resources.
      부록 C.6 필터 메타데이터에서 검증된 데이터 수집부록 C.6 필터 메타데이터에서 검증된 데이터 수집
       
          • 그림 9.3은 PeerAuthenticationRequestAuthentication 리소스가 어떻게 요청을 인증하도록 프록시를 구성하는지, 자격 증명(SVID나 JWT)에 인코딩된 정보가 어느 시점에 추출돼 필터 메타데이터로 저장되는지를 보여준다.
          • 필터 메타데이터커넥션 ID를 나타낸다. The filter metadata represents the connection identity.
          • AuthorizationPolicy 리소스는 그 커넥션 ID에 기반해 요청을 허가할지 거부할지를 결정한다.
          요청을 인증 및 인가하도록 서비스 프록시를 구성하는 리소스요청을 인증 및 인가하도록 서비스 프록시를 구성하는 리소스
            1. PeerAuthentication : 서비스-to-서비스 인증 설정, 인가를 위한 피어 정보 추출
            2. RequestAuthentication : End-user 인증 설정, 인가를 위한 유저 정보 추출
            3. AuthorizationPolicy : PeerAuthenticationRequestAuthentication 에서 추출한 피어/유저 정보에 기초하여 권한 판단을 위한 인가 정책을 설정
           

          Istio Security Architecture

          image
          • The Istio CA manages keys and certificates and the SANs in certificates are in SPIFFE format.
          • Istiod distributes authentication and authorization security policies to all sidecars in the mesh.
          • Sidecars enforce authentication and authorization as per security policies distributed by Istiod
           

          9.2. 자동 상호 TLS (Auto mTLS)

          https://netpple.github.io/docs/istio-in-action/Istio-ch9-securing-2-auto_mTLS

          들어가며 : 인증서 발급/갱신 자동화, 추가 작업(인증, 인가)
            • 사이트가 프록시가 주입된 서비스 사이의 트래픽은 기본적으로 암호화되고 서로 인증한다.
            • 인증서를 발급하고 로테이션하는 절차를 자동화하는 것은 매우 중요한데, 역사적으로 사람이 관리할 때 오류가 발생하기 쉬웠기 때문이다.
            • 이로 인해 불필요하고 비용이 많이 드는 서비스 중단이 발생했는데, 이스티오에서 구현한 것처럼 절차를 자동화했다면 피할 수 있었을 문제였다.
           
            • 그림 9.4는 컨트롤 플레인에서 발급한 인증서를 사용해 서비스들이 서로 인증하고 트래픽을 암호화하는 방식을 나타낸다.
            • 이 방식을 통해 기본적으로 안전한 상태를 유지한다.
            • 사실 ‘기본적으로 안전한’이라고 하면 기본적으로는 대부분 안전하다는 의미로, 메시를 더 안전하게 만들기 위해서는 아직 우리가 수행해야 할 작업들이 남아 있다.
            image
                1. 워크로드는 이스티오 인증 기관에서 발급한 SVID 인증서를 사용해 서로 인증한다.
             
                1. 먼저, 서비스 메시가 서로 인증한 트래픽만 허용하도록 설정해야 한다.
                2. 왜 이것이 설치할 때 기본값이 아닌지 궁금할 수 있다. 이는 메시 채택을 용이하게 하려는 설계 결정이다.
                3. 여러 팀이 자체 서비스를 관리하는 거대 엔터프라이즈에서는 모든 서비스를 메시로 옮기기까지 몇 달 혹은 몇 년에 걸치 조직적인 노력이 필요할 수 있다.
               
              • 두 번째로, 서비스를 인증하면 최소 권한 원칙을 준수할 수 있고, 각 서비스에 정책을 만들 수 있으며, 기능에 필요한 최소한의 접근만 허용할 수 있다.
              • 이는 아주 중요한데, 서비스의 ID를 나타내는 인증서가 잘못된 사람에게 넘어갔을 때 피해 범위를 ID가 접근할 수 있도록 허용된 일부 서비스만으로 좁힐 수 있기 때문이다.
               
              (기본 정보) TLS vs mTLS
                • TLS - 암호화방식 인증서 Handshake
                  • TLS는 네트워크로 통신을 하는 과정에서 도청, 간섭, 위조를 방지하기 위해서 설계됨. 암호화를 통해 인증, 통신 기밀성을 제공.
                  • TLS의 3단계 기본 절차: (1) 지원 가능한 알고리즘 서로 교환 (2) 키 교환, 인증 (3) 대칭키 암호로 암호화하고 메시지 인증
                • TLS vs MTLS - 링크 소개
                  • MTLS 절차 : 서버측클라이언트측에 대한 인증서를 확인 및 액세스 권한 확인
              https://blog.cloudflare.com/protecting-the-origin-with-tls-authenticated-origin-pulls/
               
              9.2.1 환경 설정하기 (실습~)
                • mTLS 기능 실습을 위해 3가지 서비스를 준비.
                • sleep 서비스를 추가 : 레거시 워크로드로, 사이드카 프록시가 없어서 상호 인증을 할 수 없음
              image
               
                • 실습 환경 설정
              # catalog와 webapp 배포
              kubectl apply -f services/catalog/kubernetes/catalog.yaml -n istioinaction
              kubectl apply -f services/webapp/kubernetes/webapp.yaml -n istioinaction
              
              # webapp과 catalog의 gateway, virtualservice 설정
              kubectl apply -f services/webapp/istio/webapp-catalog-gw-vs.yaml -n istioinaction
              
              # default 네임스페이스에 sleep 앱 배포
              cat ch9/sleep.yaml
              ...
                  spec:
                    serviceAccountName: sleep
                    containers:
                    - name: sleep
                      image: governmentpaas/curl-ssl
                      command: ["/bin/sleep", "3650d"]
                      imagePullPolicy: IfNotPresent
                      volumeMounts:
                      - mountPath: /etc/sleep/tls
                        name: secret-volume
                    volumes:
                    - name: secret-volume
                      secret:
                        secretName: sleep-secret
                        optional: true
              
              kubectl apply -f ch9/sleep.yaml -n default
              
              # 확인
              kubectl get deploy,pod,sa,svc,ep
              kubectl get deploy,svc -n istioinaction
              
              kubectl get gw,vs -n istioinaction
              NAME                                            AGE
              gateway.networking.istio.io/coolstore-gateway   24s
              
              NAME                                                       GATEWAYS                HOSTS                         AGE
              virtualservice.networking.istio.io/webapp-virtualservice   ["coolstore-gateway"]   ["webapp.istioinaction.io"]   24s
               
                • 기본 통신 확인 : 레거시 sleep 워크로드 → webapp 워크로드로 평문 요청 실행
                # 요청 실행
                kubectl exec deploy/sleep -c sleep -- curl -s webapp.istioinaction/api/catalog -o /dev/null -w "%{http_code}\n"
                
                # 반복 요청
                watch 'kubectl exec deploy/sleep -c sleep -- curl -s webapp.istioinaction/api/catalog -o /dev/null -w "%{http_code}\n"'
                
                    1. 키알리 확인 : 네임스페이스(default, istioinaction 선택), Show Legend 클릭 후 아이콘 확인, unkonw → webapp 구간은 평문 통신
                image
                 
                  1. 응답이 성공했다는 것은 서비스들이 올바르게 준비됐으며 webapp 서비스가 sleep 서비스의 평문 요청을 받아들였다는 사실을 보여준다.
                  2. 기본적으로 이스티오는 평문 요청을 허용하는데, 이는 모든 워크로드를 메시로 옮길 때까지 서비스 중단을 일으키지 않고 서비스 메시를 점진적으로 채택할 수 있게 하기 위해서다.
                  3. 그러나 PeerAuthentication 리소스로 평문 트래픽을 금지할 수 있다.
                  4. 위에서 말했듯이, PeerAuthentication은 service-to-service 인증을 위한 resource 이다.
                   
                    • 9.2.2 이스티오의 PeerAuthentication 리소스 이해하기*
                    들어가며
                    • PeerAuthentication 리소스를 사용하면 워크로드가 mTLS를 엄격하게 요구하거나 평문 트래픽을 허용하고 받아들이게 설정할 수 있다.
                    • 이들 각각은 STRICT 혹은 PERMISSIVE 인증 모드를 사용한다.
                    • 상호 mutual 인증 모드는 다양한 범위에서 구성할 수 있다.
                      • Mesh-wide PeerAuthentication 정책은 서비스 메시의 모든 워크로드에 적용된다.
                      • Namespace-wide PeerAuthentication 정책은 네임스페이스 내 모든 워크로드에 적용된다.
                      • Workload-specific PeerAuthentication 정책은 정책에서 명시한 셀렉터에 부합하는 모든 워크로드에 적용된다.
                     
                    메시 범위 정책으로 모든 미인증 트래픽 거부하기 DENYING ALL NON-AUTHENTICATED TRAFFIC USING A MESH-WIDE POLICY
                      • 메시의 보안을 향상시키기 위해 STRICT 상호 인증 모드를 강제하는 메시 범위 MESH-WIDE 정책을 만들어서 평문 트래픽을 금지할 수 있다.
                      • 메시 범위 PeerAuthentication 정책은 두 가지 조건을 충족해야 한다.
                      • 반드시 이스티오를 설치한 네임스페이스에 적용해야 하고, 이름은 ‘default’여야 한다.
                      👉🏻

                      메시 범위 리소스의 이름을 ‘default’로 짓는 것은 필수가 아닌 일종의 컨벤션(convention)으로, 메시 범위 PeerAuthentication 리소스를 딱 하나만 만들기 위해서다.

                      #
                      cat ch9/meshwide-strict-peer-authn.yaml 
                      apiVersion: "security.istio.io/v1beta1"
                      kind: "PeerAuthentication"
                      metadata:
                        name: "default" # Mesh-wide policies must be named "default"
                        namespace: "istio-system" # Istio installation namespace
                      spec:
                        mtls:
                          mode: STRICT # mutual TLS mode
                      
                      # 적용
                      kubectl apply -f ch9/meshwide-strict-peer-authn.yaml -n istio-system
                      
                      # 요청 실행
                      kubectl exec deploy/sleep -c sleep -- curl -s http://webapp.istioinaction/api/catalog -o /dev/null -w "%{http_code}\n"
                      000
                      command terminated with exit code 56
                      
                      # 확인
                      kubectl get PeerAuthentication -n istio-system
                      kubectl logs -n istioinaction -l app=webapp -c webapp -f
                      kubectl logs -n istioinaction -l app=webapp -c istio-proxy -f
                      [2025-05-10T08:50:17.972Z] "- - -" 0 NR filter_chain_not_found - "-" 0 0 0 - "-" "-" "-" "-" "-" - - 10.10.0.13:8080 10.10.0.14:55100 - -
                      [2025-05-10T08:50:20.105Z] "- - -" 0 NR filter_chain_not_found - "-" 0 0 0 - "-" "-" "-" "-" "-" - - 10.10.0.13:8080 10.10.0.14:55104 - -
                      # NR → Non-Route. Envoy에서 라우팅까지 가지 못한 단계에서 발생한 에러라는 의미입니다.
                      # filter_chain_not_found → 해당 Listener에서 제공된 SNI(Server Name Indication), IP, 포트, ALPN 등의 조건에 맞는 filter_chain이 설정에 없다는 뜻입니다.
                          1. 이는 평문 요청이 거부됐다는 것을 확인한다.
                          2. 상호 인증 요구 사항을 STRICT로 지정하는 것은 좋은 기본값이지만, 진행 중인 프로젝트에서는 그런 급격한 변화가 실현 가능성이 없다.
                          3. 워크로드를 옮기려면 여러 팀 간의 협업이 필요하기 때문이다.
                          4. 더 나은 방법은 적용하는 제한을 점진적으로 늘리고, 팀들이 자신의 서비스를 서비스 메시로 옮길 수 있도록 시간을 주는 것이다.
                          5. PERMISSIVE 상호 인증이 딱 그런 역할로, 워크로드가 암호화된 요청 평문 요청을 모두 받아드릴 수 있게 허용한다.
                      요청을 인증 및 인가하도록 서비스 프록시를 구성하는 리소스요청을 인증 및 인가하도록 서비스 프록시를 구성하는 리소스
                      image
                       
                      상호 인증하기 않은 트래픽 허용하기 PERMITTING NON-MUTUALLY AUTHENTICATED TRAFFIC
                        • 네임스페이스 범위 정책을 사용하면 메시 범위 정책을 덮어 쓸 수 있고, 네임스페이스의 워크로드에 더 잘 맞는 PeerAuthentication 요구 사항을 적용할 수 있다.
                        • 다음 PeerAuthentication 리소스는 istioinaction 네임스페이스의 워크로드가 sleep 서비스와 같이 메시의 일부가 아닌 레거시 워크로드로부터 평문 트래픽을 받아들이도록 허용한다.
                      cat << EOF | kubectl apply -f -
                      apiVersion: "security.istio.io/v1beta1"
                      kind: "PeerAuthentication"
                      metadata:
                        name: "default"             # Uses the "default" naming convention so that only one namespace-wide resource exists
                        namespace: "istioinaction"  # Specifies the namespace to apply the policy
                      spec:
                        mtls:
                          mode: PERMISSIVE          # PERMISSIVE allows HTTP traffic.
                      EOF
                       
                      # 요청 실행
                      kubectl exec deploy/sleep -c sleep -- curl -s http://webapp.istioinaction/api/catalog -o /dev/null -w "%{http_code}\n"
                      200
                      
                      # 확인
                      kubectl get PeerAuthentication -A 
                      NAMESPACE       NAME      MODE         AGE
                      istio-system    default   STRICT       3m40s
                      istioinaction   default   PERMISSIVE   22s
                      
                      kubectl logs -n istioinaction -l app=webapp -c webapp -f
                      
                      # 정상 200 return 확인
                      # permissive mode에 의해서 mesh의 일부가 아닌(=istio proxy가 없는) sleep pod의 요청 허용
                      kubectl logs -n istioinaction -l app=webapp -c istio-proxy -f
                      [2025-05-10T08:53:53.501Z] "GET /items HTTP/1.1" 200 - via_upstream - "-" 0 502 1 1 "-" "beegoServer" "9e0a45af-8057-9b77-83a7-a6c9ada11e23" "catalog.istioinaction:80" "10.10.0.12:3000" outbound|80||catalog.istioinaction.svc.cluster.local 10.10.0.13:44292 10.200.1.181:80 10.10.0.13:35556 - default
                      [2025-05-10T08:53:53.500Z] "GET /api/catalog HTTP/1.1" 200 - via_upstream - "-" 0 357 2 2 "-" "curl/8.5.0" "9e0a45af-8057-9b77-83a7-a6c9ada11e23" "webapp.istioinaction" "10.10.0.13:8080" inbound|8080|| 127.0.0.6:39191 10.10.0.13:8080 10.10.0.14:46398 - default
                      
                      # 다음 실습을 위해 삭제 : PeerAuthentication 단축어 pa
                      kubectl delete pa default -n istioinaction
                      
                      • 좀 더 보안을 신경써보자.
                      • 미인증 트래픽은 sleep 워크로드에서 webapp으로 향하는 것만 허용하고, catalog 워크로드에는 STRICT 상호 인증을 계속 유지하자.
                      • 이렇게 하면 보안이 뚫렸을 때 공격 표면을 더 좁힐 수 있다.
                       
                      워크로드별 PeerAuthentication 정책 적용하기 APPLYING WORKLOAD-SPECIFIC PEERAUTHENTICATION POLICIES
                        • webapp 만 목표로 하기 위해 워크로드 셀렉터를 지정해 상술했던 PeerAuthentication 정책을 업데이트 하자.
                        • 이로써 셀렉터에 부합하는 워크로드에만 적용될 것이다.
                        • 또한 이름을 ‘default’에서 webapp으로 바뀌자.
                        • 동작이 바꾸지는 않지만, 네임스페이스 전체에 적용되는 PeerAuthentication 정책만 ‘default’로 짓는 컨벤션을 따르려는 것이다.
                        # istiod 는 PeerAuthentication 리소스 생성을 수신하고, 이 리소스를 엔보이용 설정으로 변환하며, 
                        # LDS(Listener Discovery Service)를 사용해 서비스 프록시에 적용
                        docker exec -it myk8s-control-plane istioctl proxy-status
                        kubectl logs -n istio-system -l app=istiod -f
                        ...
                        2025-05-10T08:59:08.529410Z     info    ads     LDS: PUSH for node:istio-ingressgateway-6bb8fb6549-wq46d.istio-system resources:1 size:3.5kB
                        2025-05-10T08:59:08.530318Z     info    ads     LDS: PUSH for node:catalog-6cf4b97d-ks9w8.istioinaction resources:22 size:80.9kB
                        2025-05-10T08:59:08.530321Z     info    ads     LDS: PUSH for node:webapp-7685bcb84-hmdtl.istioinaction resources:22 size:80.9kB
                        ...
                        
                        #
                        cat ch9/workload-permissive-peer-authn.yaml
                        apiVersion: "security.istio.io/v1beta1"
                        kind: "PeerAuthentication"
                        metadata:
                          name: "webapp"
                          namespace: "istioinaction"
                        spec:
                          selector:
                            matchLabels:
                              app: "webapp"  # 레이블이 일치하는 워크로드만 PERMISSIVE로 동작
                          mtls:
                            mode: PERMISSIVE
                        
                        kubectl apply -f ch9/workload-permissive-peer-authn.yaml
                        
                        kubectl get pa -A
                        NAMESPACE       NAME      MODE         AGE
                        istio-system    default   STRICT       11m
                        istioinaction   webapp    PERMISSIVE   4s
                        
                        # 요청 실행 => webapp pa가 적용됨(mtls permissive mode) => http 요청 허용
                        kubectl exec deploy/sleep -c sleep -- curl -s http://webapp.istioinaction/api/catalog -o /dev/null -w "%{http_code}\n"
                        
                        kubectl logs -n istioinaction -l app=webapp -c webapp -f
                        kubectl logs -n istioinaction -l app=webapp -c istio-proxy -f
                        200
                        
                        # 요청 실행 => default pa가 적용됨(mtls strict mode) => http 요청 거부
                        kubectl exec deploy/sleep -c sleep -- curl -s http://catalog.istioinaction/api/items -o /dev/null -w "%{http_code}\n"
                        
                        kubectl logs -n istioinaction -l app=catalog -c catalog -f
                        kubectl logs -n istioinaction -l app=catalog -c istio-proxy -f
                        [2025-05-10T09:01:56.346Z] "- - -" 0 NR filter_chain_not_found - "-" 0 0 0 - "-" "-" "-" "-" "-" - - 10.10.0.12:3000 10.10.0.14:42446 - -...
                            1. 성공 응답을 반환한다! 메시 범위 정책으로 엄격한 기본값을 적용했다.
                            2. 그러나 일부 서비스(뒤처진 것들)에는 그 서비스들이 메시로 옮겨질 때까지 상호 인증이 아닌 트래픽도 허용되도록 워크로드별 정책을 사용한다.
                        webapp은 HTTP 트래픽을 받아들인다. catalog 서비스에는 상호 인증이 필요하다.webapp은 HTTP 트래픽을 받아들인다. catalog 서비스에는 상호 인증이 필요하다.
                         
                        👉🏻

                        istiodPeerAuthentication 리소스 생성을 수신하고, 이 리소스를 엔보이용 설정으로 변환하며, LDS(Listener Discovery Service)를 사용해 서비스 프록시에 적용한다. 구성된 정책들은 들어오는 요청마다 평가된다. The configured policies are evaluated for every incoming request.

                         
                        두 가지 추가적인 상호 인증 모드 TWO ADDITIONAL MUTUAL AUTHENTICATION MODES
                        • 대부분의 경우 STRICTPERMISSIVE 모드를 사용할 것이다. 그러나 두 가지 모드가 더 있다.
                          • UNSET : 부모의 PeerAuthentication 정책을 상속한다. Inherit the PeerAuthentication policy of the parent.
                          • DISABLE : 트래픽을 터널링하지 않는다. 그냥 보낸다. Do not tunnel the traffic; send it directly to the service.
                        • PeerAuthentication 리소스를 이렇게 사용할 수 있다.
                        • 상호 인증 트래픽, 평문 트래픽 등 워크로드로 터널링할 트래픽 유형을 지정하거나, 요청을 프록시로 보내지 않고 애플리케이션으로 바로 포워딩할 수 있다.
                        • 다음 절에서는 상호 TLS를 사용할 때 트래픽이 암호화되는지 확인해보자.
                         
                        tcpdump로 서비스 간 트래픽 스니핑하기 EAVESDROPPING ON SERVICE-TO-SERVICE TRAFFIC USING TCPDUMP
                          • 이스티오 프록시에는 tcpdump설치돼 있다. 이 도구는 네트워크 인터페이스르 통과하는 네트워크 트래픽을 포착하고 분석한다.
                          • tcpdump 는 보안 때문에 권한 privileged permission 이 필요한데, 기본적으로 이 권한은 꺼져 있다.
                          • 이 권한을 켜려면 istioctl로 속성 values.global.proxy.privileged=true 로 설정해 이스티오 설치를 업데이트하자.
                        👉🏻

                        격상시킨 서비스 프록시의 권한은 악의적 공격의 매개체가 될 수 있다. 운영 환경 클러스터에서 이스티오를 설치할 때는 프록시의 권한을 격상시키지 말자. 서비스 하나를 빠르게 디버깅하고 싶을때는 kubectl edit로 디플로이먼트의 필드를 수작업으로 바꿀 수 있다.

                        # demo 프로파일 컨트롤 플레인 배포 시 istio-proxy 에 privileged 설정 >> 이미 설정 되어 있음
                        istioctl install --set profile=demo --set values.global.proxy.privileged=true -y
                        # 확인
                        kubectl get istiooperator -n istio-system installed-state -o yaml
                        ...
                              proxy:
                                ...
                                privileged: true
                        ...
                        
                        kubectl get pod -n istioinaction -l app=webapp -o json
                                                "image": "docker.io/istio/proxyv2:1.17.8",
                                                "imagePullPolicy": "IfNotPresent",
                                                "name": "istio-proxy",
                                                ...
                                                "securityContext": {
                                                    "allowPrivilegeEscalation": true,
                                                    "capabilities": {
                                                        "drop": [
                                                            "ALL"
                                                        ]
                                                    },
                                                    "privileged": true,
                                                    "readOnlyRootFilesystem": true,
                                                    "runAsGroup": 1337,
                                                    "runAsNonRoot": true,
                                                    "runAsUser": 1337
                                                },
                        ...
                        
                        # 
                        kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy -- whoami
                        kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy -- id
                        kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy -- sudo whoami
                        root
                        
                        kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy -- sudo tcpdump -h
                         
                          • 파드 트래픽을 스니핑 sniffing 해보자
                        # 패킷 모니터링 실행 해두기
                        kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy \
                          -- sudo tcpdump -l --immediate-mode -vv -s 0 '(((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0) and not (port 53)'
                        # -l : 표준 출력(stdout)을 라인 버퍼 모드로 설정. 터미널에서 실시간으로 결과를 보기 좋게 함 (pipe로 넘길 때도 유용).
                        # --immediate-mode : 커널 버퍼에서 패킷을 모아서 내보내지 않고, 캡처 즉시 사용자 공간으로 넘김 → 딜레이 최소화.
                        # -vv : verbose 출력. 패킷에 대한 최대한의 상세 정보를 보여줌.
                        # -s 0 : snap length를 0으로 설정 → 패킷 전체 내용을 캡처. (기본값은 262144 bytes, 예전 버전에서는 68 bytes로 잘렸음)
                        # '(((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0) and not (port 53)' : DNS패킷 제외하고 TCP payload 길이가 0이 아닌 패킷만 캡처
                        # 즉, SYN/ACK/FIN 같은 handshake 패킷(데이터 없는 패킷) 무시, 실제 데이터 있는 패킷만 캡처
                        # 결론 : 지연 없이, 전체 패킷 내용을, 매우 자세히 출력하고, DNS패킷 제외하고 TCP 데이터(payload)가 1 byte 이상 있는 패킷만 캡처
                        
                        # 요청 실행
                        kubectl exec deploy/sleep -c sleep -- curl -s webapp.istioinaction/api/catalog -o /dev/null -w "%{http_code}\n"
                        ...
                        ## (1) sleep -> webapp 호출 HTTP
                        09:09:28.376191 IP (tos 0x0, ttl 63, id 18560, offset 0, flags [DF], proto TCP (6), length 146)
                            10-10-0-14.sleep.default.svc.cluster.local.53182 > webapp-7685bcb84-hmdtl.http-alt: Flags [P.], cksum 0x14b3 (incorrect -> 0x1b2a), seq 2712301210:2712301304, ack 27344324, win 512, options [nop,nop,TS val 205598070 ecr 2202594813], length 94: HTTP, length: 94
                                GET /api/catalog HTTP/1.1
                                Host: webapp.istioinaction
                                User-Agent: curl/8.5.0
                                Accept: */*
                        
                        ## (2) webapp -> catalog 호출 HTTPS
                        09:09:28.378523 IP (tos 0x0, ttl 64, id 61154, offset 0, flags [DF], proto TCP (6), length 1304)
                            webapp-7685bcb84-hmdtl.42824 > 10-10-0-12.catalog.istioinaction.svc.cluster.local.3000: Flags [P.], cksum 0x1937 (incorrect -> 0x58af), seq 762455895:762457147, ack 2699309704, win 871, options [nop,nop,TS val 3505077413 ecr 3377320582], length 1252
                        
                        ## (3) catalog -> webapp 응답 HTTPS
                        09:09:28.381739 IP (tos 0x0, ttl 63, id 65156, offset 0, flags [DF], proto TCP (6), length 1788)
                            10-10-0-12.catalog.istioinaction.svc.cluster.local.3000 > webapp-7685bcb84-hmdtl.42824: Flags [P.], cksum 0x1b1b (incorrect -> 0x9cb2), seq 1:1737, ack 1252, win 729, options [nop,nop,TS val 3377775587 ecr 3505077413], length 1736
                        
                        ## (4) webapp -> sleep 응답 HTTP
                        09:09:28.382862 IP (tos 0x0, ttl 64, id 57235, offset 0, flags [DF], proto TCP (6), length 662)
                            webapp-7685bcb84-hmdtl.http-alt > 10-10-0-14.sleep.default.svc.cluster.local.53182: Flags [P.], cksum 0x16b7 (incorrect -> 0xc4e5), seq 1:611, ack 94, win 512, options [nop,nop,TS val 2202594820 ecr 205598070], length 610: HTTP, length: 610
                                HTTP/1.1 200 OK
                                content-length: 357
                                content-type: application/json; charset=utf-8
                                date: Sat, 10 May 2025 09:09:28 GMT
                                x-envoy-upstream-service-time: 6
                                server: istio-envoy
                                x-envoy-decorator-operation: webapp.istioinaction.svc.cluster.local:80/*
                        
                                [{"id":1,"color":"amber","department":"Eyewear","name":"Elinor Glasses","price":"282.00"},{"id":2,"color":"cyan","department":"Clothing","name":"Atlas Shirt","price":"127.00"},{"id":3,"color":"teal","department":"Clothing","name":"Small Metal Shoes","price":"232.00"},{"id":4,"color":"red","department":"Watches","name":"Red Dragon Watch","price":"232.00"}] [|http]
                        ...
                        #
                        kubectl get svc,ep -n istioinaction
                        NAME              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
                        service/catalog   ClusterIP   10.200.1.181   <none>        80/TCP    28m
                        service/webapp    ClusterIP   10.200.1.212   <none>        80/TCP    28m
                        
                        NAME                ENDPOINTS         AGE
                        endpoints/catalog   10.10.0.12:3000   28m
                        endpoints/webapp    10.10.0.13:8080   28m
                        
                        #
                        kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy \
                          -- sudo tcpdump -l --immediate-mode -vv -s 0 'tcp port 3000 or tcp port 8080'
                        
                        # 요청 실행
                        kubectl exec deploy/sleep -c sleep -- curl -s webapp.istioinaction/api/catalog -o /dev/null -w "%{http_code}\n"
                        
                        09:14:40.243491 IP (tos 0x0, ttl 63, id 39438, offset 0, flags [DF], proto TCP (6), length 146)
                            10-10-0-14.sleep.default.svc.cluster.local.52506 > webapp-7685bcb84-hmdtl.http-alt: Flags [P.], cksum 0x14b3 (incorrect -> 0x535d), seq 1:95, ack 1, win 512, options [nop,nop,TS val 205909945 ecr 2202906687], length 94: HTTP, length: 94
                                GET /api/catalog HTTP/1.1
                                Host: webapp.istioinaction
                                User-Agent: curl/8.5.0
                                Accept: */*
                        
                        09:14:40.243494 IP (tos 0x0, ttl 64, id 46837, offset 0, flags [DF], proto TCP (6), length 52)
                            webapp-7685bcb84-hmdtl.http-alt > 10-10-0-14.sleep.default.svc.cluster.local.52506: Flags [.], cksum 0x1455 (incorrect -> 0xae6d), seq 1, ack 95, win 511, options [nop,nop,TS val 2202906688 ecr 205909945], length 0
                        
                        09:14:40.245088 IP (tos 0x0, ttl 64, id 26320, offset 0, flags [DF], proto TCP (6), length 1304)
                            webapp-7685bcb84-hmdtl.42808 > 10-10-0-12.catalog.istioinaction.svc.cluster.local.3000: Flags [P.], cksum 0x1937 (incorrect -> 0xb199), seq 3097730358:3097731610, ack 4260227584, win 871, options [nop,nop,TS val 3505389286 ecr 3377317193], length 1252
                        
                        09:14:40.247529 IP (tos 0x0, ttl 63, id 55572, offset 0, flags [DF], proto TCP (6), length 1788)
                            10-10-0-12.catalog.istioinaction.svc.cluster.local.3000 > webapp-7685bcb84-hmdtl.42808: Flags [P.], cksum 0x1b1b (incorrect -> 0xb87c), seq 1:1737, ack 1252, win 729, options [nop,nop,TS val 3378087460 ecr 3505389286], length 1736
                        
                        09:14:40.247543 IP (tos 0x0, ttl 64, id 26321, offset 0, flags [DF], proto TCP (6), length 52)
                            webapp-7685bcb84-hmdtl.42808 > 10-10-0-12.catalog.istioinaction.svc.cluster.local.3000: Flags [.], cksum 0x1453 (incorrect -> 0x436c), seq 1252, ack 1737, win 871, options [nop,nop,TS val 3505389289 ecr 3378087460], length 0
                        
                        09:14:40.248057 IP (tos 0x0, ttl 64, id 46838, offset 0, flags [DF], proto TCP (6), length 662)
                            webapp-7685bcb84-hmdtl.http-alt > 10-10-0-14.sleep.default.svc.cluster.local.52506: Flags [P.], cksum 0x16b7 (incorrect -> 0x0024), seq 1:611, ack 95, win 512, options [nop,nop,TS val 2202906692 ecr 205909945], length 610: HTTP, length: 610
                                HTTP/1.1 200 OK
                                content-length: 357
                                content-type: application/json; charset=utf-8
                                date: Sat, 10 May 2025 09:14:40 GMT
                                x-envoy-upstream-service-time: 4
                                server: istio-envoy
                                x-envoy-decorator-operation: webapp.istioinaction.svc.cluster.local:80/*
                                [{"id":1,"color":"amber","department":"Eyewear","name":"Elinor Glasses","price":"282.00"},{"id":2,"color":"cyan","department":"Clothing","name":"Atlas Shirt","price":"127.00"},{"id":3,"color":"teal","department":"Clothing","name":"Small Metal Shoes","price":"232.00"},{"id":4,"color":"red","department":"Watches","name":"Red Dragon Watch","price":"232.00"}] [|http]
                         
                        워크로드 ID가 워크로드 서비스 어카운트에 연결돼 있는지 확인하기 VERIFYING THAT WORKLOAD IDENTITIES ARE TIED TO THE WORKLOAD SERVICE ACCOUNT
                          • 상호 인증을 다룬 절을 끝내기 전에 발급된 인증서가 유효한 SVID 문서인지, SPIFFE ID가 인코딩돼 있는지, 그 ID가 워크로드 서비스 어카운트와 일치하는지 확인해보자.
                          • openssl 명령어를 사용해 catalog 워크로드의 X.509 인증서 내용물을 확인한다.
                        # (참고) 패킷 모니터링 : 아래 openssl 실행 시 동작 확인
                        kubectl exec -it -n istioinaction deploy/catalog -c istio-proxy \
                          -- sudo tcpdump -l --immediate-mode -vv -s 0 'tcp port 3000'
                          
                        
                        # catalog 의 X.509 인증서 내용 확인
                        kubectl -n istioinaction exec deploy/webapp -c istio-proxy -- ls -l /var/run/secrets/istio/root-cert.pem
                        kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy -- openssl x509 -in /var/run/secrets/istio/root-cert.pem -text -noout
                        Certificate:
                            Data:
                                Version: 3 (0x2)
                                Serial Number:
                                    cc:9f:cd:f9:f6:55:55:31:30:6d:89:ad:4d:66:86:15
                                Signature Algorithm: sha256WithRSAEncryption
                                Issuer: O = cluster.local
                                Validity
                                    Not Before: May 10 08:20:03 2025 GMT
                                    Not After : May  8 08:20:03 2035 GMT
                                Subject: O = cluster.local
                        ...
                        
                        kubectl -n istioinaction exec deploy/webapp -c istio-proxy -- openssl -h
                        kubectl -n istioinaction exec deploy/webapp -c istio-proxy -- openssl s_client -h
                        
                        # openssl s_client → TLS 서버에 연결해 handshake와 인증서 체인을 보여줌
                        # -showcerts → 서버가 보낸 전체 인증서 체인 출력
                        # -connect catalog.istioinaction.svc.cluster.local:80 → Istio 서비스 catalog로 TCP 80 연결
                        # -CAfile /var/run/secrets/istio/root-cert.pem → Istio의 root CA로 서버 인증서 검증
                        # 결론 : Envoy proxy에서 catalog 서비스로 연결하여 TLS handshake 및 인증서 체인 출력 후 사람이 읽을 수 있는 형식으로 해석
                        kubectl -n istioinaction exec deploy/webapp -c istio-proxy \
                          -- openssl s_client -showcerts \
                          -connect catalog.istioinaction.svc.cluster.local:80 \
                          -CAfile /var/run/secrets/istio/root-cert.pem | \
                          openssl x509 -in /dev/stdin -text -noout
                        ...
                                Validity
                                    Not Before: May 10 08:43:34 2025 GMT # 유효기간 1일 2분
                                    Not After : May 11 08:45:34 2025 GMT
                                ...
                                X509v3 extensions:
                                    X509v3 Extended Key Usage:
                                        TLS Web Server Authentication, TLS Web Client Authentication # 사용처 : 웹서버, 웹클라이언트
                                    ...
                                    X509v3 Subject Alternative Name: critical
                                        URI:spiffe://cluster.local/ns/istioinaction/sa/catalog # SPIFFE ID 확인
                        
                        # catalog 파드의 서비스 어카운트 확인
                        kubectl describe pod -n istioinaction -l app=catalog | grep 'Service Account'
                        Service Account:  catalog
                         
                          • 루트 인증서 서명 확인
                            • openssl verify 로 인증 기관 CA 루트 인증서에 대해 서명을 확인함으로써 X.509 SVID의 내용물이 유효한지 살펴보자.
                            • 루트 인증서는 istio-proxy 컨테이너에서 /var/run/secrets/istio/root-cert.pem 경로에 마운트돼 있다.
                        # webapp.istio-proxy 쉘 접속
                        kubectl -n istioinaction exec -it deploy/webapp -c istio-proxy -- /bin/bash
                        -----------------------------------------------
                        # 인증서 검증
                        openssl verify -CAfile /var/run/secrets/istio/root-cert.pem \
                          <(openssl s_client -connect \
                          catalog.istioinaction.svc.cluster.local:80 -showcerts 2>/dev/null)
                        /dev/fd/63: OK
                        # 검증에 성공 시 OK 메시지 출력: 이스티오 CA가 인증서에 서명했으며, 내부 데이터가 믿을 수 있다는 것임을 알려줌.
                        
                        exit
                        -----------------------------------------------
                         
                          • 이제 참가자 간 peer-to-peer 인증을 용의하게 하는 모든 구성 요소를 검증했으므로, 발급된 ID는 검증할 수 있는 것이고 트래픽은 안전하다는 것을 확신할 수 있다.
                          • 검증할 수 있는 ID가 접근 제어의 선행 조건이다. 다시 말해, 워크로드의 ID를 알고 있으므로 수행할 수 있는 작업을 정의할 수 있다.
                          • 다음 절에서는 인가 정책을 살펴본다.
                           

                          9.C. 이스티오 보안: SPIFFE(부록 C)

                          C.1 PKI를 사용한 인증 Authentication using PKI (public key infrastructure)
                            • 들어가며
                              • World Wide Web에서 통신 당사자는 PKI Public Key Infrastructure (공개 키 인프라) 규격을 따라 발급한 디지털 서명 인증서를 사용해 인증한다.
                              • PKI는 절차를 정의하는 프레임워크인데, 이 절차는 서버(웹 앱 등)에는 자신의 정체를 증명할 수 있는 디지털 인증서를 제공하고 클라이언트에는 디지털 인증서의 유효성을 검증할 수 있는 수단을 제공한다. https://www.securew2.com/blog/public-key-infrastructure-explained
                           
                              • PKI에서 제공하는 인증서에는 공개 키개인 키가 있다. 클라이언트에게 인증서를 인증 수단으로 제시하는데, 공개 키는 이 인증서 안에 포함된다.
                              • 클라이언트는 공개된 네트워크에서 서버로 데이터를 전송하기 전에 공개 키를 사용해 데이터를 암호화하며, 개인 키를 가진 서버만이 데이터를 복호화할 수 있다.
                              • 이런 방식으로 데이터는 전송 중에 안전하게 보호된다.
                          👉🏻

                          공개 키 인증서의 표준 형식을 X.509 인증서라고 한다. 이 책에서는 X.509 인증서라는 용어와 디지털 인증서라는 용어를 같은 뜻으로 사용한다.

                           
                              • 국제 인터넷 표준화 기구 IETF는 전송 계층 보안 TLS Transport Layer Security 프로토콜(PKI를 사용하기는 하지만 PKI만 사용해야 하는 것은 아님)을 정의하고, X.509 인증서를 공급해 트래픽 인증 및 암호화를 용의하게 했다.
                           

                          C.1.1 TLS 및 최종 사용자 인증을 통한 트래픽 암호화 Traffic encryption via TLS and end-user authentication

                            • TLS 프로토콜은 TLS 핸드셰이크 절차에서 서버의 유효성을 인증하고 트래픽 대칭 키 암호화용 키를 안전하게 교환하는 데 X.509 인증서를 기본 메커니즘으로 사용한다. (그림 C.1 참조)
                            TLS 핸드셰이크 단계TLS 핸드셰이크 단계
                                1. 클라이언트가 자신이 지원하는 TLS 버전과 암호화 수단을 포함한 ClientHello 로 핸드셰이크를 시작한다.
                                2. 서버ServerHello자신의 X.509 인증서로 응답한다. 인증서에는 서버의 ID 정보공개 키가 포함돼 있다.
                                3. 클라이언트는 서버의 인증서 데이터가 변조되지 않았음을 확인하고 신뢰 체인을 검증한다.
                                4. 검증에 성공하면, 클라이언트서버비밀 키를 보낸다. 이 키는 임의로 생성한 문자열서버공개 키암호화한 것이다.
                                5. 서버는 자신의 개인 키로 비밀 키를 복호화하고, 복호화된 비밀 키로 ‘finished’ 메시지를 암호화클라이언트로 보낸다.
                                6. 클라이언트비밀 키로 암호화한 ‘finished’ 메시지를 서버에 보내면 TLS 핸드셰이크가 완료된다.
                             
                                1. TLS 핸드셰이크의 결실은 클라이언트가 서버를 인증했고 대칭 키를 안전하게 교환했다는 것이다.
                                2. 이 대칭 키는 이 커넥션에서 클라이언트와 서버를 오가는 트래픽을 암호화하는데 사용한다.
                                3. 이런 방식이 비대칭 암호화보다 성능이 더 좋기 때문이다.
                                4. 최종 사용자에게 이런 절차는 브라우저가 투명하게 수행하는 것으로, 주소 표시줄에 녹색 자물쇠로 표시돼 수신자가 인증됐고 트래픽이 암호화돼 수신자만 복호화할 수 있다는 것을 확인해준다.
                               
                              • 서버에서 최종 사용자를 인증하는 것은 구현하기 나름이다.
                              • 여러 가지 방법이 있지만, 그 모든 방법의 핵심은 비밀번호를 알고 있는 사용자가 세션 쿠키JWT(JSON Web Token)를 받는 것이다.
                              • 이때 JWT수명이 짧고 사용자의 후속 요청을 서버에 인증하기 위한 정보를 포함하는 것이 이상적이다.
                              • 이스티오는 JWT를 사용하는 최종 사용자 인증을 지원한다.
                              • 실제로 동작하는 모습은 9.4절에서 살펴봤다.
                               
                                • C.2 SPIFFE: 모든 이를 위한 안전한 운영 환경 ID 프레임워크 Secure Production Identity Framework for Everyone (실습)
                                들어가며
                                  • SPIFFE는 고도로 동적이며 이질적인 환경에서 워크로드에 ID를 제공하기 위한 오픈소스 표준 집합이다.
                                  • ID를 발급하고 부트스트랩하기 위해 SPIFFE는 다음 사양을 정의한다.
                                    • SPIFFE ID : 신뢰 도메인 내에서 서비스를 고유하게 구별한다.
                                    • Workload Endpoint : 워크로드의 ID를 부트스트랩한다.
                                    • Workload API : SPIFFE ID가 포함된 인증서를 서명하고 발급한다.
                                    • SVID SPIFFE Verifiable Identity Document : 워크로드 API가 발급한 인증서로 표현된다.
                                 
                                  • SPIFFE 사양은 SPIFFE ID 형식으로 워크로드에 ID를 발급하고 이를 SVID에 인코딩하는 절차를 정의할 뿐 아니라, 컨트롤 플레인 구성 요소(워크로드 API)와 데이터 플레인 구성 요소(워크로드 엔드포인트)가 워크로드의 ID를 검증하고 할당하고 형식의 유효성을 검사하기 위해 협동하는 방법도 정의한다.
                                  • 이스티오가 이런 사양을 구현하므로 이에 대한 더 깊은 이해가 필요하다.
                                   
                                  C.2.1 SPIFFE ID: Workload identity 워크로드 ID
                                  • SPIFFE ID는 RFC 3986 호환 URI로, spiffe://trust-domain/path 형식을 따른다.
                                    • trust-domain 은 개인이나 조직 같은 ID 발급자를 나타낸다.
                                    • path는 trust-domain 내에서 워크로드를 고유하게 식별한다.
                                  • 경로 path로 워크로드를 식별하는 방법의 세부 사항에는 제약이 없으며 SPIFFE 사양 구현자가 결정할 수 있다.
                                  • 이 부록에서는 이스티오가 쿠버네티스 서비스 어카운트를 사용해 워크로드를 식별하는 경로를 정의하는 방법을 살펴본다.
                                    • path 형식이 ns/namespace/sa/serviceaccount로 구성이 됨.
                                   
                                  C.2.2 Workload API 워크로드 API
                                    • 워크로드 API는 SPIFFE 사양에서 컨트롤 플레인 구성 요소를 나타내며, 워크로드가 자신의 ID를 정의하는 SVID 형식 디지털 인증서를 가져갈 수 있도록 엔드포인트를 노출한다.
                                    • 워크로드 API는 두 가지 주요 기능은 다음과 같다.
                                      • 워크로드가 제출한 인증서 서명 요청 CSR에 인증 기관 CA 개인 키로 서명함으로써 워크로드에 인증서 발급
                                      • 워크로드 엔드포인트에서 해당 기능을 사용할 수 있도록 API 노출
                                   
                                    • 사양 specification sets 은 워크로드가 자신의 ID를 정의하는 비밀이나 기타 정보를 보유해서는 안 된다제한(규칙)을 둔다.
                                    • 그렇지 않으면, 해당 비밀에 접근할 수 있는 악의적인 사용자가 시스템을 쉽게 악용할 수 있기 때문이다.
                                    • 이 제한 때문에 워크로드에는 인증 수단이 없어 워크로드 API로 보안 통신을 시작할 수 없다.
                                    • 이 상황을 해결하기 위해 SPIFFE는 워크로드 엔드포인트 사양을 정의한다.
                                    • 이 사양은 데이터 플레인 구성 요소를 나타내고, 워크로드의 ID를 부트스트랩하는 데 필요한 모든 작업을 수행한다.
                                    • 예를 들어, 워크로드 API와 보안 통신을 시작하거나 도청 또는 중간자 공격에 취약하지 않게 SVID를 가져오는 등의 활동을 수행한다.
                                     
                                    C.2.3 Workload endpoints 워크로드 엔드포인트
                                      • 워크로드 엔드포인트는 SPIFFE 사양의 데이터 플레인 구성 요소를 나타낸다. 이는 모든 워크로드와 함께 배포돼 다음 기능을 제공한다.
                                        • 커널 검사 kernel introspection 또는 orchestrator interrogation (쿼리, 질문) 같은 방법을 사용해 워크로드의 ID를 확인한다.
                                        • 워크로드 API와 보안 통신을 시작하고 유지한다. 이 보안 통신은 SVID를 가져오고 로테이션하는 데 사용한다.
                                        • 워크로드 증명 attestation
                                        • 워크로드 API 노출 exposure
                                     
                                        • 그림 C.2는 워크로드에 ID를 발급하는 단계의 개요를 보여준다.
                                            • 이 인증서의 SAN의 URI 확장에는 SPIFFE ID가 있다.
                                            • 이 인증서는 워크로드 ID를 나타내는 SVID이다.
                                           
                                        워크로드에 ID를 발급하기워크로드에 ID를 발급하기
                                          1. 워크로드 엔드포인트는 워크로드의 무결성을 확인하고(즉, 워크로드 증명을 수행하고) SPIFFIE ID가 인코딩된 CSR을 생성한다.
                                          2. 워크로드 엔드포인트는 서명을 위해 워크로드 API에 CSR을 제출한다.
                                          3. 워크로드 API는 CSR을 서명하고 디지털 서명된 인증서로 응답한다.
                                        C.2.4 SPIFFE Verifiable Identity Documents 검증할 수 있는 ID 문서
                                          • SVID (SPIFFE 검증할 수 있는 ID 문서) 는 워크로드의 정체를 나타내는 검증할 수 있는 문서다.
                                          • 검증할 수 있다는 것이 가장 중요한 속성인데, 그렇지 않으면 수신자가 워크로드의 정체를 신뢰할 수 없기 때문이다.
                                          • 사양은 SVID 표현 기준을 충족하는 문서로 두 가지 유형인 X.509 인증서JWT를 정의한다.
                                          • 둘 다 다음과 같은 요소로 구성된다.
                                            • SPIFFE ID, 워크로드 ID를 나타낸다.
                                            • 유효한 서명, SPIFFE ID가 변조되지 않았음을 확인한다.
                                            • (선택 사항) 워크로드 간에 보안 통신 채널을 구축하기 위한 공개 키
                                         
                                            • 이스티오는 SVIDX.509 인증서로 구현한다.
                                            • 그 방법은 SAN Subject Alternative Name 확장에 SPIFFE ID를 URI로 인코딩하는 것이다.
                                            • X.509 인증서를 사용하면 추가적인 이점이 있는데, 워크로드가 서로 간의 트래픽을 상호 인증하고 암호화할 수 있다는 것이다. (그림 C.3 참조)
                                          자신의 SVID를 가져오고 보안 통신을 시작하는 워크로드자신의 SVID를 가져오고 보안 통신을 시작하는 워크로드
                                           
                                          • 이스티오가 SPIFFE 사양을 구현함으로써, 모든 워크로드가 각자의 ID를 공급받고 그 ID를 증거로 인증서를 받는다는 것이 자동으로 보장된다.
                                          • 이런 인증서는 상호 인증과 모든 서비스 간 통신을 암호화하는 데 사용한다.
                                          • 그러므로 이 기능을 자동 상호 TLS라고 한다. Hence this feature is called auto mTLS.
                                           
                                          C.2.5 How Istio implements SPIFFE 이스티오가 SPIFFE를 구현하는 방법
                                            • 이스티오를 사용하면 다음 두 구성 요소가 협업해 워크로드에 ID를 제공한다.
                                              • ID를 부트스트랩하는 워크로드 엔드포인트 (데이터플레인, 이스티오 프록시 pilot agent)
                                              • 인증서를 발급하는 워크로드 API (컨트롤플레인, istiod 의 Istio CA)
                                           
                                              • 이스티오에서 워크로드 엔드포인트 사양은 워크로드와 함께 배포되는 이스티오 프록시가 구현한다.
                                              • 이스티오 프록시는 ID를 부트스트랩하고 이스티오 CA에서 인증서를 가져오는데, 이스티오 CA는 istiod의 구성 요소로 워크로드 API 사양을 구현한다.
                                              • 그림 C.4는 이스티오가 SPIFFE 구성 요소를 구현하는 방법을 보여준다.
                                              이스티오 구성 요소를 SPIFFE 사양에 매핑하기이스티오 구성 요소를 SPIFFE 사양에 매핑하기
                                                  1. 워크로드 엔드포인트는 ID 부트스트랩을 수행하는 이스티오 파일럿 에이전트로 구현한다.
                                                  2. 워크로드 API는 인증서를 발급하는 이스티오 CA로 구현한다.
                                                  3. 이스티오에서 ID를 발급하는 워크로드는 서비스 프록시다.
                                               
                                                1. 이는 이스티오가 SPIFFE를 구현하는 방법을 고수준에서 살펴본 것이다.
                                                2. 해당 내용을 이해하고 기억하기 위해 이 과정을 단계별로 살펴보자.
                                                 
                                                C.2.6 Step-by-step bootstrapping of workload identity* 워크로드 ID의 단계별 부트스트랩
                                                  • 기본적으로 쿠버네티스에서 초기화된 모든 파드에는 /var/run/secrets/kubernetes.io/serviceaccount/ 경로에 시크릿이 마운트돼 있다.
                                                  • 이 시크릿에는 쿠버네티스 API 서버와 안전하게 통신하는 데 필요한 모든 데이터가 포함돼 있다.
                                                    • The service account token contains a set of claims for the service account representing the Pod.
                                                    • ca.crt 는 쿠버네티스 API 서버가 발급한 인증서의 유효성을 검증한다.
                                                    • 네임스페이스는 파드가 위치한 곳을 나타낸다.
                                                    • 서비스 어카운트 토큰에는 파드를 나타내는 서비스 어카운트에 대한 (토큰)클레임들이 포함된다.
                                                 
                                                    • ID 부트스트랩 과정에서 가장 중요한 요소는 쿠버네티스 API가 발급한 토큰이다. → serviceaccount에 의해 projected 됨
                                                    • 토큰의 페이로드는 수정할 수 없는데, 수정하면 서명 유효성 검사를 통과하지 못하기 때문이다.
                                                    • 페이로드에는 애플리케이션을 식별하는 데이터가 포함된다.
                                                  #
                                                  kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy -- ls -l /var/run/secrets/kubernetes.io/serviceaccount/
                                                  kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy -- cat /var/run/secrets/kubernetes.io/serviceaccount/token
                                                  TOKEN=$(kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy -- cat /var/run/secrets/kubernetes.io/serviceaccount/token)
                                                  
                                                  # 헤더 디코딩 
                                                  echo $TOKEN | cut -d '.' -f1 | base64 --decode | sed 's/$/}/' | jq
                                                  {
                                                    "alg": "RS256",
                                                    "kid": "SN10I18dRCUt2qzMCIixl46dqMB6ssinwJKzuFuDDWM"
                                                  }
                                                  
                                                  # 페이로드 디코딩
                                                  echo $TOKEN | cut -d '.' -f2 | base64 --decode | sed 's/$/}/' | jq
                                                  {
                                                    "aud": [
                                                      "https://kubernetes.default.svc.cluster.local"
                                                    ],
                                                    "exp": 1778405668,
                                                    "iat": 1746869668,
                                                    "iss": "https://kubernetes.default.svc.cluster.local",
                                                    "kubernetes.io": {
                                                      "namespace": "istioinaction",
                                                      "pod": {
                                                        "name": "webapp-7685bcb84-hmdtl",
                                                        "uid": "96cf1bee-2cdd-48cd-bd22-5fb60503bc0a"
                                                      },
                                                      "serviceaccount": {
                                                        "name": "webapp",
                                                        "uid": "438f60f1-8d40-43b5-b8e0-c14d59081ea3"
                                                      },
                                                      "warnafter": 1746873275
                                                    },
                                                    "nbf": 1746869668,
                                                    "sub": "system:serviceaccount:istioinaction:webapp"
                                                  }
                                                  
                                                  # (옵션) brew install jwt-cli  # Linux 툴 추천 부탁드립니다.
                                                  jwt decode $TOKEN
                                                  Token header
                                                  ------------
                                                  {
                                                    "alg": "RS256",
                                                    "kid": "SN10I18dRCUt2qzMCIixl46dqMB6ssinwJKzuFuDDWM"
                                                  }
                                                  
                                                  Token claims
                                                  ------------
                                                  {
                                                    "aud": [ # 이 토큰의 대상(Audience) : 토큰이 어떤 API나 서비스에서 사용될 수 있는지 정의 -> k8s api가 aud 가 일치하는지 검사하여 올바른 토큰인지 판단.
                                                      "https://kubernetes.default.svc.cluster.local"
                                                    ],
                                                    "exp": 1778405668, # 토큰 만료 시간 Expiration Time (Unix timestamp, 초 단위) , date -r 1778405668 => (1년) Sun May 10 18:34:28 KST 2026
                                                    "iat": 1746869668, # 토큰 발급 시간 Issued At (Unix timestamp), date -r 1746869668 => Sat May 10 18:34:28 KST 2025
                                                    "iss": "https://kubernetes.default.svc.cluster.local", # Issuer, 토큰을 발급한 주체, k8s api가 발급
                                                    "kubernetes.io": {
                                                      "namespace": "istioinaction",
                                                      "pod": {
                                                        "name": "webapp-7685bcb84-hmdtl",
                                                        "uid": "96cf1bee-2cdd-48cd-bd22-5fb60503bc0a" # 파드 고유 식별자
                                                      },
                                                      "serviceaccount": {
                                                        "name": "webapp",
                                                        "uid": "438f60f1-8d40-43b5-b8e0-c14d59081ea3" # 서비스 어카운트 고유 식별자
                                                      },
                                                      "warnafter": 1746873275 # 이 시간 이후에는 새로운 토큰을 요청하라는 Kubernetes의 신호 (토큰 자동 갱신용) date -r 1746873275 (1시간) => Sat May 10 19:34:35 KST 2025
                                                    },
                                                    "nbf": 1746869668, # Not Before, 이 시간 이전에는 토큰이 유효하지 않음. 보통 iat와 동일하게 설정됩니다.
                                                    "sub": "system:serviceaccount:istioinaction:webapp" # 토큰의 주체(Subject)
                                                  }
                                                  
                                                  # sa 에 토큰 유효 시간 3600초 = 1시간 + 7초
                                                  kubectl get pod -n istioinaction -l app=webapp -o yaml
                                                  ...
                                                      - name: kube-api-access-nt4qb
                                                        projected:
                                                          defaultMode: 420
                                                          sources:
                                                          - serviceAccountToken:
                                                              expirationSeconds: 3607
                                                              path: token
                                                  ...
                                                  
                                                   
                                                    • 파일럿 에이전트는 토큰을 디코딩하고 이 페이로드 데이터를 사용해 SPIFFE ID(예 spiffe://cluster.local/ns/istioinaction/sa/default)를 생성한다.
                                                    • 이 SPIFFE ID는 CSR안에서 URI 유형의 SAN 확장으로 사용한다.
                                                    • 이스티오 CA로 보낸 요청토큰과 CSR이 모두 전송되며, CSR에 대한 응답으로 발급된 인증서가 반환된다.
                                                      • Both the token and the CSR are sent in the request to the Istio CA to get a certificate issued for the CSR.
                                                   
                                                      • CSR에 서명하기 전에 이스티오 CATokenReview API를 사용해 토큰이 쿠버네티스 API가 발급한 것이 맞는지 확인한다.
                                                      • 이는 SPIFFE 사양에서 약간 벗어난 것인데, SPIFFE 사양에서는 워크로드 엔드포인트(이스티오 에이전트)가 워크로드 증명을 수행해야 하기 때문이다.
                                                      • 검증을 통과하면 CSR에 서명하고, 결과 인증서가 파일럿 에이전트에 반환된다.
                                                    #
                                                    kubectl api-resources | grep -i token
                                                    tokenreviews                                   authentication.k8s.io/v1               false        TokenReview
                                                    
                                                    kubectl explain tokenreviews.authentication.k8s.io
                                                    ...
                                                    DESCRIPTION:
                                                         TokenReview attempts to authenticate a token to a known user. Note:
                                                         TokenReview requests may be cached by the webhook token authenticator
                                                         plugin in the kube-apiserver.
                                                    ...
                                                    
                                                    # Kubernetes API 서버에 TokenReview API 를 호출하여 토큰이 여전히 유효한지 확인 : C(Create)
                                                    ## 이때 사용되는 Kubernetes API 가 POST /apis/authentication.k8s.io/v1/tokenreviews
                                                    ## 즉, istiod가 이 API를 호출하려면 tokenreviews.authentication.k8s.io 리소스에 create 권한이 필요. C(Create)
                                                    kubectl rolesum istiod -n istio-system
                                                    ...
                                                    • [CRB] */istiod-clusterrole-istio-system ⟶  [CR] */istiod-clusterrole-istio-system
                                                      Resource                                                                                                                                             Name               Exclude    Verbs    G L W C U P D DC  
                                                      ...
                                                      signers.certificates.k8s.io                                                                                                             [kubernetes.io/legacy-unknown]    [-]    [approve]  ✖ ✖ ✖ ✖ ✖ ✖ ✖ ✖   
                                                      subjectaccessreviews.authorization.k8s.io                                                                                                            [*]                  [-]       [-]     ✖ ✖ ✖ ✔ ✖ ✖ ✖ ✖   
                                                      tokenreviews.authentication.k8s.io                                                                                                                   [*]                  [-]       [-]     ✖ ✖ ✖ ✔ ✖ ✖ ✖ ✖   
                                                      validatingwebhookconfigurations.admissionregistration.k8s.io                                                                                         [*]                  [-]       [-]     ✔ ✔ ✔ ✖ ✔ ✖ ✖ ✖   
                                                    ...
                                                    👉🏻

                                                    istiod 컨테이너 내부에서 자신의 token 으로 k8s 에 tokenreviews api 호출해보는 실습 명령어 추가해두기. (https 로 호출해야되고, k8s root ca 인증서를 지정 필요)

                                                    👉🏻

                                                    istio-proxy 배포 실행 로그와 위 과정 실행 절차를 확인하는 실습 명령어 추가해두기.

                                                     
                                                      • 파일럿 에이전트는 SDS Secrets Discovery Service 를 통해 인증서와 키를 엔보이 프록시로 전달하고, 이로써 ID 부트스트랩 과정이 마무리된다.
                                                    image
                                                    # 유닉스 도메인 소켓 listen 정보 확인
                                                    kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy -- ss -xpl
                                                    Netid       State        Recv-Q        Send-Q                                              Local Address:Port                   Peer Address:Port       Process                                    
                                                    u_str       LISTEN       0             4096                                          etc/istio/proxy/XDS 14868651                          * 0           users:(("pilot-agent",pid=1,fd=10))       
                                                    u_str       LISTEN       0             4096                 ./var/run/secrets/workload-spiffe-uds/socket 14868650                          * 0           users:(("pilot-agent",pid=1,fd=9)) 
                                                    
                                                    kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy -- ss -xp
                                                    Netid State Recv-Q Send-Q                                Local Address:Port    Peer Address:Port   Process                             
                                                    u_str ESTAB 0      0                               etc/istio/proxy/XDS 15306557            * 15309457 users:(("pilot-agent",pid=1,fd=3))
                                                    u_str ESTAB 0      0      ./var/run/secrets/workload-spiffe-uds/socket 14871603            * 14872623 users:(("pilot-agent",pid=1,fd=15))
                                                    ...
                                                    
                                                    # 유닉스 도메인 소켓 정보 확인
                                                    kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy -- lsof -U
                                                    COMMAND   PID        USER   FD   TYPE             DEVICE SIZE/OFF    NODE NAME
                                                    pilot-age   1 istio-proxy    3u  unix 0x00000000f22fa8f3      0t0 15306557 etc/istio/proxy/XDS type=STREAM # 소켓 경로 및 스트림 타입
                                                    pilot-age   1 istio-proxy   15u  unix 0x00000000a8a093ea      0t0 14871603 ./var/run/secrets/workload-spiffe-uds/socket type=STREAM # SPIFFE UDS (SPIFFE SVID 인증용)
                                                    # TYPE 파일 유형 (unix → Unix Domain Socket)
                                                    ## 3u → 3번 디스크립터, u = 읽기/쓰기
                                                    ## 15u → 15번 디스크립터, u = 읽기/쓰기
                                                    
                                                    # 유닉스 도메인 소켓 파일 정보 확인
                                                    kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy -- ls -l /var/run/secrets/workload-spiffe-uds/socket
                                                    srw-rw-rw- 1 istio-proxy istio-proxy 0 May 10 08:45 /var/run/secrets/workload-spiffe-uds/socket
                                                    
                                                    # istio 인증서 확인 : 
                                                    docker exec -it myk8s-control-plane istioctl proxy-config secret deploy/webapp.istioinaction
                                                    RESOURCE NAME     TYPE           STATUS     VALID CERT     SERIAL NUMBER                               NOT AFTER                NOT BEFORE
                                                    default           Cert Chain     ACTIVE     true           63745088412196707502357302102324860411      2025-05-11T08:45:40Z     2025-05-10T08:43:40Z
                                                    ROOTCA            CA             ACTIVE     true           271992264038647563728819189773953828373     2035-05-08T08:20:03Z     2025-05-10T08:20:03Z
                                                    
                                                    docker exec -it myk8s-control-plane istioctl proxy-config secret deploy/webapp.istioinaction -o json
                                                    ...
                                                    
                                                    echo "." | base64 -d  | openssl x509 -in /dev/stdin -text -noout
                                                    
                                                    
                                                    # istio ca 관련
                                                    kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy -- ls -l /var/run/secrets/istio
                                                    kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy -- openssl x509 -in /var/run/secrets/istio/root-cert.pem -text -noout
                                                    
                                                    kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy -- ls -l /var/run/secrets/tokens
                                                    kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy -- cat /var/run/secrets/tokens/istio-token
                                                    TOKEN=$(kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy -- cat /var/run/secrets/tokens/istio-token)
                                                    # 헤더 디코딩 
                                                    echo $TOKEN | cut -d '.' -f1 | base64 --decode | sed 's/$/}/' | jq
                                                    # 페이로드 디코딩
                                                    echo $TOKEN | cut -d '.' -f2 | base64 --decode | sed 's/$/"}/' | jq
                                                    # (옵션) brew install jwt-cli 
                                                    jwt decode $TOKEN
                                                    Token header
                                                    ------------
                                                    {
                                                      "alg": "RS256",
                                                      "kid": "SN10I18dRCUt2qzMCIixl46dqMB6ssinwJKzuFuDDWM"
                                                    }
                                                    
                                                    Token claims
                                                    ------------
                                                    {
                                                      "aud": [
                                                        "istio-ca" # aud가 istiod의 istio-ca(=workload api)
                                                      ],
                                                      "exp": 1746909925,
                                                      "iat": 1746866725,
                                                      "iss": "https://kubernetes.default.svc.cluster.local",
                                                      "kubernetes.io": {
                                                        "namespace": "istioinaction",
                                                        "pod": {
                                                          "name": "webapp-7685bcb84-hmdtl",
                                                          "uid": "96cf1bee-2cdd-48cd-bd22-5fb60503bc0a"
                                                        },
                                                        "serviceaccount": {
                                                          "name": "webapp",
                                                          "uid": "438f60f1-8d40-43b5-b8e0-c14d59081ea3"
                                                        }
                                                      },
                                                      "nbf": 1746866725,
                                                      "sub": "system:serviceaccount:istioinaction:webapp"
                                                    }
                                                    
                                                    # (참고) k8s ca 관련
                                                    kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy -- ls -l /var/run/secrets
                                                    kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy -- ls -l /var/run/secrets/kubernetes.io/serviceaccount
                                                    kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy -- openssl x509 -in /var/run/secrets/kubernetes.io/serviceaccount/ca.crt -text -noout
                                                    kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy -- cat /var/run/secrets/kubernetes.io/serviceaccount/token
                                                    TOKEN=$(kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy -- cat /var/run/secrets/kubernetes.io/serviceaccount/token)
                                                    # 헤더 디코딩 
                                                    echo $TOKEN | cut -d '.' -f1 | base64 --decode | sed 's/$/}/' | jq
                                                    # 페이로드 디코딩
                                                    echo $TOKEN | cut -d '.' -f2 | base64 --decode | sed 's/$/}/' | jq
                                                    # (옵션) brew install jwt-cli 
                                                    jwt decode $TOKEN
                                                    Token header
                                                    ------------
                                                    {
                                                      "alg": "RS256",
                                                      "kid": "SN10I18dRCUt2qzMCIixl46dqMB6ssinwJKzuFuDDWM"
                                                    }
                                                    
                                                    Token claims
                                                    ------------
                                                    {
                                                      "aud": [
                                                        "https://kubernetes.default.svc.cluster.local" # aud가 kubernetes api server
                                                      ],
                                                      "exp": 1778408596,
                                                      "iat": 1746872596,
                                                      "iss": "https://kubernetes.default.svc.cluster.local",
                                                      "kubernetes.io": {
                                                        "namespace": "istioinaction",
                                                        "pod": {
                                                          "name": "webapp-7685bcb84-hmdtl",
                                                          "uid": "96cf1bee-2cdd-48cd-bd22-5fb60503bc0a"
                                                        },
                                                        "serviceaccount": {
                                                          "name": "webapp",
                                                          "uid": "438f60f1-8d40-43b5-b8e0-c14d59081ea3"
                                                        },
                                                        "warnafter": 1746876203
                                                      },
                                                      "nbf": 1746872596,
                                                      "sub": "system:serviceaccount:istioinaction:webapp"
                                                    }
                                                    
                                                    # (참고)
                                                    kubectl port-forward deploy/webapp -n istioinaction 15000:15000
                                                    open http://localhost:15000
                                                    curl http://localhost:15000/certs
                                                    
                                                     
                                                      • 이제 프록시는 클라이언트에게 자신의 정체를 증명할 수 있으며 상호 인증 커넥션을 시작할 수 있다.
                                                      • 그림 C.5는 이 과정을 간략하게 요약한 것이다.
                                                      쿠버네티스에서 이스티오로 SVID 발급하기쿠버네티스에서 이스티오로 SVID 발급하기
                                                          1. 이스티오 프록시 컨테이너에 서비스 어카운트 토큰이 할당된다.
                                                          2. 토큰과 CSR이 istiod로 전송된다.
                                                          3. istiod는 쿠버네티스 TokenReview API로 토큰의 유효성을 검사한다.
                                                          4. 성공하면, 인증서에 서명하고 응답으로 제공한다.
                                                          5. 파일럿 에이전트는 엔보이 SDS를 통해 엔보이가 ID를 포함한 인증서를 사용하도록 설정한다.
                                                       
                                                          1. 그리고 이것이 이스티오가 워크로드ID를 프로비저닝하기 위해 SPIFFE 사양을 구현하는 전체 과정이다.
                                                          2. 이 과정은 이스티오 프록시 사이드카가 주입되는 모든 워크로드에서 자동으로 수행된다.
                                                        image
                                                         
                                                        (참고) [사전 지식] K8S 파드의 애플리케이션이 사용할 수 있는 인증 관련 정보 - Docs , Link
                                                        https://ssup2.github.io/blog-old/theory_analysis/Kubernetes_Authentication_Service_Account/
                                                          • 서비스 어카운트 Service Account
                                                            • 서비스어카운트(ServiceAccount) 는 파드에서 실행되는 애플리케이션 프로세스에 대한 식별자를 제공한다.
                                                            • 파드 내부의 애플리케이션 프로세스는, 자신에게 부여된 서비스 어카운트의 식별자를 사용하여 클러스터의 API 서버에 인증할 수 있다.
                                                          • 서비스 어카운트 토큰 serviceAccountToken
                                                            • 서비스어카운트토큰(serviceAccountToken) 정보는 kubelet이 kube-apiserver로부터 취득한 토큰을 포함한다.
                                                            • kubelet은 TokenRequest API를 통해 일정 시간 동안 사용할 수 있는 토큰을 발급 받는다.
                                                            • 이렇게 취득한 토큰은 파드가 삭제되거나 지정된 수명 주기 이후에 만료된다(기본값은 1시간이다).
                                                            • 이 토큰은 특정한 파드에 바인딩되며 kube-apiserver를 그 대상으로 한다.
                                                          • 토큰 컨트롤러 token Controller
                                                            • kube-controller-manager 의 일부로써 실행되며, 비동기적으로 동작한다.
                                                            • 서비스어카운트에 대한 삭제를 감시하고, 해당하는 모든 서비스어카운트 토큰 시크릿을 같이 삭제한다.
                                                            • 서비스어카운트 토큰 시크릿에 대한 추가를 감시하고, 참조된 서비스어카운트가 존재하는지 확인하며, 필요한 경우 시크릿에 토큰을 추가한다.
                                                            • 시크릿에 대한 삭제를 감시하고, 필요한 경우 해당 서비스어카운트에서 참조 중인 항목들을 제거한다.
                                                          • 서비스 어카운트 어드미션 컨트롤러 Service Account Admission Controller
                                                            • 만약 해당하는 이름의 서비스어카운트가 존재하지 않는 경우, 어드미션 컨트롤러는 파드를 실행시키지 않는다.
                                                            • 이는 default 서비스어카운트에 대해서도 동일하게 적용된다.
                                                            • 어드미션 컨트롤러는 실행하려는 파드에 API에 접근할 수 있는 토큰을 포함하는 볼륨 을 추가한다.
                                                            • 어드미션 컨트롤러는 파드의 각 컨테이너에 volumeMount를 추가한다.
                                                            • 이미 /var/run/secrets/kubernetes.io/serviceaccount 경로에 볼륨이 마운트 되어있는 컨테이너에 대해서는 추가하지 않는다.
                                                            • 리눅스 컨테이너의 경우, 해당 볼륨은 /var/run/secrets/kubernetes.io/serviceaccount 위치에 마운트된다

                                                          어드미션 컨트롤러는 파드의 생성 시점에 다음 작업들을 수행한다.

                                                            1. 파드에 .spce.serviceAccountName 항목이 지정되지 않았다면, 어드미션 컨트롤러는 실행하려는 파드의 서비스어카운트 이름을 default로 설정한다.
                                                            2. 어드미션 컨트롤러는 실행되는 파드가 참조하는 서비스어카운트가 존재하는지 확인한다.
                                                            3. 서비스어카운트의 automountServiceAccountToken 또는 파드의 automountServiceAccountToken 중 어느 것도 false 로 설정되어 있지 않다면,
                                                            4. 파드의 spec에 imagePullSecrets 이 없는 경우, 어드미션 컨트롤러는 ServiceAccount의 imagePullSecrets을 복사하여 추가된다.
                                                          • TokenRequest API
                                                            • 서비스어카운트의 하위 리소스인 TokenRequest를 사용하여 일정 시간 동안 해당 서비스어카운트에서 사용할 수 있는 토큰을 가져올 수 있다.
                                                            • 컨테이너 내에서 사용하기 위한 API 토큰을 얻기 위해 이 요청을 직접 호출할 필요는 없는데, kubelet이 프로젝티드 볼륨 을 사용하여 이를 설정하기 때문이다.
                                                          • 프로젝티드 볼륨 Projected Volumes - Docs
                                                          (참고) Vault 사용 on K8S : Vault 에 시크릿 생성 및 애플리케이션에서 시크릿 가져와보기 🔑Hashicorp Vault/VSO on K8S (공개)
                                                          https://developer.hashicorp.com/vault/tutorials/kubernetes/agent-kubernetes

                                                          (1) Vault 에 Secret 를 요청 처리를 위해 사전에 Role(Policy) 설정

                                                          (2) 파드 생성 시, 서비스 어카운트 토큰(JWT) 생성

                                                          (3) 파드의 애플리케이션이 Vault 에 로그인 과정

                                                          3-1) 애플리케이션은 JWT를 전달하여 Vault 로그인 요청

                                                          3-2) Vault 는 정보 확인을 위해 K8S API 서버에 TokenReview API 호출

                                                          3-3) K8S API 서버는 서비스 어카운트의 이름과 네임스페이스를 반환

                                                          3-4) Vault 는 ‘서비스 어카운트 이름, 네임스페이스’를 Vault 해당 시크릿에 정책과 매칭 확인

                                                          3-5) 확인 후 Vault 는 Auth Token 을 애플리케이션에게 반환

                                                          (4) 파드의 애플리케이션이 Vault 에 Secret 요청 과정

                                                          4-1) 애플리케이션은 (3)에서 받은 Auth Token 으로 Vault 해당 시크릿 정보를 요청

                                                          4-2) Vault 는 Auth Token 확인 및 매칭 정책 확인

                                                          4-3) 확인 후 Vault 는 최종적으로 해당 시크릿 정보를 반환

                                                          👉🏻

                                                          Vault 에서 k8s-auth 인증은 아래 VSO에서도 활용됨. 참고로 AWS EKS에 aws-auth 인증/인가 시에도 유사한 과정을 사용. 🪕[NEW] AWS EKS Access Mgmt & Pod Identity

                                                           
                                                              1. C.3 요청 ID 이해하기 Understanding request identity
                                                          들어가며 : 필터 메타데이터 - Principal, Namespace, Request principal, Request authentication claims
                                                          필터 메타데이터에서 검증된 데이터 수집필터 메타데이터에서 검증된 데이터 수집
                                                          요청을 인증 및 인가하도록 서비스 프록시를 구성하는 리소스요청을 인증 및 인가하도록 서비스 프록시를 구성하는 리소스
                                                            • 요청 ID는 요청의 필터 메타데이터 저장된 값으로 표현된다. Request identity is represented by the values stored in the filter metadata of the request.
                                                            • 이 필터 메타데이터에는 JWT피어 인증서에서 추출한 사실 또는 클레임이 포함돼 있으므로 신뢰할 수 있다. This filter metadata contains facts or claims that were extracted from either the JWT or the peer certificate and therefore can be trusted.
                                                            • 9장에서는 JWT의 정보를 검증하기 위해 필요한 RequestAuthentication 리소스를 살펴봤다.
                                                            • 마찬가지로 클라이언트 워크로드 정보(워크로드의 네임스페이스 등)를 인증하려면 워크로드들이 상호 인증 mutually authenticate 해야 한다.
                                                            • PeerAuthentication 리소스는 워크로드가 상호 인증만 사용하도록 강제 only mutual authentication 할 수 있다.
                                                           
                                                            • JWT를 검증하거나 워크로드가 상호 인증마치면, 여기에 포함된 정보필터 메타데이터로 저장된다.
                                                            • 필터 메타데이터에 저장되는 정보 중 일부는 다음과 같다.
                                                              • Principal 주체 : PeerAuthentication 에서 정의한 워크로드 ID
                                                              • Namespace 네임스페이스 : PeerAuthentication 에서 정의한 워크로드 네임스페이스
                                                              • Request principal 요청 주체 : RequestAuthentication에서 정의한 최종 사용자 요청 주체 The end-user request principal defined by the RequestAuthentication.
                                                              • Request authentication claims 요청 인증 클레임 : 최종 사용자 토큰에서 추출한 최종 사용자 클레임 The end-user claims extracted from the end-user token.
                                                           
                                                            • 수집된 메타데이터를 관찰하고자 서비스 프록시가 이를 표준 출력에 기록하도록 설정할 수 있다.
                                                             
                                                            C.3.1 RequestAuthentication 리소스로 수집한 메타데이터 Metadata collected by the RequestAuthentication resource (실습)
                                                              • 기본적으로 엔보이 rbac 로거는 메타데이터를 로그에 출력하지 않는다. 따라서 출력하려면 로깅 수준의 debug로 설정해야 한다.
                                                            docker exec -it myk8s-control-plane istioctl proxy-config log deploy/istio-ingressgateway -n istio-system --level rbac:debug
                                                            ...
                                                             
                                                              • 다음으로, 사용할 서비스가 몇 가지 필요하다.
                                                              • 실습 중 자주 사용하는 실습 환경 초기화 후 워크로드로 트래픽을 라우팅하도록 인그레스 게이트웨이를 설정하기만 하면 된다.
                                                            kubectl apply -f services/catalog/kubernetes/catalog.yaml -n istioinaction
                                                            kubectl apply -f services/webapp/kubernetes/webapp.yaml -n istioinaction
                                                            kubectl apply -f services/webapp/istio/webapp-catalog-gw-vs.yaml -n istioinaction
                                                             
                                                              • 이어서 필터 메타데이터를 사용하는 RequestAuthentication 리소스와 AuthorizationPolicy 를 만든다
                                                            # end-user authentication을 위한 RequestAuthentication 생성
                                                            # jwtRules에 맞는 issuer와 jwks값에 대해서 인증
                                                            kubectl apply -f ch9/enduser/jwt-token-request-authn.yaml
                                                            
                                                            # :30000 포트 추가 필요, 아래 실습 설정 참고.
                                                            # requestPrincipals: ["auth@istioinaction.io/*"]에 맞는 jwt로 요청하면 webapp에 Get요청을 허용하겠다는 의미
                                                            cat ch9/enduser/allow-all-with-jwt-to-webapp.yaml 
                                                            apiVersion: security.istio.io/v1beta1
                                                            kind: AuthorizationPolicy
                                                            metadata:
                                                              name: allow-all-with-jwt-to-webapp
                                                              namespace: istio-system
                                                            spec:
                                                              selector:
                                                                matchLabels:
                                                                  app: istio-ingressgateway
                                                              action: ALLOW
                                                              rules:
                                                              - from:
                                                                - source:
                                                                    requestPrincipals: ["auth@istioinaction.io/*"]
                                                                to:
                                                                - operation:
                                                                    hosts: ["webapp.istioinaction.io:30000"]
                                                                    methods: ["GET"]
                                                            
                                                            kubectl apply -f ch9/enduser/allow-all-with-jwt-to-webapp.yaml
                                                            
                                                            kubectl get requestauthentication,authorizationpolicy -A
                                                            NAMESPACE      NAME                                                              AGE
                                                            istio-system   requestauthentication.security.istio.io/jwt-token-request-authn   7s
                                                            
                                                            NAMESPACE      NAME                                                                 AGE
                                                            istio-system   authorizationpolicy.security.istio.io/allow-all-with-jwt-to-webapp   3s
                                                             
                                                              • admin 토큰을 사용하는 요청을 해보자. 인그레스 게이트웨이에 로그를 남길 것이다
                                                            # admin 토큰을 사용하는 요청 : 필터 메타데이터 확인
                                                            ADMIN_TOKEN=$(< ch9/enduser/admin.jwt)
                                                            
                                                            jwt decode $ADMIN_TOKEN                       
                                                            
                                                            Token header
                                                            ------------
                                                            {
                                                              "typ": "JWT",
                                                              "alg": "RS256",
                                                              "kid": "CU-ADJJEbH9bXl0tpsQWYuo4EwlkxFUHbeJ4ckkakCM"
                                                            }
                                                            
                                                            Token claims
                                                            ------------
                                                            {
                                                              "exp": 4745145071,
                                                              "group": "admin",
                                                              "iat": 1591545071,
                                                              "iss": "auth@istioinaction.io",
                                                              "sub": "218d3fb9-4628-4d20-943c-124281c80e7b"
                                                            }
                                                            
                                                            # 요청 without ADMIN_TOKEN
                                                            curl \                                        
                                                                 -sSl -o /dev/null -w "%{http_code}\n" webapp.istioinaction.io:30000/api/catalog
                                                            403
                                                            
                                                            # 로깅
                                                            kubectl logs -n istio-system -l app=istio-ingressgateway -f
                                                            
                                                            [2025-05-10T10:59:45.441Z] "GET /api/catalog HTTP/1.1" 403 - rbac_access_denied_matched_policy[none] - "-" 0 19 0 - "192.168.65.1" "curl/8.7.1" "7443a756-5c57-97e7-9f32-50b353dd1cbd" "webapp.istioinaction.io:30000" "-" outbound|80||webapp.istioinaction.svc.cluster.local - 10.10.0.6:8080 192.168.65.1:34638 - -
                                                            
                                                            # 요청 with ADMIN_TOKEN
                                                            curl -H "Authorization: Bearer $ADMIN_TOKEN" \
                                                                 -sSl -o /dev/null -w "%{http_code}\n" webapp.istioinaction.io:30000/api/catalog
                                                            200
                                                            
                                                            # 로깅
                                                            kubectl logs -n istio-system -l app=istio-ingressgateway -f
                                                            
                                                            ...
                                                            # filter metadata에 jwt 값이 저장된다.
                                                            # iss가 auth@istioinaction.io이므로 authentication되고 authorizationPolicy에 의해서 webapp GET 요청이 authorization되어 200 return
                                                            dynamicMetadata: filter_metadata {
                                                              key: "envoy.filters.http.jwt_authn"
                                                              value {
                                                                fields {
                                                                  key: "auth@istioinaction.io"
                                                                  value {
                                                                    struct_value {
                                                                      fields {
                                                                        key: "exp"
                                                                        value {
                                                                          number_value: 4745145071
                                                                        }
                                                                      }
                                                                      fields {
                                                                        key: "group"
                                                                        value {
                                                                          string_value: "admin"
                                                                        }
                                                                      }
                                                                      fields {
                                                                        key: "iat"
                                                                        value {
                                                                          number_value: 1591545071
                                                                        }
                                                                      }
                                                                      fields {
                                                                        key: "iss"
                                                                        value {
                                                                          string_value: "auth@istioinaction.io"
                                                                        }
                                                                      }
                                                                      fields {
                                                                        key: "sub"
                                                                        value {
                                                                          string_value: "218d3fb9-4628-4d20-943c-124281c80e7b"
                                                                        }
                                                                      }
                                                                    }
                                                                  }
                                                                }
                                                              }
                                                            }
                                                            filter_metadata {
                                                              key: "istio_authn"
                                                              value {
                                                                fields {
                                                                  key: "request.auth.claims"
                                                                  value {
                                                                    struct_value {
                                                                      fields {
                                                                        key: "group"
                                                                        value {
                                                                          list_value {
                                                                            values {
                                                                              string_value: "admin"
                                                                            }
                                                                          }
                                                                        }
                                                                      }
                                                                      fields {
                                                                        key: "iss"
                                                                        value {
                                                                          list_value {
                                                                            values {
                                                                              string_value: "auth@istioinaction.io"
                                                                            }
                                                                          }
                                                                        }
                                                                      }
                                                                      fields {
                                                                        key: "sub"
                                                                        value {
                                                                          list_value {
                                                                            values {
                                                                              string_value: "218d3fb9-4628-4d20-943c-124281c80e7b"
                                                                            }
                                                                          }
                                                                        }
                                                                      }
                                                                    }
                                                                  }
                                                                }
                                                                fields {
                                                                  key: "request.auth.principal"
                                                                  value {
                                                                    string_value: "auth@istioinaction.io/218d3fb9-4628-4d20-943c-124281c80e7b"
                                                                  }
                                                                }
                                                                fields {
                                                                  key: "request.auth.raw_claims"
                                                                  value {
                                                                    string_value: "{\"iss\":\"auth@istioinaction.io\",\"exp\":4745145071,\"group\":\"admin\",\"iat\":1591545071,\"sub\":\"218d3fb9-4628-4d20-943c-124281c80e7b\"}"
                                                                  }
                                                                }
                                                              }
                                                            }
                                                                    thread=31
                                                            ...
                                                             
                                                              • 출력은 RequestAuthentication 필터가 최종 사용자 토큰의 클레임을 검증했고, 클레임을 필터 메타데이터로 저장했다는 것을 보여준다.
                                                              • 이제 정책들은 이 필터 메타데이터를 기반으로 작동할 수 있다.
                                                             
                                                              • 다음 실습을 위해 RequestAuthentication, AuthorizationPolicy 삭제
                                                            kubectl delete -f ch9/enduser/jwt-token-request-authn.yaml
                                                            kubectl delete -f ch9/enduser/allow-all-with-jwt-to-webapp.yaml
                                                             
                                                            C.3.2 한 요청의 대략적인 흐름* Overview of the flow of one request
                                                            • 워크로드가 목적지인 요청은 모두 다음 필터를 거친다 (그림 C.6 참조)
                                                            필터 메타데이터에서 검증된 데이터 수집필터 메타데이터에서 검증된 데이터 수집
                                                            요청을 인증 및 인가하도록 서비스 프록시를 구성하는 리소스요청을 인증 및 인가하도록 서비스 프록시를 구성하는 리소스
                                                            1. JWT authentication filter 인증 필터
                                                              • 인증 정책의 JWT 사양에 따라(=RequestAuthentication의 정의) JWT의 유효성을 검사하고 인증 클레임과 커스텀 클레임 같은 클레임을 추출해 필터 메타데이터로 저장하는 엔보이 필터 An Envoy filter that does JWT validation based on the JWT specification in authentication policies and extracts claims such as the authentication claims and custom claims, which are stored as filter metadata.
                                                            2. PeerAuthentication filter 피어인증 필터
                                                              • 서비스 인증 요구 사항을 강제하고 인증된 속성(소스 네임스페이스나 주체 같은 피어 ID)을 추출하는 엔보이 필터 An Envoy filter that enforces service authentication requirements and extracts authenticated attributes (peer identity such as source namespace and principal).
                                                            3. Authorization filter 인가 필터
                                                              • 앞선 필터들이 수집한 필터 메타데이터를 확인하고 워크로드에 적용된 정책(AuthorizationPolicy)에 따라 요청에 권한을 부여하는 인가 엔진 The authorization engine that checks the filter metadata collected by the previous filters and authorizes the request based on the policies applied to the workload.
                                                             
                                                              • webapp 서비스에 도달해야 하는 요청의 시나리오를 살펴보자.
                                                                • 이 필터는 토큰에서 클레임을 추출해 필터 메타데이터에 저장한다. 이로써 요청에 ID가 주어진다.
                                                                • 피어 간 인증 필터는 클라이언트의 ID 데이터를 추출해 필터 메타데이터에 저장한다.
                                                                • Custom authorization filters 커스텀 인가 필터들 : 요청을 허용하거나 거부할지 추가로 평가한다. Reject or allow further evaluation of the request.
                                                                • Deny authorization filters 거부 인가 필터들 : 요청을 허용하거나 거부할지 추가로 평가한다.
                                                                • Allow authorization filters 허용 인가 필터들 : 필터 조건에 맞으면 요청을 허용한다. Allow the request if the filter matches.
                                                                • Last (catch-all) authorization filter 마지막 (포괄적) 인가 필터 : 앞서 요청을 처리한 필터가 없는 경우에만 실행된다. Executed only if no prior filter has handled the request.
                                                                1. 요청JWT 인증 필터를 통과한다. The request passes the JWT authentication filter ⇒ RequestAuthentication resource 정의에 의해 수행
                                                                2. 인그레스 게이트웨이와 webapp 간에 피어 간 인증이 수행된다. ⇒ PeerAuthentication resource 정의에 의해 수행 Peer-to-peer authentication is performed between the ingress gateway and the webapp.
                                                                3. 인가 필터는 다음 순서대로 실행된다. Authorization filters are executed in order ⇒ AuthorizationPolicy 정의에 의해 수행
                                                             
                                                              1. 이것이 webapp 서비스로 향하는 요청이 인증되고 인가되는 방식이다. And that’s how the request is authenticated and authorized for the request to get to the webapp service.
                                                               
                                                               
                                                               
                                                               

                                                              9.3. 서비스 간 트래픽 인가하기

                                                              https://netpple.github.io/docs/istio-in-action/Istio-ch9-securing-3-authorizing

                                                              • [ssup2] Istio Authorization Policy - Link
                                                              들어가며 : Understanding request identity
                                                                • 인가란 인증된 주체가 리소스 접근, 편집, 삭제 같은 작업을 수행하도록 허용됐는지 정의하는 절차다.
                                                                • 정책은 인증된 주체(’누가’)와 인가(’무엇’)를 결합해 형성되며, 누가 무슨 일을 할 수 있는지 정의한다.
                                                               
                                                                • 이스티오에는 AuthorizationPolicy 리소스가 있는데, 이 리소스는 서비스 메시에 메시 범위, 네임스페이스 범위, 워크로드별 접근 정책을 정의하는 선언전 API이다.
                                                                • 그림 9.9는 특정 ID가 뚫렸을 때 접근 정책이 어떻게 접근 범위나 폭팔 반경을 제한하는지 보여준다.
                                                              인가는 공격 범위를 단지 도난당한 ID가 접근할 수 있는 것으로 축소한다.인가는 공격 범위를 단지 도난당한 ID가 접근할 수 있는 것으로 축소한다.
                                                               
                                                              • 인가 정책을 살펴보기 전에 이스티오에서 인가를 어떻게 구현하는지 먼저 이해하면 좋다.
                                                              • 다음 절에서 기초를 빠르게 살펴보자.
                                                               
                                                              9.3.1 이스티오에서 인가 이해하기 : AuthorizationPolicy - selector, rules(from, to, when), action
                                                              부록 C.6 필터 메타데이터에서 검증된 데이터 수집부록 C.6 필터 메타데이터에서 검증된 데이터 수집
                                                              요청을 인증 및 인가하도록 서비스 프록시를 구성하는 리소스요청을 인증 및 인가하도록 서비스 프록시를 구성하는 리소스
                                                               
                                                                • 각 서비스와 함께 배포되는 서비스 프록시가 인가 또는 집행 enforcement 엔진이다.
                                                                • 서비스 프록시요청을 거절하거나 허용할지 여부를 판단하기 위한 정책을 모두 포함하고 있기 때문이다.
                                                                • 그러므로 이스티오의 접근 제어는 대단히 효율적이다. 모든 결정이 프록시에서 직접 내려지기 때문이다.
                                                                • 프록시는 AuthorizationPolicy 리소스로 설정하는데, 이 리소스가 정책을 정의한다.
                                                                • 예시 AuthorizationPolicy 정의는 다음과 같다.
                                                              # cat ch9/allow-catalog-requests-in-web-app.yaml
                                                              apiVersion: "security.istio.io/v1beta1"
                                                              kind: "AuthorizationPolicy"
                                                              metadata:
                                                                name: "allow-catalog-requests-in-web-app"
                                                                namespace: istioinaction
                                                              spec:
                                                                selector:
                                                                  matchLabels:
                                                                    app: webapp
                                                                rules:
                                                                - to:
                                                                  - operation:
                                                                      paths: ["/api/catalog*"]
                                                                action: ALLOW
                                                               
                                                              • istiod가 새 AuthorizationPolicy 가 클러스터에 적용됐음을 확인하면, 다른 이스티오 리소스들처럼 해당 리소스로 데이터 플레인 프록시를 처리하고 업데이트 한다.
                                                              • 설정의 각 부분을 아직 이해하지 못한다고 걱정하지 말자. 다음 절에서 자세히 살펴볼 것이다.
                                                               

                                                              인가 정책의 속성 PROPERTIES OF AN AUTHORIZATION POLICY

                                                              • AuthorizationPolicy 리소스 사양에서 정책을 설정하고 정의하는 필드는 세 가지다.
                                                                • action은 규칙 중 하나가 요청과 일치하는 경우에만 적용된다.
                                                                • selector 필드는 정책을 적용할 워크로드 부분집합을 정의한다.
                                                                • action 필드는 이 정책이 허용(ALLOW)인지, 거부(DENY)인지, 커스텀(CUSTOM)인지 지정한다.
                                                                • rules 필드는 정책을 활성화할 요청을 식별하는 규칙 목록을 정의한다.
                                                              • rules 속성은 좀 더 복잡해서 더 깊이 살펴봐야 한다.
                                                               

                                                              인가 정책 규칙 이해하기 UNDERSTANDING AUTHORIZATION POLICY RULES

                                                                • 인가 정책 규칙은 커넥션은 출처 source 를 지정하며, 일치해야 규칙을 활성화하는 작업 operation 조건을 (원한다면) 지정할 수도 있다.
                                                                  • Authorization policy rules specify the source of the connection and (optionally) the operation that, when matched, activates the rule.
                                                                • 인가 정책은 규칙 중 하나의 출처와 작업 조건을 모두 만족시키는 경우에만 집행된다.
                                                                • 이 경우에만 정책이 활성화되고, 커넥션은 action 속성에 따라 허용되거나 거부된다.
                                                               
                                                              • 단일 규칙의 필드는 다음과 같다. The fields of a single rule are as follows:
                                                                • principals : 출처 ID(mTLS 예제에서 볼 수 있는 SPIFFE ID). 요청이 주체 principal 집합에서 온 것이 아니면 부정 속성인 notprincipals 가 적용된다. 이 기능이 작동하려면 서비스가 상호 인증해야 한다.
                                                                • namespaces : 출처 네임스페이스와 비교할 네임스페이스 목록. 출처 네임스페이스는 참가자의 SVID에서 가져온다. 이런 이유로, 작동하려면 mTLS가 활성화돼야 한다.
                                                                • ipBlocks : 출처 IP 주소와 비교할 단일 IP 주소나 CIDR 범위 목록.
                                                                • from 필드는 요청의 출처 source 를 다음 유형 중 하나로 지정한다.
                                                                • to 필드는 요청의 작업을 지정하며, 호스트나 요청의 메서드 등이 있다.
                                                                • when 필드는 규칙이 부합한 후 충족해야 하는 조건 목록을 지정한다.
                                                              • 공식 문서 https://istio.io/latest/docs/reference/config/security/authorization-policy/
                                                              9.3.2 작업 공간 설정하기 : 실습 환경 구성 확인 (실습~)
                                                                • 실습 환경 구성 : 9.2에서 이미 배포함
                                                              # 9.2.1 에서 이미 배포함
                                                              kubectl -n istioinaction apply -f services/catalog/kubernetes/catalog.yaml
                                                              kubectl -n istioinaction apply -f services/webapp/kubernetes/webapp.yaml
                                                              kubectl -n istioinaction apply -f services/webapp/istio/webapp-catalog-gw-vs.yaml
                                                              kubectl -n default apply -f ch9/sleep.yaml
                                                              
                                                              # gw,vs 확인 
                                                              kubectl -n istioinaction get gw,vs
                                                              
                                                              
                                                              # PeerAuthentication 설정 : 앞에서 이미 설정함
                                                              cat ch9/meshwide-strict-peer-authn.yaml
                                                              apiVersion: "security.istio.io/v1beta1"
                                                              kind: "PeerAuthentication"
                                                              metadata:
                                                                name: "default"
                                                                namespace: "istio-system"
                                                              spec:
                                                                mtls:
                                                                  mode: STRICT
                                                                  
                                                              kubectl -n istio-system apply -f ch9/meshwide-strict-peer-authn.yaml
                                                              kubectl get peerauthentication -n istio-system
                                                              NAME      MODE     AGE
                                                              default   STRICT   5h29m
                                                              
                                                              cat ch9/workload-permissive-peer-authn.yaml
                                                              apiVersion: "security.istio.io/v1beta1"
                                                              kind: "PeerAuthentication"
                                                              metadata:
                                                                name: "webapp"
                                                                namespace: "istioinaction"
                                                              spec:
                                                                selector:
                                                                  matchLabels:
                                                                    app: webapp 
                                                                mtls:
                                                                  mode: PERMISSIVE
                                                              
                                                              kubectl -n istioinaction apply -f ch9/workload-permissive-peer-authn.yaml
                                                              kubectl get peerauthentication -n istioinaction
                                                              gylee@GYLEEui-Macmini book-source-code-master % kubectl get peerauthentication -n istioinaction
                                                              NAME     MODE         AGE
                                                              webapp   PERMISSIVE   5h18m
                                                              webapp은 HTTP 트래픽을 받아들인다. catalog 서비스에는 상호 인증이 필요하다.webapp은 HTTP 트래픽을 받아들인다. catalog 서비스에는 상호 인증이 필요하다.
                                                               
                                                              • 실습 환경 요약
                                                                • sleep 워크로드는 default 네임스페이스에 배포했고, 평문 HTTP 요청을 만드는 데 사용한다.
                                                                • webapp 워크로드는 istioinaction 네임스페이스에 배포했고, default 네임스페이스에 있는 워크로드에서 미인증 요청을 받아들이고 있다.
                                                                • catalog 워크로드는 istioinaction 네임스페이스에 배포했고, 같은 네임스페이스의 인증된 워크로드로부터만 요청을 받아들이고 있다.
                                                              9.3.3 워크로드에 정책 적용 시 동작 확인
                                                                • 세부 사항으로 들어가기 전에 미리 알아둬야 할 것이 있다. 문제가 발생하기 쉽기 때문이다 (그리고 디버깅에 시간이 많이 낭비된다!).
                                                                  • Before we go into the details, there is one "gotcha" that you should know up front, as it’s easy to get bitten by (and waste many hours of debugging!)
                                                                • 워크로드에 하나 이상의 ALLOW 인가 정책이 적용되면, 모든 트래픽에서 해당 워크로드로의 접근은 기본적으로 거부된다.
                                                                • 트래픽을 받아들이려면, ALLOW 정책이 최소 하나는 부합해야 한다.
                                                               
                                                                • 예를 들어 설명해보자.
                                                                • 다음 AuthorizationPolicy 리소스는 webapp 으로의 요청 중 HTTP 경로에 /api/catalog* 가 포함된 것을 허용한다.
                                                              # cat ch9/allow-catalog-requests-in-web-app.yaml
                                                              apiVersion: "security.istio.io/v1beta1"
                                                              kind: "AuthorizationPolicy"
                                                              metadata:
                                                                name: "allow-catalog-requests-in-web-app"
                                                                namespace: istioinaction
                                                              spec:
                                                                selector:
                                                                  matchLabels:
                                                                    app: webapp # 워크로드용 셀렉터 Selector for workloads -> webapp이라는 service가 하나의 workloads가 된다.
                                                                rules:
                                                                - to:
                                                                  - operation:
                                                                      paths: ["/api/catalog*"] # 요청을 경로 /api/catalog 와 비교한다 Matches requests with the path /api/catalog
                                                                action: ALLOW # 일치하면 허용한다 If a match, ALLOW
                                                               
                                                                • 적용 후 확인
                                                                # 로그
                                                                kubectl logs -n istioinaction -l app=webapp -c istio-proxy -f
                                                                
                                                                # 적용 전 확인 
                                                                kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction/api/catalog
                                                                kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction/hello/world # 404 리턴
                                                                
                                                                # AuthorizationPolicy 리소스 적용
                                                                kubectl apply -f ch9/allow-catalog-requests-in-web-app.yaml
                                                                
                                                                kubectl get authorizationpolicy -n istioinaction
                                                                NAME                                AGE
                                                                allow-catalog-requests-in-web-app   4s
                                                                
                                                                # 
                                                                docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/webapp.istioinaction --port 15006
                                                                docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/webapp.istioinaction --port 15006 -o json > webapp-listener.json
                                                                ...
                                                                          {
                                                                              "name": "envoy.filters.http.rbac",
                                                                              "typedConfig": {
                                                                                  "@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC",
                                                                                  "rules": {
                                                                                      "policies": {
                                                                                          "ns[istioinaction]-policy[allow-catalog-requests-in-web-app]-rule[0]": {
                                                                                              "permissions": [
                                                                                                  {
                                                                                                      "andRules": {
                                                                                                          "rules": [
                                                                                                              {
                                                                                                                  "orRules": {
                                                                                                                      "rules": [
                                                                                                                          {
                                                                                                                              "urlPath": {
                                                                                                                                  "path": {
                                                                                                                                      "prefix": "/api/catalog"
                                                                                                                                  }
                                                                                                                              }
                                                                                                                          }
                                                                                                                      ]
                                                                                                                  }
                                                                                                              }
                                                                                                          ]
                                                                                                      }
                                                                                                  }
                                                                                              ],
                                                                                              "principals": [
                                                                                                  {
                                                                                                      "andIds": {
                                                                                                          "ids": [
                                                                                                              {
                                                                                                                  "any": true
                                                                                                              }
                                                                                                          ]
                                                                                                      }
                                                                                                  }
                                                                                              ]
                                                                                          }
                                                                                      }
                                                                                  },
                                                                                  "shadowRulesStatPrefix": "istio_dry_run_allow_" #  실제로 차단하지 않고, 정책이 적용됐을 때 통계만 수집 , istio_dry_run_allow_로 prefix된 메트릭 생성됨
                                                                              }
                                                                          },
                                                                ...
                                                                
                                                                # istio proxy log level 변경
                                                                docker exec -it myk8s-control-plane istioctl proxy-config log deploy/webapp -n istioinaction --level rbac:debug
                                                                
                                                                # 적용 후 확인
                                                                kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction/api/catalog
                                                                kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction/hello/world # 403 리턴
                                                                RBAC: access denied
                                                                
                                                                # 로그 : 403 리턴 체크!
                                                                kubectl logs -n istioinaction -l app=webapp -c istio-proxy -f
                                                                [2025-05-10T14:24:03.607Z] "GET /hello/world HTTP/1.1" 403 - rbac_access_denied_matched_policy[none] - "-" 0 19 0 - "-" "curl/8.5.0" "de03bdcc-3ae5-9e74-ad71-cce4ce5dc800" "webapp.istioinaction" "-" inbound|8080|| - 10.10.0.13:8080 10.10.0.14:45240 - -
                                                                
                                                                # 다음 실습을 위해 정책 삭제
                                                                kubectl delete -f ch9/allow-catalog-requests-in-web-app.yaml
                                                                
                                                                    1. 첫 번째 호출은 경로가 일치하기 때문에 요청을 허용한다.
                                                                    2. 두 번째 호출은 놀라울 수 있다. 정책이 요청을 허용하거나 거부하지 않았는데 왜 요청이 거부되는가?
                                                                    3. 이것은 바로 ALLOW 정책을 워크로드에 적용했을 때만 적용되는 기본 거부 deny-by-default 동작이다.
                                                                    4. 다시 말해 워크로드에 ALLOW 정책이 있는 경우, 트래픽이 허용되려면 정책 하나는 반드시 부합해야 한다.
                                                                image
                                                                image
                                                                 
                                                                    1. 정책 설정 과정을 단순화해 서비스마다 호출이 허용되는지, ALLOW 정책이 적용되는지를 스스로에게 되묻지 않으려면, 들어오는 트래픽에 다른 정책이 적용되지 않을 때 활성화되는 전체 catch-all 거부 정책을 추가하는 것을 권장한다.
                                                                    2. 그럼 허용허려는 트래픽에 대해서만 생각하고, 그 트래픽용 정책만 만들면 된다.
                                                                    3. 그림 9.10은 전체 거부 정책이 어떻게 ‘명시적으로 지정되지 않으면 요청을 거부한다’로 바꾸는지 보여준다. 그럼 트래픽을 허용하기만 하면 된다.
                                                                  image
                                                                  9.3.4 전체 정책으로 기본적으로 모든 요청 거부하기 Denying all requests by default with a catch-all policy
                                                                    • 보안성을 증가시키고 과정을 단순화하기 위해, ALLOW 정책을 명시적으로 지정하지 않은 모든 요청을 거부하는 메시 범위 정책을 정의해보자.
                                                                    • 즉, 기본 거부 catch-all-deny-all 정책을 정의한다.
                                                                  # cat ch9/policy-deny-all-mesh.yaml
                                                                  apiVersion: security.istio.io/v1beta1
                                                                  kind: AuthorizationPolicy
                                                                  metadata:
                                                                    name: deny-all
                                                                    namespace: istio-system # 이스티오를 설치한 네임스페이스의 정책은 메시의 모든 워크로드에 적용된다
                                                                  spec: {} # spec 이 비어있는 정책은 모든 요청을 거부한다
                                                                   
                                                                    • 적용 후 요청 테스트
                                                                  # 적용 전 확인 
                                                                  kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction/api/catalog
                                                                  [{"id":1,"color":"amber","department":"Eyewear","name":"Elinor Glasses","price":"282.00"},{"id":2,"color":"cyan","department":"Clothing","name":"Atlas Shirt","price":"127.00"},{"id":3,"color":"teal","department":"Clothing","name":"Small Metal Shoes","price":"232.00"},{"id":4,"color":"red","department":"Watches","name":"Red Dragon Watch","price":"232.00"}]
                                                                  
                                                                  curl -s http://webapp.istioinaction.io:30000/api/catalog
                                                                  [{"id":1,"color":"amber","department":"Eyewear","name":"Elinor Glasses","price":"282.00"},{"id":2,"color":"cyan","department":"Clothing","name":"Atlas Shirt","price":"127.00"},{"id":3,"color":"teal","department":"Clothing","name":"Small Metal Shoes","price":"232.00"},{"id":4,"color":"red","department":"Watches","name":"Red Dragon Watch","price":"232.00"}]
                                                                  
                                                                  
                                                                  # 정책 적용
                                                                  kubectl apply -f ch9/policy-deny-all-mesh.yaml
                                                                  
                                                                  kubectl get authorizationpolicy -A
                                                                  NAMESPACE      NAME       AGE
                                                                  istio-system   deny-all   3s
                                                                  
                                                                  # 적용 후 확인 1
                                                                  kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction/api/catalog
                                                                  ...
                                                                  kubectl logs -n istioinaction -l app=webapp -c istio-proxy -f
                                                                  [2025-05-10T14:35:58.777Z] "GET /api/catalog HTTP/1.1" 403 - rbac_access_denied_matched_policy[none] - "-" 0 19 0 - "-" "curl/8.5.0" "b851c23a-fe75-9d90-bb16-1afdab14a37b" "webapp.istioinaction" "-" inbound|8080|| - 10.10.0.13:8080 10.10.0.14:35850 - -
                                                                  
                                                                  # 적용 후 확인 2
                                                                  curl -s http://webapp.istioinaction.io:30000/api/catalog
                                                                  ...
                                                                  kubectl logs -n istio-system -l app=istio-ingressgateway -f
                                                                  [2025-05-10T14:36:19.367Z] "GET /api/catalog HTTP/1.1" 403 - rbac_access_denied_matched_policy[none] - "-" 0 19 0 - "-" "curl/8.5.0" "502c9e49-4f0d-9644-a3bc-7e85ef18bdd0" "webapp.istioinaction" "-" inbound|8080|| - 10.10.0.13:8080 10.10.0.14:49788 - -
                                                                   
                                                                    • (참고) Catch-all authorization policies : 빈 규칙 rules 모든 요청을 허용 의미
                                                                  # cat ch9/policy-allow-all-mesh.yaml                         
                                                                  apiVersion: security.istio.io/v1beta1
                                                                  kind: AuthorizationPolicy
                                                                  metadata:
                                                                    name: allow-all
                                                                    namespace: istio-system
                                                                  spec: 
                                                                    rules: 
                                                                    - {}
                                                                  9.3.5 특정 네임스페이스에서 온 요청 허용하기 Allowing requests originating from a single namespace
                                                                    • 종종 특정 네임스페이스에서 시작한, 모든 서비스에 대한 트래픽을 허용하고 싶을 것이다.
                                                                    • 이는 source.namespace 속성으로 할 수 있다.
                                                                    • 다음 예제는 한 네임스페이스에서 온 HTTP GET 트래픽을 허용한다.
                                                                  # 
                                                                  cat << EOF | kubectl apply -f -
                                                                  apiVersion: "security.istio.io/v1beta1"
                                                                  kind: "AuthorizationPolicy"
                                                                  metadata:
                                                                    name: "webapp-allow-view-default-ns"
                                                                    namespace: istioinaction # istioinaction의 워크로드
                                                                  spec:
                                                                    rules:
                                                                    - from: # default 네임스페이스에서 시작한
                                                                      - source:
                                                                          namespaces: ["default"]
                                                                      to:   # HTTP GET 요청에만 적용 
                                                                      - operation:
                                                                          methods: ["GET"]
                                                                  EOF
                                                                  
                                                                  #
                                                                  kubectl get AuthorizationPolicy -A
                                                                  
                                                                  NAMESPACE       NAME                           AGE
                                                                  istio-system    deny-all                       118s
                                                                  istioinaction   webapp-allow-view-default-ns   3s
                                                                  
                                                                  docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/webapp.istioinaction --port 15006 -o json
                                                                  ...
                                                                                  {
                                                                                      "name": "envoy.filters.http.rbac",
                                                                                      "typedConfig": {
                                                                                          "@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC",
                                                                                          "rules": {
                                                                                              "policies": {
                                                                                                  "ns[istio-system]-policy[deny-all]-rule[0]": {
                                                                                                      "permissions": [
                                                                                                          {
                                                                                                              "notRule": {
                                                                                                                  "any": true
                                                                                                              }
                                                                                                          }
                                                                                                      ],
                                                                                                      "principals": [
                                                                                                          {
                                                                                                              "notId": {
                                                                                                                  "any": true
                                                                                                              }
                                                                                                          }
                                                                                                      ]
                                                                                                  },
                                                                                                  "ns[istioinaction]-policy[webapp-allow-view-default-ns]-rule[0]": {
                                                                                                      "permissions": [
                                                                                                          {
                                                                                                              "andRules": {
                                                                                                                  "rules": [
                                                                                                                      {
                                                                                                                          "orRules": {
                                                                                                                              "rules": [
                                                                                                                                  {
                                                                                                                                      "header": {
                                                                                                                                          "name": ":method",
                                                                                                                                          "exactMatch": "GET"
                                                                                                                                      }
                                                                                                                                  }
                                                                                                                              ]
                                                                                                                          }
                                                                                                                      }
                                                                                                                  ]
                                                                                                              }
                                                                                                          }
                                                                                                      ],
                                                                                                      "principals": [
                                                                                                          {
                                                                                                              "andIds": {
                                                                                                                  "ids": [
                                                                                                                      {
                                                                                                                          "orIds": {
                                                                                                                              "ids": [
                                                                                                                                  {
                                                                                                                                      "filterState": {
                                                                                                                                          "key": "io.istio.peer_principal",
                                                                                                                                          "stringMatch": {
                                                                                                                                              "safeRegex": {
                                                                                                                                                  "regex": ".*/ns/default/.*"
                                                                  ...
                                                                  
                                                                  # 호출 테스트
                                                                  kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction/api/catalog
                                                                  ...
                                                                  
                                                                  #
                                                                  kubectl logs -n istioinaction -l app=webapp -c istio-proxy -f
                                                                  [2025-05-10T14:43:41.147Z] "GET /api/catalog HTTP/1.1" 403 - rbac_access_denied_matched_policy[none] - "-" 0 19 0 - "-" "curl/8.5.0" "8396092f-7f32-9b3d-9263-12d59362c917" "webapp.istioinaction" "-" inbound|8080|| - 10.10.0.13:8080 10.10.0.14:55752 - -
                                                                   
                                                                    • sleep 서비스는 레거시 워크로드다. The sleep service is a legacy workload.
                                                                    • 사이트카가 없으므로, ID도 없다. 그러므로 webapp 프록시는 요청이 default 네임이스페이스의 워크로드에서 온 것인지 확인할 수 없다.
                                                                    • 이를 해결하려면 다음 중 하나를 할 수 있다.
                                                                      1. sleep 서비스에 서비스 프록시 주입하기 → 실습 진행
                                                                      2. webapp에서 미인증 요청 허용하기
                                                                   
                                                                      1. 권장하는 방식은 sleep 서비스에 서비스 프록시를 주입하는 것이다.
                                                                      2. 그렇게 하면 ID를 부트스트랩하고 다른 워크로드와의 상호 인증을 수행해서 다른 워크로드가 요청의 출처와 네임스페이스를 확인 할 수 있다.
                                                                      3. 그러나 시연을 위해, 첫 번째 접근법이 불가능해(예를 들면, 팀 전체가 휴가 중이라서) 어쩔 수 없이 두 번째 접근법(덜 안전한)을 취해야 한다고 해보자.
                                                                      4. 미인증 요청을 허용하는 것이다.
                                                                     
                                                                      • 실습
                                                                    #
                                                                    kubectl label ns default istio-injection=enabled
                                                                    kubectl delete pod -l app=sleep
                                                                    
                                                                    #
                                                                    docker exec -it myk8s-control-plane istioctl proxy-status
                                                                    NAME                                                   CLUSTER        CDS        LDS        EDS        RDS          ECDS         ISTIOD                    VERSION
                                                                    sleep-6f8cfb8c8f-cnlqq.default                         Kubernetes     SYNCED     SYNCED     SYNCED     SYNCED       NOT SENT     istiod-8d74787f-bkwnr     1.17.8
                                                                    ...
                                                                    
                                                                    # 호출 테스트 : webapp
                                                                    kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction # default -> webapp 은 성공
                                                                    ...
                                                                    
                                                                    # webapp istio proxy log level 변경
                                                                    docker exec -it myk8s-control-plane istioctl proxy-config log deploy/webapp -n istioinaction --level rbac:debug
                                                                    
                                                                    # 호출 테스트: catalog
                                                                    kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction/api/catalog 
                                                                    error calling Catalog service
                                                                    
                                                                    kubectl logs -n istioinaction -l app=webapp -c istio-proxy -f # webapp -> catalog 는 deny-all 로 거부됨
                                                                    [2025-05-10T15:03:50.669Z] "GET /items HTTP/1.1" 403 - via_upstream - "-" 0 19 0 0 "-" "beegoServer" "2f0bec28-59bd-9379-832c-c3858274d9de" "catalog.istioinaction:80" "10.10.0.12:3000" outbound|80||catalog.istioinaction.svc.cluster.local 10.10.0.13:50766 10.200.1.181:80 10.10.0.13:39724 - default
                                                                    [2025-05-10T15:03:50.667Z] "GET /api/catalog HTTP/1.1" 500 - via_upstream - "-" 0 29 3 2 "-" "curl/8.5.0" "2f0bec28-59bd-9379-832c-c3858274d9de" "webapp.istioinaction" "10.10.0.13:8080" inbound|8080|| 127.0.0.6:54073 10.10.0.13:8080 10.10.0.15:43204 outbound_.80_._.webapp.istioinaction.svc.cluster.local default
                                                                    
                                                                    # 호출 테스트 : catalog
                                                                    # istioinaction ns에는 default workload에서 오는 모든 요청은 허용하는 authorizationPolicy가 적용되어있음
                                                                    kubectl logs -n istioinaction -l app=catalog -c istio-proxy -f
                                                                    kubectl exec deploy/sleep -- curl -sSL catalog.istioinaction/items # default -> catalog 은 성공
                                                                    [
                                                                      {
                                                                        "id": 1,
                                                                        "color": "amber",
                                                                        "department": "Eyewear",
                                                                        "name": "Elinor Glasses",
                                                                        "price": "282.00"
                                                                      },
                                                                    ...
                                                                    
                                                                    # 호출 테스트: local -> catalog
                                                                    # 실패!! => 이유는 catch-all-deny authorizationPolicy가 있기 때문에!
                                                                    curl -s http://catalog.istioinaction.io:30000/api/catalog        
                                                                    RBAC: access denied
                                                                    
                                                                    # 다음 실습을 위해 default 네임스페이스 원복
                                                                    kubectl label ns default istio-injection-
                                                                    kubectl rollout restart deploy/sleep
                                                                    
                                                                    docker exec -it myk8s-control-plane istioctl proxy-status
                                                                    
                                                                    kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction # 거부 확인
                                                                    RBAC: access denied
                                                                    9.3.6 미인증 레거시 워크로드에서 온 요청 허용하기 Allowing requests from non-authenticated legacy workloads
                                                                      • 미인증 워크로드에서 온 요청을 허용하려면 from 필드를 삭제해야 한다.
                                                                      • 아래 정책을 webapp에만 적용하기 위해 app:webapp 셀렉터를 추가한다.
                                                                      • 이렇게 하면 catalog 서비스에는 여전히 상호 인증이 필요하다. This way, the catalog service still requires mutual authentication.
                                                                    # cat ch9/allow-unauthenticated-view-default-ns.yaml
                                                                    apiVersion: "security.istio.io/v1beta1"
                                                                    kind: "AuthorizationPolicy"
                                                                    metadata:
                                                                      name: "webapp-allow-unauthenticated-view-default-ns"
                                                                      namespace: istioinaction
                                                                    spec:
                                                                      selector:
                                                                        matchLabels:
                                                                          app: webapp
                                                                      rules:
                                                                        - to:
                                                                          - operation:
                                                                              methods: ["GET"]
                                                                     
                                                                      • 실습
                                                                    #
                                                                    kubectl apply -f ch9/allow-unauthenticated-view-default-ns.yaml
                                                                    kubectl get AuthorizationPolicy -A
                                                                    NAMESPACE       NAME                                           AGE
                                                                    istio-system    deny-all                                       12h
                                                                    istioinaction   webapp-allow-unauthenticated-view-default-ns   14s
                                                                    istioinaction   webapp-allow-view-default-ns                   11h
                                                                    
                                                                    # 여러개의 정책이 적용 시에 우선순위는?
                                                                    # ns내에서 CUSTOM -> DENY -> ALLOW -> CATCH-ALL 순서로 동작
                                                                    # selector가 추가되어서 ns내에서 특정 workload에 대한 ALLOW authorizationPolicy가 있더라도, ns에 selector가 없는 전체에 대한 DENY가 있으면 DENY가 우선!!
                                                                    # 단, CATCH-ALL-DENY-ALL(위에서 생성한 istio-system ns의 deny-all authorizationPolicy) 처럼 istiod가 설치된 ns에 적용된 deny action은 모든 ns에 걸쳐 적용된다.
                                                                    # 하지만 CATCH-ALL은 우선순위가 가장 낮음
                                                                    docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/webapp.istioinaction --port 15006 -o json | jq
                                                                    ...
                                                                             "name": "envoy.filters.http.rbac",
                                                                                            "typedConfig": {
                                                                                                "@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC",
                                                                                                "rules": {
                                                                                                    "policies": {
                                                                                                        "ns[istio-system]-policy[deny-all]-rule[0]": {
                                                                                                            "permissions": [
                                                                                                                {
                                                                                                                    "notRule": {
                                                                                                                        "any": true
                                                                                                                    }
                                                                                                                }
                                                                                                            ],
                                                                                                            "principals": [
                                                                                                                {
                                                                                                                    "notId": {
                                                                                                                        "any": true
                                                                                                                    }
                                                                                                                }
                                                                                                            ]
                                                                                                        },
                                                                                                        "ns[istioinaction]-policy[webapp-allow-unauthenticated-view-default-ns]-rule[0]": {
                                                                                                            "permissions": [
                                                                                                                {
                                                                                                                    "andRules": {
                                                                                                                        "rules": [
                                                                                                                            {
                                                                                                                                "orRules": {
                                                                                                                                    "rules": [
                                                                                                                                        {
                                                                                                                                            "header": {
                                                                                                                                                "name": ":method",
                                                                                                                                                "exactMatch": "GET"
                                                                                                                                            }
                                                                                                                                        }
                                                                                                                                    ]
                                                                                                                                }
                                                                                                                            }
                                                                                                                        ]
                                                                                                                    }
                                                                                                                }
                                                                                                            ],
                                                                                                            "principals": [
                                                                                                                {
                                                                                                                    "andIds": {
                                                                                                                        "ids": [
                                                                                                                            {
                                                                                                                                "any": true
                                                                                                                            }
                                                                                                                        ]
                                                                                                                    }
                                                                                                                }
                                                                                                            ]
                                                                                                        },
                                                                                                        "ns[istioinaction]-policy[webapp-allow-view-default-ns]-rule[0]": {
                                                                                                            "permissions": [
                                                                                                                {
                                                                                                                    "andRules": {
                                                                                                                        "rules": [
                                                                                                                            {
                                                                                                                                "orRules": {
                                                                                                                                    "rules": [
                                                                                                                                        {
                                                                                                                                            "header": {
                                                                                                                                                "name": ":method",
                                                                                                                                                "exactMatch": "GET"
                                                                                                                                            }
                                                                                                                                        }
                                                                                                                                    ]
                                                                                                                                }
                                                                                                                            }
                                                                                                                        ]
                                                                                                                    }
                                                                                                                }
                                                                                                            ],
                                                                                                            "principals": [
                                                                                                                {
                                                                                                                    "andIds": {
                                                                                                                        "ids": [
                                                                                                                            {
                                                                                                                                "orIds": {
                                                                                                                                    "ids": [
                                                                                                                                        {
                                                                                                                                            "filterState": {
                                                                                                                                                "key": "io.istio.peer_principal",
                                                                                                                                                "stringMatch": {
                                                                                                                                                    "safeRegex": {
                                                                                                                                                        "regex": ".*/ns/default/.*"
                                                                                                                                                    }
                                                                    ...
                                                                    
                                                                    # 호출 테스트 : webapp
                                                                    kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction # default -> webapp 은 성공
                                                                    ...
                                                                    
                                                                    # webapp -> catalog 는 deny-all 로 거부됨
                                                                    # deny-all은 istio-system ns에 생성된 CATCH-ALL action임
                                                                    kubectl logs -n istioinaction -l app=webapp -c istio-proxy -f 
                                                                    kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction/api/catalog 
                                                                    error calling Catalog service
                                                                    
                                                                    # (옵션) 호출 테스트 : catalog
                                                                    kubectl logs -n istioinaction -l app=catalog -c istio-proxy -f
                                                                    kubectl exec deploy/sleep -- curl -sSL catalog.istioinaction/items
                                                                    curl: (56) Recv failure: Connection reset by peer
                                                                    command terminated with exit code 56
                                                                    
                                                                    # 위에서 설정한 istio-system ns의 mtls STRICT Mode의 PeerAuthentication에 의해서 거부됨
                                                                    kubectl get pa -A
                                                                    NAMESPACE       NAME      MODE         AGE
                                                                    istio-system    default   STRICT       6h54m
                                                                    istioinaction   webapp    PERMISSIVE   6h43m
                                                                     
                                                                    • webapp 은 sleep 서비스에서 요청을 허용했지만, 메시 범위 전체 거부 정책이 catalog 서비스로의 후속 요청을 거부헀다.
                                                                    • 다음절에서 해결해보자.
                                                                    9.3.7 특정 서비스 어카운트에서 온 요청 허용하기 Allowing requests from a single service account
                                                                      • 트래픽이 webapp 서비스에서 왔는지 인증할 수 있는 간단한 방법은 트래픽에 주입된 서비스 어카운트를 사용하는 것이다.
                                                                      • 서비스 어카운트 정보는 SVID에 인코딩돼 있으며, 상호 인증 중에 그 정보를 검증하고 필터 메타데이터에 저장한다.
                                                                      • 다음 정책은 catalog 서비스가 필터 메타데이터를 사용해 서비스 어카운트가 webapp인 워크로드에서 온 트래픽만 허용하도록 설정한다.
                                                                    # cat ch9/catalog-viewer-policy.yaml
                                                                    apiVersion: "security.istio.io/v1beta1"
                                                                    kind: "AuthorizationPolicy"
                                                                    metadata:
                                                                      name: "catalog-viewer"
                                                                      namespace: istioinaction
                                                                    spec:
                                                                      selector:
                                                                        matchLabels:
                                                                          app: catalog
                                                                      rules:
                                                                      - from:
                                                                        - source: 
                                                                            principals: ["cluster.local/ns/istioinaction/sa/webapp"] # Allows requests with the identity of webapp
                                                                        to:
                                                                        - operation:
                                                                            methods: ["GET"]
                                                                     
                                                                      • 실습
                                                                    #
                                                                    kubectl apply -f ch9/catalog-viewer-policy.yaml
                                                                    kubectl get AuthorizationPolicy -A
                                                                    NAMESPACE       NAME                                           AGE
                                                                    istio-system    deny-all                                       6m24s
                                                                    istioinaction   catalog-viewer                                 4s
                                                                    istioinaction   webapp-allow-unauthenticated-view-default-ns   35m
                                                                    istioinaction   webapp-allow-view-default-ns                   6m29s
                                                                    
                                                                    #
                                                                    docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/catalog.istioinaction --port 15006 -o json
                                                                    ...
                                                                                "principals": [
                                                                                    {
                                                                                        "andIds": {
                                                                                            "ids": [
                                                                                                {
                                                                                                    "orIds": {
                                                                                                        "ids": [
                                                                                                            {
                                                                                                                "filterState": {
                                                                                                                    "key": "io.istio.peer_principal",
                                                                                                                    "stringMatch": {
                                                                                                                        "exact": "spiffe://cluster.local/ns/istioinaction/sa/webapp"
                                                                                                                    }
                                                                    ...
                                                                    
                                                                    # 호출 테스트 : sleep --(미인증 레거시 허용)--> webapp --(principals webapp 허용)--> catalog
                                                                    kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction
                                                                    kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction/api/catalog 
                                                                    
                                                                    # 호출 성공
                                                                    kubectl logs -n istioinaction -l app=webapp -c istio-proxy -f
                                                                    [2025-05-10T15:47:53.477Z] "GET /items HTTP/1.1" 200 - via_upstream - "-" 0 502 12 11 "-" "beegoServer" "54b880b5-cbb8-9ec9-bea9-c8e63e7da79b" "catalog.istioinaction:80" "10.10.0.12:3000" outbound|80||catalog.istioinaction.svc.cluster.local 10.10.0.13:55852 10.200.1.181:80 10.10.0.13:57040 - default
                                                                    [2025-05-10T15:47:53.475Z] "GET /api/catalog HTTP/1.1" 200 - via_upstream - "-" 0 357 14 13 "-" "curl/8.5.0" "54b880b5-cbb8-9ec9-bea9-c8e63e7da79b" "webapp.istioinaction" "10.10.0.13:8080" inbound|8080|| 127.0.0.6:54073 10.10.0.13:8080 10.10.0.16:51212 - default
                                                                    
                                                                    # webapp --(principals webapp 허용)--> catalog: 호출 성공
                                                                    kubectl logs -n istioinaction -l app=catalog -c istio-proxy -f
                                                                    [2025-05-10T15:47:53.480Z] "GET /items HTTP/1.1" 200 - via_upstream - "-" 0 502 5 5 "-" "beegoServer" "54b880b5-cbb8-9ec9-bea9-c8e63e7da79b" "catalog.istioinaction:80" "10.10.0.12:3000" inbound|3000|| 127.0.0.6:40577 10.10.0.12:3000 10.10.0.13:55852 outbound_.80_._.catalog.istioinaction.svc.cluster.local default
                                                                    
                                                                    # (옵션) 호출 테스트 : catalog
                                                                    kubectl exec deploy/sleep -- curl -sSL catalog.istioinaction/items
                                                                    ...
                                                                     
                                                                    • 그러나 워크로드 ID가 도난당한 경우 피해를 가능한 한 최소한의 범위로 제한하도록 어멱한 인가 정책을 갖고 있다는 점이 더 중요하다.
                                                                    • But more importantly, we have strict authorization policies in place so that if the identity of a workload is stolen, the damage will be limited to the smallest scope possible.
                                                                    9.3.8 정책의 조건부 적용 Conditional matching of policies
                                                                      • 가끔 어떤 정책은 특정 조건이 충족되는 경우에만 적용되기도 한다.
                                                                      • 사용자가 관리자일 때는 모든 작업을 허용하는 식이다.
                                                                      • 이는 다음 예제처럼 인가 정책의 when 속성을 사용해 구현할 수 있다.
                                                                    apiVersion: "security.istio.io/v1beta1"
                                                                    kind: "AuthorizationPolicy"
                                                                    metadata:
                                                                      name: "allow-mesh-all-ops-admin"
                                                                      namespace: istio-system
                                                                    spec:
                                                                      rules:
                                                                      - from:
                                                                        - source:
                                                                            requestPrincipals: ["auth@istioinaction.io/*"]
                                                                        when:
                                                                        - key: request.auth.claims[groups] # 이스티오 속성을 지정한다
                                                                          values: ["admin"] # 반드시 일치해야 하는 값의 목록을 지정한다
                                                                     
                                                                    • 이 정책은 다음 두 조건이 모두 충족될 때만 요청을 허용한다.
                                                                      • 첫째, 토큰은 요청 주체 auth@istioinaction.io/* 가 발급한 것이어야 한다.
                                                                      • 둘째, JWT에 값이 ‘admin’인 group 클레임 claim이 포함돼 있어야 한다.

                                                                     

                                                                    ADMIN_TOKEN=$(< ch9/enduser/admin.jwt)
                                                                    jwt decode $ADMIN_TOKEN
                                                                    
                                                                    Token header
                                                                    ------------
                                                                    {
                                                                      "typ": "JWT",
                                                                      "alg": "RS256",
                                                                      "kid": "CU-ADJJEbH9bXl0tpsQWYuo4EwlkxFUHbeJ4ckkakCM"
                                                                    }
                                                                    
                                                                    Token claims
                                                                    ------------
                                                                    {
                                                                      "exp": 4745145071,
                                                                      "group": "admin",
                                                                      "iat": 1591545071,
                                                                      "iss": "auth@istioinaction.io",
                                                                      "sub": "218d3fb9-4628-4d20-943c-124281c80e7b"
                                                                    }
                                                                    
                                                                    kubectl apply -f ch9/enduser/jwt-token-request-authn.yaml
                                                                    kubectl apply -f ch9/enduser/allow-mesh-all-ops-admin.yaml
                                                                    
                                                                    curl -H "Authorization: Bearer $ADMIN_TOKEN" \
                                                                         -sSl -o /dev/null -w "%{http_code}\n" webapp.istioinaction.io:30000/api/catalog
                                                                    200
                                                                     
                                                                     
                                                                      • 또는 notValues 속성을 사용해 이 정책을 적용하지 않아야 하는 값들을 정의할 수도 있다.
                                                                      • 조건에서 사용할 수 있는 이스티오 속성 전체 목록은 이스티오 문서에서 찾을 수 있다.

                                                                    https://istio.io/latest/docs/reference/config/security/conditions/

                                                                     
                                                                    👉🏻

                                                                    Principals vs. request principals 차이점 source 를 정의하는 문서를 보면 https://istio.io/latest/docs/reference/config/security/authorization-policy/#Source from 절에서 요청의 주체를 인식하는 방법에는 Principals , request principals 가 있다는 것을 알 수 있다. PrincipalsPeerAuthentication 으로 설정한 상호 TLS 커넥션의 참가자인 것과 달리, request principals 는 최종 사용자 Request Authentication 용이며 JWT에서 온다는 점에서 차이가 있다.

                                                                     
                                                                    9.3.9 값 비교 표현식 이해하기 Understanding value-match expressions
                                                                      • 앞 선 예제에서 값이 항상 정확히 일치할 필요는 없다는 것을 확인했다.
                                                                      • 이스티오는 규칙을 더 다양하게 만들 수 있도록 간단한 비교 표현식을 지원한다.
                                                                        • Exact matching of values 일치. 예를 들어 GET은 값이 정확히 일치해야 한다.
                                                                        • Prefix matching of values 접두사 (매칭)비교. 예를 들어 /api/catlog*/api/catalog/1 과 같이 접두사로 시작하는 모든 값에 부합한다.
                                                                        • Suffix matching of values 접미사 (매칭)비교. 예를 들어 *.istioinaction.iologin.istioinaction.io 와 같이 모든 서브도메인에 부합한다.
                                                                        • Presence matching 존재성 (매칭)비교. 모든 값에 부합하며 *로 표기한다. 이는 필드가 존재해야 하지만, 값은 중요하지 않아 어떤 값이든 괜찮음을 의미한다.
                                                                     

                                                                    정책 규칙이 어떻게 평가되는지 이해하기 UNDERSTANDING HOW POLICY RULES ARE EVALUATED

                                                                      • 정책 규칙을 이해하기 위해 좀 더 복잡한 규칙이 어떤 요청에 적용되는지 구체적으로 분석해보자
                                                                    apiVersion: "security.istio.io/v1beta1"
                                                                    kind: "AuthorizationPolicy"
                                                                    metadata:
                                                                      name: "allow-mesh-all-ops-admin"
                                                                      namespace: istio-system
                                                                    spec:
                                                                      rules: 
                                                                      - from: # 첫 번째 규칙
                                                                        - source:
                                                                            principals: ["cluster.local/ns/istioinaction/sa/webapp"]
                                                                        - source:
                                                                            namespace: ["default"]
                                                                        to:
                                                                        - operation:
                                                                            methods: ["GET"]
                                                                            paths: ["/users*"]
                                                                        - operation:
                                                                            methods: ["POST"]
                                                                            paths: ["/data"]
                                                                        when:
                                                                        - key: request.auth.claims[group]
                                                                          values: ["beta-tester", "admin", "developer"]
                                                                      - to: # 두 번째 규치
                                                                        - operation:
                                                                            paths: ["*.html", "*.js", "*.png"]
                                                                     
                                                                      • 위 인가 정책이 요청에 적용되려면, 첫 번째 규칙이나 두 번째 규칙에 해당해야 한다.
                                                                      • 첫 번째 규칙에 해당하는 경우를 좀 더 자세히 살펴보자.
                                                                        - from: # 소스들 Sources
                                                                          - source:
                                                                              principals: ["cluster.local/ns/istioinaction/sa/webapp"]
                                                                          - source:
                                                                              namespace: ["default"]
                                                                          to: # operations 들
                                                                          - operation:
                                                                              methods: ["GET"]
                                                                              paths: ["/users*"]
                                                                          - operation:
                                                                              methods: ["POST"]
                                                                              paths: ["/data"]
                                                                          when: # 조건들 Conditions
                                                                          - key: request.auth.claims[group]
                                                                            values: ["beta-tester", "admin", "developer"]
                                                                          1. 요청이 이 규칙에 해당하려면, 세 가지 속성에서 모두 부합해야 한다.
                                                                          2. source 목록에서 정의한 source 중 하나가 operation 목록에서 정의한 operation 과 맞아야 하고, 모든 조건이 부합해야 한다.
                                                                          3. 다시 말해 from 에서 정의한 source 가 to 에 정의한 operation 중 하나와 AND 연산되고, 둘 다 when 에서 지정한 조건들 모두와 AND 연산된다.
                                                                       
                                                                          1. operation 에 어떻게 해당하는지 이해하기 위해 operation을 좀 더 자세히 살펴보자.
                                                                              to: # operations 들
                                                                              - operation: # 첫 번째 operation
                                                                                  methods: ["GET"]   # 첫 번째 operation에 해당하려면 일치해야 하는 두 속성
                                                                                  paths: ["/users*"] # 첫 번째 operation에 해당하려면 일치해야 하는 두 속성
                                                                              - operation: # 첫 번째 operation
                                                                                  methods: ["POST"]  # 두 번째 operation에 해당하려면 일치해야 하는 두 속성
                                                                                  paths: ["/data"]   # 두 번째 operation에 해당하려면 일치해야 하는 두 속성
                                                                              1. 이 규칙에서 operation 이 부합하려면, 첫 번째나 두 번째 operation이 부합해야 한다.
                                                                              2. operation 이 부합하려면 모든 속성이 부합해야 한다. 즉, 모든 속성이 AND로 연결된다.
                                                                           
                                                                            1. 한편 when 속성의 경우도 AND로 연결되기 때문에 모든 조건이 부합해야 한다.
                                                                            9.3.10 인가 정책이 평가되는 순서 이해하기 Understanding the order in which authorization policies are evaluated
                                                                            https://istio.io/v1.17/docs/concepts/security/#implicit-enablement
                                                                              • 한 워크로드에 많은 정책이 적용되고 순서를 이해하기 어려울 때 정책의 복잡성이 대두된다.
                                                                              • 많은 솔루션이 priority 필드를 사용해 순서를 정의한다.
                                                                              • 이스티오는 정책 평가에 다른 접근법을 사용한다.
                                                                                • 추후 외부 인가 서버와 통합할 때 CUSTOM 정책의 사례를 보여줄 것이다.
                                                                                • 다음으로 DENY 정책이 평가된다. 일치하는 DENY 정책이 없으면…
                                                                                • ALLOW 정책이 평가된다. 일치하는 것이 있으면 허용된다. 그렇지 않으면…
                                                                                1. When a catch-all policy is present, it determines whether the request is approved. 일반 정책이 존재하면, 일반 정책이 요청 승인 여부를 결정한다.
                                                                                2. When a catch-all policy is absent, the request is: 일반 정책이 없으면, 요청은 다음과 같다.
                                                                                  • Allowed if there are no ALLOW policies, or it’s ALLOW 정책이 없으면 허용된다.
                                                                                  • Rejected when there are ALLOW policies but none matches. ALLOW 정책이 있지만 아무것도 해당되지 않으면 거부된다.
                                                                                1. CUSTOM policies are evaluated first. CUSTOM 정책이 가장 먼저 평가된다.
                                                                                2. DENY policies are evaluated next. If no DENY policy is matched . . .
                                                                                3. ALLOW policies are evaluated. If one matches, the request is allowed. Otherwise. . .
                                                                                4. According to the presence or absence of a catch-all policy, we have two outcomes: 일반 정책의 존재 유무에 따라 두 가지 결과가 나타난다.
                                                                             
                                                                                1. 조건에 따라 동작이 바뀌므로, 그림 9.11 같은 흐름도를 사용하면 더 이해하기 쉬울 수 있다.
                                                                                2. 흐름이 조금 복잡하지만, 일반 DENY 정책을 정의하면 휠씬 간단해진다.
                                                                                3. 요청을 거부하는 CUSTOM과 DENY 정책이 없으면, 허용할 ALLOW 정책이 있는지만 확인하면 된다.
                                                                              image
                                                                               
                                                                              • 지금까지 워크로드 사이의 요청에 대한 인증 및 인가를 다뤘다.
                                                                              • 다음 절에서는 최종 사용자 인증 및 인가 기능을 살펴본다.
                                                                               

                                                                              9.4. 최종 사용자 인증 및 인가

                                                                              https://netpple.github.io/docs/istio-in-action/Istio-ch9-securing-4-end-user-auth

                                                                              사전 지식 : Service Account Token Volume Projection, Admission Control, JWT(JSON Web Token), OIDC

                                                                              Service Account Token Volume Projection : '서비스 계정 토큰'의 시크릿 기반 볼륨 대신 'projected volume' 사용

                                                                                • Service Account Token (SAT) Volume Projection - 링크
                                                                                image
                                                                                    1. 서비스 계정 토큰을 이용해서 서비스와 서비스, 즉 파드(pod)와 파드(pod)의 호출에서 자격 증명으로 사용할 수 있을까요?
                                                                                    2. 불행히도 기본 서비스 계정 토큰으로는 사용하기에 부족함이 있습니다. 토큰을 사용하는 대상(audience), 유효 기간(expiration) 등 토큰의 속성을 지정할 필요가 있기 때문입니다.
                                                                                    3. Service Account Token Volume Projection 기능을 사용하면 이러한 부족한 점들을 해결할 수 있습니다.
                                                                                apiVersion: v1
                                                                                kind: Pod
                                                                                metadata:
                                                                                  name: nginx
                                                                                spec:
                                                                                  containers:
                                                                                  - image: nginx
                                                                                    name: nginx
                                                                                    volumeMounts:
                                                                                    - mountPath: /var/run/secrets/tokens
                                                                                      name: vault-token
                                                                                  serviceAccountName: build-robot
                                                                                  volumes:
                                                                                  - name: vault-token
                                                                                    projected:
                                                                                      sources:
                                                                                      - serviceAccountToken:
                                                                                          path: vault-token
                                                                                          expirationSeconds: 7200
                                                                                          audience: vault
                                                                                 
                                                                                  • Bound Service Account Token Volume 바인딩된 서비스 어카운트 토큰 볼륨 - 링크 영어
                                                                                    • FEATURE STATE: Kubernetes v1.22 [stable]
                                                                                    • 서비스 어카운트 어드미션 컨트롤러는 토큰 컨트롤러에서 생성한 만료되지 않은 서비스 계정 토큰에 시크릿 기반 볼륨 대신 다음과 같은 프로젝티드 볼륨을 추가한다.
                                                                                - name: kube-api-access-<random-suffix>
                                                                                  projected:
                                                                                    defaultMode: 420 # 420은 rw- 로 소유자는 읽고쓰기 권한과 그룹내 사용자는 읽기만, 보통 0644는 소유자는 읽고쓰고실행 권한과 나머지는 읽고쓰기 권한
                                                                                    sources:
                                                                                      - serviceAccountToken:
                                                                                          expirationSeconds: 3607
                                                                                          path: token
                                                                                      - configMap:
                                                                                          items:
                                                                                            - key: ca.crt
                                                                                              path: ca.crt
                                                                                          name: kube-root-ca.crt
                                                                                      - downwardAPI:
                                                                                          items:
                                                                                            - fieldRef:
                                                                                                apiVersion: v1
                                                                                                fieldPath: metadata.namespace
                                                                                              path: namespace
                                                                                    • 프로젝티드 볼륨은 세 가지로 구성된다. PSAT (Projected Service Account Tokens)
                                                                                image
                                                                                  • kube-apiserver로부터 TokenRequest API를 통해 얻은 서비스어카운트토큰(ServiceAccountToken). 서비스어카운트토큰은 기본적으로 1시간 뒤에, 또는 파드가 삭제될 때 만료된다. 서비스어카운트토큰은 파드에 연결되며 kube-apiserver를 위해 존재한다.
                                                                                  • kube-apiserver에 대한 연결을 확인하는 데 사용되는 CA 번들을 포함하는 컨피그맵(ConfigMap).
                                                                                  • 파드의 네임스페이스를 참조하는 DownwardA
                                                                                 
                                                                                  • Configure a Pod to Use a Projected Volume for Storage : 시크릿 컨피그맵 downwardAPI serviceAccountToken의 볼륨 마운트를 하나의 디렉터리에 통합 - 링크
                                                                                    • This page shows how to use a projected Volume to mount several existing volume sources into the same directory. Currently, secretconfigMapdownwardAPI, and serviceAccountToken volumes can be projected.
                                                                                    • Note: serviceAccountToken is not a volume type.
                                                                                apiVersion: v1
                                                                                kind: Pod
                                                                                metadata:
                                                                                  name: test-projected-volume
                                                                                spec:
                                                                                  containers:
                                                                                  - name: test-projected-volume
                                                                                    image: busybox:1.28
                                                                                    args:
                                                                                    - sleep
                                                                                    - "86400"
                                                                                    volumeMounts:
                                                                                    - name: all-in-one
                                                                                      mountPath: "/projected-volume"
                                                                                      readOnly: true
                                                                                  volumes:
                                                                                  - name: all-in-one
                                                                                    projected:
                                                                                      sources:
                                                                                      - secret:
                                                                                          name: user
                                                                                      - secret:
                                                                                          name: pass
                                                                                # Create the Secrets:
                                                                                ## Create files containing the username and password:
                                                                                echo -n "admin" > ./username.txt
                                                                                echo -n "1f2d1e2e67df" > ./password.txt
                                                                                
                                                                                ## Package these files into secrets:
                                                                                kubectl create secret generic user --from-file=./username.txt
                                                                                kubectl create secret generic pass --from-file=./password.txt
                                                                                
                                                                                # 파드 생성
                                                                                kubectl apply -f https://k8s.io/examples/pods/storage/projected.yaml
                                                                                
                                                                                # 파드 확인
                                                                                kubectl get pod test-projected-volume -o yaml | kubectl neat
                                                                                ...
                                                                                volumes:
                                                                                  - name: all-in-one
                                                                                    projected:
                                                                                      defaultMode: 420
                                                                                      sources:
                                                                                      - secret:
                                                                                          name: user
                                                                                      - secret:
                                                                                          name: pass
                                                                                  - name: kube-api-access-n6n9v
                                                                                    projected:
                                                                                      defaultMode: 420
                                                                                      sources:
                                                                                      - serviceAccountToken:
                                                                                          expirationSeconds: 3607
                                                                                          path: token
                                                                                      - configMap:
                                                                                          items:
                                                                                          - key: ca.crt
                                                                                            path: ca.crt
                                                                                          name: kube-root-ca.crt
                                                                                      - downwardAPI:
                                                                                          items:
                                                                                          - fieldRef:
                                                                                              apiVersion: v1
                                                                                              fieldPath: metadata.namespace
                                                                                            path: namespace
                                                                                
                                                                                # 시크릿 확인
                                                                                kubectl exec -it test-projected-volume -- ls /projected-volume/
                                                                                password.txt  username.txt
                                                                                
                                                                                kubectl exec -it test-projected-volume -- cat /projected-volume/username.txt ;echo
                                                                                admin
                                                                                
                                                                                kubectl exec -it test-projected-volume -- cat /projected-volume/password.txt ;echo
                                                                                1f2d1e2e67df
                                                                                
                                                                                # 삭제
                                                                                kubectl delete pod test-projected-volume && kubectl delete secret user pass
                                                                                 

                                                                                k8s api 접근 단계

                                                                                • AuthNAuthZAdmisstion Control 권한이 있는 사용자에 한해서 관리자(Admin)가 특정 행동을 제한(validate) 혹은 변경(mutate) - 링크 Slack
                                                                                • AuthN & AuthZ - MutatingWebhook - Object schema validation - ValidatingWebhook → etcd
                                                                                https://kubernetes.io/blog/2019/03/21/a-guide-to-kubernetes-admission-controllers/
                                                                                • Admission Control도 Webhook으로 사용자에게 API가 열려있고, 사용자는 자신만의 Admission Controller를 구현할 수 있으며, 이를 Dynamic Admission Controller라고 부르고, 크게 MutatingWebhookValidatingWebhook 로 나뉩니다.
                                                                                • MutatingWebhook은 사용자가 요청한 request에 대해서 관리자가 임의로 값을 변경하는 작업입니다.
                                                                                • ValidatingWebhook은 사용자가 요청한 request에 대해서 관리자기 허용을 막는 작업입니다.
                                                                                #
                                                                                kubectl get mutatingwebhookconfigurations
                                                                                NAME                              WEBHOOKS   AGE
                                                                                aws-load-balancer-webhook         3          98m
                                                                                kube-prometheus-stack-admission   1          96m
                                                                                pod-identity-webhook              1          175m
                                                                                vpc-resource-mutating-webhook     1          175m
                                                                                
                                                                                #
                                                                                kubectl get validatingwebhookconfigurations
                                                                                NAME                              WEBHOOKS   AGE
                                                                                aws-load-balancer-webhook         3          97m
                                                                                kube-prometheus-stack-admission   1          96m
                                                                                vpc-resource-validating-webhook   2          175m
                                                                                 

                                                                                JWT : Bearer type - JWT(JSON Web Token) X.509 Certificatelightweight JSON 버전

                                                                                • Bearer type 경우, 서버에서 지정한 어떠한 문자열도 입력할 수 있습니다. 하지만 굉장히 허술한 느낌을 받습니다.
                                                                                • 이를 보완하고자 쿠버네티스에서 Bearer 토큰을 전송할 때 주로 JWT (JSON Web Token) 토큰을 사용합니다.
                                                                                • JWT는 X.509 Certificate와 마찬가지로 private key를 이용하여 토큰을 서명하고 public key를 이용하여 서명된 메세지를 검증합니다.
                                                                                • 이러한 메커니즘을 통해 해당 토큰이 쿠버네티스를 통해 생성된 valid한 토큰임을 인증할 수 있습니다.
                                                                                • X.509 Certificatelightweight JSON 버전이라고 생각하면 편리합니다.
                                                                                • jwtJSON 형태로 토큰 형식을 정의한 스펙입니다. jwt는 쿠버네티스에서 뿐만 아니라 다양한 웹 사이트에서 인증, 권한 허가, 세션관리 등의 목적으로 사용합니다.
                                                                                  • Header: 토큰 형식와 암호화 알고리즘을 선언합니다.
                                                                                  • Payload: 전송하려는 데이터를 JSON 형식으로 기입합니다.
                                                                                  • Signature: Header와 Payload의 변조 가능성을 검증합니다.
                                                                                • 각 파트는 base64 URL 인코딩이 되어서 .으로 합쳐지게 됩니다.
                                                                                https://research.securitum.com/jwt-json-web-token-security/
                                                                                https://coffeewhale.com/kubernetes/authentication/http-auth/2020/05/03/auth02/
                                                                                https://jwt.io/
                                                                                 

                                                                                OIDC : 사용자를 인증해 사용자에게 액세스 권한을 부여할 수 있게 해주는 프로토콜 ⇒ [커피고래]님 블로그 OpenID Connect - 링크

                                                                                • OAuth 2.0 : 권한허가 처리 프로토콜, 다른 서비스에 접근할 수 있는 권한을 획득하거나 반대로 다른 서비스에게 권한을 부여할 수 있음 - 생활코딩
                                                                                  • 위임 권한 부여 Delegated Authorization, 사용자 인증 보다는 제한된 사람에게(혹은 시스템) 제한된 권한을 부여하는가, 예) 페이스북 posting 권한
                                                                                  • Access Token : 발급처(OAuth 2.0), 서버의 리소스 접근 권한
                                                                                • OpenID : 비영리기관인 OpenID Foundation에서 추진하는 개방형 표준 및 분산 인증 Authentication 프로토콜, 사용자 인증 및 사용자 정보 제공(id token) - 링크
                                                                                  • ID Token : 발급처(OpenID Connect), 유저 프로필 정보 획득
                                                                                • OIDC OpenID Connect = OpenID 인증 + OAuth2.0 인가, JSON 포맷을 이용한 RESful API 형식으로 인증 - 링크
                                                                                  • iss: 토큰 발행자
                                                                                  • sub: 사용자를 구분하기 위한 유니크한 구분자
                                                                                  • email: 사용자의 이메일
                                                                                  • iat: 토큰이 발행되는 시간을 Unix time으로 표기한 것
                                                                                  • exp: 토큰이 만료되는 시간을 Unix time으로 표기한 것
                                                                                  • aud: ID Token이 어떤 Client를 위해 발급된 것인지.
                                                                                • IdP Open Identify Provider : 구글, 카카오와 같이 OpenID 서비스를 제공하는 신원 제공자.
                                                                                  • OpenID Connect에서 IdP의 역할을 OAuth가 수행 - 링크
                                                                                • RP Relying Party : 사용자를 인증하기 위해 IdP에 의존하는 주체
                                                                                 
                                                                                9.4.1 JSON 웹 토큰이란 무엇인가? - wiki* (실습~)
                                                                                • 이스티오에서 JWT를 사용해 최종 사용자 인증 및 인가를 지원한다는 사실은 앞서 간단히 언급했다.
                                                                                • 요청의 인증 및 인가가 작동하는 방식을 자세히 다루기 전에 JWT를 간단히 살펴보자.
                                                                                • 이 주제에 대한 기본 지식을 이미 갖췄다면 다음 절로 건너뛰어도 된다.
                                                                                 
                                                                                  • JWT는 클라이언트르 서버에 인증하는 데 사용하는 간단한 클레임 표현이다. A JWT is a compact claims representation used to authenticate a client to a server.
                                                                                  • JWT는 다음 세 가지 부분으로 이뤄져 있다.
                                                                                    • 헤더 : 유형 및 해싱 알고리듬으로 구성
                                                                                    • 페이로드 : 사용자 클레임 포함
                                                                                    • 서명 : JWT의 진위 여부를 파악하는 데 사용
                                                                                 
                                                                                    • 이 세 부분, 즉 헤더, 페이로드, 서명이 점(.)으로 구분되고 Base64 URL로 인코딩되기 때문에 JWT는 HTTP 요청으로 사용하기에 매우 적합하다.
                                                                                    • ch9/enduser/user.jwt 에 있는 토큰의 내용물을 확인해보고, 페이로드를 디코딩해보자.
                                                                                  #
                                                                                  cat ./ch9/enduser/user.jwt
                                                                                  
                                                                                  # 디코딩 방법 1
                                                                                  jwt decode $(cat ./ch9/enduser/user.jwt)
                                                                                  
                                                                                  # 디코딩 방법 2
                                                                                  cat ./ch9/enduser/user.jwt | cut -d '.' -f1 | base64 --decode | sed 's/$/}/'  | jq
                                                                                  cat ./ch9/enduser/user.jwt | cut -d '.' -f2 | base64 --decode | sed 's/$/"}/' | jq
                                                                                  {
                                                                                    "exp": 4745145038, # 만료 시간 Expiration time
                                                                                    "group": "user",   # 'group' 클레임
                                                                                    "iat": 1591545038, # 발행 시각 Issue time
                                                                                    "iss": "auth@istioinaction.io", # 토큰 발행자 Token issuer
                                                                                    "sub": "9b792b56-7dfa-4e4b-a83f-e20679115d79" # 토큰의 주체 Subject or principal of the token
                                                                                  }
                                                                                   
                                                                                  • 이 데이터는 주체 subject 에 대한 클레임을 표현한다. 클레임 덕분에 서비스는 클라이언트의 ID인가를 판단할 수 있다. The claims enable the service to determine the identity and authorization of a client.
                                                                                  • 예를 들어 이 토큰이 사용자 그룹에 있는 주체에 속한다고 해보자. 서비스는 이 정보를 사용해 이 주체의 접근 수준을 결정할 수 있다.
                                                                                  • 클레임을 신뢰하려면 토큰이 검증될 수 있어야 한다. For claims to be trusted, the token needs to be verifiable.
                                                                                   

                                                                                  JWT는 어떻게 발행되고 검증되는가? HOW IS A JWT ISSUED AND VALIDATED?

                                                                                    • JWT(JSON 웹 토큰)는 인증 서버에서 발급되는데, 인증 서버는 토큰을 서명하는 비밀 키와 검증하기 위한 공개 키를 갖고 있다.
                                                                                    • 공개 키JWKS JSON Web Key Set, JSON 웹 키셋 라고 하며, well-known HTTP 엔드포인트노출된다.
                                                                                    • 서비스는 이 엔드포인트에서 공개 키를 가져와 인증 서버가 발급한 토큰을 검증할 수 있다.
                                                                                   
                                                                                    • 인증 서버는 여러 솔루션으로 준비할 수 있다.
                                                                                      1. 애플리케이션 백엔드 프레임워크에서 구현할 수 있다.
                                                                                      2. OpenIAM 혹은 Keycloak 등의 서비스로, 자체적으로 구현할 수 있다.
                                                                                      3. Auth0, Okta 등의 서비스형 ID Identity-as-a-Service 솔루션으로 구현할 수 있다.
                                                                                   
                                                                                      1. 그림 9.12는 서버가 토큰을 검증하는 데 JWKS를 어떻게 사용하는지 시각화한다.
                                                                                      2. JWKS는 서명을 복호화하는 데 사용하는 공개 키를 포함한다.
                                                                                      3. 서명을 복호화하고 토큰 데이터의 해시값과 비교하는데, 일치하면 토큰 클레임을 신뢰할 수 있다.
                                                                                      서버는 클라이언트가 제시한 토큰을 검증하기 위해 JWKS를 가져온다.서버는 클라이언트가 제시한 토큰을 검증하기 위해 JWKS를 가져온다.
                                                                                        1. 인증서버는 “토큰 서명”을 위한 private key 와 “토큰 검증”을 위한 public key를 가지고 있음
                                                                                        2. 인증서버에서 private key 로 서명한 JWT (JSON Web Token) 을 발급
                                                                                        3. 인증서버의 public key는 JWKS (JSON Web Key Set) 형태의 HTTP 엔드포인트로 제공
                                                                                        4. 서비스는 인증서버에서 발급된 JWT 를 검증하기 위해 필요한 public key를 JWKS 에서 찾습니다
                                                                                        5. public key로 JWT 서명을 복호화 하여 얻은 해시값과 JWT 토큰 데이터의 해시값을 비교하여
                                                                                        6. 해시값이 동일할 경우 토큰 claim에 변조가 없었음을 보장하므로 신뢰할 수 있습니다
                                                                                       
                                                                                      9.4.2 인그레스 게이트웨이에서의 최종 사용자 인증 및 인가 End-user authentication and authorization at the ingress gateway
                                                                                        • 이스티오 워크로드가 JWT로 최종 사용자 요청을 인증하고 인가하도록 설정할 수 있다. Istio workloads can be configured to authenticate and authorize end-user requests with JWTs.
                                                                                        • 최종 사용자 ID 제공자에게 인증받고 ID클레임을 나타내는 토큰발급받은 사용자를 말한다. End user means the user that was authenticated by an identity provider and received a token issued to represent its identity and claims.
                                                                                       
                                                                                      • 최종 사용자 인가는 모든 워크로드 수준에서 수행할 수 있지만, 보통은 이스티오 인그레스 게이트웨이에서 수행한다.
                                                                                      • 이렇게 하면 유효하지 않은 요청을 조기에 거부하므로 성능이 좋아진다. This improves performance, as invalid requests are rejected early on.
                                                                                      • 또한 요청에서 JWT를 제거하는데, 후속 서비스가 사고로 유출되거나 악의적인 사용자가 재전송 공격 replay attack 에 사용하는 것을 방지하기 위해서다.
                                                                                       
                                                                                        • 실습 환경 준비
                                                                                      #
                                                                                      kubectl delete virtualservice,deployment,service,\
                                                                                      destinationrule,gateway,peerauthentication,authorizationpolicy --all -n istioinaction
                                                                                      
                                                                                      #
                                                                                      kubectl delete peerauthentication,authorizationpolicy -n istio-system --all
                                                                                      
                                                                                      # 삭제 확인
                                                                                      kubectl get gw,vs,dr,peerauthentication,authorizationpolicy -A
                                                                                      
                                                                                      
                                                                                      # 실습 환경 배포
                                                                                      kubectl apply -f services/catalog/kubernetes/catalog.yaml -n istioinaction
                                                                                      kubectl apply -f services/webapp/kubernetes/webapp.yaml -n istioinaction
                                                                                      cat ch9/enduser/ingress-gw-for-webapp.yaml
                                                                                      kubectl apply -f ch9/enduser/ingress-gw-for-webapp.yaml -n istioinaction
                                                                                      
                                                                                       
                                                                                        • 9.4.3 RequestAuthentication으로 JWT 검증하기* Validating JWTs with RequestAuthentication
                                                                                      들어가며
                                                                                      부록 C.6 필터 메타데이터에서 검증된 데이터 수집부록 C.6 필터 메타데이터에서 검증된 데이터 수집
                                                                                      요청을 인증 및 인가하도록 서비스 프록시를 구성하는 리소스요청을 인증 및 인가하도록 서비스 프록시를 구성하는 리소스
                                                                                       
                                                                                        • RequestAuthentication 리소스의 주목적은 JWT를 검증하고, 유효한 토큰의 클레임추출하고, 이 클레임을 필터 메타데이터에 저장하는 것이다.
                                                                                        • 이 필터 메타데이터는 인가 정책이 조치를 취하는 근거로 사용한다.
                                                                                        • 필터 메타데이터란 서비스 프록시에서 필터 간 요청을 처리하는 동안 사용할 수 있는 키-값 쌍의 모음을 말한다.
                                                                                        • 이스티오 사용자로서 이것은 대부분 구현 세부 사항에 해당한다.
                                                                                        • 예를 들어 클레임 group: admin 이 있는 요청이 검증되면 이 값은 필터 메타데이터로 저장되며, 필터 메타데이터는 인가 정책이 요청을 허용하거나 거부하는 데 사용한다.
                                                                                        • 최종 사용자 요청에 따라 결과는 셋 중 하나가 된다.
                                                                                          • 유효한 토큰을 갖고 있는 요청은 클러스터로 받아들여지며, 이들의 클레임은 필터 메타데이터 형태로 정책에 전달된다.
                                                                                          • 유효하지 않은 토큰을 갖고 있는 요청은 거부된다.
                                                                                          • 토큰이 없는 요청은 클러스터로 받아들여지지만 요청 ID가 없다. 즉, 어떤 클레임도 필터 메타데이터에 저장되지 않는다.
                                                                                       
                                                                                          • JWT가 있는 요청과 JWT가 없는 요청은 무엇이 다를까?
                                                                                          • JWT가 있는 요청은 RequestAuthentication 필터로 검증되고 JWT 클레임이 커넥션 필터 메타데이터에 저장돼 있는 반면,
                                                                                          • JWT가 없는 요청은 커넥션 필터 메타데이터에 클레임이 없다.
                                                                                          • 여기서 암시하는 중요한 세부 사항은 RequestAuthentication 리소스 그 자체는 인가를 적용하지 않는다(’인가를 강제하지 않는다’)는 것이다. 토큰 검증과 claim 추출을 통해 인증의 유효성을 검증하고 인가에서 활용할 정보를 저장하는 역할을 한다. An important implicit detail here is that RequestAuthentication resources by themselves do not enforce authorizations.
                                                                                          • 즉, 인가를 위해서는 여전히 AuthorizationPolicy 가 필요하다. You still need an AuthorizationPolicy for that.
                                                                                         
                                                                                        • 다음 절에서는 RequestAuthentication 리소스를 만들고, 앞서 언급한 경우 모두를 실제 예제와 함께 보여준다.
                                                                                         
                                                                                        RequestAuthentication 리소스 만들기 CREATING A REQUESTAUTHENTICATION RESOURCE
                                                                                          • 다음 RequestAuthentication 리소스는 이스티오의 인그레스 게이트웨이에 적용된다.
                                                                                          • 이는 인그레스 게이트웨이가 auth@istioinaction.io 에서 발급한 토큰을 검증하도록 설정한다.
                                                                                        # cat ch9/enduser/jwt-token-request-authn.yaml 
                                                                                        apiVersion: "security.istio.io/v1beta1"
                                                                                        kind: "RequestAuthentication"
                                                                                        metadata:
                                                                                          name: "jwt-token-request-authn"
                                                                                          namespace: istio-system # 적용할 네임스페이스
                                                                                        spec:
                                                                                          selector:
                                                                                            matchLabels:
                                                                                              app: istio-ingressgateway
                                                                                          jwtRules:
                                                                                          - issuer: "auth@istioinaction.io" # 발급자 Expected issuer
                                                                                            jwks: | # 특정 JWKS로 검증
                                                                                              { "keys":[ {"e":"AQAB","kid":"CU-ADJJEbH9bXl0tpsQWYuo4EwlkxFUHbeJ4ckkakCM","kty":"RSA","n":"zl9VRDbmVvyXNdyoGJ5uhuTSRA2653KHEi3XqITfJISvedYHVNGoZZxUCoiSEumxqrPY_Du7IMKzmT4bAuPnEalbY8rafuJNXnxVmqjTrQovPIerkGW5h59iUXIz6vCznO7F61RvJsUEyw5X291-3Z3r-9RcQD9sYy7-8fTNmcXcdG_nNgYCnduZUJ3vFVhmQCwHFG1idwni8PJo9NH6aTZ3mN730S6Y1g_lJfObju7lwYWT8j2Sjrwt6EES55oGimkZHzktKjDYjRx1rN4dJ5PR5zhlQ4kORWg1PtllWy1s5TSpOUv84OPjEohEoOWH0-g238zIOYA83gozgbJfmQ"}]}
                                                                                              
                                                                                        #
                                                                                        kubectl apply -f ch9/enduser/jwt-token-request-authn.yaml
                                                                                        kubectl get requestauthentication -A
                                                                                        
                                                                                        #
                                                                                        docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/istio-ingressgateway.istio-system
                                                                                        docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/istio-ingressgateway.istio-system --port 8080 -o json
                                                                                        ...
                                                                                                "httpFilters": [
                                                                                                        {
                                                                                                            "name": "istio.metadata_exchange",
                                                                                                            "typedConfig": {
                                                                                                                "@type": "type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm",
                                                                                                                "config": {
                                                                                                                    "vmConfig": {
                                                                                                                        "runtime": "envoy.wasm.runtime.null",
                                                                                                                        "code": {
                                                                                                                            "local": {
                                                                                                                                "inlineString": "envoy.wasm.metadata_exchange"
                                                                                                                            }
                                                                                                                        }
                                                                                                                    },
                                                                                                                    "configuration": {
                                                                                                                        "@type": "type.googleapis.com/envoy.tcp.metadataexchange.config.MetadataExchange"
                                                                                                                    }
                                                                                                                }
                                                                                                            }
                                                                                                        },
                                                                                                        {
                                                                                                            "name": "envoy.filters.http.jwt_authn",
                                                                                                            "typedConfig": {
                                                                                                                "@type": "type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication",
                                                                                                                "providers": {
                                                                                                                    "origins-0": {
                                                                                                                        "issuer": "auth@istioinaction.io",
                                                                                                                        "localJwks": {
                                                                                                                            "inlineString": "{ \"keys\":[ {\"e\":\"AQAB\",\"kid\":\"CU-ADJJEbH9bXl0tpsQWYuo4EwlkxFUHbeJ4ckkakCM\",\"kty\":\"RSA\",\"n\":\"zl9VRDbmVvyXNdyoGJ5uhuTSRA2653KHEi3XqITfJISvedYHVNGoZZxUCoiSEumxqrPY_Du7IMKzmT4bAuPnEalbY8rafuJNXnxVmqjTrQovPIerkGW5h59iUXIz6vCznO7F61RvJsUEyw5X291-3Z3r-9RcQD9sYy7-8fTNmcXcdG_nNgYCnduZUJ3vFVhmQCwHFG1idwni8PJo9NH6aTZ3mN730S6Y1g_lJfObju7lwYWT8j2Sjrwt6EES55oGimkZHzktKjDYjRx1rN4dJ5PR5zhlQ4kORWg1PtllWy1s5TSpOUv84OPjEohEoOWH0-g238zIOYA83gozgbJfmQ\"}]}\n"
                                                                                                                        },
                                                                                                                        "payloadInMetadata": "auth@istioinaction.io"
                                                                                                                    }
                                                                                                                },
                                                                                                                "rules": [
                                                                                                                    {
                                                                                                                        "match": {
                                                                                                                            "prefix": "/"
                                                                                                                        },
                                                                                                                        "requires": {
                                                                                                                            "requiresAny": {
                                                                                                                                "requirements": [
                                                                                                                                    {
                                                                                                                                        "providerName": "origins-0"
                                                                                                                                    },
                                                                                                                                    {
                                                                                                                                        "allowMissing": {}
                                                                                                                                    }
                                                                                                                                ]
                                                                                                                            }
                                                                                                                        }
                                                                                                                    }
                                                                                                                ],
                                                                                                                "bypassCorsPreflight": true
                                                                                                            }
                                                                                                        },
                                                                                                        {
                                                                                                            "name": "istio_authn",
                                                                                                            "typedConfig": {
                                                                                                                "@type": "type.googleapis.com/istio.envoy.config.filter.http.authn.v2alpha1.FilterConfig",
                                                                                                                "policy": {
                                                                                                                    "origins": [
                                                                                                                        {
                                                                                                                            "jwt": {
                                                                                                                                "issuer": "auth@istioinaction.io"
                                                                                                                            }
                                                                                                                        }
                                                                                                                    ],
                                                                                                                    "originIsOptional": true,
                                                                                                                    "principalBinding": "USE_ORIGIN"
                                                                                                                },
                                                                                                                "skipValidateTrustDomain": true
                                                                                        ...
                                                                                         
                                                                                        유효한 발행자의 토큰이 있는 요청은 받아들여진다 REQUESTS WITH TOKENS FROM VALID ISSUERS ARE ACCEPTED
                                                                                          • 유효한 JWT로 요청해보자
                                                                                          #
                                                                                          cat ch9/enduser/user.jwt
                                                                                          USER_TOKEN=$(< ch9/enduser/user.jwt)
                                                                                          jwt decode $USER_TOKEN
                                                                                          
                                                                                          Token header
                                                                                          ------------
                                                                                          {
                                                                                            "typ": "JWT",
                                                                                            "alg": "RS256",
                                                                                            "kid": "CU-ADJJEbH9bXl0tpsQWYuo4EwlkxFUHbeJ4ckkakCM"
                                                                                          }
                                                                                          
                                                                                          Token claims
                                                                                          ------------
                                                                                          {
                                                                                            "exp": 4745145038,
                                                                                            "group": "user",
                                                                                            "iat": 1591545038,
                                                                                            "iss": "auth@istioinaction.io",
                                                                                            "sub": "9b792b56-7dfa-4e4b-a83f-e20679115d79"
                                                                                          }
                                                                                          
                                                                                          # 호출 with TOKEN
                                                                                          curl -H "Authorization: Bearer $USER_TOKEN" \
                                                                                               -sSl -o /dev/null -w "%{http_code}" webapp.istioinaction.io:30000/api/catalog
                                                                                          
                                                                                          # istio-ingressgateway istio-proxy log level 변경
                                                                                          docker exec -it myk8s-control-plane istioctl proxy-config log deploy/istio-ingressgateway -n istio-system --level rbac:debug
                                                                                          
                                                                                          # 로그
                                                                                          kubectl logs -n istio-system -l app=istio-ingressgateway -f 
                                                                                          [2025-05-10T16:59:10.177Z] "GET /api/catalog HTTP/1.1" 200 - via_upstream - "-" 0 357 59 56 "192.168.65.1" "curl/8.7.1" "629e2991-ae39-97b5-9d76-f90d8f7e5c06" "webapp.istioinaction.io:30000" "10.10.0.18:8080" outbound|80||webapp.istioinaction.svc.cluster.local 10.10.0.6:40670 10.10.0.6:8080 192.168.65.1:58872 - -
                                                                                          
                                                                                          # 호출 without TOKEN
                                                                                          curl \
                                                                                               -sSl -o /dev/null -w "%{http_code}" webapp.istioinaction.io:30000/api/catalog
                                                                                          200
                                                                                          
                                                                                          kubectl logs -n istio-system -l app=istio-ingressgateway -f 
                                                                                          [2025-05-10T17:00:46.638Z] "GET /api/catalog HTTP/1.1" 200 - via_upstream - "-" 0 357 30 29 "192.168.65.1" "curl/8.7.1" "133a1311-688b-970e-8f83-707bf122b0bf" "webapp.istioinaction.io:30000" "10.10.0.18:8080" outbound|80||webapp.istioinaction.svc.cluster.local 10.10.0.6:60624 10.10.0.6:8080 192.168.65.1:24120 - -
                                                                                            1. 워크로드에 적용된 인가 정책 AuthorizationPolicy 이 없으므로 기본적으로 token과 무관하게 허용 ALLOW 된다.
                                                                                           
                                                                                          유효하지 않은 발행자의 토큰이 있는 요청은 거부된다 REQUESTS WITH TOKENS FROM INVALID ISSUERS ARE REJECTED
                                                                                            • 유효하지 않은 JWT로 요청해보자
                                                                                          #
                                                                                          cat ch9/enduser/not-configured-issuer.jwt
                                                                                          WRONG_ISSUER=$(< ch9/enduser/not-configured-issuer.jwt)
                                                                                          jwt decode $WRONG_ISSUER
                                                                                          
                                                                                          Token header
                                                                                          ------------
                                                                                          {
                                                                                            "typ": "JWT",
                                                                                            "alg": "RS256",
                                                                                            "kid": "CU-ADJJEbH9bXl0tpsQWYuo4EwlkxFUHbeJ4ckkakCM"
                                                                                          }
                                                                                          
                                                                                          Token claims
                                                                                          ------------
                                                                                          {
                                                                                            "exp": 4745151548,
                                                                                            "group": "user",
                                                                                            "iat": 1591551548,
                                                                                            "iss": "old-auth@istioinaction.io", # issuer가 잘못된 JWT TOKEN
                                                                                            "sub": "79d7506c-b617-46d1-bc1f-f511b5d30ab0"
                                                                                          }
                                                                                          
                                                                                          
                                                                                          # 호출 
                                                                                          curl -H "Authorization: Bearer $WRONG_ISSUER" \
                                                                                               -sSl -o /dev/null -w "%{http_code}" webapp.istioinaction.io:30000/api/catalog
                                                                                          401
                                                                                          
                                                                                          # 로그
                                                                                          kubectl logs -n istio-system -l app=istio-ingressgateway -f
                                                                                          [2025-05-10T17:02:56.297Z] "GET /api/catalog HTTP/1.1" 401 - jwt_authn_access_denied{Jwt_issuer_is_not_configured} - "-" 0 28 0 - "192.168.65.1" "curl/8.7.1" "06c046b1-7faa-985f-9b47-8a324d428205" "webapp.istioinaction.io:30000" "-" outbound|80||webapp.istioinaction.svc.cluster.local - 10.10.0.6:8080 192.168.65.1:64353 - -
                                                                                           
                                                                                          토큰이 없는 요청은 클러스터로 받아들여진다 REQUESTS WITHOUT TOKENS ARE ADMITTED INTO THE CLUSTER
                                                                                            • 토큰 없이 curl 요청 실행
                                                                                            # 호출 
                                                                                            curl -sSl -o /dev/null -w "%{http_code}" webapp.istioinaction.io:30000/api/catalog
                                                                                            
                                                                                            # 로그
                                                                                            kubectl logs -n istio-system -l app=istio-ingressgateway -f
                                                                                            
                                                                                                1. 응답 코드 요청이 클러스터로 받아들여졌음을 보여준다.
                                                                                             
                                                                                              1. 토큰이 없는 요청은 거부될 것으로 예상할 수 있으므로 혼란스러울 수 있다.
                                                                                              2. 그러나 실제로는 애플리케이션의 프론트엔드에 서비스를 제공하는 등 요청에 토큰이 없는 시나리오가 많이 있다.
                                                                                              3. 이런 이유로, 토큰이 없는 요청을 거부하려면 다음에 설명할 약간의 추가 작업이 필요하다.
                                                                                               
                                                                                              JWT가 없는 요청 거부하기 DENYING REQUESTS WITHOUT JWTS
                                                                                                • JWT가 없는 요청 거부하려면 명시적으로 거부하는 AuthorizationPolicy 리소스를 만들어야 한다.
                                                                                                • 이 정책은 requestPrincipals 속성이 없는 source 에서 온 모든 요청에 적용되며, (action 속성에 지정된 대로) 요청을 거부한다.
                                                                                                • requestPrincipals초기화 방식을 하게 되면 놀랄지도 모르는데, 바로 JWT의 발행자 issuer 와 주체 subject 클레임을 ‘iss/sub’ 형태로 결합한 것이다. You might be surprised where requestPrincipals is initialized: it is composed of the issuer and subject JWT claims (concatenated in the format iss/sub).
                                                                                                • 클레임RequestPrincipals 리소스로 인증되고, AuthorizationPolicy 필터 등 다른 필터가 사용할 수 있도록 커넥션 메타데이터가공된다.
                                                                                                # cat ch9/enduser/app-gw-requires-jwt.yaml # vi/vim, vscode 에서 포트 30000 추가
                                                                                                apiVersion: security.istio.io/v1beta1
                                                                                                kind: AuthorizationPolicy
                                                                                                metadata:
                                                                                                  name: app-gw-requires-jwt
                                                                                                  namespace: istio-system
                                                                                                spec:
                                                                                                  selector:
                                                                                                    matchLabels:
                                                                                                      app: istio-ingressgateway
                                                                                                  action: DENY
                                                                                                  rules:
                                                                                                  - from:
                                                                                                    - source:
                                                                                                        notRequestPrincipals: ["*"] # 요청 주체에 값이 없는 source는 모두 해당된다
                                                                                                    to:
                                                                                                    - operation:
                                                                                                        hosts: ["webapp.istioinaction.io:30000"] # 이 규칙은 이 특정 호스트에만 적용된다
                                                                                                
                                                                                                #
                                                                                                kubectl apply -f ch9/enduser/app-gw-requires-jwt.yaml
                                                                                                
                                                                                                #
                                                                                                kubectl get AuthorizationPolicy -A
                                                                                                NAMESPACE      NAME                  AGE
                                                                                                istio-system   app-gw-requires-jwt   2m14s
                                                                                                
                                                                                                docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/istio-ingressgateway.istio-system --port 8080 -o json
                                                                                                ...
                                                                                                          {
                                                                                                              "name": "envoy.filters.http.rbac",
                                                                                                              "typedConfig": {
                                                                                                                  "@type": "type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC",
                                                                                                                  "rules": {
                                                                                                                      "action": "DENY",
                                                                                                                      "policies": {
                                                                                                                          "ns[istio-system]-policy[app-gw-requires-jwt]-rule[0]": {
                                                                                                                              "permissions": [
                                                                                                                                  {
                                                                                                                                      "andRules": {
                                                                                                                                          "rules": [
                                                                                                                                              {
                                                                                                                                                  "orRules": {
                                                                                                                                                      "rules": [
                                                                                                                                                          {
                                                                                                                                                              "header": {
                                                                                                                                                                  "name": ":authority",
                                                                                                                                                                  "stringMatch": {
                                                                                                                                                                      "exact": "webapp.istioinaction.io:30000",
                                                                                                                                                                      "ignoreCase": true
                                                                                                                                                                  }
                                                                                                                                                              }
                                                                                                                                                          }
                                                                                                                                                      ]
                                                                                                                                                  }
                                                                                                                                              }
                                                                                                                                          ]
                                                                                                                                      }
                                                                                                                                  }
                                                                                                                              ],
                                                                                                                              "principals": [
                                                                                                                                  {
                                                                                                                                      "andIds": {
                                                                                                                                          "ids": [
                                                                                                                                              {
                                                                                                                                                  "notId": {
                                                                                                                                                      "orIds": {
                                                                                                                                                          "ids": [
                                                                                                                                                              {
                                                                                                                                                                  "metadata": {
                                                                                                                                                                      "filter": "istio_authn",
                                                                                                                                                                      "path": [
                                                                                                                                                                          {
                                                                                                                                                                              "key": "request.auth.principal"
                                                                                                                                                                          }
                                                                                                                                                                      ],
                                                                                                                                                                      "value": {
                                                                                                                                                                          "stringMatch": {
                                                                                                                                                                              "safeRegex": {
                                                                                                                                                                                  "regex": ".+"
                                                                                                ...
                                                                                                
                                                                                                # LDS에 의해서 Listener에 rules가 추가된것을 확인
                                                                                                kubectl logs -n istio-system -l app=istiod -f
                                                                                                2025-05-10T17:04:04.442056Z     info    ads     LDS: PUSH for node:istio-egressgateway-85df6b84b7-blp9q.istio-system resources:0 size:0B
                                                                                                2025-05-10T17:04:04.443263Z     info    ads     LDS: PUSH for node:istio-ingressgateway-6bb8fb6549-wq46d.istio-system resources:1 size:4.6kB
                                                                                                2025-05-10T17:04:04.445845Z     info    ads     LDS: PUSH for node:catalog-6cf4b97d-qfc6c.istioinaction resources:22 size:89.4kB
                                                                                                2025-05-10T17:04:04.446298Z     info    ads     LDS: PUSH for node:webapp-7685bcb84-7nphs.istioinaction resources:22 size:89.4kB
                                                                                                
                                                                                                
                                                                                                # 호출 1
                                                                                                curl -sSl -o /dev/null -w "%{http_code}" webapp.istioinaction.io:30000/api/catalog
                                                                                                403
                                                                                                
                                                                                                # 로그
                                                                                                kubectl logs -n istio-system -l app=istio-ingressgateway -f
                                                                                                [2025-05-10T17:06:45.296Z] "GET /api/catalog HTTP/1.1" 403 - rbac_access_denied_matched_policy[ns[istio-system]-policy[app-gw-requires-jwt]-rule[0]] - "-" 0 19 1 - "192.168.65.1" "curl/8.7.1" "779ba6b0-9e53-982d-ad6c-4f783c8cb7be" "webapp.istioinaction.io:30000" "-" outbound|80||webapp.istioinaction.svc.cluster.local - 10.10.0.6:8080 192.168.65.1:62736 - -
                                                                                                
                                                                                                # 호출 2
                                                                                                curl -H "Authorization: Bearer $USER_TOKEN" \
                                                                                                     -sSl -o /dev/null -w "%{http_code}" webapp.istioinaction.io:30000/api/catalog
                                                                                                200
                                                                                                
                                                                                                # 로그
                                                                                                kubectl logs -n istio-system -l app=istio-ingressgateway -f
                                                                                                2025-05-10T17:06:50.125349Z     debug   envoy rbac external/envoy/source/extensions/filters/http/rbac/rbac_filter.cc:114        checking request: requestedServerName: , sourceIP: 192.168.65.1:63517, directRemoteIP: 192.168.65.1:63517, remoteIP: 192.168.65.1:63517,localAddress: 10.10.0.6:8080, ssl: none, headers: ':authority', 'webapp.istioinaction.io:30000'
                                                                                                ':path', '/api/catalog'
                                                                                                ':method', 'GET'
                                                                                                ':scheme', 'http'
                                                                                                'user-agent', 'curl/8.7.1'
                                                                                                'accept', '*/*'
                                                                                                'x-forwarded-for', '192.168.65.1'
                                                                                                'x-forwarded-proto', 'http'
                                                                                                'x-envoy-internal', 'true'
                                                                                                'x-request-id', '0d8f5f5c-b5ec-9cb3-8c2a-1d2f9374f1b7'
                                                                                                'x-envoy-decorator-operation', 'webapp.istioinaction.svc.cluster.local:80/*'
                                                                                                'x-envoy-peer-metadata', 'ChQKDkFQUF9DT05UQUlORVJTEgIaAAoaCgpDTFVTVEVSX0lEEgwaCkt1YmVybmV0ZXMKGwoMSU5TVEFOQ0VfSVBTEgsaCTEwLjEwLjAuNgoZCg1JU1RJT19WRVJTSU9OEggaBjEuMTcuOAqcAwoGTEFCRUxTEpEDKo4DCh0KA2FwcBIWGhRpc3Rpby1pbmdyZXNzZ2F0ZXdheQoTCgVjaGFydBIKGghnYXRld2F5cwoUCghoZXJpdGFnZRIIGgZUaWxsZXIKNgopaW5zdGFsbC5vcGVyYXRvci5pc3Rpby5pby9vd25pbmctcmVzb3VyY2USCRoHdW5rbm93bgoZCgVpc3RpbxIQGg5pbmdyZXNzZ2F0ZXdheQoZCgxpc3Rpby5pby9yZXYSCRoHZGVmYXVsdAowChtvcGVyYXRvci5pc3Rpby5pby9jb21wb25lbnQSERoPSW5ncmVzc0dhdGV3YXlzChIKB3JlbGVhc2USBxoFaXN0aW8KOQofc2VydmljZS5pc3Rpby5pby9jYW5vbmljYWwtbmFtZRIWGhRpc3Rpby1pbmdyZXNzZ2F0ZXdheQovCiNzZXJ2aWNlLmlzdGlvLmlvL2Nhbm9uaWNhbC1yZXZpc2lvbhIIGgZsYXRlc3QKIgoXc2lkZWNhci5pc3Rpby5pby9pbmplY3QSBxoFZmFsc2UKGgoHTUVTSF9JRBIPGg1jbHVzdGVyLmxvY2FsCi8KBE5BTUUSJxolaXN0aW8taW5ncmVzc2dhdGV3YXktNmJiOGZiNjU0OS13cTQ2ZAobCglOQU1FU1BBQ0USDhoMaXN0aW8tc3lzdGVtCl0KBU9XTkVSElQaUmt1YmVybmV0ZXM6Ly9hcGlzL2FwcHMvdjEvbmFtZXNwYWNlcy9pc3Rpby1zeXN0ZW0vZGVwbG95bWVudHMvaXN0aW8taW5ncmVzc2dhdGV3YXkKFwoRUExBVEZPUk1fTUVUQURBVEESAioACicKDVdPUktMT0FEX05BTUUSFhoUaXN0aW8taW5ncmVzc2dhdGV3YXk='
                                                                                                'x-envoy-peer-metadata-id', 'router~10.10.0.6~istio-ingressgateway-6bb8fb6549-wq46d.istio-system~istio-system.svc.cluster.local'
                                                                                                ...
                                                                                                  1. 이제 토큰 없이 요청을 보내고, 요청 주체가 없기 때문에 인가하는 데 실패하는 것을 확인했다.
                                                                                                 
                                                                                                JWT 클레임에 기반한 다양한 접근 수준 DIFFERENT LEVELS OF ACCESS BASED ON JWT CLAIMS
                                                                                                  • 유저별로 다른 접근 정책 설정해보자.
                                                                                                  • 이 예제에서는 일반 사용자가 API에서 데이터를 읽는 것은 허용하지만 새 데이터를 쓰거나 기존 데이터를 바꾸는 것은 금지한다.
                                                                                                  • 한편, 관리자에게는 모든 권한을 허용할 것이다. 각 토큰들은 클레임이 다름.
                                                                                                # 일반 사용자 토큰 : 'group: user' 클레임
                                                                                                jwt decode $(cat ch9/enduser/user.jwt)
                                                                                                ...
                                                                                                {
                                                                                                  "exp": 4745145038,
                                                                                                  "group": "user",
                                                                                                  "iat": 1591545038,
                                                                                                  "iss": "auth@istioinaction.io",
                                                                                                  "sub": "9b792b56-7dfa-4e4b-a83f-e20679115d79"
                                                                                                }
                                                                                                
                                                                                                # 관리자 토큰 : 'group: admin' 클레임
                                                                                                jwt decode $(cat ch9/enduser/admin.jwt)
                                                                                                ...
                                                                                                {
                                                                                                  "exp": 4745145071,
                                                                                                  "group": "admin",
                                                                                                  "iat": 1591545071,
                                                                                                  "iss": "auth@istioinaction.io",
                                                                                                  "sub": "218d3fb9-4628-4d20-943c-124281c80e7b"
                                                                                                }
                                                                                                 
                                                                                                  • 일반 사용자가 webapp 에서 데이터를 읽을 수 있게 허용하도록 AuthorizationPolicy 리소스 설정
                                                                                                # cat ch9/enduser/allow-all-with-jwt-to-webapp.yaml # vi/vim, vscode 에서 포트 30000 추가
                                                                                                apiVersion: security.istio.io/v1beta1
                                                                                                kind: AuthorizationPolicy
                                                                                                metadata:
                                                                                                  name: allow-all-with-jwt-to-webapp
                                                                                                  namespace: istio-system
                                                                                                spec:
                                                                                                  selector:
                                                                                                    matchLabels:
                                                                                                      app: istio-ingressgateway
                                                                                                  action: ALLOW
                                                                                                  rules:
                                                                                                  - from:
                                                                                                    - source:
                                                                                                        requestPrincipals: ["auth@istioinaction.io/*"] # 최종 사용자 요청 주체를 표현 Represents the end-user request principal
                                                                                                    to:
                                                                                                    - operation:
                                                                                                        hosts: ["webapp.istioinaction.io:30000"]
                                                                                                        methods: ["GET"]
                                                                                                 
                                                                                                  • 관리자에게 모든 작업을 허용하는 AuthorizationPolicy 리소스 설정
                                                                                                # cat ch9/enduser/allow-mesh-all-ops-admin.yaml
                                                                                                apiVersion: "security.istio.io/v1beta1"
                                                                                                kind: "AuthorizationPolicy"
                                                                                                metadata:
                                                                                                  name: "allow-mesh-all-ops-admin"
                                                                                                  namespace: istio-system
                                                                                                spec:
                                                                                                  selector:
                                                                                                    matchLabels:
                                                                                                      app: istio-ingressgateway
                                                                                                  action: ALLOW
                                                                                                  rules:
                                                                                                  - from:
                                                                                                    - source:
                                                                                                        requestPrincipals: ["auth@istioinaction.io/*"]
                                                                                                    when:
                                                                                                    - key: request.auth.claims[group]
                                                                                                      values: ["admin"] # 이 클레임을 포함한 요청만 허용.
                                                                                                 
                                                                                                  • 실습
                                                                                                  #
                                                                                                  kubectl apply -f ch9/enduser/allow-all-with-jwt-to-webapp.yaml
                                                                                                  kubectl apply -f ch9/enduser/allow-mesh-all-ops-admin.yaml
                                                                                                  
                                                                                                  #
                                                                                                  kubectl get authorizationpolicy -A
                                                                                                  NAMESPACE      NAME                           AGE
                                                                                                  istio-system   allow-all-with-jwt-to-webapp   4s
                                                                                                  istio-system   allow-mesh-all-ops-admin       4s
                                                                                                  istio-system   app-gw-requires-jwt            5m45s
                                                                                                  
                                                                                                  #
                                                                                                  docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/istio-ingressgateway.istio-system --port 8080 -o json
                                                                                                  ...
                                                                                                                  "policies": {
                                                                                                                      "ns[istio-system]-policy[allow-all-with-jwt-to-webapp]-rule[0]": {
                                                                                                                          "permissions": [
                                                                                                                              {
                                                                                                                                  "andRules": {
                                                                                                                                      "rules": [
                                                                                                                                          {
                                                                                                                                              "orRules": {
                                                                                                                                                  "rules": [
                                                                                                                                                      {
                                                                                                                                                          "header": {
                                                                                                                                                              "name": ":authority",
                                                                                                                                                              "stringMatch": {
                                                                                                                                                                  "exact": "webapp.istioinaction.io:30000",
                                                                                                                                                                  "ignoreCase": true
                                                                                                  ...
                                                                                                                      "ns[istio-system]-policy[allow-mesh-all-ops-admin]-rule[0]": {
                                                                                                                          "permissions": [
                                                                                                                              {
                                                                                                                                  "andRules": {
                                                                                                                                      "rules": [
                                                                                                                                          {
                                                                                                                                              "any": true
                                                                                                                                          }
                                                                                                                                      ]
                                                                                                                                  }
                                                                                                                              }
                                                                                                                          ],
                                                                                                                          "principals": [
                                                                                                                              {
                                                                                                                                  "andIds": {
                                                                                                                                      "ids": [
                                                                                                                                          {
                                                                                                                                              "orIds": {
                                                                                                                                                  "ids": [
                                                                                                                                                      {
                                                                                                                                                          "metadata": {
                                                                                                                                                              "filter": "istio_authn",
                                                                                                                                                              "path": [
                                                                                                                                                                  {
                                                                                                                                                                      "key": "request.auth.principal"
                                                                                                                                                                  }
                                                                                                                                                              ],
                                                                                                                                                              "value": {
                                                                                                                                                                  "stringMatch": {
                                                                                                                                                                      "prefix": "auth@istioinaction.io/"
                                                                                                                                                                  }
                                                                                                                                                              }
                                                                                                                                                          }
                                                                                                                                                      }
                                                                                                                                                  ]
                                                                                                                                              }
                                                                                                                                          },
                                                                                                                                          {
                                                                                                                                              "orIds": {
                                                                                                                                                  "ids": [
                                                                                                                                                      {
                                                                                                                                                          "metadata": {
                                                                                                                                                              "filter": "istio_authn",
                                                                                                                                                              "path": [
                                                                                                                                                                  {
                                                                                                                                                                      "key": "request.auth.claims"
                                                                                                                                                                  },
                                                                                                                                                                  {
                                                                                                                                                                      "key": "group"
                                                                                                                                                                  }
                                                                                                                                                              ],
                                                                                                                                                              "value": {
                                                                                                                                                                  "listMatch": {
                                                                                                                                                                      "oneOf": {
                                                                                                                                                                          "stringMatch": {
                                                                                                                                                                              "exact": "admin"
                                                                                                  ...
                                                                                                  
                                                                                                  # LDS에 의해 Listener에 rules 추가된것 확인
                                                                                                  kubectl logs -n istio-system -l app=istiod -f
                                                                                                  2025-05-10T17:09:45.833993Z     info    ads     Push debounce stable[81] 2 for config AuthorizationPolicy/istio-system/allow-mesh-all-ops-admin and 1 more configs: 100.165208ms since last change, 169.961083ms since last push, full=true
                                                                                                  2025-05-10T17:09:45.834798Z     info    ads     XDS: Pushing:2025-05-10T17:09:45Z/65 Services:14 ConnectedEndpoints:4 Version:2025-05-10T17:09:45Z/65
                                                                                                  2025-05-10T17:09:45.836436Z     info    ads     LDS: PUSH for node:istio-egressgateway-85df6b84b7-blp9q.istio-system resources:0 size:0B
                                                                                                  2025-05-10T17:09:45.837511Z     info    ads     LDS: PUSH for node:istio-ingressgateway-6bb8fb6549-wq46d.istio-system resources:1 size:5.2kB
                                                                                                  2025-05-10T17:09:45.839211Z     info    ads     LDS: PUSH for node:webapp-7685bcb84-7nphs.istioinaction resources:22 size:89.4kB
                                                                                                  2025-05-10T17:09:45.839517Z     info    ads     LDS: PUSH for node:catalog-6cf4b97d-qfc6c.istioinaction resources:22 size:89.4kB
                                                                                                  
                                                                                                  
                                                                                                  # 수집된 메타데이터를 관찰하고자 서비스 프록시에 rbac 로거 설정
                                                                                                  ## 기본적으로 envoy rbac 로거는 메타데이터를 로그에 출력하지 않는다. 출력을 위해 로깅 수준을 debug 로 설정하자
                                                                                                  docker exec -it myk8s-control-plane istioctl proxy-config log deploy/istio-ingressgateway -n istio-system --level rbac:debug
                                                                                                  
                                                                                                  
                                                                                                  # 일반유저 : [GET]과 [POST] 호출
                                                                                                  USER_TOKEN=$(< ch9/enduser/user.jwt)
                                                                                                  
                                                                                                  curl -H "Authorization: Bearer $USER_TOKEN" \
                                                                                                       -sSl -o /dev/null -w "%{http_code}\n" webapp.istioinaction.io:30000/api/catalog
                                                                                                  
                                                                                                  curl -H "Authorization: Bearer $USER_TOKEN" \
                                                                                                       -XPOST webapp.istioinaction.io:30000/api/catalog \
                                                                                                       --data '{"id": 2, "name": "Shoes", "price": "84.00"}'
                                                                                                  
                                                                                                  # 로그
                                                                                                  # get 요청에 대해서는 성공했으나, post 요청에 대해서는 error
                                                                                                  # allow-all-with-jwt-to-webapp authorizationPolicy에 정의된데로 jwt claim의 group이 user인 경우, GET 요청에 대해서만 ALLOW 됨
                                                                                                  kubectl logs -n istio-system -l app=istio-ingressgateway -f
                                                                                                  ...
                                                                                                  , dynamicMetadata: filter_metadata {
                                                                                                    key: "envoy.filters.http.jwt_authn"
                                                                                                    value {
                                                                                                      fields {
                                                                                                        key: "auth@istioinaction.io"
                                                                                                        value {
                                                                                                          struct_value {
                                                                                                            fields {
                                                                                                              key: "exp"
                                                                                                              value {
                                                                                                                number_value: 4745145038
                                                                                                              }
                                                                                                            }
                                                                                                            fields {
                                                                                                              key: "group"
                                                                                                              value {
                                                                                                                string_value: "user"
                                                                                                              }
                                                                                                            }
                                                                                                  ...
                                                                                                  [2025-05-10T17:11:52.619Z] "POST /api/catalog HTTP/1.1" 403 - rbac_access_denied_matched_policy[none] - "-" 0 19 1 - "192.168.65.1" "curl/8.7.1" "adf4b9dc-f7dd-99d5-989f-49ea791cdc18" "webapp.istioinaction.io:30000" "-" outbound|80||webapp.istioinaction.svc.cluster.local - 10.10.0.6:8080 192.168.65.1:25633 - -
                                                                                                  
                                                                                                  # 관리자 : [GET]과 [POST] 호출
                                                                                                  ADMIN_TOKEN=$(< ch9/enduser/admin.jwt)
                                                                                                  
                                                                                                  curl -H "Authorization: Bearer $ADMIN_TOKEN" \
                                                                                                       -sSl -o /dev/null -w "%{http_code}\n" webapp.istioinaction.io:30000/api/catalog
                                                                                                  200
                                                                                                  
                                                                                                  curl -H "Authorization: Bearer $ADMIN_TOKEN" \
                                                                                                       -XPOST webapp.istioinaction.io:30000/api/catalog \
                                                                                                       --data '{"id": 2, "name": "Shoes", "price": "84.00"}'
                                                                                                  [{"id":1,"color":"amber","department":"Eyewear","name":"Elinor Glasses","price":"282.00"},{"id":2,"color":"cyan","department":"Clothing","name":"Atlas Shirt","price":"127.00"},{"id":3,"color":"teal","department":"Clothing","name":"Small Metal Shoes","price":"232.00"},{"id":4,"color":"red","department":"Watches","name":"Red Dragon Watch","price":"232.00"}]
                                                                                                  
                                                                                                  # 로그
                                                                                                  # get, post 요청에 대해서는 모두 성공
                                                                                                  # allow-mesh-all-ops-admin authorizationPolicy에 정의된데로 jwt claim의 group이 admin인 경우, GET,POST 모두 ALLOW 됨
                                                                                                  kubectl logs -n istio-system -l app=istio-ingressgateway -f
                                                                                                  2025-05-10T17:15:17.636683Z     debug   envoy rbac external/envoy/source/extensions/filters/http/rbac/rbac_filter.cc:114        checking request: requestedServerName: , sourceIP: 192.168.65.1:55478, directRemoteIP: 192.168.65.1:55478, remoteIP: 192.168.65.1:55478,localAddress: 10.10.0.6:8080, ssl: none, headers: ':authority', 'webapp.istioinaction.io:30000'
                                                                                                  ...
                                                                                                  , dynamicMetadata: filter_metadata {
                                                                                                    key: "envoy.filters.http.jwt_authn"
                                                                                                    value {
                                                                                                      fields {
                                                                                                        key: "auth@istioinaction.io"
                                                                                                        value {
                                                                                                          struct_value {
                                                                                                            fields {
                                                                                                              key: "exp"
                                                                                                              value {
                                                                                                                number_value: 4745145071
                                                                                                              }
                                                                                                            }
                                                                                                            fields {
                                                                                                              key: "group"
                                                                                                              value {
                                                                                                                string_value: "admin"
                                                                                                              }
                                                                                                            }
                                                                                                            
                                                                                                  [2025-05-10T17:15:17.635Z] "POST /api/catalog HTTP/1.1" 200 - via_upstream - "-" 44 357 14 13 "192.168.65.1" "curl/8.7.1" "adac4479-d5c6-91a7-987c-4da6e629155a" "webapp.istioinaction.io:30000" "10.10.0.18:8080" outbound|80||webapp.istioinaction.svc.cluster.local 10.10.0.6:60624 10.10.0.6:8080 192.168.65.1:55478 - -
                                                                                                    1. 관리자가 catalog 에 새 아이템을 만들 수 있도록 허용하고 있음을 확인.
                                                                                                   

                                                                                                  9.5. 커스텀 외부 인가 서비스와 통합하기

                                                                                                  https://netpple.github.io/docs/istio-in-action/Istio-ch9-securing-5-external_authz

                                                                                                  들어가며 : 외부 인가 서비스 호출
                                                                                                    • SPIFFE를 기반으로 구축된 이스티오의 인증 메커니즘이 서비스 인가를 구축할 수 있는 기반을 제공하는 방법을 살펴봤다.
                                                                                                    • 이스티오는 엔보이의 기본 RBAC 기능을 사용해 인가를 구현한다.
                                                                                                    • 그런데 인가에 좀 더 정교한 커스텀 메커니즘이 필요하면 어떻게 해야 할까?
                                                                                                    • 요청을 허용할지 여부를 결정할 때 외부 인가 서비스를 호출하도록 이스티오의 서비스 프록시를 설정할 수 있다.
                                                                                                  외부 서버에서 요청을 인가받도록 CUSTOM 정책 사용하기외부 서버에서 요청을 인가받도록 CUSTOM 정책 사용하기
                                                                                                   
                                                                                                   
                                                                                                      • 외부 인가 서비스는 프록시가 인가를 집행하는 데 사용하는 ‘허용’이나 ‘거부’ 메시지를 반환한다. The ExtAuthz service returns an “allow” or “deny” message that the proxy then uses to enforce authorizations.
                                                                                                     
                                                                                                    👉🏻

                                                                                                    ExtAuthz performance tradeoffs 외부 인가 성능 트레이드오프 요청 경로 중에 외부 인가 서비스를 호출하기 때문에 이 방법을 사용할 때는 지연 시간 증가에 대비해야 한다. 이스티오의 내장 인가 기능은 대체로 충분하고 유연하게 작동하지만, 완벽히 통제해야 한다면 외부 인가 서비스를 호출하면서 생기는 성능 트레이드오프를 평가해야 한다. 이전 단락에서 언급했듯이, 외부 인가 서비스를 애플리케이션 사이드카로 배포해 네트워크 오버헤드를 최소화할 수 있다. https://istio.io/latest/docs/tasks/security/authorization/authz-custom/

                                                                                                     
                                                                                                    9.5.1 외부 인가 실습 Hands-on with external authorization (실습~)
                                                                                                      • 실습 환경 초기화
                                                                                                      # 기존 인증/인가 정책 모두 삭제
                                                                                                      kubectl delete authorizationpolicy,peerauthentication,requestauthentication --all -n istio-system
                                                                                                      
                                                                                                      # 실습 애플리케이션 배포
                                                                                                      kubectl apply -f services/catalog/kubernetes/catalog.yaml -n istioinaction
                                                                                                      kubectl apply -f services/webapp/kubernetes/webapp.yaml -n istioinaction
                                                                                                      kubectl apply -f services/webapp/istio/webapp-catalog-gw-vs.yaml -n istioinaction
                                                                                                      kubectl apply -f ch9/sleep.yaml -n default
                                                                                                      
                                                                                                      # 이스티오 샘플에서 샘플 외부 인가 서비스 배포
                                                                                                      docker exec -it myk8s-control-plane bash
                                                                                                      -----------------------------------
                                                                                                      # 
                                                                                                      ls -l istio-$ISTIOV/samples/extauthz/
                                                                                                      total 24
                                                                                                      -rw-r--r-- 1 root root 4238 Oct 11  2023 README.md
                                                                                                      drwxr-xr-x 3 root root 4096 Oct 11  2023 cmd
                                                                                                      drwxr-xr-x 2 root root 4096 Oct 11  2023 docker
                                                                                                      -rw-r--r-- 1 root root 1330 Oct 11  2023 ext-authz.yaml
                                                                                                      -rw-r--r-- 1 root root 2369 Oct 11  2023 local-ext-authz.yaml
                                                                                                      
                                                                                                      cat istio-$ISTIOV/samples/extauthz/ext-authz.yaml
                                                                                                      apiVersion: v1
                                                                                                      kind: Service
                                                                                                      metadata:
                                                                                                        name: ext-authz
                                                                                                        labels:
                                                                                                          app: ext-authz
                                                                                                      spec:
                                                                                                        ports:
                                                                                                        - name: http
                                                                                                          port: 8000
                                                                                                          targetPort: 8000
                                                                                                        - name: grpc
                                                                                                          port: 9000
                                                                                                          targetPort: 9000
                                                                                                        selector:
                                                                                                          app: ext-authz
                                                                                                      ---
                                                                                                      apiVersion: apps/v1
                                                                                                      kind: Deployment
                                                                                                      metadata:
                                                                                                        name: ext-authz
                                                                                                      spec:
                                                                                                        replicas: 1
                                                                                                        selector:
                                                                                                          matchLabels:
                                                                                                            app: ext-authz
                                                                                                        template:
                                                                                                          metadata:
                                                                                                            labels:
                                                                                                              app: ext-authz
                                                                                                          spec:
                                                                                                            containers:
                                                                                                            - image: gcr.io/istio-testing/ext-authz:latest
                                                                                                              imagePullPolicy: IfNotPresent
                                                                                                              name: ext-authz
                                                                                                              ports:
                                                                                                              - containerPort: 8000
                                                                                                              - containerPort: 9000
                                                                                                      
                                                                                                      kubectl apply -f istio-$ISTIOV/samples/extauthz/ext-authz.yaml -n istioinaction
                                                                                                      
                                                                                                      # 빠져나오기
                                                                                                      exit
                                                                                                      -----------------------------------
                                                                                                      
                                                                                                      # 설치 확인 : ext-authz
                                                                                                      kubectl get deploy,svc ext-authz -n istioinaction
                                                                                                      NAME                        READY   UP-TO-DATE   AVAILABLE   AGE
                                                                                                      deployment.apps/ext-authz   0/1     1            0           6s
                                                                                                      
                                                                                                      NAME                TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)             AGE
                                                                                                      service/ext-authz   ClusterIP   10.200.1.55   <none>        8000/TCP,9000/TCP   6s
                                                                                                      
                                                                                                      # 로그
                                                                                                      kubectl logs -n istioinaction -l app=ext-authz -c ext-authz -f
                                                                                                      
                                                                                                        1. 배포한 ext-authz 서비스는 아주 간단해서 들어온 요청x-ext-authz 헤더가 있고 그 값이 allow 인지만 검사한다.
                                                                                                        2. 이 헤더가 요청에 들어 있으면 요청허용되고, 들어 있지 않으면 요청은 거부된다.
                                                                                                        3. 요청의 다른 속성을 평가하도록 외부 인가 서비스를 직접 작성하거나, 상술한 기존 서비스 중 하나를 사용할 수 있다.
                                                                                                       
                                                                                                      9.5.2 이스티오에 외부 인가 설정하기 Configuring Istio for ExtAuthz
                                                                                                        • 이스티오가 새로운 외부 인가 서비스를 인식하도록 설정해야 한다.
                                                                                                        • 이를 위해서는 이스티오 meshconfig 설정에서 extensionProviders 를 설정해야 한다.
                                                                                                        • 이 설정은 istio-system 네임스페이스의 istio configmap에 있다.
                                                                                                        • 이 configmap 을 수정해 새 외부 인가 서비스에 대한 적절한 설정을 추가해보자.
                                                                                                      # includeHeadersInCheck (DEPRECATED)
                                                                                                      KUBE_EDITOR="nano" kubectl edit -n istio-system cm istio
                                                                                                      --------------------------------------------------------
                                                                                                      ...
                                                                                                          extensionProviders:
                                                                                                          - name: "sample-ext-authz-http"
                                                                                                            envoyExtAuthzHttp:
                                                                                                              service: "ext-authz.istioinaction.svc.cluster.local"
                                                                                                              port: "8000"
                                                                                                              includeRequestHeadersInCheck: ["x-ext-authz"]
                                                                                                      ...
                                                                                                      --------------------------------------------------------
                                                                                                      
                                                                                                      # 확인
                                                                                                      kubectl describe -n istio-system cm istio
                                                                                                      
                                                                                                       
                                                                                                      • 이스티오가 envoyExtAuthz 서비스의 HTTP 구현체새 확장 sample-ext-authz-http 를 인식하도록 설정했다. We’ve configured Istio to be aware of a new extension called sample-ext-authz-http, an HTTP implementation of the envoyExtAuthz service.
                                                                                                      • 이 서비스는 ext-authz.istioinaction.svc.cluster.local 에위치하는 것으로 정의했는데, 앞 절에서 봤던 쿠버네티스 서비스에 맞춘 것이다.
                                                                                                      • 외부 인가 서비스에 전달할 헤더를 구성할 수 있는데, 이 설정에서는 x-ext-authz 헤더를 전달한다. We can configure what headers to pass along to the ExtAuthz service: in this configuration, we pass along the x-ext-authz header.
                                                                                                      • 예제 외부 인가 서비스에서는 이 헤더인가 결과를 결정하는 데 사용한다. In our example ExtAuthz service, this header is used to determine an authorization result
                                                                                                      • 이 외부 인가 기능을 사용하기 위한 마지막 단계는 이 기능을 사용하도록 AuthorizationPolicy 리소스를 설정하는 것이다.
                                                                                                      • 어떻게 동작하는지 살펴보자.
                                                                                                       
                                                                                                      9.5.3. 커스텀 AuthorizationPolicy 리소스 사용하기 Using a custom AuthorizationPolicy resource
                                                                                                        • 앞 절에서는 action 이 DENY 혹은 ALLOW 인 AuthorizationPolicy 리소스를 만들었다.
                                                                                                        • 이 절에서는 action 이 CUSTOMAuthorizationPolicy 를 만들고 정확히 어떤 외부 인가 서비스를 사용할지 지정해본다.
                                                                                                      # 아래 AuthorizationPolicy 는 istioinaction 네임스페이스에 webapp 워크로드에 적용되며, 
                                                                                                      # sample-ext-authz-http 이라는 외부 인가 서비스에 위임한다.
                                                                                                      cat << EOF | kubectl apply -f -
                                                                                                      apiVersion: security.istio.io/v1beta1
                                                                                                      kind: AuthorizationPolicy
                                                                                                      metadata:
                                                                                                        name: ext-authz
                                                                                                        namespace: istioinaction
                                                                                                      spec:
                                                                                                        selector:
                                                                                                          matchLabels:
                                                                                                            app: webapp
                                                                                                        action: CUSTOM    # custom action 사용 -> 우선순위 가장 높음
                                                                                                        provider:
                                                                                                          name: sample-ext-authz-http  # istio-system ns의 istio cm의 meshconfig 이름과 동일해야 한다
                                                                                                        rules:
                                                                                                        - to:
                                                                                                          - operation:
                                                                                                              paths: ["/*"]  # 인가 정책을 적용할 경로
                                                                                                      EOF
                                                                                                      
                                                                                                      #
                                                                                                      kubectl get AuthorizationPolicy -A
                                                                                                      NAMESPACE       NAME        AGE
                                                                                                      istioinaction   ext-authz   5s
                                                                                                       
                                                                                                        • 호출 확인
                                                                                                        #
                                                                                                        docker exec -it myk8s-control-plane istioctl proxy-config log deploy/webapp -n istioinaction --level rbac:debug
                                                                                                        kubectl logs -n istioinaction -l app=webapp -c istio-proxy -f
                                                                                                        kubectl logs -n istioinaction -l app=ext-authz -c ext-authz -f
                                                                                                        
                                                                                                        
                                                                                                        # 헤더 없이 호출 -> 호출 실패
                                                                                                        kubectl -n default exec -it deploy/sleep -- curl webapp.istioinaction/api/catalog
                                                                                                        denied by ext_authz for not found header `x-ext-authz: allow` in the request
                                                                                                        
                                                                                                        kubectl logs -n istioinaction -l app=webapp -c istio-proxy -f
                                                                                                        2025-05-10T17:38:08.526119Z     debug   envoy rbac external/envoy/source/extensions/filters/http/rbac/rbac_filter.cc:114        checking request: requestedServerName: , sourceIP: 10.10.0.16:33928, directRemoteIP: 10.10.0.16:33928, remoteIP: 10.10.0.16:33928,localAddress: 10.10.0.18:8080, ssl: none, headers: ':authority', 'webapp.istioinaction'
                                                                                                        ':path', '/api/catalog'
                                                                                                        ':method', 'GET'
                                                                                                        ':scheme', 'http'
                                                                                                        'user-agent', 'curl/8.5.0'
                                                                                                        'accept', '*/*'
                                                                                                        'x-forwarded-proto', 'http'
                                                                                                        'x-request-id', '02c01fc9-9730-97c7-b0a0-6b33b279a35a'
                                                                                                        , dynamicMetadata:      thread=31
                                                                                                        2025-05-10T17:38:08.526167Z     debug   envoy rbac external/envoy/source/extensions/filters/http/rbac/rbac_filter.cc:130        shadow denied, matched policy istio-ext-authz-ns[istioinaction]-policy[ext-authz]-rule[0]  thread=31
                                                                                                        2025-05-10T17:38:08.526177Z     debug   envoy rbac external/envoy/source/extensions/filters/http/rbac/rbac_filter.cc:167        no engine, allowed by default   thread=31
                                                                                                        [2025-05-10T17:38:08.525Z] "GET /api/catalog HTTP/1.1" 403 UAEX ext_authz_denied - "-" 0 76 10 9 "-" "curl/8.5.0" "02c01fc9-9730-97c7-b0a0-6b33b279a35a" "webapp.istioinaction" "-" inbound|8080|| - 10.10.0.18:8080 10.10.0.16:33928 - -
                                                                                                        
                                                                                                        kubectl logs -n istioinaction -l app=ext-authz -c ext-authz -f
                                                                                                        2025/05/10 17:38:08 [HTTP][denied]: GET webapp.istioinaction/api/catalog, headers: map[Content-Length:[0] X-B3-Parentspanid:[3b627d52ed57a502] X-B3-Sampled:[1] X-B3-Spanid:[5e45bc51d71d84f1] X-B3-Traceid:[8377da371db013c24025cc4e745569fd] X-Envoy-Expected-Rq-Timeout-Ms:[600000] X-Envoy-Internal:[true] X-Forwarded-Client-Cert:[By=spiffe://cluster.local/ns/istioinaction/sa/default;Hash=b1a1d5a2aba395b565076483d7cd7e0357312ac3ce3900fb22d855182b821a8f;Subject="";URI=spiffe://cluster.local/ns/istioinaction/sa/webapp] X-Forwarded-For:[10.10.0.18] X-Forwarded-Proto:[https] X-Request-Id:[dafc0ab1-1d39-9fae-9854-1919f8341484]], body: []
                                                                                                        
                                                                                                        # 헤더 적용 호출 -> 호출 성공!
                                                                                                        kubectl -n default exec -it deploy/sleep -- curl -H "x-ext-authz: allow" webapp.istioinaction/api/catalog
                                                                                                        [{"id":1,"color":"amber","department":"Eyewear","name":"Elinor Glasses","price":"282.00"},{"id":2,"color":"cyan","department":"Clothing","name":"Atlas Shirt","price":"127.00"},{"id":3,"color":"teal","department":"Clothing","name":"Small Metal Shoes","price":"232.00"},{"id":4,"color":"red","department":"Watches","name":"Red Dragon Watch","price":"232.00"}]
                                                                                                        
                                                                                                        kubectl logs -n istioinaction -l app=webapp -c istio-proxy -f
                                                                                                        2025-05-10T17:39:37.873826Z     debug   envoy rbac external/envoy/source/extensions/filters/http/rbac/rbac_filter.cc:114        checking request: requestedServerName: , sourceIP: 10.10.0.16:43402, directRemoteIP: 10.10.0.16:43402, remoteIP: 10.10.0.16:43402,localAddress: 10.10.0.18:8080, ssl: none, headers: ':authority', 'webapp.istioinaction'
                                                                                                        ':path', '/api/catalog'
                                                                                                        ':method', 'GET'
                                                                                                        ':scheme', 'http'
                                                                                                        'user-agent', 'curl/8.5.0'
                                                                                                        'accept', '*/*'
                                                                                                        'x-ext-authz', 'allow'
                                                                                                        'x-forwarded-proto', 'http'
                                                                                                        'x-request-id', 'adb34fec-6c81-96e1-aee1-edfa54eeb92e'
                                                                                                        , dynamicMetadata:      thread=30
                                                                                                        2025-05-10T17:39:37.873867Z     debug   envoy rbac external/envoy/source/extensions/filters/http/rbac/rbac_filter.cc:130        shadow denied, matched policy istio-ext-authz-ns[istioinaction]-policy[ext-authz]-rule[0]  thread=30
                                                                                                        2025-05-10T17:39:37.873912Z     debug   envoy rbac external/envoy/source/extensions/filters/http/rbac/rbac_filter.cc:167        no engine, allowed by default   thread=30
                                                                                                        [2025-05-10T17:39:37.883Z] "GET /items HTTP/1.1" 200 - via_upstream - "-" 0 502 5 5 "-" "beegoServer" "adb34fec-6c81-96e1-aee1-edfa54eeb92e" "catalog.istioinaction:80" "10.10.0.17:3000" outbound|80||catalog.istioinaction.svc.cluster.local 10.10.0.18:49608 10.200.1.51:80 10.10.0.18:43802 - default
                                                                                                        [2025-05-10T17:39:37.873Z] "GET /api/catalog HTTP/1.1" 200 - via_upstream - "-" 0 357 16 8 "-" "curl/8.5.0" "adb34fec-6c81-96e1-aee1-edfa54eeb92e" "webapp.istioinaction" "10.10.0.18:8080" inbound|8080|| 127.0.0.6:49583 10.10.0.18:8080 10.10.0.16:43402 - default
                                                                                                        
                                                                                                        kubectl logs -n istioinaction -l app=ext-authz -c ext-authz -f
                                                                                                        2025/05/10 17:39:37 [HTTP][allowed]: GET webapp.istioinaction/api/catalog, headers: map[Content-Length:[0] X-B3-Parentspanid:[79bc7deefa2c66ca] X-B3-Sampled:[1] X-B3-Spanid:[1979fa7b87e7c2b5] X-B3-Traceid:[8ae03168d35e59c4dc28008b6fa04179] X-Envoy-Expected-Rq-Timeout-Ms:[600000] X-Envoy-Internal:[true] X-Ext-Authz:[allow] X-Forwarded-Client-Cert:[By=spiffe://cluster.local/ns/istioinaction/sa/default;Hash=b1a1d5a2aba395b565076483d7cd7e0357312ac3ce3900fb22d855182b821a8f;Subject="";URI=spiffe://cluster.local/ns/istioinaction/sa/webapp] X-Forwarded-For:[10.10.0.18] X-Forwarded-Proto:[https] X-Request-Id:[7a161f7b-db40-961e-bce3-479183b485bb]], body: []
                                                                                                        
                                                                                                        image
                                                                                                          1. jaeger UI에서 볼 수 있듯이 중간에 authorization을 위해서 ext-authz 서비스에 요청을 한 것을 볼 수 있다.
                                                                                                          2. "x-ext-authz: allow" header가 있을 경우만 webapp workload의 “/*” 에 대해서 접근을 ALLOW 한다.
                                                                                                        9.5.4. Summary
                                                                                                        • PeerAuthentication피어인증을 정의하는 데 사용하며, 엄격한 인증 요구 사항을 적용하면 트래픽암호화돼 도청할 수 없다.
                                                                                                          • PERMISSIVE 정책은 이스티오 워크로드가 암호화된 트래픽과 평문 트래픽을 모두 수용할 수 있게 해서 다운타임 없이 천천히 마이그레이션할 수 있도록 해준다.
                                                                                                        • AuthorizationPolicy 는 워크로드 ID 인증서나 최종 사용자 JWT에서 추출한 검증 가능한 메타데이터를 근거로 서비스 사이의 요청이나 최종 사용자의 요청을 인가(허용, 차단)하는 데 사용한다.
                                                                                                        • RequestAuthentication 은 JWT가 포함된 최종 사용자 요청을 인증하는 데 사용한다.
                                                                                                        • AuthorizationPolicy 에서 CUSTOM action을 사용하면 외부 인가 서비스를 통합할 수 있다.
                                                                                                      Designed by Tistory.