5주차 - microservice 통신보안

bocopile·2025년 5월 10일

istio

목록 보기
5/5
post-thumbnail

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

1) 서비스간 인증

서비스 간 통신이 많은 마이크로서비스 환경에서, 각 서비스는 자신이 통신하는 상대 서비스가 “정말 그 서비스가 맞는지” 확인할 필요가 있습니다. 이때 사용하는 것이 SPIFFE (Secure Production Identity Framework For Everyone) 프레임워크입니다.

  • SPIFFE는 짧은 수명의 암호화된 ID 문서(SVID) 를 발급해 서비스에게 고유한 신원을 부여합니다.
  • 이 ID는 TLS 연결이나 JWT 서명 등에 사용되며, 통신 중 상대 서비스의 신원을 검증합니다.
  • Istio는 이 SPIFFE 기반의 ID를 자동으로 발급하고 관리하여, 보안 통신의 신뢰 기반을 구축합니다.

2) 최종 사용자 인증 (End-user Authentication) - JWT

사용자 인증은 사용자 데이터 보호의 핵심입니다. 일반적인 인증 흐름은 다음과 같습니다

  1. 사용자는 인증 서버(예: OAuth, OpenID Connect)에 로그인
  2. 인증 서버는 사용자 정보가 담긴 자격 증명(JWT 또는 쿠키) 을 발급
  3. 사용자는 이 자격 증명을 서비스에 제시
  4. 서비스는 자격 증명의 유효성을 인증 서버에 확인

Istio 환경에서도 JWT 기반 인증이 쉽게 통합되며, Istio의 Policy 설정을 통해 인증 흐름을 자동화할 수 있습니다.

3) 인가 (Authorization)

서비스가 사용자의 신원을 확인했다면, 다음 단계는 “이 사용자가 이 작업을 할 수 있는가?”입니다. 이를 인가(Authorization)라고 하며, 다음과 같은 방식으로 동작합니다

  • 인증된 ID가 접근하려는 리소스에 대해 어떤 역할 또는 권한을 가졌는지를 검사
  • Istio는 서비스 간 또는 사용자 요청에 대해 세분화된 인가 정책(RBAC) 을 적용할 수 있습니다

예시) 특정 사용자만 /admin API에 접근 가능하도록 제한

4) 모놀리스 VS 마이크로 서비스

모놀리스 아키텍처

  • 정적 환경(VM, 온프레미스)
  • 고정 IP 주소 기반의 인증이 유효
  • 보통 방화벽 규칙이나 SSL 인증서에 IP를 사용

마이크로서비스 아키텍처

  • 동적 환경(클라우드, 컨테이너 오케스트레이션)
  • 서비스 수명 짧고, 위치 유동적
  • 서비스는 멀티 클라우드 또는 하이브리드 환경에서 운영 가능
  • IP는 신뢰할 수 없는 ID 수단

이런 문제를 해결해 고도로 동적이고 이질적인 환경에서 ID를 제공하고자 이스티오는 SPIFFE 사양을 사용한다.

SPIFFE는 고도로 동적이고 이질적인 환경에서 워크로드에 ID를 제공하기 위한 일렬의 오픈소스 표준이다.

5) Istio가 SPIFFE를 구현하는 방법

Istio는 각 워크로드(서비스)마다 SPIFFE ID를 발급합니다. 이 ID는 다음 형식의 URI로 표현됩니다:

spiffe://<trust-domain>/<path>
  • trust-domain: 조직이나 프로젝트 단위의 ID 발급자
  • path: 해당 워크로드를 식별하는 값 (예: 서비스 어카운트 기반)

Istio는 이 SPIFFE ID를 X.509 인증서 형태로 인코딩한 SVID(Spiffe Verifiable Identity Document)를 생성해 제공합니다. 이 인증서는 mTLS 통신에서 사용되며, 서비스 간 트래픽의 암호화 및 인증 기반이 됩니다.

6) istio 보안 핵심 요소

istio에서는 보안 정책을 아래 세가지 커스텀 리소스를 통해 선언적으로 관리합니다.

PeerAuthentication

서비스간 트래픽을 인증(mTLS) 하기 위한 설정입니다.

→ 인증 성공 시, 상대 서비스의 SPIFFE ID 정보를 추출해 인가 판단에 활용할 수 있습니다.

apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
spec:
  mtls:
    mode: STRICT

RequestAuthentication

최종 사용자 인증(JWT 등)을 위한 설정입니다.

→ 사용자 자격증명에서 사용자 정보를 추출하여 인가에 활용합니다.

apiVersion: security.istio.io/v1beta1
kind: RequestAuthentication
spec:
  jwtRules:
    - issuer: "https://secure.bocopile.com"
      jwksUri: "https://secure.bocopile.com/.well-known/jwks.json"

AuthorizationPolicy

인가 판단을 담당하는 정책입니다.

위 두 리소스에서 추출된 정보 (SPIFFE ID , JWT claims 등)를 기반으로 접근 허용 여부를 결정합니다.

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
spec:
  rules:
    - from:
        - source:
            principals: ["spiffe://test.bocopile.io/ns/default/sa/frontend"]

