Istio Ingress Gateway에서 JWT 인증 활성화하기
Istio는 서비스 메시 기술을 통해 마이크로서비스 아키텍처에 다양한 기능을 제공합니다. 그중 하나가 Istio Ingress Gateway로, 외부 트래픽을 내부 서비스로 라우팅하는 역할을 합니다. 이번 포스트에서는 Istio Ingress Gateway에 JWT(JSON Web Token) 인증을 적용하는 방법을 간단한 테스트와 함께 알아봅니다.
JWT는 마이크로서비스 환경에서 안전한 인증을 위해 널리 사용되는 표준입니다. Istio에 JWT 인증을 적용하면 외부 요청에 대한 안정성을 높일 수 있습니다. 이 과정에서 Istio의 다양한 기능을 활용하여 인증 정책을 효과적으로 설정하는 방법을 살펴보겠습니다.
(※ 이 포스트에서는 Istio를 설치하는 과정은 포함하지 않습니다.)
목차
1. 전제 조건
2. Istio AuthorizationPolicy 생성
3. Istio RequestAuthentication 리소스 생성
4. Istio Ingress Gateway 리소스 생성
5. Istio Ingress VirtualService 리소스 생성
6. JWT 인증 테스트
1. 전제 조건
이번 테스트에서는 Istio를 profile=demo로 배포했습니다.
| default | demo | minimal | remote | empty | preview | ambient | |
| Core componets | |||||||
| istio-egressgateway | ✔ | ||||||
| istio-ingressgateway | ✔ | ✔ | ✔ | ||||
| istiod | ✔ | ✔ | ✔ | ✔ | ✔ | ||
| CNI | ✔ | ||||||
| Ztunnel | ✔ |
$ kubectl get pods -n istio-system
NAME READY STATUS RESTARTS AGE
istio-egressgateway-7cfd5c8676-zglc9 1/1 Running 0 2d21h
istio-ingressgateway-86f46c495b-rgztf 1/1 Running 0 2d21h
istiod-f68799b78-5dmbr 1/1 Running 0 2d21h
현재 Istio Ingress Gateway는 NodePort 형식으로 다음과 같이 포트 바인딩되어 있습니다:
$ kubectl get svc -n istio-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-ingressgateway NodePort 10.109.89.42 <none> 80:31000/TCP
Profile=demo로 배포하게 되면 Istio-Ingressgateway라는 파드가 생성되는데, 이 파드가 Istio Ingress Gateway의 역할을 수행합니다.
테스트에서 사용할 서비스는 다음과 같습니다.
nginx1:
apiVersion: apps/v1
kind: Deployment
metadata:
name: ho-deploy1
namespace: nginx-ingress
spec:
selector:
matchLabels:
app: ho-deploy1
template:
metadata:
labels:
app: ho-deploy1
spec:
containers:
- name: ho-container1
image: gusgh13900/nginxstore-text-plain:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: ho-svc1
namespace: nginx-ingress
spec:
ports:
- name: http-ho-svc-port1
port: 80
targetPort: 80
selector:
app: ho-deploy1
nginx1 response:
$ curl test.com:31000/nginx1/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx1!</h1>
</body>
</html>
nginx2:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy1
namespace: istio-system
spec:
selector:
matchLabels:
app: nginx-deploy1
template:
metadata:
labels:
app: nginx-deploy1
spec:
containers:
- name: nginx-container1
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-svc1
namespace: istio-system
spec:
ports:
- name: http-ho-svc-port1
port: 80
targetPort: 80
selector:
app: nginx-deploy1
nginx2 response:
$ curl test.com:31000/nginx2/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx2!</h1>
</body>
</html>
2. Istio AuthorizationPolicy 생성
Istio Ingress 의 Authorization 리소스는 서비스 메시 내의 서비스에 대한 액세스 제어와 권한 부여 정책을 정의하는데 사용됩니다. 이를 통해 마이크로서비스 아키텍처에서 보안을 강화할 수 있습니다.
JWT를 활성화하기 위한 AuthorizationPolicy 리소스 구성은 다음과 같습니다:
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: jwt-auth
namespace: istio-system
spec:
selector:
matchLabels:
istio: ingressgateway # ingressgateway label이 붙은 리소스에 적용
action: ALLOW # 정책에 부합하는 요청 허용
rules:
- from:
- source:
requestPrincipals: ["*"] # 모든 인증된 요청에 대해 정책 적용
위 리소스 구성에서 중요한 부분은 다음과 같습니다:
- spec.selector.matchLabels.istio: ingressgateway – ingressgateway label이 붙은 리소스에 JWT를 적용합니다. 여기서 ingressgateway label이 붙은 리소스는 위의 전제 조건 섹션에서 확인할 수 있는 istio-ingressgateway 파드에 default로 label 되어 있습니다.
- spec.rules[0].from.source.requestPrincipals: [“*”] – 모든 인증된 요청에 대해 이 정책이 적용됩니다.
필드에 대한 자세한 내용은 여기를 클릭하세요.
해당 리소스를 배포한 뒤 확인합니다:
$ kubectl get authorizationpolicy -n istio-system
NAME ACTION AGE
jwt-auth ALLOW 57m
3. Istio RequestAuthentication 리소스 생성
Istio Ingress의 RequestAuthentication 리소스는 Istio의 보안 기능을 강화하고, 서비스 간 통신의 신뢰성을 높이는 데 중요한 역할을 합니다. 여기서는 JWT인증에 필요한 JWK 정보를 저장합니다:
apiVersion: security.istio.io/v1beta1
kind: RequestAuthentication
metadata:
name: jwt-authentication
namespace: istio-system
spec:
selector:
matchLabels:
istio: ingressgateway # ingressgateway label이 붙은 리소스에 적용
jwtRules: # JWT 인증 규칙 정의
- issuer: "testing@secure.istio.io"
jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.22/security/tools/jwt/samples/jwks.json"
해당 테스트에서 사용한 JWT 인증 규칙은 Istio에서 제공하는 jwk를 사용하였습니다.
4. Istio Ingress Gateway 리소스 생성
이제 JWT가 적용되었는지 확인하기 위해 요청을 받을 Gateway와 VirtualService 리소스를 생성합니다.
먼저 Gateway는 다음과 같습니다:
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: main-gateway
namespace: istio-system
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- test.com
5. Istio Ingress VirtualService 리소스 생성
위에서 생성한 JWT가 적용되었는지 확인하기 위해 VirtualService 리소스를 생성하여 서비스들에 접근해보겠습니다.
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: istio-virtualservice
namespace: istio-system
spec:
hosts:
- test.com
gateways: # 위에서 생성한 Gateway 정의
- main-gateway
http:
- name: nginxstore-text-plain
match:
- uri:
prefix: /nginx1/ # /text로 들어온 요청을 라우팅
route:
- destination:
host: ho-svc1.istio-system.svc.cluster.local # ho-svc1이라는 서비스로 라우팅
- name: nginx-image
match:
- uri:
prefix: /nginx2/ # /text로 들어온 요청을 라우팅
route:
- destination:
host: nginx-svc1.istio-system.svc.cluster.local # ho-svc1이라는 서비스로 라우팅
이제 test.com:31000/nginx1/, test.com:31000/nginx2/ 로 요청을 보내면 전제 조건에서의 서비스 응답이 나타납니다.
6. JWT 인증 테스트
이제 JWT 인증이 잘 적용되었는지 확인합니다.
JWT 토큰이 없을 때:
$ curl test.com:31000/nginx1/
RBAC: access denied
$ curl test.com:31000/nginx2/
RBAC: access denied
JWT 토큰이 일치하지 않을 때:
$ curl -H "Authorization: Bearer Invalid" test.com:31000/nginx1/
Jwt is not in the form of Header.Payload.Signature with two dots and 3 sections
$ curl -H "Authorization: Bearer Invalid" test.com:31000/nginx2/
Jwt is not in the form of Header.Payload.Signature with two dots and 3 sections
JWT 토큰이 일치할 때:
$ curl -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0vxyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFWjcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgefSj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPTAa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUg" test.com:31000/nginx1/ -i
HTTP/1.1 200 OK
server: istio-envoy
date: Fri, 14 Jun 2024 05:51:15 GMT
content-type: text/html
content-length: 261
last-modified: Fri, 14 Jun 2024 01:51:03 GMT
etag: "666ba207-105"
accept-ranges: bytes
x-envoy-upstream-service-time: 1
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx1!</h1>
</body>
</html>
$ curl -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0vxyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFWjcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgefSj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPTAa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUg" test.com:31000/nginx2/ -i
HTTP/1.1 200 OK
server: istio-envoy
date: Fri, 14 Jun 2024 05:53:20 GMT
content-type: text/html
content-length: 261
last-modified: Fri, 14 Jun 2024 01:50:24 GMT
etag: "666ba1e0-105"
accept-ranges: bytes
x-envoy-upstream-service-time: 214
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx2!</h1>
</body>
</html>
JWT가 모든 서비스에 배포된 것을 확인할 수 있습니다.
Istio에 대한 컨설팅 및 기술지원이 필요하시면 아래 폼을 제출해주세요.