📌 Notice
Istio Hands-on Study (=Istio)
직접 실습을 통해 Isito를 배포 및 설정하는 내용을 정리한 블로그입니다.
CloudNet@
에서 스터디를 진행하고 있습니다.
Gasida
님께 다시한번 🙇 감사드립니다.
EKS 관련 이전 스터디 내용은 아래 링크를 통해 확인할 수 있습니다.
이 블로그는 앞선 [Istio] 보안 (Securing - 1)에 이어, Istio에서 외부 인가 시스템과 통합하는 방식을 다룹니다.
앞 단계에서는 JWT 기반으로 최종 사용자를 검증하고, 클레임에 따라 역할별 접근 제어를 구성했습니다.
이번 포스팅에서는 한 발 더 나아가, Istio가 요청을 수락할지 결정하기 위해 외부 인가 서비스(ExtAuthz)를 호출하는 구조를 실습합니다.
이를 통해 다음과 같은 실전 시나리오에 대비할 수 있습니다:
- 내부 RBAC 정책만으로는 표현할 수 없는 복잡한 인가 로직 대응
- 조직 외부에 위치한 정책 서버 또는 중앙 정책 엔진과의 연동
- 헤더, 사용자 컨텍스트 등 다양한 정보를 바탕으로 인가 결정 위임
Istio의 extensionProviders
와 AuthorizationPolicy
의 CUSTOM
액션을 조합해
프록시 레벨에서 인가 로직을 외부화하고 세밀하게 제어할 수 있는 실습을 진행합니다.
최종 사용자 인증 및 인가 기능을 다루기 전, 다음과 같은 핵심 사전 개념을 이해할 필요가 있습니다.
ServiceAccount의 자격 증명을 기존의 시크릿 기반 볼륨 대신 projected volume을 사용해 보다 정교하게 제어할 수 있습니다. 이 기능을 활용하면 토큰의 audience
, expirationSeconds
등 다양한 속성을 지정할 수 있으며, 보안성과 유연성을 동시에 확보할 수 있습니다.
volumes:
- name: vault-token
projected:
sources:
- serviceAccountToken:
path: vault-token
expirationSeconds: 7200
audience: vault
또한 Kubernetes는 기본적으로 Bound Service Account Token Volume 기능을 통해 token
, ca.crt
, namespace
를 포함한 projected volume을 자동으로 생성해 파드에 마운트합니다.
projected
볼륨 타입은 secret
, configMap
, downwardAPI
, serviceAccountToken
을 하나의 경로에 통합해 마운트할 수 있습니다. 아래는 username.txt
와 password.txt
를 각각 시크릿으로 만들어 projected 볼륨으로 마운트한 예입니다.
kubectl create secret generic user --from-file=./username.txt
kubectl create secret generic pass --from-file=./password.txt
kubectl apply -f projected-volume.yaml
이렇게 구성된 파드에서 파일을 직접 확인할 수 있으며, 시크릿 값이 잘 마운트되었는지 검증할 수 있습니다.
Kubernetes API 서버 접근은 다음과 같은 흐름으로 진행됩니다:
kubectl get mutatingwebhookconfigurations
kubectl get validatingwebhookconfigurations
MutatingWebhook은 요청을 변형하고, ValidatingWebhook은 요청을 거부할 수 있습니다. 이를 통해 클러스터 내에서 정책을 동적으로 통제할 수 있습니다.
JWT는 X.509 인증서를 경량화한 JSON 포맷의 토큰으로, 세 부분(Header, Payload, Signature)으로 구성됩니다.
JWT는 Bearer
타입으로 Authorization 헤더에 포함되어 전송되며, Istio의 최종 사용자 인증에서 주로 활용됩니다.
OIDC는 OAuth 2.0에 기반한 인증 프로토콜로, 최종 사용자 인증을 위해 사용됩니다. 사용자 ID 확인을 위해 IdP (Identity Provider, 예: Google, Kakao)가 인증 토큰을 발급하고, RP (Relying Party)는 이 토큰을 검증해 사용자의 신원을 파악합니다.
OIDC는 인증(ID Token
)과 인가(Access Token
)의 두 기능을 통합하고 있으며, 다음과 같은 정보를 포함합니다:
iss
: 토큰 발급자sub
: 사용자 고유 식별자email
: 사용자 이메일exp
: 만료 시간aud
: 발급 대상 클라이언트이러한 구조를 통해 최종 사용자 기반의 인증/인가를 Istio 환경에서도 확장 가능하게 구성할 수 있습니다.
Istio는 최종 사용자 인증과 인가를 위해 JWT (JSON Web Token)를 지원합니다. 본 절에서는 JWT의 구조와 검증 방식, 그리고 Istio 환경에서의 활용에 앞서 기본 개념을 정리합니다.
JWT는 클라이언트를 서버에 인증하는 데 사용하는 간단한 클레임 표현으로, 다음과 같은 세 부분으로 구성됩니다:
각 부분은 Base64 URL로 인코딩되며, 점(.
)으로 구분되어 HTTP 요청에서 사용하기 적합합니다.
# JWT 토큰 확인
cat ./ch9/enduser/user.jwt
# 디코딩 방법 1
jwt decode $(cat ./ch9/enduser/user.jwt)
# 디코딩 방법 2
cat ./ch9/enduser/user.jwt | cut -d '.' -f2 | base64 --decode | jq
디코딩된 JWT 페이로드 예시는 다음과 같습니다:
{
"exp": 4745145038,
"group": "user",
"iat": 1591545038,
"iss": "auth@istioinaction.io",
"sub": "9b792b56-7dfa-4e4b-a83f-e20679115d79"
}
이 페이로드는 사용자의 ID와 권한을 나타내는 클레임(claim)으로 구성되며, 이를 통해 서비스는 요청 주체의 신원을 식별하고 인가 여부를 판단할 수 있습니다.
JWT는 일반적으로 인증 서버에서 발급되며, 이 서버는 다음을 포함합니다:
서버 측에서는 클라이언트가 제시한 JWT를 다음과 같은 순서로 검증합니다:
인증 서버는 다음과 같은 방식으로 구현할 수 있습니다:
- 직접 구현한 애플리케이션 백엔드
- Keycloak, OpenIAM 등 자체 인증 솔루션
- Auth0, Okta 같은 SaaS 형태의 인증 서비스
이러한 구조를 통해 Istio는 최종 사용자 인증을 서비스 프록시 차원에서 처리하며, JWT 기반의 클레임을 바탕으로 세밀한 접근 제어를 가능하게 합니다.
Istio는 JWT(JSON Web Token)를 활용해 최종 사용자 인증과 인가를 수행할 수 있으며, 이 기능은 워크로드 단위뿐 아니라 인그레스 게이트웨이 수준에서도 적용할 수 있습니다.
최종 사용자는 일반적으로 ID 제공자(IdP)로부터 인증을 받고, 신원을 나타내는 토큰(JWT)을 발급받은 사용자입니다. 이 토큰은 클레임 기반의 정보를 담고 있으며, 이를 기반으로 인가 정책을 정의할 수 있습니다.
최종 사용자 인증과 인가를 인그레스 게이트웨이에서 수행하는 이유는 다음과 같습니다:
기존 설정을 정리하고, 실습에 필요한 웹앱 및 카탈로그 서비스를 배포합니다.
# 기존 리소스 정리
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
Istio에서 최종 사용자 인증을 다룰 때 가장 핵심이 되는 리소스 중 하나가 바로 RequestAuthentication
입니다. 이 리소스는 JWT를 검증하고, 유효한 토큰이라면 클레임 정보를 추출하여 필터 메타데이터에 저장합니다.
이렇게 저장된 메타데이터는 후속 AuthorizationPolicy
에서 인가를 위한 조건으로 활용됩니다.
요청이 들어오면 다음과 같은 세 가지 경우 중 하나로 처리됩니다:
즉, RequestAuthentication
리소스는 인가를 강제하지 않고, JWT의 유효성을 검증하고 클레임 데이터를 추출하는 역할만 담당합니다.
인가를 수행하려면 반드시 별도의
AuthorizationPolicy
리소스가 필요합니다.
group: admin
클레임이 포함된 JWT가 인증되면, 해당 값은 필터 메타데이터에 저장되며 AuthorizationPolicy
에서 "request.auth.claims[groups]" == "admin"
와 같은 형태로 참조할 수 있게 됩니다.이제 다음 단계에서는 실제 RequestAuthentication
리소스를 정의하고, 다양한 시나리오에서 JWT 유무에 따른 처리 결과를 실습을 통해 확인해보겠습니다.
이번 단계에서는 Istio 인그레스 게이트웨이에서 JWT 토큰을 검증하기 위한 RequestAuthentication
리소스를 구성합니다. 이 리소스를 통해 Istio는 유효한 토큰의 클레임을 추출하고, 이를 기반으로 후속 인가 정책이 동작할 수 있는 메타데이터를 생성합니다.
RequestAuthentication
은 토큰이 유효한지 검증만 수행합니다.# 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"
jwks: |
{ "keys":[ { "e":"AQAB", "kid":"CU-ADJJEbH9bXl0tpsQWYuo4EwlkxFUHbeJ4ckkakCM", "kty":"RSA", "n":"..." } ]}
# 리소스 적용
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 --port 8080 -o json
이 명령을 통해 envoy.filters.http.jwt_authn
필터가 정상적으로 구성되었는지, 지정된 issuer와 JWKS가 포함되어 있는지 확인할 수 있습니다.
이제 인그레스 게이트웨이는 auth@istioinaction.io
에서 발급된 JWT가 첨부된 요청만 처리할 준비가 되었습니다.
이번 실습에서는 유효한 JWT를 포함한 요청이 Istio 인그레스 게이트웨이를 통해 정상적으로 처리되는지 확인합니다.
현재 RequestAuthentication
리소스만 적용된 상태이므로, 별도의 AuthorizationPolicy
가 없다면 요청은 기본적으로 허용됩니다.
# JWT 토큰 확인
cat ch9/enduser/user.jwt
USER_TOKEN=$(< ch9/enduser/user.jwt)
jwt decode $USER_TOKEN
# 인그레스를 통해 웹앱 호출
curl -H "Authorization: Bearer $USER_TOKEN" \
-sSl -o /dev/null -w "%{http_code}" \
webapp.istioinaction.io:30000/api/catalog
# 결과: 200
# 인그레스 게이트웨이 로그 확인
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
요청 헤더에 JWT를 포함하면, RequestAuthentication
리소스에 의해 해당 토큰이 검증됩니다.
현재는 AuthorizationPolicy
가 설정되어 있지 않기 때문에 검증된 요청은 기본적으로 허용(Allow) 됩니다.
이후 단계에서는 클레임 정보 기반 인가 정책을 추가하여 보다 정교한 접근 제어를 수행할 수 있습니다.
이번 실습에서는 RequestAuthentication
리소스에 등록되지 않은 발급자(issuer)를 사용한 JWT를 포함해 요청을 보냈을 때, Istio가 해당 요청을 어떻게 처리하는지 확인합니다.
# 잘못된 issuer를 가진 JWT 확인
cat ch9/enduser/not-configured-issuer.jwt
WRONG_ISSUER=$(< ch9/enduser/not-configured-issuer.jwt)
# 디코딩
jwt decode $WRONG_ISSUER
{
"exp": 4745151548,
"group": "user",
"iat": 1591551548,
"iss": "old-auth@istioinaction.io", # 설정된 issuer와 다름
"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 Unauthorized
# 인그레스 게이트웨이 로그 확인
kubectl logs -n istio-system -l app=istio-ingressgateway -f
[2025-05-04T06:36:22.089Z] "GET /api/catalog HTTP/1.1" 401 - jwt_authn_access_denied{Jwt_issuer_is_not_configured} ...
토큰의 iss
(issuer)가 현재 RequestAuthentication
리소스에 명시된 값과 다르면, Istio는 해당 요청을 401 Unauthorized 상태로 거부합니다.
로그에서 jwt_authn_access_denied{Jwt_issuer_is_not_configured}
메시지를 통해 원인을 확인할 수 있습니다.
이 과정을 통해 잘못된 토큰이 서비스 메시 내부로 유입되는 것을 조기에 차단할 수 있습니다.
이번 실습에서는 JWT 토큰 없이 인그레스 게이트웨이를 통해 서비스를 호출할 경우, Istio가 요청을 어떻게 처리하는지 확인합니다.
# Authorization 헤더 없이 요청
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
응답 코드 200 OK
는 토큰 없이 보낸 요청이 클러스터로 정상 전달되었음을 의미합니다.
일반적인 보안 상식으로는 "토큰이 없으면 접근이 거부된다"고 생각할 수 있지만, 현재 RequestAuthentication
리소스는 토큰 검증만 수행할 뿐, 토큰이 없을 경우 이를 거부하지는 않습니다.
실제 웹 환경에서는 사용자 로그인 전의 요청처럼 토큰이 없는 요청이 존재하므로, Istio는 이를 유연하게 허용하도록 기본 동작을 구성하고 있습니다.
토큰이 없는 요청을 명시적으로 거부하려면, AuthorizationPolicy를 통해 정책을 추가로 정의해야 합니다.
JWT가 없는 요청을 거부하려면 명시적으로 DENY 정책을 설정해야 합니다. 아래 AuthorizationPolicy는 JWT가 없는 요청(requestPrincipals가 없는 요청)을 거부합니다. notRequestPrincipals
속성은 해당 클레임이 없는 경우를 식별합니다.
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: ["*"]
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
# 호출 1
curl -sSl -o /dev/null -w "%{http_code}" webapp.istioinaction.io:30000/api/catalog
403
# 호출 2
curl -H "Authorization: Bearer $USER_TOKEN" \
-sSl -o /dev/null -w "%{http_code}" webapp.istioinaction.io:30000/api/catalog
# 로그
kubectl logs -n istio-system -l app=istio-ingressgateway -f
[2025-05-04T07:04:01.791Z] "GET /api/catalog HTTP/1.1" 403 - rbac_access_denied_matched_policy[ns[istio-system]-policy[app-gw-requires-jwt]-rule[0]] - "-" 0 19 0 - "172.18.0.1" "curl/8.7.1" "41678cf6-6ef8-986e-beb4-4e5af46e7a26" "webapp.istioinaction.io:30000" "-" outbound|80||webapp.istioinaction.svc.cluster.local - 10.10.0.5:8080 172.18.0.1:65424 - -
이제 토큰이 없는 요청은 모두 403으로 거부됩니다. 이후, 필요한 요청만 명시적으로 허용하는 ALLOW 정책을 추가해 운영 정책을 구성합니다.
JWT 클레임을 기반으로 일반 사용자와 관리자에게 서로 다른 권한을 부여할 수 있습니다. 아래는 두 개의 JWT 예시입니다.
{
"group": "user",
"iss": "auth@istioinaction.io"
}
{
"group": "admin",
"iss": "auth@istioinaction.io"
}
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"]
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"]
GET /api/catalog
: 200 OKPOST /api/catalog
: 403 ForbiddenGET /api/catalog
: 200 OKPOST /api/catalog
: 200 OK이처럼 JWT의 클레임을 활용하면 사용자 유형에 따라 정밀한 권한 분리가 가능합니다.
Istio는 기본적으로 Envoy 프록시의 RBAC 필터를 통해 인증된 주체에 대한 인가 정책을 정의할 수 있습니다. 그러나 더 정교한 인가 로직이 필요할 경우, 서비스 프록시가 외부 인가 서비스(External Authorization, ExtAuthz)를 호출하도록 구성할 수 있습니다.
외부 인가 흐름은 다음과 같습니다:
- Envoy 프록시로 요청이 유입되면, 프록시는 외부 인가 서비스에
CheckRequest
API를 통해 요청 정보를 전달합니다.- 외부 인가 서비스는 이 정보를 기반으로 정책 판단을 수행하고, 요청을 허용(allow)하거나 거부(deny)하는 응답을 반환합니다.
- 이 응답에 따라 프록시는 요청을 계속 전달하거나 차단합니다.
외부 인가 서비스는 메시 내부 또는 외부에 배치될 수 있으며, 다음과 같은 대표적인 구현체를 사용할 수 있습니다:
외부 인가 서비스를 구현하려면 Envoy의 CheckRequest API를 지원해야 합니다. 예시 구현은 go-control-plane 프로젝트를 참고하면 됩니다.
⏱ ExtAuthz의 성능 트레이드오프
외부 인가 방식은 요청이 프록시를 통과하기 전에 외부 인가 서비스를 반드시 거쳐야 하므로, 네트워크 호출에 따른 지연(latency)이 발생합니다.
Istio의 내장 인가 기능은 대부분의 상황에서 유연하고 성능적으로 우수하지만, 보안 또는 규정상 외부 판단 로직이 필요한 경우에만 ExtAuthz를 사용해야 하며, 이때는 성능 트레이드오프를 반드시 고려해야 합니다.
특히, 외부 인가 서비스를 애플리케이션 사이드카와 동일한 네임스페이스에 배치하면 네트워크 지연을 최소화할 수 있습니다.
관련 공식 문서: https://istio.io/latest/docs/tasks/security/authorization/authz-custom/
Istio의 외부 인가(External Authorization) 기능을 실습을 통해 적용해보는 단계입니다. 이를 통해 Istio가 Envoy의 외부 인가 필터와 통합되어 요청을 허용 또는 거부하는 방식에 대해 명확히 이해할 수 있습니다.
먼저 기존에 적용된 인증 및 인가 관련 리소스를 삭제하고, 필요한 워크로드와 Istio 샘플 외부 인가 서비스를 배포합니다.
# 기존 인증/인가 정책 제거
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에서 제공하는 샘플 외부 인가 서비스 ext-authz를 배포합니다. 이 서비스는 단순히 x-ext-authz: allow 헤더가 포함된 요청만 허용합니다.
# Docker 컨트롤 플레인에서 배포 파일 확인 및 적용
docker exec -it myk8s-control-plane bash
ls -l istio-$ISTIOV/samples/extauthz/
# 예시 파일 확인
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 서비스는 Envoy의 외부 인가 필터를 통해 연동되며, 요청 헤더에 x-ext-authz: allow가 포함되어 있으면 요청을 허용합니다. 해당 헤더가 없으면 요청은 거부됩니다.
이 샘플은 단순한 구조이지만, 실무에서는 Open Policy Agent(OPA), Keycloak, Auth0 등의 외부 인가 서버와 연동해 더욱 복잡한 정책을 집행할 수 있습니다.
Istio가 외부 인가 서비스(ExtAuthz)를 인식하고 연동할 수 있도록 구성하기 위해서는 meshconfig
설정의 extensionProviders
항목에 해당 서비스를 명시해야 합니다.
이 설정은 istio-system
네임스페이스의 ConfigMap
리소스인 istio
에 존재합니다.
먼저, Istio가 HTTP 기반 외부 인가 서비스를 사용할 수 있도록 ConfigMap을 수정합니다. 이 설정은 샘플 서비스 ext-authz.istioinaction.svc.cluster.local
을 대상으로 구성되며, 요청 헤더 중 x-ext-authz
를 전달하도록 지정합니다.
# ConfigMap 수정 (에디터 사용)
KUBE_EDITOR="nano" kubectl edit -n istio-system cm istio
아래와 같이 extensionProviders 항목을 추가합니다:
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 설정에 등록한 sample-ext-authz-http
확장(Envoy HTTP 기반 외부 인가 서비스)을 실제 정책에 적용해보겠습니다.
# 외부 인가 서비스를 사용하는 CUSTOM AuthorizationPolicy 생성
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
provider:
name: sample-ext-authz-http
rules:
- to:
- operation:
paths: ["/*"]
EOF
# 적용된 정책 확인
kubectl get authorizationpolicy -n istioinaction
이 정책은 istioinaction 네임스페이스의 webapp 워크로드에 대해 모든 경로 요청을 sample-ext-authz-http 외부 인가 서비스에 위임합니다.
헤더 없이 호출 (거부됨)
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 # Webapp 프록시 로그: 403 UAEX ext_authz_denied
헤더 포함 호출 (허용됨)
kubectl -n default exec -it deploy/sleep -- curl -H "x-ext-authz: allow" webapp.istioinaction/api/catalog # 응답 코드: 200 OK # 외부 인가 서버 로그: [HTTP][allowed]: GET webapp.istioinaction/api/catalog
AuthorizationPolicy의 CUSTOM 액션을 통해 외부 인가 서버와 연동하면, 서비스 프록시가 요청을 위임하고 인가 여부를 판단할 수 있습니다.
이 예제에서는 요청 헤더에 x-ext-authz: allow
가 포함되어야만 요청이 허용됩니다.
외부 인가 서버는 Envoy의 CheckRequest API를 구현해야 하며, 운영 환경에서는 OPA(Open Policy Agent)나 Gloo Edge ExtAuth 등의 솔루션을 사용할 수 있습니다.
Istio 설정에는 extensionProviders를 통해 외부 인가 서버를 mesh에 등록하고, 해당 이름을 AuthorizationPolicy에서 참조합니다.
이와 같이 외부 인가를 사용하면 기존 RBAC 정책만으로는 표현하기 어려운 세밀한 인가 로직을 확장 가능한 방식으로 구현할 수 있습니다.
이번 블로그는 앞서 다뤘던 JWT 기반 인증/인가 흐름을 외부 인가 시스템과의 연동으로 확장한 실전 예제였습니다.
RequestAuthentication
과 AuthorizationPolicy
조합으로도 많은 제어가 가능하지만, 보다 정밀한 인가 판단이 필요한 경우에는 외부 인가 서비스(ExtAuthz)를 활용하는 것이 효과적입니다.
이번 실습을 통해 Istio가 외부 인가 서비스와 연동하여, 프록시 수준에서 인가 판단을 위임할 수 있는 구조를 확인했습니다.
CUSTOM
액션을 사용하는 AuthorizationPolicy
를 통해, 요청 흐름에 외부 판단 로직을 삽입할 수 있으며,
단순 헤더 검사부터 OPA, Keycloak 같은 정책 엔진과의 통합까지 유연하게 확장 가능합니다.
단, 외부 호출에 따른 지연(latency)과 성능 트레이드오프를 고려해, 실시간 요구사항에 따라 신중히 설계해야 합니다.