→ 인증된 신원 정보는 Envoy 필터 메타데이터에 저장되어, 이후 인가 판단에 참조됩니다.

Istio 보안 아키텍처 요약

아래 아키텍처는 Istio 보안의 흐름을 한눈에 보여줍니다

  • Istio CA: 키와 X.509 인증서(SVID)를 생성하며, SAN에 SPIFFE ID를 포함
  • Istiod: mTLS 인증과 인가 정책을 모든 사이드카 프록시로 배포
  • Sidecar(Envoy Proxy): 실제 트래픽에 대해 정책을 검증, 인가, 암호화

9.2 자동 상호 TLS

1) 들어가며

서비스 메시 환경에서는 프록시가 주입된 서비스들 간의 트래픽이 기본적으로 암호화되고, 상호 인증이 이루어집니다. 하지만 여기서 중요한 점은, 이 모든 과정의 핵심인 인증서 발급과 로테이션(갱신)을 자동화하는 것입니다.

과거에는 인증서를 수동으로 관리하면서 실수로 인한 서비스 중단이 빈번하게 발생했으며, 이는 운영 비용 상승으로 이어졌습니다. 하지만 Istio와 같은 서비스 메시 프레임워크는 이러한 인증서 관리를 자동화하여 오류를 최소화하고 서비스 중단을 예방할 수 있도록 지원합니다.

아래 그림은 Istio의 컨트롤 플레인에서 발급한 인증서를 사용해 서비스 간 인증과 트래픽 암호화를 수행하는 구조를 보여줍니다.

서비스 간 인증은 SVID(Spiffe Verifiable Identity Document) 인증서를 통해 이루어지며, 서비스 메시의 보안 기반을 형성합니다.

하지만 기본값은 아니다?

Istio를 설치하더라도, 서비스 간 트래픽이 자동으로 상호 인증되지는 않습니다. 이는 모든 트래픽을 상호 인증으로 제한하면, 점진적인 메시 도입이 어려운 대규모 조직에서 문제가 생길 수 있기 때문입니다. 다양한 팀이 독립적으로 운영하는 상황에서는 메시 전체 도입까지 긴 시간이 필요하고, 이를 고려해 설치 시 기본값은 보수적으로 설정되어 있습니다.

최소 권한 원칙과 서비스 간 정책

서비스 간 인증이 가능해지면, 다음 단계는 정책 기반 인가(Authorization) 입니다. 즉, 각 서비스가 필요한 리소스에만 접근하도록 최소 권한을 설정하는 것입니다.

이는 보안상 매우 중요한 개념입니다. 만약 인증서가 외부로 유출되더라도, 그 인증서가 접근 가능한 서비스 범위만큼으로 피해를 제한할 수 있기 때문입니다. ID 기반 정책 제어를 통해 더 견고한 보안 아키텍처를 구축할 수 있습니다.

2) 환경 설정 하기

mTLS 기능 실습을 위해 아래와 같이 3가지 서비스를 준비합니다.

  • sleep 서비스를 추가 : 레거시 워크로드로, 사이드카 프록시가 없어서 상호 인증을 할 수 없습니다.

실습 환경 설정

  • 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
    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

기본 통신 확인

  • 레거시 sleep 워크로드 → webapp 워크로드로 평문 요청 실행

    watch 'kubectl exec deploy/sleep -c sleep -- curl -s webapp.istioinaction/api/catalog -o /dev/null -w "%{http_code}\n"'
    • 키알리 확인 : unknown → webapp 구간은 평문 통신

응답이 성공했다는 것은 서비스들이 올바르게 준비됐으며 webapp 서비스가 sleep 서비스의 평문 요청을 받아들였다는 사실을 보여줍니다.

그러나 PerAuthentication 리로스로 평문 트래픽을 금지할수 있습니다.

3) 이스티오의 PerAuthentication 리소스 이해하기

PeerAuthentication란?

Istio의 PeerAuthentication 리소스는 워크로드 간 통신에서 mTLS를 어떻게 적용할지를 제어합니다. 주요 모드는 다음과 같습니다:

  • STRICT: 오직 암호화된(mTLS) 트래픽만 허용
  • PERMISSIVE: 암호화된 트래픽과 평문 트래픽을 모두 허용
  • DISABLE: 암호화 없이 평문만 허용
  • UNSET: 상위 정책을 따름

정책 적용 범위는 세 가지입니다:

범위설명
Mesh-wide메시 전체에 적용 (istio-system/default)
Namespace-wide네임스페이스에 적용
Workload-specific레이블로 지정한 워크로드에만 적용

메시 범위 정책으로 모든 미인증 트래픽 거부하기

서비스 메시 전반에 mTLS를 강제하려면 istio-system 네임스페이스에 default 이름의 정책을 STRICT 모드로 적용합니다.

  • 설정 파일 확인

    cat ch9/meshwide-strict-peer-authn.yaml

  • 적용

    kubectl apply -f ch9/meshwide-strict-peer-authn.yaml -n istio-system
  • 적용 확인

    kubectl get PeerAuthentication -n istio-system

  • 요청 실행 - 에러 발생

    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

