JWT 인증, API Key 인증 활성화 – NGINX Plus Ingress Controller
이 포스트에서는 NGINX Plus Ingress Controller를 사용하여 API Key 인증과 JWT 토큰 인증 방법에 대해 설명합니다.
목차
1. API Key 인증 활성화
1-1. ConfigMap 리소스 생성
1-2. VirtualServer 리소스 생성
1-3. API Key 인증 테스트
2. JWT 토큰 인증 활성화
2-1. Secret 리소스 생성
2-2. Policy 리소스 생성
2-3. VirtualServer 리소스 생성
2-4. JWT 토큰 인증 테스트
3. API Key 인증과 JWT 토큰 인증 동시 활성화
3-1. Policy 리소스 수정
3-2. VirtualServer 리소스 수정
3-3. API Key와 JWT 토큰 인증 테스트
1. API Key 인증 활성화
NGINX Plus Ingress Controller에서 API Key 인증을 활성화하는 방법은 NGINX Plus에서의 API Key 인증 로직과 같습니다.
이 인증 로직은 snippet을 사용하여 구현할 수 있습니다.
NGINX Plus Ingress Controller에서는 snippet은 기본적으로 비활성화되어 있습니다. snippet을 사용하려면 enable-snippets을 NGINX Plus Ingress Controller Deployment 리소스의 args에 추가해야 합니다.
Snippet에는 다음과 같은 단점이 있습니다.
- 복잡성 – Snippet을 사용하려면 다음을 수행해야 합니다.
- NGINX의 구성 기본 요소를 이해하고 올바른 NGINX 구성을 구현해야 합니다.
- Snippet이 구성의 다른 기능을 방해하지 않도록 IC가 NGINX 구성을 생성하는 방법을 이해합니다.
- 견고성 감소 – 잘못된 snippet 사용으로 인해 NGINX 구성이 유효하지 않게 되어 reload가 실패하게 됩니다. 이렇게 하면 snippet이 수정될 때까지 다른 Ingress 리소스에 대한 업데이트를 포함한 새로운 구성 업데이트가 방지됩니다.
- 보안에 미치는 영향 – Snippet은 NGINX 구성 기본 요소에 대한 액세스를 제공하며 해당 기본 요소는 Ingress Controller에 의해 검증되지 않습니다. 예를 들어, Snippet은 Ingress 리소스에 대한 TLS Termination에 사용되는 TLS 인증서 및 키를 제공하도록 NGINX를 구성할 수 있습니다.
참고: NGINX 구성에 잘못된 코드 조각이 포함된 경우 NGINX는 유효한 최신 구성으로 계속 작동합니다.
1-1. ConfigMap 리소스 생성
ConfigMap 리소스를 사용하면 NGINX 동작을 사용자 정의하거나 더욱더 미세한 조정을 할 수 있습니다.
ConfigMap 리소스에서 사용할 수 있는 snippet은 아래와 같습니다:
| ConfigMap Key | Description | Default | Example |
|---|---|---|---|
main-snippets | main 컨텍스트에서 사용자 지정 snippet을 설정합니다. | N/A | |
http-snippets | http 컨텍스트에서 사용자 지정 snippet을 설정합니다. | N/A | |
location-snippets | location 컨텍스트에서 사용자 지정 snippet을 설정합니다. | N/A | |
server-snippets | server 컨텍스트에서 사용자 지정 snippet을 설정합니다. | N/A | |
stream-snippets | stream 컨텍스트에서 사용자 지정 snippet을 설정합니다. | N/A | Support for TCP/UDP Load Balancing. |
API Key는 아래와 같이 정의할 수 있습니다:
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
namespace: nginx-ingress
data:
http-snippets: |
map_hash_bucket_size 128;
map $http_apikey $api_key_name {
default "";
"testapikey" "key_one";
}
map에 대한 자세한 가이드는 여기를 클릭하세요.
위에서 작성한 ConfigMap 리소스를 배포 및 확인합니다.
$ kubectl apply -f configmap.yaml
configmap/nginx-config configured
$ kubectl get configmap -n nginx-ingress
nginx-config 1 18h
1-2. VirtualServer 리소스 생성
VirtualServer 및 VirtualServerRoute 리소스는 Ingress 리소스의 대안으로 권장되는 부하 분산 구성입니다.
Release 1.5에 도입된 VirtualServer 및 VirtualServerRoute 리소스는 트래픽 분할 및 고급 콘텐츠 기반 라우팅과 같이 Ingress 리소스에서 지원되지 않는 사용 사례를 활성화합니다. 리소스는 Custom Resources로 구현됩니다.
이 문서는 리소스에 대한 참조 문서입니다. 특정 사용 사례에 대한 리소스 사용에 대한 추가 예를 보려면 GitHub 리포지토리의 example/custom-resources 폴더로 이동하세요.
아래와 같이 VirtualServer 리소스를 작성합니다:
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
name: apikey-virtualserver
namespace: nginx-ingress
spec:
host: apikey.example.com
server-snippets: | # server내에 snippet 추가
location = /_validate_apikey { # /_validate_apikey location 생성
internal; # 내부 요청에만 사용
if ($http_apikey = "") { return 401; } # apikey가 없을 경우 status code 401
if ($api_key_name = "") { return 403; } # apikey가 일치하지 않을 경우 status code 403
return 204; # 인증 성공시 return 204
}
ingressClassName: nginx
upstreams:
- name: root
service: svc1
port: 80
routes:
- path: /
location-snippets: auth_request /_validate_apikey; # / 경로에 API Key 인증 활성화
action:
pass: root
위 VirtualServer와 연결된 Service 리소스는 아래와 같습니다:
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy1
namespace: nginx-ingress
spec:
replicas: 2
selector:
matchLabels:
app: deploy1
template:
metadata:
labels:
app: deploy1
spec:
containers:
- name: containerd1
image: nginxdemos/nginx-hello:plain-text
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: svc1
namespace: nginx-ingress
spec:
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: http
selector:
app: deploy1
VirtualServer 리소스와 Deployment 및 Service를 배포 및 확인합니다.
$ kubectl apply -f virtualserver.yaml
virtualserver.k8s.nginx.org/apikey-virtualserver created
$ kubectl apply -f service.yaml
deployment.apps/deploy1 created
service/svc1 created
$ kubectl get vs -n nginx-ingress
NAME STATE HOST IP PORTS AGE
apikey-virtualserver Valid apikey.example.com 57s
$ kubectl get svc -n nginx-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc1 ClusterIP 10.99.117.5 <none> 80/TCP 58s
1-3. API Key 인증 테스트
Curl 명령어를 사용하여 API Key 인증이 적용 되었는지 확인합니다.
API Key가 없는 경우:
$ curl -I apikey.example.com
HTTP/1.1 401 Unauthorized
Server: nginx/1.25.3
Date: Fri, 12 Apr 2024 02:20:51 GMT
Content-Type: text/html
Content-Length: 179
Connection: keep-alive
API Key가 일치하지 않는 경우:
$ curl -I -H "apikey: Invalid" apikey.example.com
HTTP/1.1 403 Forbidden
Server: nginx/1.25.3
Date: Fri, 12 Apr 2024 02:22:22 GMT
Content-Type: text/html
Content-Length: 153
Connection: keep-alive
API Key 인증이 통과된 경우:
$ curl -I -H "apikey: testapikey" apikey.example.com
HTTP/1.1 200 OK
Server: nginx/1.25.3
Date: Fri, 12 Apr 2024 02:23:14 GMT
Content-Type: text/plain
Content-Length: 156
Connection: keep-alive
Expires: Fri, 12 Apr 2024 02:23:13 GMT
Cache-Control: no-cache
2. JWT 토큰 인증 활성화
NGINX Plus Ingress Controller에서 JWT 토큰 인증을 활성화하기 위해서는 Policy와 Secret 리소스가 사용됩니다.
2-1. Secret 리소스 생성
Secret 리소스는 암호, 토큰 또는 키와 같은 소량의 중요한 데이터를 포함하는 오브젝트입니다. 이를 사용하지 않으면 중요한 정보가 Pod 명세나 컨테이너 이미지에 포함될 수 있습니다.
아래는 JWT 토큰 인증에 사용되는 JWK를 명시하는 Secret 리소스 입니다:
apiVersion: v1
kind: Secret
metadata:
name: jwk-secret
namespace: nginx-ingress
type: nginx.org/jwk
data:
jwk: IHsia2V5cyI6DQogW3sNCiAiayI6ImJtZHBibmciLA0KICJrdHkiOiJvY3QiDQogfV0NCiB9
위에서 data.jwk는 Base64로 인코딩된, JWK입니다. 해당 JWK 내용은 아래와 같습니다:
{
"keys":
[{
"k":"bmdpbng",
"kty":"oct"
}]
}
여기서 “k”:”bmdpbng”는 디코딩하면 “nginx”라는 값입니다.
2-2. Policy 리소스 생성
Policy 리소스를 사용하면 액세스 제어 및 속도 제한과 같은 기능을 구성할 수 있습니다.
JWT 토큰 인증을 위한 구성을 작성합니다:
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
name: jwt-policy
namespace: nginx-ingress
spec:
jwt:
realm: MyAPI # JWT 영역
secret: jwk-secret # 위에서 생성한 Secret 리소스의 이름.
token: $http_token # JSON 웹 토큰이 포함된 변수를 지정.
# token에 혀용되는 변수는 $http_, $arg_, $cookie_ 입니다.
작성한 Policy 리소스를 배포 및 확인합니다.
$ kubectl apply -f policy.yaml
policy.k8s.nginx.org/jwt-policy created
$ kubectl get policy -n nginx-ingress
NAME STATE AGE
jwt-policy Valid 68m
2-3. VirtualServer 리소스 생성
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
name: jwt-virtualserver
namespace: nginx-ingress
spec:
host: jwt.example.com
ingressClassName: nginx
policies: # Policy 정의
- name: jwt-policy # 위에서 생성한 Policy name
upstreams:
- name: root
service: svc1
port: 80
routes:
- path: /
action:
pass: root
위와 같이 policies를 구성하면 전역에 JWT 토큰 인증이 실행됩니다. 이제 생성한 VirtualServer 리소스를 배포 및 확인합니다:
$ kubectl apply -f virtualserver.yaml
virtualserver.k8s.nginx.org/jwt-virtualserver created
$ kubectl get vs -n nginx-ingress
NAME STATE HOST IP PORTS AGE
jwt-virtualserver Valid jwt.example.com 30s
2-4. JWT 토큰 인증 테스트
Curl 명령어를 사용하여 JWT 토큰 인증이 적용 되었는지 확인합니다.
JWT 토큰이 없는 경우:
$ curl jwt.example.com
HTTP/1.1 401 Unauthorized
Server: nginx/1.25.3
Date: Fri, 12 Apr 2024 04:24:12 GMT
Content-Type: text/html
Content-Length: 179
Connection: keep-alive
WWW-Authenticate: Bearer realm="MyAPI"
JWT 토큰이 일치하지 않는 경우:
$ curl -I -H "token: Invalid" jwt.example.com
HTTP/1.1 401 Unauthorized
Server: nginx/1.25.3
Date: Fri, 12 Apr 2024 04:26:36 GMT
Content-Type: text/html
Content-Length: 179
Connection: keep-alive
WWW-Authenticate: Bearer realm="MyAPI",error="invalid_token"
JWT 토큰 인증이 통과된 경우:
$ curl -I -H "token: eyJrZXlzIjpbeyJrIjoiYm1kcGJuZyIsImt0eSI6Im9jdCJ9XSwiYWxnIjoiSFMyNTYifQ.e30.KQYoEaVx_xjdwz0u1JVEyzFR-tORwghAuvtfDip0Rs4" jwt.example.com
HTTP/1.1 200 OK
Server: nginx/1.25.3
Date: Fri, 12 Apr 2024 04:25:23 GMT
Content-Type: text/plain
Content-Length: 156
Connection: keep-alive
Expires: Fri, 12 Apr 2024 04:25:22 GMT
Cache-Control: no-cache
3. API Key 인증과 JWT 토큰 인증 동시 활성화
API Key와 JWT 토큰 인증을 같이 사용할 수도 있습니다.
이 예시에서는 API Key는 헤더로, JWT 토큰은 파라미터로 값을 넘겨 인증합니다.
이 포스트에서 사용했던 Secret, Policy, ConfigMap, VirtualServer를 그대로 사용하며, Policy 리소스와 VirtualServer 리소스를 수정합니다.
3-1. Policy 리소스 수정
기존 Policy 리소스:
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
name: jwt-policy
namespace: nginx-ingress
spec:
jwt:
realm: MyAPI
secret: jwk-secret
수정한 Policy 리소스:
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
name: jwt-policy
namespace: nginx-ingress
spec:
jwt:
realm: MyAPI
secret: jwk-secret
token: $arg_token
위 섹션에서 사용한 Policy 리소스 파일에서 spec.jwt.token 부분을 $http_token에서 $arg_token으로 수정한 뒤 배포합니다.
$ kubectl apply -f policy.yaml
위 구성으로 인해 JWT 토큰은 파라미터 형식으로 인증합니다.
3-2. VirtualServer 리소스 수정
위에서 구성한 API Key 인증 로직과 JWT 토큰 인증 로직을 전부 추가하면 됩니다:
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
name: auth-virtualserver
namespace: nginx-ingress
spec:
host: auth.example.com
server-snippets: | # server내에 snippet 추가
location = /_validate_apikey { # /_validate_apikey location 생성
internal; # 내부 요청에만 사용
if ($http_apikey = "") { return 401; } # apikey가 없을 경우 status code 401
if ($api_key_name = "") { return 403; } # apikey가 일치하지 않을 경우 status code 403
return 204; # 인증 성공시 return 204
}
ingressClassName: nginx
policies: # Policy 정의
- name: jwt-policy # 위에서 생성한 Policy name
upstreams:
- name: root
service: svc1
port: 80
routes:
- path: /
location-snippets: auth_request /_validate_apikey; # / 경로에 API Key 인증 활성화
action:
pass: root
배포 및 확인:
$ kubectl apply -f virtualserver.yaml
virtualserver.k8s.nginx.org "auth-virtualserver" deleted
$ kubectl get vs -n nginx-ingress
NAME STATE HOST IP PORTS AGE
auth-virtualserver Valid auth.example.com 3s
3-3. API Key와 JWT 토큰 인증 테스트
Curl 명령어를 사용하여 API Key와 JWT 토큰 인증이 적용 되었는지 확인합니다.
API Key와 JWT 토큰 모두 없는 경우:
$ curl -I auth.example.com
HTTP/1.1 401 Unauthorized
Server: nginx/1.25.3
Date: Fri, 12 Apr 2024 05:20:36 GMT
Content-Type: text/html
Content-Length: 179
Connection: keep-alive
WWW-Authenticate: Bearer realm="MyAPI"
인증된 API Key만 있는 경우:
$ curl -I -H "apikey: testapikey" auth.example.com
HTTP/1.1 401 Unauthorized
Server: nginx/1.25.3
Date: Fri, 12 Apr 2024 05:21:56 GMT
Content-Type: text/html
Content-Length: 179
Connection: keep-alive
WWW-Authenticate: Bearer realm="MyAPI"
인증된 JWT 토큰만 있는 경우:
$ curl -I auth.example.com/?token=eyJrZXlzIjpbeyJrIjoiYm1kcGJuZyIsImt0eSI6Im9jdCJ9XSwiYWxnIjoiSFMyNTYifQ.e30.KQYoEaVx_xjdwz0u1JVEyzFR-tORwghAuvtfDip0Rs4
HTTP/1.1 401 Unauthorized
Server: nginx/1.25.3
Date: Fri, 12 Apr 2024 05:22:49 GMT
Content-Type: text/html
Content-Length: 179
Connection: keep-alive
API Key와 JWT 토큰 인증이 모두 통과된 경우:
$ curl -I -H "apikey: testapikey" auth.example.com/?token=eyJrZXlzIjpbeyJrIjoiYm1kcGJuZyIsImt0eSI6Im9jdCJ9XSwiYWxnIjoiSFMyNTYifQ.e30.KQYoEaVx_xjdwz0u1JVEyzFR-tORwghAuvtfDip0Rs4
HTTP/1.1 200 OK
Server: nginx/1.25.3
Date: Fri, 12 Apr 2024 05:24:31 GMT
Content-Type: text/plain
Content-Length: 281
Connection: keep-alive
Expires: Fri, 12 Apr 2024 05:24:30 GMT
Cache-Control: no-cache
위 예시 외에도 API Key가 일치하지 않거나, 반대로 JWT 토큰이 일치하지 않아도 인증에 실패합니다.
NGINX Plus를 직접 사용해 보시려면 30일 무료 평가판을 신청하거나 NGINX STORE에 연락하여 논의하십시오.
NGINX에 대한 최신 정보들을 빠르게 전달받고 싶으시다면, 아래의 뉴스레터를 구독하세요.