본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 22. · 2 Views
Istio 보안 완벽 가이드
마이크로서비스 환경에서 필수적인 Istio 보안 기능을 실무 중심으로 설명합니다. mTLS부터 인증, 인가까지 단계별로 학습하여 안전한 서비스 메시를 구축할 수 있습니다.
목차
- mTLS_개념
- PeerAuthentication_정책
- RequestAuthentication_JWT_인증
- AuthorizationPolicy_접근_제어
- JWT_검증_상세
- 보안_모범_사례
1. mTLS 개념
어느 날 김개발 씨가 마이크로서비스 아키텍처로 전환한 회사에 입사했습니다. 첫 주 미팅에서 보안팀 이과장님이 물었습니다.
"서비스 간 통신은 어떻게 암호화할 계획인가요?" 김개발 씨는 당황했습니다. 내부 통신까지 암호화해야 한다고요?
**mTLS(Mutual TLS)**는 서버와 클라이언트가 서로의 신원을 확인하고 암호화된 통신을 하는 보안 프로토콜입니다. 마치 두 사람이 만날 때 서로 신분증을 교환하고 확인하는 것과 같습니다.
Istio는 이 과정을 자동으로 처리해주어 개발자가 코드 한 줄 수정하지 않고도 모든 서비스 간 통신을 보호할 수 있습니다.
다음 코드를 살펴봅시다.
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: default
namespace: istio-system
spec:
# 전체 메시에 대해 mTLS를 강제합니다
mtls:
mode: STRICT
---
# 특정 네임스페이스에만 적용하는 예시
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: payment-mtls
namespace: payment
spec:
mtls:
mode: PERMISSIVE # mTLS와 평문 모두 허용 (전환기)
김개발 씨는 입사 1주 차 주니어 개발자입니다. 이전 회사에서는 모놀리식 애플리케이션을 개발했기 때문에 서비스 간 통신 보안에 대해 깊이 고민해본 적이 없었습니다.
그런데 새 회사는 100개가 넘는 마이크로서비스로 구성되어 있었습니다. 보안팀의 이과장님이 화이트보드에 그림을 그리며 설명했습니다.
"우리 서비스는 겉으로는 HTTPS로 보호되지만, 내부는 어떨까요? 주문 서비스가 결제 서비스를 호출할 때, 재고 서비스가 배송 서비스와 통신할 때, 이 모든 것이 평문으로 전송되면 어떻게 될까요?" 그렇다면 mTLS란 정확히 무엇일까요?
쉽게 비유하자면, mTLS는 마치 외교관들이 서로 외교관 신분증을 확인하고 암호화된 채널로 대화하는 것과 같습니다. 일반 TLS는 클라이언트가 서버의 신원만 확인하지만, mTLS는 서버도 클라이언트의 신원을 확인합니다.
마치 은행에서 직원이 고객의 신분증을 확인하는 동시에, 고객도 직원의 신분증을 확인하는 것처럼 양방향 인증이 이루어집니다. mTLS가 없던 시절에는 어땠을까요?
개발자들은 각 서비스마다 TLS 인증서를 직접 발급하고 관리해야 했습니다. 코드에 인증서 경로를 하드코딩하고, 만료일을 추적하며, 갱신할 때마다 서비스를 재시작해야 했습니다.
더 큰 문제는 서비스가 100개가 넘어가면 인증서 관리만으로도 풀타임 인력이 필요했다는 점입니다. 실수로 인증서가 만료되면 서비스 장애로 이어지기 일쑤였습니다.
바로 이런 문제를 해결하기 위해 Istio의 mTLS 기능이 등장했습니다. Istio를 사용하면 인증서 발급, 배포, 갱신이 모두 자동으로 처리됩니다.
개발자는 코드 한 줄 수정하지 않아도 됩니다. 또한 설정 파일 몇 줄만으로 전체 메시에 mTLS를 적용할 수 있습니다.
무엇보다 Envoy 프록시가 모든 암호화 작업을 대신 처리하기 때문에 애플리케이션 성능에 영향을 최소화할 수 있다는 큰 이점이 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 첫 번째 설정을 보면 istio-system 네임스페이스에 전역 정책을 정의하는 것을 알 수 있습니다. 이 부분이 핵심입니다.
mode: STRICT는 모든 서비스 간 통신에 mTLS를 강제한다는 의미입니다. 평문 트래픽은 거부됩니다.
다음으로 두 번째 예시에서는 PERMISSIVE 모드를 사용합니다. 이는 mTLS와 평문 트래픽을 모두 허용하는 전환기 설정입니다.
기존 서비스를 점진적으로 마이그레이션할 때 유용합니다. 실제 현업에서는 어떻게 활용할까요?
예를 들어 금융 서비스를 개발한다고 가정해봅시다. 계좌 조회, 이체, 대출 심사 등 수십 개의 마이크로서비스가 서로 통신합니다.
이 중 하나라도 평문으로 통신하면 내부 네트워크가 침해당했을 때 모든 데이터가 노출됩니다. Istio mTLS를 적용하면 개발팀은 비즈니스 로직에만 집중하고, 통신 보안은 플랫폼 팀이 중앙에서 관리할 수 있습니다.
토스, 카카오뱅크 같은 핀테크 기업들이 이런 패턴을 적극적으로 사용하고 있습니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 처음부터 STRICT 모드를 전체에 적용하는 것입니다. 이렇게 하면 Istio 사이드카가 없는 레거시 서비스와의 통신이 즉시 차단됩니다.
따라서 먼저 PERMISSIVE 모드로 전환하여 모든 서비스에 사이드카를 배포한 후, 트래픽을 모니터링하면서 단계적으로 STRICT로 전환해야 합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
이과장님의 설명을 들은 김개발 씨는 고개를 끄덕였습니다. "아, 그래서 인증서 관리를 하지 않아도 되는 거군요!" mTLS를 제대로 이해하면 더 안전하고 관리하기 쉬운 마이크로서비스 환경을 구축할 수 있습니다.
여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - PERMISSIVE 모드로 시작하여 점진적으로 STRICT로 전환하세요
- istio-proxy 로그에서 mTLS 연결 상태를 확인할 수 있습니다
- 네임스페이스별로 다른 정책을 적용할 수 있으니 민감한 서비스부터 강화하세요
2. PeerAuthentication 정책
김개발 씨가 mTLS를 전사에 적용하려고 하자 박시니어 개발자가 말했습니다. "잠깐, 레거시 서비스들은 아직 Istio 사이드카가 없는데 한 번에 적용하면 장애가 납니다." 어떻게 안전하게 전환할 수 있을까요?
PeerAuthentication은 서비스 간 통신(peer-to-peer)에 대한 인증 정책을 정의하는 Istio 리소스입니다. 마치 건물 출입 시 보안 등급을 층별로 다르게 설정하는 것처럼, 네임스페이스나 특정 워크로드별로 mTLS 요구사항을 세밀하게 조정할 수 있습니다.
전역, 네임스페이스, 워크로드 세 가지 레벨에서 정책을 적용할 수 있습니다.
다음 코드를 살펴봅시다.
# 네임스페이스 레벨 정책
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: namespace-policy
namespace: production
spec:
mtls:
mode: STRICT
# 특정 워크로드에만 적용
---
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: workload-policy
namespace: production
spec:
selector:
matchLabels:
app: payment-service
mtls:
mode: STRICT
# 특정 포트만 예외 처리
portLevelMtls:
9090:
mode: DISABLE # 모니터링 포트는 mTLS 비활성화
김개발 씨는 이제 mTLS의 중요성을 이해했습니다. 하지만 실제 적용은 생각보다 복잡했습니다.
회사에는 Kubernetes로 마이그레이션된 신규 서비스도 있고, VM에서 돌아가는 레거시 서비스도 있었습니다. 모든 것을 한 번에 STRICT 모드로 전환하면 절반의 서비스가 통신 불가 상태가 될 것이 분명했습니다.
박시니어 개발자가 화면을 공유하며 설명했습니다. "PeerAuthentication은 세 가지 레벨에서 적용할 수 있어요.
메시 전역, 네임스페이스, 그리고 특정 워크로드까지요. 우선순위는 워크로드 > 네임스페이스 > 전역 순입니다." 그렇다면 PeerAuthentication 정책은 어떻게 동작할까요?
쉽게 비유하자면, PeerAuthentication은 마치 아파트 단지의 보안 시스템과 같습니다. 단지 전체에 기본 보안 등급을 설정하되, 특정 동은 더 강화하고, 경비실이나 관리사무소는 예외로 둘 수 있습니다.
이처럼 PeerAuthentication도 전역 기본값을 설정하고, 중요한 네임스페이스는 강화하며, 특수한 워크로드는 개별 정책을 적용할 수 있습니다. 정책 우선순위를 이해하지 못하면 어떤 문제가 생길까요?
많은 팀이 실수하는 부분이 바로 정책 충돌입니다. 메시 전역에 STRICT를 설정했는데 특정 서비스가 연결되지 않아 한참을 디버깅한 경험이 있을 것입니다.
알고 보니 네임스페이스 정책에서 PERMISSIVE로 설정되어 있었고, 다른 서비스는 STRICT를 기대했던 경우입니다. 정책 우선순위를 명확히 이해하지 못하면 이런 혼란이 계속됩니다.
바로 이런 문제를 해결하기 위해 명확한 정책 계층 구조가 필요합니다. PeerAuthentication을 사용하면 **선택자(selector)**를 통해 특정 워크로드에만 정책을 적용할 수 있습니다.
또한 portLevelMtls를 사용하면 포트별로 다른 정책을 적용할 수 있습니다. 무엇보다 레이블 기반 선택이라 동일한 정책을 여러 서비스에 일괄 적용하기 쉽다는 큰 이점이 있습니다.
위의 코드를 한 줄씩 살펴보겠습니다. 먼저 첫 번째 정책을 보면 production 네임스페이스 전체에 STRICT 모드를 적용하는 것을 알 수 있습니다.
이 네임스페이스의 모든 워크로드는 mTLS를 사용해야 합니다. 다음으로 두 번째 정책에서는 selector를 사용하여 payment-service 앱에만 정책을 적용합니다.
여기서 핵심은 portLevelMtls입니다. 9090 포트는 Prometheus 같은 모니터링 도구가 접근하는 포트인데, 이런 내부 도구는 mTLS를 지원하지 않는 경우가 많아 예외 처리한 것입니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 이커머스 플랫폼을 운영한다고 가정해봅시다.
주문, 결제, 배송 서비스는 민감한 정보를 다루므로 STRICT 모드를 적용하고, 상품 카탈로그나 리뷰 서비스는 상대적으로 덜 민감하니 PERMISSIVE 모드로 시작할 수 있습니다. 모니터링 대시보드는 모든 서비스의 메트릭을 수집해야 하므로 메트릭 포트는 mTLS를 비활성화합니다.
쿠팡, 배민 같은 대형 플랫폼이 이런 방식으로 단계적 보안을 적용합니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 정책 이름을 default로 통일하는 것입니다. 같은 네임스페이스에 동일한 이름의 PeerAuthentication이 여러 개 있으면 하나만 적용되고 나머지는 무시됩니다.
따라서 명확하고 설명적인 이름을 사용해야 합니다. 또한 워크로드 정책을 적용할 때는 레이블이 정확히 일치하는지 확인해야 합니다.
오타 하나로 정책이 적용되지 않을 수 있습니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 설명을 들은 김개발 씨는 계획을 수정했습니다. "먼저 메시 전역은 PERMISSIVE로 두고, production 네임스페이스만 STRICT로 전환한 다음, 트래픽을 모니터링하면서 점차 확대해야겠네요!" PeerAuthentication 정책을 제대로 이해하면 안전하고 단계적인 mTLS 도입이 가능합니다.
여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - 정책 우선순위를 기억하세요: 워크로드 > 네임스페이스 > 메시 전역
- 모니터링 포트(메트릭, 헬스체크)는 portLevelMtls로 예외 처리하세요
- istioctl x describe pod 명령으로 어떤 정책이 적용되었는지 확인할 수 있습니다
3. RequestAuthentication JWT 인증
서비스 간 통신은 mTLS로 보호했지만, 박시니어 개발자가 또 물었습니다. "그런데 최종 사용자는 어떻게 인증하죠?
API 게이트웨이를 통과한 사용자가 진짜 로그인한 사용자인지 어떻게 확인하나요?" 김개발 씨는 또다시 고민에 빠졌습니다.
RequestAuthentication은 최종 사용자의 인증 정보(주로 JWT 토큰)를 검증하는 Istio 정책입니다. 마치 공항에서 탑승권과 여권을 확인하듯이, 서비스로 들어오는 HTTP 요청의 인증 토큰을 검증합니다.
OAuth2, OIDC 등 표준 인증 프로토콜과 통합되며, 토큰 서명 검증, 발급자 확인, 만료 시간 체크 등을 자동으로 수행합니다.
다음 코드를 살펴봅시다.
apiVersion: security.istio.io/v1beta1
kind: RequestAuthentication
metadata:
name: jwt-auth
namespace: production
spec:
selector:
matchLabels:
app: api-gateway
# JWT 검증 규칙 정의
jwtRules:
- issuer: "https://accounts.example.com"
# JWKS 엔드포인트에서 공개키를 가져옴
jwksUri: "https://accounts.example.com/.well-known/jwks.json"
# JWT가 포함될 위치 지정
fromHeaders:
- name: Authorization
prefix: "Bearer "
# 사용자 정보를 어디에 저장할지 지정
outputPayloadToHeader: "x-jwt-payload"
김개발 씨는 이제 서비스 간 통신 보안은 mTLS로 해결했습니다. 하지만 새로운 문제에 직면했습니다.
모바일 앱이나 웹 브라우저에서 들어오는 사용자 요청은 어떻게 검증할까요? API 게이트웨이까지는 HTTPS로 보호되지만, 그 사용자가 정말 로그인한 사용자인지 확인해야 합니다.
박시니어 개발자가 노트북을 열며 설명을 시작했습니다. "우리는 OAuth2 인증을 사용하고 있어요.
사용자가 로그인하면 인증 서버가 JWT 토큰을 발급하고, 클라이언트는 매 요청마다 이 토큰을 헤더에 넣어 보냅니다. Istio가 이 토큰을 자동으로 검증해줄 수 있어요." 그렇다면 RequestAuthentication은 어떻게 동작할까요?
쉽게 비유하자면, RequestAuthentication은 마치 콘서트장 입구의 티켓 검증 시스템과 같습니다. 관객이 티켓을 제시하면 스캐너가 바코드를 읽고, 발급처가 정당한지, 유효기간이 지나지 않았는지, 위조되지 않았는지 자동으로 확인합니다.
이처럼 RequestAuthentication도 HTTP 요청에 포함된 JWT를 읽고, 발급자(issuer)를 확인하며, 서명을 검증하고, 만료 시간을 체크합니다. JWT 검증을 직접 구현하면 어떤 문제가 있을까요?
예전에는 각 마이크로서비스마다 JWT 검증 로직을 직접 구현해야 했습니다. 공개키를 관리하고, 라이브러리를 추가하고, 검증 실패 시 에러 처리를 작성해야 했습니다.
더 큰 문제는 인증 서버가 바뀌거나 알고리즘이 업데이트되면 모든 서비스를 수정해야 한다는 점입니다. 100개 서비스에 동일한 코드를 복사-붙여넣기하다 보면 어딘가는 빠뜨리기 마련입니다.
바로 이런 문제를 해결하기 위해 Istio의 RequestAuthentication이 등장했습니다. RequestAuthentication을 사용하면 인증 로직을 중앙에서 선언적으로 정의할 수 있습니다.
애플리케이션 코드는 전혀 수정하지 않아도 됩니다. 또한 JWKS(JSON Web Key Set) 엔드포인트를 지정하면 Istio가 자동으로 공개키를 가져와 서명을 검증합니다.
무엇보다 검증된 JWT 페이로드를 헤더로 전달받아 애플리케이션에서 사용자 정보를 바로 활용할 수 있다는 큰 이점이 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 selector를 보면 api-gateway 앱에만 이 인증 정책을 적용하는 것을 알 수 있습니다. 게이트웨이에서 한 번만 검증하면 내부 서비스는 신뢰할 수 있습니다.
다음으로 jwtRules에서 발급자(issuer)와 JWKS URI를 지정합니다. Istio는 이 URI에서 공개키를 다운로드해 JWT 서명을 검증합니다.
fromHeaders는 토큰이 Authorization 헤더의 Bearer 스킴에 있다고 알려줍니다. 마지막으로 outputPayloadToHeader를 설정하면 검증된 JWT의 내용(사용자 ID, 권한 등)이 x-jwt-payload 헤더로 전달되어 백엔드 서비스에서 사용할 수 있습니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 소셜 미디어 플랫폼을 개발한다고 가정해봅시다.
사용자가 로그인하면 Auth0, Keycloak 같은 인증 서버가 JWT를 발급합니다. 모바일 앱은 게시글 조회, 좋아요, 댓글 작성 등 모든 API 호출 시 이 토큰을 헤더에 넣습니다.
Istio가 게이트웨이에서 한 번만 검증하면, 내부의 수십 개 마이크로서비스는 x-jwt-payload 헤더에서 사용자 ID를 꺼내 쓰기만 하면 됩니다. 넷플릭스, 스포티파이 같은 글로벌 서비스가 이런 아키텍처를 사용합니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 RequestAuthentication만 정의하고 AuthorizationPolicy를 함께 설정하지 않는 것입니다.
RequestAuthentication은 토큰이 유효하면 검증만 하고 통과시킵니다. 토큰이 없어도 통과됩니다.
따라서 반드시 AuthorizationPolicy와 함께 사용하여 "토큰이 없으면 거부"하는 규칙을 추가해야 합니다. 이 부분을 놓치면 인증을 우회할 수 있습니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨의 설명을 들은 김개발 씨는 눈이 커졌습니다.
"그럼 우리 백엔드 서비스들은 JWT 라이브러리를 전부 제거해도 되는 건가요?" "맞아요. Istio가 대신 해주니까요." RequestAuthentication을 제대로 이해하면 중앙화된 인증 관리로 일관성과 보안을 동시에 확보할 수 있습니다.
여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - RequestAuthentication은 검증만 하고 거부하지 않습니다. AuthorizationPolicy와 함께 사용하세요
- JWKS URI는 캐시되므로 성능 걱정은 없습니다
- 여러 발급자를 지원하려면 jwtRules에 여러 규칙을 추가하세요
4. AuthorizationPolicy 접근 제어
JWT 검증까지 완료한 김개발 씨는 이제 완벽하다고 생각했습니다. 하지만 보안팀 이과장님이 지적했습니다.
"인증된 사용자라도 모든 API에 접근할 수 있으면 안 됩니다. 일반 사용자가 관리자 API를 호출하는 것을 막아야죠." 인증과 인가는 다른 개념이었습니다.
AuthorizationPolicy는 누가 무엇에 접근할 수 있는지를 정의하는 인가 정책입니다. 마치 회사에서 직급별로 접근 가능한 층과 방이 다른 것처럼, 사용자의 역할, JWT 클레임, 요청 경로 등에 따라 세밀한 접근 제어를 수행합니다.
ALLOW, DENY, CUSTOM 액션을 지원하며, 명시적으로 허용하지 않은 모든 것을 거부하는 화이트리스트 방식을 권장합니다.
다음 코드를 살펴봅시다.
# 기본 DENY 정책 - 명시적으로 허용되지 않으면 모두 거부
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: deny-all
namespace: production
spec:
{} # 빈 spec은 모든 요청을 거부
---
# 관리자만 접근 가능한 API
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: admin-only
namespace: production
spec:
selector:
matchLabels:
app: admin-service
action: ALLOW
rules:
- from:
- source:
# JWT의 role 클레임이 admin인 경우만 허용
requestPrincipals: ["*"]
when:
- key: request.auth.claims[role]
values: ["admin"]
to:
- operation:
methods: ["GET", "POST", "PUT", "DELETE"]
paths: ["/api/admin/*"]
김개발 씨는 이제 모든 사용자가 JWT로 인증되도록 설정했습니다. 테스트를 돌려보니 잘 동작했습니다.
그런데 보안 감사를 진행하던 이과장님이 심각한 표정으로 말했습니다. "일반 사용자 계정으로 로그인했는데 관리자 대시보드에 접근이 되네요?" 김개발 씨는 깜짝 놀랐습니다.
분명 JWT 검증은 통과했지만, 그 사용자가 관리자인지 일반 사용자인지는 확인하지 않았던 것입니다. 인증(Authentication)과 인가(Authorization)는 별개의 문제였습니다.
그렇다면 AuthorizationPolicy는 어떻게 동작할까요? 쉽게 비유하자면, AuthorizationPolicy는 마치 호텔 키카드 시스템과 같습니다.
투숙객 모두가 키카드를 받지만(인증), 각 카드는 특정 층의 특정 방만 열 수 있습니다(인가). VIP 고객은 라운지와 헬스장에 접근할 수 있지만, 일반 고객은 자신의 객실만 열 수 있습니다.
이처럼 AuthorizationPolicy도 인증된 사용자 중에서 누가 어떤 리소스에 접근할 수 있는지 세밀하게 제어합니다. 접근 제어가 없으면 어떤 문제가 생길까요?
과거 많은 서비스 장애와 보안 사고가 접근 제어 누락으로 발생했습니다. 2019년 한 금융 앱은 사용자 ID만 바꾸면 다른 사람의 계좌를 조회할 수 있었습니다.
JWT는 있었지만 해당 계좌의 소유자인지 확인하지 않았기 때문입니다. 또한 내부 관리 API가 외부에 노출되어 공격자가 시스템을 장악한 사례도 있습니다.
인증만으로는 부족합니다. 반드시 인가가 필요합니다.
바로 이런 문제를 해결하기 위해 Istio의 AuthorizationPolicy가 등장했습니다. AuthorizationPolicy를 사용하면 화이트리스트 방식으로 명시적으로 허용된 것만 접근할 수 있습니다.
JWT 클레임의 role, group, email 등을 조건으로 사용할 수 있습니다. 또한 HTTP 메서드, 경로, IP 주소 등 다양한 조건을 조합하여 정교한 규칙을 만들 수 있습니다.
무엇보다 애플리케이션 코드 수정 없이 정책 파일만으로 권한 체계를 변경할 수 있다는 큰 이점이 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 첫 번째 정책은 deny-all입니다. 빈 spec을 가진 AuthorizationPolicy는 모든 요청을 거부합니다.
이것을 네임스페이스에 먼저 적용하면 기본적으로 모든 것이 차단됩니다. 다음으로 두 번째 정책에서 admin-service에 대한 ALLOW 규칙을 정의합니다.
requestPrincipals가 있다는 것은 JWT가 있어야 한다는 의미입니다. 그리고 when 조건에서 JWT의 role 클레임이 "admin"인 경우만 허용합니다.
to 섹션에서는 /api/admin/* 경로에 대한 모든 HTTP 메서드를 허용합니다. 이렇게 하면 관리자만 관리 API에 접근할 수 있습니다.
실제 현업에서는 어떻게 활용할까요? 예를 들어 SaaS 플랫폼을 개발한다고 가정해봅시다.
Free, Pro, Enterprise 세 가지 구독 플랜이 있습니다. Free 사용자는 읽기만 가능하고, Pro는 월 1000건까지 쓰기가 가능하며, Enterprise는 무제한입니다.
이런 복잡한 권한을 코드로 구현하면 서비스마다 중복되고 실수하기 쉽습니다. AuthorizationPolicy로 JWT의 subscription_tier 클레임을 확인하여 중앙에서 관리하면 일관성이 보장됩니다.
Slack, Notion 같은 SaaS 기업들이 이런 방식을 사용합니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 DENY와 ALLOW 정책의 우선순위를 혼동하는 것입니다. Istio에서는 DENY가 ALLOW보다 우선합니다.
따라서 DENY 규칙에 걸리면 ALLOW 규칙이 있어도 거부됩니다. 또한 정책을 너무 세밀하게 만들면 관리가 어려워집니다.
역할 기반(RBAC)으로 시작하여 필요할 때만 속성 기반(ABAC)으로 확장하는 것이 좋습니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
이과장님의 지적을 받은 김개발 씨는 즉시 AuthorizationPolicy를 추가했습니다. "먼저 deny-all을 적용하고, 필요한 경로만 하나씩 열어가는 방식으로 하겠습니다!" "정확해요.
그게 가장 안전한 방법입니다." AuthorizationPolicy를 제대로 이해하면 제로 트러스트 보안 모델을 구현하여 내부 위협도 방어할 수 있습니다. 여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - deny-all을 먼저 적용하고 필요한 것만 ALLOW하는 화이트리스트 방식을 사용하세요
- DENY 정책이 ALLOW보다 우선한다는 점을 기억하세요
- istioctl analyze 명령으로 정책 충돌을 미리 감지할 수 있습니다
5. JWT 검증 상세
김개발 씨가 JWT 검증을 적용했지만, 로그를 보니 이상한 요청들이 보였습니다. 만료된 토큰, 잘못된 발급자, 심지어 서명이 위조된 토큰까지.
박시니어 개발자가 말했습니다. "JWT 검증은 생각보다 복잡해요.
제대로 설정하지 않으면 보안 구멍이 생깁니다."
JWT 검증은 단순히 토큰 존재 여부만 확인하는 것이 아니라 서명 검증, 발급자 확인, 만료 시간 체크, 오디언스 검증 등 여러 단계를 거칩니다. Istio는 이 모든 과정을 자동화하되, 개발자가 정확한 설정을 제공해야 합니다.
JWKS 엔드포인트, 알고리즘, 클레임 검증 등을 올바르게 구성하는 것이 핵심입니다.
다음 코드를 살펴봅시다.
apiVersion: security.istio.io/v1beta1
kind: RequestAuthentication
metadata:
name: jwt-validation-detailed
namespace: production
spec:
selector:
matchLabels:
app: api-gateway
jwtRules:
- issuer: "https://auth.example.com"
# JWKS URI - 공개키를 가져올 위치
jwksUri: "https://auth.example.com/.well-known/jwks.json"
# 오디언스 검증 - 이 서비스를 대상으로 한 토큰인지 확인
audiences:
- "api.example.com"
- "mobile.example.com"
# 토큰 위치 지정
fromHeaders:
- name: Authorization
prefix: "Bearer "
# 쿼리 파라미터나 쿠키에서도 토큰을 받을 수 있음
fromParams:
- "access_token"
# JWT 페이로드를 헤더로 전달
outputPayloadToHeader: "x-jwt-payload"
김개발 씨는 RequestAuthentication을 적용하고 안심했습니다. 하지만 며칠 후 모니터링 대시보드를 보니 이상한 패턴이 보였습니다.
401 에러가 간헐적으로 발생하고, 어떤 요청은 통과되었다가 같은 토큰으로 다시 요청하면 거부되었습니다. 박시니어 개발자가 로그를 분석하며 설명했습니다.
"JWT 검증은 여러 단계로 이루어져요. 서명 검증만 통과한다고 끝이 아닙니다.
발급자가 맞는지, 만료되지 않았는지, 이 서비스를 대상으로 한 토큰인지 모두 확인해야 해요." 그렇다면 JWT 검증은 구체적으로 어떤 과정을 거칠까요? 쉽게 비유하자면, JWT 검증은 마치 공항 보안 검사와 같습니다.
먼저 여권이 진짜인지 확인하고(서명 검증), 발급 국가가 맞는지 보고(발급자 확인), 유효기간이 지나지 않았는지 체크하며(만료 시간), 목적지가 이 공항이 맞는지 확인합니다(오디언스). 이 중 하나라도 실패하면 탑승이 거부됩니다.
JWT 검증도 동일한 다단계 프로세스를 거칩니다. JWT 검증을 제대로 하지 않으면 어떤 문제가 생길까요?
실제로 많은 보안 사고가 잘못된 JWT 검증으로 발생했습니다. 2015년 어떤 서비스는 서명 검증을 하지 않아 공격자가 임의의 JWT를 생성하여 관리자 권한을 획득했습니다.
2018년에는 만료 시간을 체크하지 않아 탈취된 토큰이 영원히 유효했던 사례도 있습니다. 또한 오디언스를 검증하지 않아 다른 서비스용 토큰으로 접근이 가능했던 경우도 있습니다.
각 검증 단계를 빠짐없이 수행해야 합니다. 바로 이런 문제를 해결하기 위해 Istio는 포괄적인 JWT 검증 기능을 제공합니다.
Istio의 JWT 검증은 **JWKS(JSON Web Key Set)**를 자동으로 다운로드하고 캐시합니다. 서명 알고리즘은 RS256, ES256 등 표준 알고리즘을 지원합니다.
audiences 필드로 오디언스를 검증하고, 만료 시간(exp)과 발급 시간(iat)도 자동으로 체크합니다. 무엇보다 검증에 실패하면 상세한 로그를 남겨 디버깅이 쉽다는 큰 이점이 있습니다.
위의 코드를 한 줄씩 살펴보겠습니다. 먼저 issuer 필드는 JWT의 iss 클레임과 정확히 일치해야 합니다.
대소문자와 슬래시 하나까지 정확해야 합니다. 다음으로 jwksUri는 공개키를 가져올 위치입니다.
Istio는 여기서 키를 다운로드하여 서명을 검증합니다. audiences는 JWT의 aud 클레임이 이 목록에 포함되어야 한다는 의미입니다.
여러 클라이언트(웹, 모바일)가 있으면 모두 나열합니다. fromHeaders와 fromParams는 토큰을 어디서 찾을지 지정합니다.
RESTful API는 헤더를, WebSocket이나 스트리밍은 쿼리 파라미터를 사용할 수 있습니다. 실제 현업에서는 어떤 시나리오가 있을까요?
예를 들어 글로벌 게임 플랫폼을 운영한다고 가정해봅시다. Auth0, Firebase Auth, 자체 인증 서버 등 여러 발급자가 있을 수 있습니다.
각 발급자마다 jwtRules를 추가하면 됩니다. 또한 웹 클라이언트는 헤더로, 게임 클라이언트는 WebSocket 연결 시 쿼리 파라미터로 토큰을 보낼 수 있습니다.
Istio는 모든 경우를 처리합니다. Epic Games, Riot Games 같은 대형 게임사가 이런 복잡한 인증 시나리오를 관리합니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 issuer URL의 트레일링 슬래시입니다.
JWT에 "https://auth.example.com/"이 있는데 설정에 "https://auth.example.com"로 했다면 검증이 실패합니다. 정확히 일치해야 합니다.
또한 JWKS URI가 HTTPS가 아니면 보안 위험이 있습니다. 공격자가 중간에 공개키를 바꿀 수 있기 때문입니다.
반드시 HTTPS를 사용해야 합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 설명을 들은 김개발 씨는 설정을 다시 점검했습니다. "아, issuer에 트레일링 슬래시가 빠졌네요.
그리고 audiences도 추가해야겠어요!" 설정을 수정하자 401 에러가 사라졌습니다. JWT 검증을 제대로 이해하면 토큰 기반 인증의 모든 보안 요구사항을 충족할 수 있습니다.
여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - issuer와 audiences는 정확히 일치해야 합니다. 트레일링 슬래시도 확인하세요
- JWKS URI는 반드시 HTTPS를 사용하세요
- istio-proxy 로그에서 "Jwt verification fails" 메시지로 실패 원인을 확인할 수 있습니다
6. 보안 모범 사례
김개발 씨는 이제 mTLS, 인증, 인가까지 모두 적용했습니다. 하지만 보안팀 이과장님이 최종 점검을 하며 말했습니다.
"기술적으로는 완벽하지만, 운영 관점에서 몇 가지 개선할 점이 있어요." 보안은 한 번 설정하고 끝이 아니라 지속적인 관리가 필요했습니다.
보안 모범 사례는 Istio 보안 기능을 올바르게 적용하고 유지하기 위한 지침입니다. 기술적 설정뿐만 아니라 정책 관리, 모니터링, 감사, 사고 대응까지 포함합니다.
최소 권한 원칙, 심층 방어, 정기 감사, 자동화 등 보안 운영의 핵심 원칙을 Istio 환경에 적용하는 것이 목표입니다.
다음 코드를 살펴봅시다.
# 보안 모니터링을 위한 메트릭 수집
apiVersion: v1
kind: ConfigMap
metadata:
name: istio-security-monitoring
namespace: istio-system
data:
# Prometheus에서 보안 관련 메트릭 수집
monitoring: |
# mTLS 연결 성공/실패 모니터링
- istio_tcp_connections_closed_total
- istio_tcp_connections_opened_total
# JWT 검증 성공/실패 모니터링
- istio_requests_total{response_code="401"}
- istio_requests_total{response_code="403"}
# AuthorizationPolicy deny 카운트
- envoy_http_rbac_denied
---
# 정기적인 보안 설정 감사를 위한 스크립트
# 실행: istioctl analyze --all-namespaces
# 잘못된 설정, 정책 충돌, 보안 구멍 자동 탐지
김개발 씨는 6개월간 Istio 보안 기능을 적용하며 많은 것을 배웠습니다. mTLS로 서비스 간 통신을 암호화하고, JWT로 사용자를 인증하며, AuthorizationPolicy로 세밀한 권한을 제어했습니다.
기술적으로는 완벽해 보였습니다. 하지만 첫 보안 감사에서 여러 문제가 발견되었습니다.
이과장님이 보고서를 보여주며 설명했습니다. "일부 개발팀이 PERMISSIVE 모드를 테스트 후 STRICT로 바꾸지 않았어요.
또 몇몇 서비스는 deny-all 정책이 없어서 기본적으로 모든 접근을 허용하고 있습니다. 정책 변경 이력도 추적되지 않고 있고요." 그렇다면 보안 모범 사례는 구체적으로 무엇을 의미할까요?
쉽게 비유하자면, 보안 모범 사례는 마치 건물 보안 관리와 같습니다. 최신 보안 시스템을 설치하는 것(기술)도 중요하지만, 정기적으로 점검하고, CCTV 영상을 모니터링하며, 출입 기록을 감사하고, 비상 상황에 대응하는 프로세스(운영)가 더 중요합니다.
아무리 좋은 시스템도 관리하지 않으면 무용지물입니다. 보안 설정을 한 번만 하고 관리하지 않으면 어떻게 될까요?
많은 기업이 초기에는 완벽하게 설정했지만 시간이 지나면서 보안 수준이 저하됩니다. 개발팀이 바쁘다는 이유로 임시로 PERMISSIVE 모드를 켜놓고 잊어버립니다.
퇴사한 직원의 JWT가 여전히 유효합니다. 새로 추가된 서비스는 AuthorizationPolicy가 없어 누구나 접근할 수 있습니다.
이런 보안 부채가 쌓이다가 결국 사고로 이어집니다. 바로 이런 문제를 해결하기 위해 체계적인 보안 운영이 필요합니다.
보안 모범 사례를 적용하면 최소 권한 원칙으로 필요한 최소한의 권한만 부여할 수 있습니다. 심층 방어로 여러 계층의 보안 장치를 배치합니다.
정기 감사로 설정 오류나 정책 위반을 조기에 발견합니다. 무엇보다 **자동화와 IaC(Infrastructure as Code)**로 사람의 실수를 최소화하고 일관성을 유지할 수 있다는 큰 이점이 있습니다.
위의 설정을 단계별로 살펴보겠습니다. 먼저 보안 모니터링을 위한 메트릭을 정의합니다.
istio_tcp_connections 메트릭으로 mTLS 연결 성공률을 추적할 수 있습니다. 갑자기 mTLS 실패가 증가하면 설정 오류나 공격 시도일 수 있습니다.
401/403 응답 코드를 모니터링하여 인증/인가 실패 패턴을 파악합니다. 정상적인 사용자라면 401이 거의 없어야 하는데 많이 발생한다면 토큰 갱신 로직에 문제가 있거나 공격받고 있을 수 있습니다.
envoy_http_rbac_denied는 AuthorizationPolicy로 거부된 요청 수입니다. 이를 추적하여 정책이 너무 엄격한지 적절한지 판단할 수 있습니다.
실제 현업에서는 어떤 프로세스를 구축할까요? 예를 들어 대형 이커머스 플랫폼을 운영한다고 가정해봅시다.
보안팀은 매주 istioctl analyze를 실행하여 정책 충돌이나 설정 오류를 찾습니다. 개발팀이 새 서비스를 배포하면 CI/CD 파이프라인에서 자동으로 deny-all 정책을 함께 배포하도록 강제합니다.
Grafana 대시보드에서 mTLS 사용률, JWT 검증 성공률, 인가 거부율을 실시간 모니터링합니다. 분기마다 외부 보안 전문가가 펜테스트를 수행하여 놓친 부분을 점검합니다.
쿠팡, 마켓컬리 같은 대형 플랫폼이 이런 체계를 갖추고 있습니다. 하지만 주의할 점도 있습니다.
초보 개발자들이 흔히 하는 실수 중 하나는 보안을 일회성 프로젝트로 생각하는 것입니다. "한 번 설정했으니 끝"이라고 생각하지만, 보안은 지속적인 프로세스입니다.
또한 모든 것을 자동화하려다 복잡도가 높아져 오히려 실수가 늘어날 수 있습니다. 중요한 20%에 집중하여 점진적으로 자동화를 확대하는 것이 좋습니다.
완벽한 보안은 없습니다. 계속 개선하는 것이 목표입니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. 이과장님의 감사 결과를 받은 김개발 씨는 액션 플랜을 수립했습니다.
"먼저 모든 네임스페이스의 mTLS 모드를 점검하여 PERMISSIVE를 STRICT로 전환하겠습니다. 그리고 Grafana 대시보드를 만들어 보안 메트릭을 모니터링하고, 매주 istioctl analyze를 실행하는 CI 잡을 추가하겠습니다." 6개월 후, 김개발 씨는 이제 보안 챔피언으로 불립니다.
새로 입사한 주니어 개발자에게 Istio 보안을 가르치며 말합니다. "보안은 한 번 설정하고 끝이 아니에요.
매일 모니터링하고, 매주 점검하며, 지속적으로 개선해야 합니다." 보안 모범 사례를 제대로 이해하면 기술적 설정뿐만 아니라 조직 차원의 보안 문화를 구축할 수 있습니다. 여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - istioctl analyze를 CI/CD 파이프라인에 통합하여 배포 전 자동 검증하세요
- Grafana 대시보드로 보안 메트릭을 실시간 모니터링하세요
- 정책 변경은 반드시 Git으로 버전 관리하고 리뷰 프로세스를 거치세요
- 최소 권한 원칙을 적용하여 deny-all부터 시작하고 필요한 것만 허용하세요
- 정기적으로 토큰 만료 시간, 인증서 갱신 주기를 점검하세요
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
Istio 트래픽 관리 완벽 가이드
Istio의 트래픽 관리 기능을 마스터하는 완벽 가이드입니다. VirtualService와 DestinationRule을 활용한 라우팅부터 트래픽 분할, 헤더 기반 라우팅까지 실무에 필요한 모든 내용을 다룹니다.
Istio 설치와 구성 완벽 가이드
Kubernetes 환경에서 Istio 서비스 메시를 설치하고 구성하는 방법을 초급 개발자도 쉽게 이해할 수 있도록 실무 스토리와 비유로 풀어낸 가이드입니다. istioctl 설치부터 사이드카 주입까지 단계별로 학습합니다.
서비스 메시 완벽 가이드
마이크로서비스 간 통신을 안전하고 효율적으로 관리하는 서비스 메시의 핵심 개념부터 실전 도입까지, 초급 개발자를 위한 완벽한 입문서입니다. Istio와 Linkerd 비교, 사이드카 패턴, 실무 적용 노하우를 담았습니다.
Helm 마이크로서비스 패키징 완벽 가이드
Kubernetes 환경에서 마이크로서비스를 효율적으로 패키징하고 배포하는 Helm의 핵심 기능을 실무 중심으로 학습합니다. Chart 생성부터 릴리스 관리까지 체계적으로 다룹니다.
Helm 차트 기초 완벽 가이드
쿠버네티스 패키지 매니저 Helm의 기본 개념부터 차트 구조까지 실무에서 바로 활용할 수 있도록 친절하게 설명합니다. 초급 개발자도 쉽게 따라할 수 있는 실전 예제와 함께 Helm 차트의 핵심을 마스터해보세요.