특정 네임스페이스에 예외 허용

레거시 워크로드 등 메시 외부에서 오는 트래픽도 있기 때문에 PERMISSIVE 모드를 네임스페이스에 적용해 예외를 줄 수 있습니다.

  • PeerAuthentication 적용

    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 get PeerAuthentication -A

  • 요청 실행 - 200 OK

    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

  • 다음 실습을 위한 PeerAuthentication 삭제

    kubectl delete pa default -n istioinaction

워크로드별 PeerAuthentication 정책 적용

네임스페이스 전체가 아닌 특정 워크로드만 PERMISSIVE로 설정하려면 selector를 사용합니다.

  • myk8s-control-plane 서비스 프록시 상태 확인

    docker exec -it myk8s-control-plane istioctl proxy-status

  • webapp PeerAuthentication 적용

    cat ch9/workload-permissive-peer-authn.yaml
    kubectl apply -f ch9/workload-permissive-peer-authn.yaml

  • 적용 확인

    kubectl get pa -A

  • webapp 요청 실행 - 200 OK

    kubectl exec deploy/sleep -c sleep -- curl -s http://webapp.istioinaction/api/catalog -o /dev/null -w "%{http_code}\n"

  • webapp 로그 확인

    kubectl logs -n istioinaction -l app=webapp -c webapp -f
    kubectl logs -n istioinaction -l app=webapp -c istio-proxy -f

  • catalog 요청 실행 - 502 에러 발생

    kubectl exec deploy/sleep -c sleep -- curl -s http://catalog.istioinaction/api/items -o /dev/null -w "%{http_code}\n"

  • catalog 로그 확인

    kubectl logs -n istioinaction -l app=catalog -c catalog -f
    kubectl logs -n istioinaction -l app=catalog -c istio-proxy -f

tcpdump로 서비스간 트래픽 스니핑 하기

istio Proxy에는 기본적으로 tcpdump가 설치되어 있습니다.

해당 도구는 네트워크 인터페이스를 통과하는 네트워크 트래픽을 포착하고 분석합니다.

tcpdump 는 보안 때문에 권한 privileged permission 이 필요하나 기본적으로 이 권한은 꺼져 있습니다.

해당 권한을 부여하기 위해선 values.global.proxy.privileged=true 로 설정해 이스티오 설치를 업데이트를 진행해야 합니다.

해당 실습에선 이미 해당 권한을 부여 하였으므로 해당 작업을 스킵하고 진행합니다.

  • 확인

     kubectl get istiooperator -n istio-system installed-state -o yaml
     kubectl get pod -n istioinaction -l app=webapp -o json

  • 기타 확인

    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
    kubectl exec -it -n istioinaction deploy/webapp -c istio-proxy -- sudo tcpdump -h

  • 패킷 모니터링 실행

    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)'

  • 요청 실행

    kubectl exec deploy/sleep -c sleep -- curl -s webapp.istioinaction/api/catalog -o /dev/null -w "%{http_code}\n"

워크로드 ID가 워크로드 Service Account에 연결되어 있는지 확인

openssl 명령어를 사용해 catalog 워크로드의 X.509 인증서 내용물을 확인 합니다.

  • 패킷 모니터링 실행

    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

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와 인증서 체인 확인

    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

  • catalog 파드의 Service Account 확인

    kubectl describe pod -n istioinaction -l app=catalog | grep 'Service Account'

  • 루트 인증서 서명 확인 - 검증에 성공하여 OK 메세지 출력

    # 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)

    • Istio CA가 인증서에 서명했으며, 내부 데이터가 신뢰된 데이터임을 알려줌

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

1) Istio에서 인가 처리 방식

istio는 AuthorizationPolicy라는 리소스를 통해 인가 정책을 정의합니다.

  • 서비스 프록시(Envoy) 가 정책의 집행자 역할을 합니다.

  • 각 서비스 옆에 배포된 프록시는 요청을 허용하거나 거부할지 로컬에서 직접 판단합니다.

  • 해당 작업으로 인해 정책 평가 속도가 빠르고 효율적입니다.

  • 예시 - webapp 워크로드에서 /api/catalog* 경로로 들어오는 요청만 허용하도록 설정

    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

2) AuthorizationPolicy 속성 이해하기

AuthorizationPolicy는 세 가지 주요 필드로 구성됩니다.

  1. selector - 정책을 적용할 워크로드를 matchLabels 로 지정
  2. action - ALLOW(허용), DENY(거부), 사용자 정의(Custom)
  3. rules - 어떤 요청이 정책에 부합하는지 정의

3) rules 속성 상세

