AuthorizationPolicy의 ext_authz를 Envoy config로 톺아보기
Istio AuthorizationPolicy의 action: 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: ['*'] | principals → x-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-authzprefix로 시작하면 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 → HttpConnectionManager 내 http_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