Jaehong Jung

AuthorizationPolicy의 ext_authz를 Envoy config로 톺아보기

Istio AuthorizationPolicyaction: CUSTOM이 Envoy config에서 어떻게 동작하는지를 실제 envoy config dump 기반으로 분석합니다.

AuthorizationPolicy

# raw manifest
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: my-auth-ext
  namespace: istio-gateway
spec:
  action: CUSTOM
  provider:
    name: my-auth-ext
  rules:
  - to:
    - operation:
        hosts:
        - '*'
    when:
    - key: request.headers[x-account]
      values:
      - '*'
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: Gateway
    name: istio-public-ingress
---
# istiod chart
istiod:
  meshConfig:
    extensionProviders:
    - name: "my-auth-ext"
      envoyExtAuthzGrpc:
        service: my-auth-server.app.svc.cluster.local
        port: 4003

핵심 구조: 2개의 HTTP Filter

Istio는 action: CUSTOM AuthorizationPolicy를 2개의 Envoy HTTP filter로 변환합니다. 이 두 필터가 순서대로 연동되어 조건부 external authorization을 구현합니다.

Filter 1: RBAC Shadow Filter (apply할지 말지 판별)

0.0.0.0:443 active listener의 http_filter로 추가됩니다.

- name: envoy.filters.http.rbac
  typed_config:
    '@type': type.googleapis.com/envoy.extensions.filters.http.rbac.v3.RBAC
    shadow_rules: # ← "shadow" = 실제 트래픽 차단 없음
      action: DENY
      policies:
        istio-ext-authz-ns[istio-gateway]-policy[my-auth-ext]-rule[0]:
          permissions:
          - and_rules:
              rules:
              - or_rules:
                  rules:
                  - header:
                      name: :authority
                      present_match: true # ← hosts: ['*'] 매핑
          principals:
          - and_ids:
              ids:
              - or_ids:
                  ids:
                  - header:
                      name: x-account
                      present_match: true # ← when: x-account header 존재
    shadow_rules_stat_prefix: istio_ext_authz_

역할: shadow_rules를 사용하기 때문에 실제로 요청을 차단하지 않습니다. Shadow mode는 실제 요청에 영향을 주지 않고, stats만 emit하고 결과를 로깅하는 용도입니다. 여기서는 단순히 “이 요청이 조건에 매칭되는가?”만 판별하고, 그 결과를 dynamic metadata로 남깁니다.

AuthorizationPolicy → RBAC 매핑 테이블

AuthorizationPolicy 필드Envoy RBAC 변환
hosts: ['*']permissions:authority header present_match: true
when: request.headers[x-account] values: ['*']principalsx-account header present_match: true

매칭되면 RBAC filter는 dynamic metadata에 shadow_effective_policy_id 값을 기록합니다. 이 값은 policy 이름인 istio-ext-authz-ns[istio-gateway]-policy[my-auth-ext]-rule[0]이며, istio-ext-authz prefix를 가집니다.

Filter 2: ext_authz Filter (실제 인증 호출)

위 “Filter 1” 바로 다음 http_filter에 아래 filter가 추가되어 있습니다.

- name: envoy.filters.http.ext_authz
  typed_config:
    '@type': type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
    filter_enabled_metadata:
      filter: envoy.filters.http.rbac
      path:
      - key: istio_ext_authz_shadow_effective_policy_id
      value:
        string_match:
          prefix: istio-ext-authz
    grpc_service:
      envoy_grpc:
        authority: my-auth-server.app.svc.cluster.local
        cluster_name: outbound|4003||my-auth-server.app.svc.cluster.local
      timeout: 600s
    transport_api_version: V3

filter_enabled_metadata가 핵심입니다. 이 설정의 의미:

  • 앞선 RBAC filter가 남긴 dynamic metadata를 확인
  • istio_ext_authz_shadow_effective_policy_id 값이 istio-ext-authz prefix로 시작하면 ext_authz를 활성화
  • 즉, Filter 1(RBAC shadow)에서 조건이 매칭된 경우에만 Filter 2(ext_authz)가 실제로 외부 인증 서비스를 호출