rules 안에는 세가지 세부 필드가 존재합니다.

  1. from : 요청의 출처를 지정합니다.
    • principals: SPIFFE ID (예: spiffe://cluster.local/ns/default/sa/webapp)
    • namespaces: 요청의 네임스페이스
    • ipBlocks: 특정 IP 또는 CIDR
    • (참고) mTLS가 활성화되어야 principals, namespaces 사용 가능
  2. to : 요청이 어디로 가는지 정의합니다.
    • Paths, methods, hosts 등을 지정 가능
  3. when : 요청 헤더, 클레임, 시간 조건 등 부가적인 조건등을 정의합니다.

4) 작업 공간 설정하기

  • 9.2 에서 이미 배포가 완료됨

5) 워크로드에 정책 적용시 동작 확인

워크로드에 하나 이상의 ALLOW 인가 정책이 적용되면,
모든 트래픽에서 해당 워크로드의 접근은 기본적으로 거부된다.

트래픽을 받아들이려면, ALLOW 정책이 최소 하나는 부합해야 합니다.

예를 들면 다음 AuthorizationPolicy 리소스는 webapp 으로의 요청 중 HTTP 경로에 /api/catalog* 가 포함된 것을 허용합니다.

  • 설정 파일 확인

    cat ch9/allow-catalog-requests-in-web-app.yaml

  • 적용전 요청 테스트 - /hello/world 404 에러 발생

    kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction/api/catalog
    kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction/hello/world

  • AuthrizationPolicy 리소스 적용

    kubectl apply -f ch9/allow-catalog-requests-in-web-app.yaml
  • 리소스 적용 확인

    kubectl get authorizationpolicy -n istioinaction

  • listener 확인

    docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/webapp.istioinaction --port 15006 -o json > webapp-listener.json
    cat webapp-listener.json

  • 요청 테스트 진행 - 403 에러 발생

    kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction/hello/world

  • kiali 확인

위의 예시 처럼 /api/catalog* ALLOW 정책을 워크로드에 적용했을 때 /hello/world 기본적으로 거부 되는 것을 확인 할수 있습니다.

  • 적용 리소스 삭제

    kubectl delete -f ch9/allow-catalog-requests-in-web-app.yaml

6) 전체 정책으로 모든 요청 거부 하기

보안성을 증가시키고, 과정을 단순화하기 위하여,
ALLOW 정책을 명시적으로 지정하지 않는 모든 요청을 거부하도록 정의해야 합니다.

  • 신규 적용 할 정책 확인

     cat ch9/policy-deny-all-mesh.yaml

  • 정책 적용 전 요청 테스트 - 200 OK

    kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction/api/catalog
    curl -s http://webapp.istioinaction.io:30000/api/catalog

  • 정책 적용

    kubectl apply -f ch9/policy-deny-all-mesh.yaml
  • 정책 적용 확인

    kubectl get authorizationpolicy -A

  • 정책 적용 후 요청 테스트 - 403 에러 발생

    kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction/api/catalog

  • 로그 확인

    kubectl logs -n istioinaction -l app=webapp -c istio-proxy -f

  • 정책 적용 후 요청 테스트2 - 403 에러 발생

    curl -s http://webapp.istioinaction.io:30000/api/catalog

  • 로그 확인

    kubectl logs -n istio-system -l app=istio-ingressgateway -f

7) 특정 네임스페이스에서 온 요청 허용

이번에는 네임스페이스에서 시작한, 모든 서비스에 대한 트래픽을 허용하고자 합니다.

해당 설정은 source.namespace 속성으로 진행할수 있습니다.

  • 정책 적용

    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

  • listener 확인

    docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/webapp.istioinaction --port 15006 -o json

현재 적용한 sleep은 레거시 워크로드입니다.

따라서 사이드카가 존재하지 않으며, ID도 없습니다.

그러므로 webapp 프록시는 요청이 default 네임이스페이스의 워크로드에서 온 것인지 확인할 수 없습니다.

이를 해결하기 위해선 다음 중 하나를 선택해야 합니다.

  1. sleep 서비스에 서비스 프록시 주입하기 → 권장
  2. webapp에서 미인증 요청 허용하기

1번 방식으로 하는 것을 권장합니다.

그러나 실습을 위해 첫번째 방법이 불가능하단 가정하에 두번째 방법으로 진행하고자 합니다.

  • Envoy가 자동으로 주입(auto-injection) 되도록 설정

    kubectl label ns default istio-injection=enabled
  • sleep pod 제거

    kubectl delete pod -l app=sleep
  • 파드 상태 확인 - istiod 연결 확인

    docker exec -it myk8s-control-plane istioctl proxy-status

  • default → webapp 호출 테스트

    kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction

  • webapp → catalog 요청 - 500 에러 발생

    kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction/api/catalog

  • 로그 확인

    kubectl logs -n istioinaction -l app=webapp -c istio-proxy -f

  • default → catalog 호출 테스트 : 200 OK

    kubectl logs -n istioinaction -l app=catalog -c istio-proxy -f
    kubectl exec deploy/sleep -- curl -sSL catalog.istioinaction/items

  • 다음 실습을 위해 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

8) 미인증 레거시 워크로드에서 온 요청 허용하기

이번엔 미인증 워크로드에서 온 요청을 허용하고자 합니다.

해당 정책을 적용하기 위해서 from 필드를 삭제하고 반영해야 합니다.

아래 정책을 webapp에만 적용하기 위해 app:webapp 샐럭터를 추가합니다.

이렇게 하면 catalog 서비스에는 여전히 상호 인증이 필요합니다.

  • 신규 적용 할 정책 확인

    cat ch9/allow-unauthenticated-view-default-ns.yaml

  • 정책 적용

    kubectl apply -f ch9/allow-unauthenticated-view-default-ns.yaml
  • 정책 확인

    kubectl get AuthorizationPolicy -A

  • listener 확인

    docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/webapp.istioinaction --port 15006 -o json | jq

  • 호출 테스트 - webapp : 200 OK

    kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction # default -> webapp 은 성공
  • 로그 확인

    kubectl logs -n istioinaction -l app=webapp -c istio-proxy -f

  • 호출 테스트 - catalog : 500 에러 발생

    kubectl exec deploy/sleep -- curl -sSL webapp.istioinaction/api/catalog

webapp 은 sleep 서비스에서 요청을 허용했지만, 메시 범위 전체 거부 정책이 catalog 서비스로의 후속 요청을 거부 하였습니다.

9) 특정 서비스 어카운트에서 온 요청 허용 하기

트래픽이 webapp 서비스에 왔는지 인증할 수 있는 가장 간단한 방법은 트래픽에 주입된 서비스 어카운트를 사용하는 것입니다.

서비스 어카운트 정노는 SVID에 인코딩돼 있으며, 상호 인증 중에 그 정보를 검증하고 필터 메타데이터에 저장합니다.

해당 실습에서는 catalog 서비스가 필터 메타데이터를 사용해 서비스 어카운트가 webapp인 워크로드에서 온 트래픽만 허용하도록 설정 합니다.

  • 신규 적용할 정책 확인

    cat ch9/catalog-viewer-policy.yaml

  • 정책 적용

    kubectl apply -f ch9/catalog-viewer-policy.yaml
  • 정책 확인

    kubectl get AuthorizationPolicy -A

  • listener 확인

    docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/catalog.istioinaction --port 15006 -o json | jq

  • 호출 테스트 - 200OK

    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
    kubectl logs -n istioinaction -l app=catalog -c istio-proxy -f

10) 정책의 조건부 허용

정책 중 일부는 특정 조건이 충족 되는 경우에만 적용이 되기도 합니다.

예를 들면 사용자가 관리자일 때는 모든 작업이 허용되도록 적용할 수 있습니다.

이는 인가 정책의 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"] # 반드시 일치해야 하는 값의 목록을 지정한다

이 정책은 다음 두 조건이 모두 충족될 때만 요청을 허용합니다.

  1. 토큰은 요청 주체 auth@istioinaction.io/* 가 발급한 것
  2. JWT에 값이 ‘admin’인 group claim이 포함될 것

또는 notValues 속성을 사용하여 이 정책을 적용하지 않아야 하는 값들을 정의할 수도 있습니다.

11) 값 비교 표현식 이해하기

Istio에서는 인가 정책의 조건을 정할 때, 단순한 문자열 비교 외에도 여러 비교 방식이 가능합니다

이를 통해 보다 유연하고 정확한 정책 정의가 가능합니다.

비교 표현식 종류

  • Exact: 정확히 일치하는 값 (예: method: GET)
  • Prefix: 접두사 비교 (예: /api/catalog*/api/catalog/1 등과 매치)
  • Suffix: 접미사 비교 (예: .istioinaction.iologin.istioinaction.io 등과 매치)
  • Presence: 값이 존재하면 허용, 값 자체는 중요하지 않음 (로 표기)

인가 정책 규칙이 어떻게 평가되는가?

istio에서는 하나의 정책 안에서도 여러 규칙을 정의할 수 있으며, 각 규칙은 다중조건을 포함합니다.

이를 통해 다층적인 제어가 가능합니다.

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, to, when은 AND 조건으로 모두 만족해야 적용됩니다.
  • to 안에 여러 operation이 있으면 OR 조건으로 평가됩니다.
  • from도 여러 소스가 있을 수 있으며 OR로 평가되지만, 전체 조건은 AND로 묶입니다.
  • when 조건은 모두 일치해야 적용됩니다.

12) 인가 정책이 평가되는 순서 이해하기

Istio는 priority 필드를 따로 두지 않고, 정해진 순서대로 정책을 평가합니다.

평가 순서

  • CUSTOM policies
    • 외부 인가 서버와 연동 시 사용
  • DENY policies
    • 요청 차단 정책. 하나라도 매치되면 즉시 거부
  • ALLOW policies
    • DENY가 없을 경우, 하나라도 매치되면 허용
  • Catch-all 정책 존재 여부
    • ALLOW 정책이 없거나 매치 안 될 경우
      • Catch-all(ALLOW)이 있으면 그에 따름
      • 없으면 기본 허용 혹은 기본 거부로 분기