매칭이 되면, gRPC 프로토콜로 my-auth-server.app.svc.cluster.local:4003 클러스터에 CheckRequest를 전송하여 해당 요청의 허용/거부를 결정합니다.

전체 요청 처리 흐름

0.0.0.0:443 listener → HttpConnectionManagerhttp_filters 체인에서 순서대로 실행됩니다.

요청 인입


① istio.metadata_exchange        ← peer metadata 교환 (just for metrics)


② envoy.filters.http.rbac        ← CUSTOM policy의 shadow rule 평가
 │                                   조건: :authority 존재 AND x-account 헤더 존재
 │                                   결과: dynamic metadata에 policy_id 기록
 │                                   (shadow이므로 요청 차단 없음)

③ envoy.filters.http.ext_authz   ← filter_enabled_metadata 확인
 │  ├─ metadata에 istio-ext-authz prefix 있음
 │  │   → gRPC call to my-auth-server:4003
 │  │   ├─ 인증 성공 (OK) → 다음 필터로 진행
 │  │   └─ 인증 실패 (Denied) → 403 반환
 │  └─ metadata에 prefix 없음
 │      → ext_authz skip, 다음 필터로 바로 진행

④ 이후 필터들 (grpc_stats, alpn, cors, fault, router 등)


upstream으로 라우팅

동작 요약

요청 조건RBAC Shadow 결과ext_authz 동작최종 결과
x-account 헤더 있음매칭 → metadata 기록gRPC call 발생auth 서버 응답에 따라 허용/거부
x-account 헤더 없음미매칭 → metadata 없음skip (호출 안 함)ext_authz 없이 다음 필터로 진행

이 구조의 핵심은 Istio가 RBAC shadow mode를 조건부 트리거 메커니즘으로 활용한다는 점입니다.

  • RBAC filter 자체는 트래픽을 차단하지 않고, 오직 “이 요청이 external auth가 필요한 조건에 해당하는가?”를 판별하는 역할만 수행
  • ext_authz filter가 filter_enabled_metadata를 통해 그 판별 결과를 읽어, 조건에 해당하는 요청에 대해서만 외부 인증 서비스를 호출

ext_authz의 auth 서버 호출 경로: via Envoy Cluster

ext_authz filter의 gRPC 호출은 단순한 네트워크 요청이 아닌 Gateway Envoy 프로세스 내부에 존재하는 outbound|4003||my-auth-server.app.svc.cluster.local Envoy 클러스터를 통해 라우팅됩니다. 즉, (waypoint가 enable 되어있다면) 기존 gateway → waypoint 서비스 트래픽과 동일한 Envoy 내부 라우팅 경로를 탑니다.

전체 호출 경로

ext_authz filter
 │  gRPC call: outbound|4003||my-auth-server.app.svc.cluster.local

Cluster: outbound|4003||my-auth-server
 │  endpoint: envoy_internal_address → connect_originate listener
 │  metadata: waypoint=10.90.145.210:15008, local=172.20.121.177:4003

Internal Listener: connect_originate
 │  TCP Proxy + tunneling_config (HTTP/2 CONNECT)

Cluster: connect_originate
 │  original_dst_lb → metadata에서 waypoint IP 추출
 │  upstream_port_override: 15008
 │  mTLS + HBONE tunnel

Waypoint Proxy (:15008)
 │  L7 policy 처리 (AuthorizationPolicy, HTTPRoute 등)

my-auth-server Pod (:4003)

결론: ext_authz의 gRPC 호출은 Gateway Envoy 내부의 auth 서버 클러스터를 경유하며, waypoint가 enable 되어있다면 기존 Gateway → Waypoint으로 가는 flow(connect_originate → HBONE 터널)를 그대로 탑니다. 일반 backend 서비스 라우팅과 완전히 동일한 경로입니다.

failOpen

ext_authz filter에 직접 mapping 되는 field가 있습니다.

- name: envoy.filters.http.ext_authz
  typed_config:
    '@type': type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
    failure_mode_allow: true  # ← 여기로 매핑됨
    grpc_service:
      envoy_grpc:
        cluster_name: outbound|4003||my-auth-server.app.svc.cluster.local
      timeout: 600s

Reference