참고 이미지

  • 정책 간 우선순위가 없기 때문에 정책 조합이 복잡할수록 흐름도가 유용합니다.
  • DENY 정책을 명시적으로 설정하면 전체 흐름이 단순해집니다.

정책 흐름도

요청이 허용/거부 될지 시각적으로 표현한 흐름도는 다음과 같습니다.

9.4 최종 사용자 인증 및 인가

서비스 간 인증 이외에도 최종 사용자의 인증과 인가도 중요합니다.

Istio는 JWT를 사용한 최종 사용자 인증을 지원합니다.

1) JWT 어떻게 발행하고 검증하는가?

JWT(JSON Web Token)는 클라이언트-서버 간 인증을 위한 대표적인 방식으로, 토큰 기반 인증 시스템에서 널리 사용됩니다.

JWT 발급 과정

  • 클라이언트가 인증 요청 (ex. 사용자 로그인).
  • 인증 서버는 사용자 인증 후, 고유한 사용자 정보를 담은 JWT를 생성.
  • 이 JWT는 인증 서버가 소유한 비밀 키(private key) 로 디지털 서명됨.
  • 클라이언트는 이 서명된 JWT를 응답으로 전달받아 저장(보통 Authorization 헤더에 포함해 사용).

JWT 검증 과정

  • 클라이언트는 서비스 요청 시 JWT를 함께 전송.
  • 서비스 서버는 JWT의 서명을 검증하기 위해 인증 서버의 공개 키(public key) 가 필요함.
  • 이 공개 키는 JWKS (JSON Web Key Set) 형태로, well-known HTTP 엔드포인트에 노출되어 있음.
  • 서비스 서버는 이 엔드포인트에서 공개 키를 가져와 JWT 서명을 검증함.
  • 검증은 다음 방식으로 진행됨
    • 공개 키로 JWT 서명 부분을 복호화하여 서명 당시의 해시값을 얻음.
    • JWT 내부의 페이로드(payload)에 대해 동일한 방식으로 해시값을 다시 계산함.
    • 두 해시값이 일치하면 토큰이 변조되지 않았음을 확인, 토큰의 클레임(claim) 을 신뢰할 수 있음.

2) 인그레스 게이트웨이에서의 최종 사용자 인증 및 인가

Istio에서 JWT 기반 최종 사용자 인증/인가 요약

  • Istio는 JWT(JSON Web Token)를 이용해 최종 사용자 요청을 인증하고 인가할 수 있다.
  • 최종 사용자란, ID 제공자(IdP)에게 인증받아 ID와 클레임을 담은 토큰(JWT)을 발급받은 사용자이다.
  • *인가(Authorization)는 모든 워크로드 수준에서 설정할 수 있지만, 보통은 Istio Ingress Gateway에서 수행한다.
    • 이렇게 하면 유효하지 않은 요청을 게이트웨이에서 조기에 차단해 전체 시스템 성능이 향상된다.
    • 또한 JWT를 요청에서 제거함으로써, 이후 서비스에서 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

3) RequestAuthentication으로 JWT 검증 하기

들어가며

  • RequestAuthentication 리소스

    • 역할: 요청에 포함된 JWT를 검증하고, 유효한 토큰에서 클레임을 추출하여 필터 메타데이터에 저장합니다.
    • 인가 적용 여부: 스스로는 인가를 강제하지 않습니다.
  • 필터 메타데이터

    • 서비스 프록시 내 필터 간에 공유되는 키–값 쌍의 모음입니다.
    • AuthorizationPolicy가 인가 결정을 내릴 때 기준으로 사용됩니다.
  • 요청 처리 흐름

1. 유효한 JWT가 있는 경우
    - 요청이 클러스터로 전달되고, 추출된 클레임이 메타데이터에 저장되어 인가 정책에 활용됩니다.
2. 유효하지 않은 JWT가 있는 경우
    - 요청이 즉시 거부됩니다.
3. JWT가 없는 경우
    - 요청은 클러스터로 전달되지만, 메타데이터에 클레임이 없어 인가 정책에서 활용되지 않습니다.
  • 주의 사항
    • RequestAuthentication은 인증(토큰 검증 및 클레임 추출)만 담당하므로, 실제 인가(허용·거부)를 위해서는 반드시 별도의 AuthorizationPolicy를 정의해야 합니다.

RequestAuthentication 리소스 만들기

다음 RequestAuthentication 리소스는 Istio의 IngressGateway에 적용됩니다.

이는 IngressGateway가 uth@istioinaction.io 에서 발급한 토큰을 검증하도록 설정합니다.

  • 적용할 정책 확인

    cat ch9/enduser/jwt-token-request-authn.yaml
    

  • RequestAuthentication 적용

    kubectl apply -f ch9/enduser/jwt-token-request-authn.yaml
  • 적용 확인

    kubectl get requestauthentication -A

  • Listener 확인

    docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/istio-ingressgateway.istio-system --port 8080 -o json

유효한 발행자의 토큰이 있는 요청 테스트

  • JWT 확인

    cat ch9/enduser/user.jwt
    USER_TOKEN=$(< ch9/enduser/user.jwt)
    

  • JWT 디코딩 - https://jwt.io/ : webapp.istioinaction.io 도메인이 /etc/hosts에만 설정 되어있어 해당 사이트에서 디코딩시 유효하지 않는 도메인으로 인식

  • catalog 호출 - 200 OK

    curl -H "Authorization: Bearer $USER_TOKEN" \
         -sSl -o /dev/null -w "%{http_code}" webapp.istioinaction.io:30000/api/catalog

  • 로그 확인 - 워크로드에 적용된 AuthorizationPolicy가 없으므로 기본적으로 ALLOW가 됩니다.

    kubectl logs -n istio-system -l app=istio-ingressgateway -f

유효하지 않는 발행자의 토큰이 있는 요청 테스트

이번엔 인가되지 않는 JWT로 요청 테스트를 진행합니다.

  • JWT 확인 - 현재 설정한 정책의 발급자와 다름 issuer: "auth@istioinaction.io"

    cat ch9/enduser/not-configured-issuer.jwt
    WRONG_ISSUER=$(< ch9/enduser/not-configured-issuer.jwt)

  • 호출 - 401 에러 발생

    curl -H "Authorization: Bearer $WRONG_ISSUER" \
         -sSl -o /dev/null -w "%{http_code}" webapp.istioinaction.io:30000/api/catalog

  • 로그 확인

    kubectl logs -n istio-system -l app=istio-ingressgateway -f

토큰이 없는 요청 확인

  • 토큰 없이 curl 요청 실행 - 200 OK

    # 호출
    curl -sSl -o /dev/null -w "%{http_code}" webapp.istioinaction.io:30000/api/catalog

  • 로그 확인 - 응답코드 요청이 클러스터로 받아들여짐

    kubectl logs -n istio-system -l app=istio-ingressgateway -f

위와 같은 결과로 인해 토큰이 없는 요청을 거부하려면, 다음에 설명할 약간의 추가 작업이 필요합니다.

JWT가 없는 요청 거부하기

JWT가 없는 요청을 거부하려면 명시적으로 거부하는 AuthorizationPolicy 리소스를 만들어야 합니다.

해당 정책은 RequestPrincipals 속성이 없는 source에서 온 모든 요청에 적용되며, action 속성에 지정되는대로 요청을 거부합니다.

RequestPrincipals의 초기화 방식은 JWT의 발행자 issuer와 주체 subject 클래임을 ‘iss/sub’ 형태로 결합한 것입니다.

클레임은 RequestPrincipals 리소스로 인증되고, AuthorizationPolicy 필터 등 다른 필터가 사용할 . 수있도록 커넥션 메타데이터로 가공됩니다.

  • 적용할 정책 확인

    vi ch9/enduser/app-gw-requires-jwt.yaml -> port 30000 추가
    cat ch9/enduser/app-gw-requires-jwt.yaml 

  • 해당 정책 적용

    kubectl apply -f ch9/enduser/app-gw-requires-jwt.yaml
  • 정책 적용 확인

    kubectl get AuthorizationPolicy -A

  • listener 확인

    docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/istio-ingressgateway.istio-system --port 8080 -o json

  • 호출 1 테스트 (JWT 미인증) - 403 에러 발생

    curl -sSl -o /dev/null -w "%{http_code}" webapp.istioinaction.io:30000/api/catalog

  • 호출 2 테스트 (JWT 인증) - 200 OK

    curl -H "Authorization: Bearer $USER_TOKEN" \
         -sSl -o /dev/null -w "%{http_code}" webapp.istioinaction.io:30000/api/catalog

  • 로그 확인 - 403, 200 로그 확인

    kubectl logs -n istio-system -l app=istio-ingressgateway -f

JWT 클레임에 기반한 다양한 접근 수준

이번엔 유저별로 다른 정책을 설정하고자 합니다.

해당 예제에서는 일반 사용자가 API에서 데이터를 읽는 것은 허용하지만 새 데이터를 쓰거나 기존 데이터를 금지합니다.

물론 관리자에겐 모든 권한을 부여합니다.

  • 일반 사용자 - group : user

    cat ch9/enduser/user.jwt

  • 관리자 - group : admin

    cat ch9/enduser/admin.jwt

  • 일반 사용자가 webapp 에서 데이터를 읽을 수 있게 허용하도록 AuthorizationPolicy 리소스 설정

    vi ch9/enduser/allow-all-with-jwt-to-webapp.yaml # 포트 30000 추가
    cat ch9/enduser/allow-all-with-jwt-to-webapp.yaml

  • 관리자는 모든 작업을 허용하도록 AuthorizationPolicy 리소스 설정

    cat ch9/enduser/allow-mesh-all-ops-admin.yaml

  • 정책 적용

    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

  • listener 확인

    docker exec -it myk8s-control-plane istioctl proxy-config listener deploy/istio-ingressgateway.istio-system --port 8080 -o json
    • 사용자

  • 관리자

  • 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 요청 - 200 OK / POST 요청 - RBAC access denied

  • 로그 확인

    kubectl logs -n istio-system -l app=istio-ingressgateway -f

  • 관리자 - GET / POST 요청 - 두개 요청 모두 200OK

    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
    
    curl -H "Authorization: Bearer $ADMIN_TOKEN" \
         -XPOST webapp.istioinaction.io:30000/api/catalog \
         --data '{"id": 2, "name": "Shoes", "price": "84.00"}'

  • 로그 확인

    kubectl logs -n istio-system -l app=istio-ingressgateway -f

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

1) 외부 Authentication 서비스 호출

istio Envoy의 기본 RBAC 기능을 사용해 인가를 구현합니다.

그런데 Authentication를 좀 더 정교한 커스텀 메커니즘이 필요한 경우에는 어떻게 해야 할까?

요청을 허용할지 여부를 결정할 때 외부 Authentication 서비스를 호출하도록 istio 서비스 Proxy를 설정할 수 있습니다.

외부 Authentication 적용을 위해선 엔보이의 CheckRequest API를 구현해야 합니다.

API를 구현하는 외부 서비스에는 다음과 같습니다.

외부 인가 서비스는 프록시가 인가를 집행하는 데 사용하는 ‘허용’이나 ‘거부’ 메시지를 반환합니다.

2) 외부 Authentication 실습

  • 실습 환경 초기화

    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
  • istio 샘플에서 샘플 외부 Authentication 배포

    docker exec -it myk8s-control-plane bash
    -----------------------------------
    cat istio-$ISTIOV/samples/extauthz/ext-authz.yaml
    kubectl apply -f istio-$ISTIOV/samples/extauthz/ext-authz.yaml -n istioinaction
    
    # 빠져나오기
    exit
    -----------------------------------

  • 배포 확인

    kubectl get deploy,svc ext-authz -n istioinaction

  • 로그 모니터링 설정

    kubectl logs -n istioinaction -l app=ext-authz -c ext-authz -f

배포한 ext-authz 서비스는 아주 간단해서 들어온 요청에 x-ext-authz 헤더가 있고 그 값이 allow 인지만 검사합니다.

해당 헤더가 요청에 들어 있으면 요청은 허용되고, 들어 있지 않으면 요청은 거부됩니다.

요청의 다른 속성을 평가하도록 외부 인가 서비스를 직접 작성하거나, 상술한 기존 서비스 중 하나를 사용할 수 있습니다.

Istio에 외부 Authentication 설정하기

istio가 새로운 외부 Authentication 서비스를 인식하도록 설정해야 합니다.

이를 위해서는 istio meshconfig 설정에서 extensionProviders 를 설정해야 합니다.

해당 설정은 istio-system 네임스페이스의 istio configmap에 존재합니다.

이 configmap을 수정하여 Authentication 서비스에 대한 적절한 설정을 추가한다.

  • includeHeadersInCheck 적용

    # includeHeadersInCheck (DEPRECATED)
    KUBE_EDITOR="vi" 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

istio envoyExtAuthz 서비스의 HTTP 구현체인 새 확장 sample-ext-authz-http 를 인식하도록 설정했습니다.

해당 서비스는 ext-authz.istioinaction.svc.cluster.local 위치하는 것으로 정의했는데, 앞 절에서 봤던 쿠버네티스 서비스에 맞추었습니다.

외부 Authentication 서비스에 전달할 헤더를 구성할 수 있는데, 이 설정에서는 x-ext-authz 헤더를 전달 합니다.

예제 외부 Authentication 서비스에서는 이 헤더를 인가 결과를 결정하는 데 사용 히며, 외부 Authentication 기능을 사용하기 위한 마지막 단계는 이 기능을 사용하도록 AuthorizationPolicy 리소스를 설정하는 것입니다.

3) 커스텀 AuthorizationPolicy 리소스 사용하기

action 이 CUSTOM 인 AuthorizationPolicy 를 만들고 정확히 어떤 외부 인가 서비스를 사용할지 지정해봅니다.

아래 AuthorizationPolicy 는 istioinaction 네임스페이스에 webapp 워크로드에 적용되며, sample-ext-authz-http 이라는 외부 인가 서비스에 위임합니다.

  • custom action 적용

    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  # meshconfig 이름과 동일해야 한다
      rules:
      - to:
        - operation:
            paths: ["/*"]  # 인가 정책을 적용할 경로
    EOF
    
    # 적용 확인
    kubectl get AuthorizationPolicy -A

  • rbac log level debug 변경

    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
  • 헤더 없이 호출 - denied 발생

    kubectl -n default exec -it deploy/sleep -- curl webapp.istioinaction/api/catalog

  • 로그 확인

  • 헤더 적용 호출 - 200 OK

    kubectl -n default exec -it deploy/sleep -- curl -H "x-ext-authz: allow" webapp.istioinaction/api/catalog

  • 로그 확인

profile
DevOps Engineer

0개의 댓글