NGINX는 오랫동안 고성능 웹 서버와 로드 밸런서를 구동하는 최고의 선택 중 하나로 인정받아 왔습니다. 그러나 마이크로서비스 아키텍처의 등장과 효율적인 API 관리에 대한 필요성으로 인해 NGINX도 API 게이트웨이를 구축하는 데 인기 있는 선택이 되었습니다.
이 블로그에서는 선언적 API 접근 방식을 사용하여 OpenAPI 스키마 정의를 웹 애플리케이션 방화벽 보안과 개발자 포털을 갖춘 API 게이트웨이로 실행되는 완전한 기능의 NGINX 구성으로 변환하는 방법을 살펴보겠습니다.
NGINX Plus를 활용하여 API 관리 프로세스를 간소화하고 애플리케이션의 최적 성능을 보장하는 방법에 대한 단계별 지침과 통찰력을 제공합니다.
API 게이트웨이는 클라이언트와 백엔드 서비스 간의 통신을 관리하고 보호하는 중앙 허브 역할을 합니다. 이는 클라이언트와 백엔드 서버 사이에 위치하여 들어오는 요청을 라우팅하고 적절한 서비스에 분산시키는 역방향 프록시 역할을 합니다. 이를 통해 클라이언트와 서비스 간의 효율적인 통신이 가능해지고, API 게이트웨이가 인증 , 권한 부여 , 속도 제한 , 캐싱 등의 작업을 처리할 수 있습니다.
또한 API 게이트웨이는 보안 계층 역할을 하여 백엔드 서비스를 잠재적인 위협과 공격으로부터 보호할 수 있습니다. 암호화, 토큰 기반 인증, 액세스 제어와 같은 보안 조치를 시행하여 권한이 있는 사용자만 서비스에 액세스할 수 있도록 보장합니다. API 게이트웨이는 이러한 보안 기능을 한곳에서 통합하고 관리함으로써 시스템의 전반적인 보안 아키텍처를 단순화하고 여러 서비스에 걸쳐 보안 조치를 구현하는 데 따른 복잡성을 줄이는 데 도움이 됩니다.
커뮤니티가 지원하는 NGINX Declarative API 프로젝트는 NGINX Instance Manager를 위한 선언적 REST API 세트를 제공합니다.
NGINX Plus 구성 라이프사이클을 관리하고 JSON 서비스 정의를 사용하여 NGINX Plus 구성을 생성하는 데 사용할 수 있습니다. NGINX Instance Manager와 함께 사용하면 GitOps 통합이 지원됩니다. 참조된 개체 업데이트에 대한 진실 출처를 확인하고 NGINX 구성은 자동으로 동기화됩니다.
OpenAPI 스키마를 사용하면 NGINX를 API 게이트웨이로 자동으로 구성할 수 있습니다. 개발자 포털 생성은 Redocly를 통해 지원됩니다.
이 블로그의 내용을 실행하려면 다음이 필요합니다.
선언적API테스트
인스턴스 그룹모든 필수 구성 요소를 설치하고 실행한 후 NGINX Instance Manager는 NGINX Plus 인스턴스가 NGINX App Protect WAF를 통해 온라인 상태로 표시됩니다.
NGINX Plus 인스턴스는 declarativeAPITest
인스턴스 그룹의 일부입니다.
NGINX 선언적 API 프로젝트는 NGINX Instance Manager가 제공하는 REST API에 의존하며 선언적이고 JSON 기반 추상화를 제공합니다. 선언적 API 프로젝트를 실행하려면 다음 지침을 따르세요.
1. Docker가 실행 중인지 확인하려면 docker ps를 실행하세요.
f5@ubuntu:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2. Linux 호스트에서 Github 저장소를 복제합니다.
f5@ubuntu:~$ git clone https://github.com/f5devcentral/NGINX-Declarative-API/
Cloning into 'NGINX-Declarative-API'...
remote: Enumerating objects: 4072, done.
remote: Counting objects: 100% (1982/1982), done.
remote: Compressing objects: 100% (1332/1332), done.
remote: Total 4072 (delta 668), reused 876 (delta 609), pack-reused 2090
Receiving objects: 100% (4072/4072), 19.05 MiB | 4.88 MiB/s, done.
Resolving deltas: 100% (1154/1154), done.
f5@ubuntu:~$
3. docker-compose 디렉토리로 변경:
f5@ubuntu:~$ cd NGINX-Declarative-API/contrib/docker-compose/
4. nginx-dapi.sh
스크립트를 사용하여 docker-compose를 통해 모든 컨테이너를 시작합니다. 초기 시작 시 모든 Docker 이미지가 자동으로 빌드됩니다.
f5@ubuntu:~/NGINX-Declarative-API/contrib/docker-compose$ ./nginx-dapi.sh -c start
-> Updating docker images
[+] Pulling 11/11
[...]
-> Deploying NGINX Declarative API
[+] Running 4/4
✔ Network nginx-dapi_dapi-network Created 0.1s
✔ Container redis Started 1.5s
✔ Container devportal Started 1.5s
✔ Container nginx-dapi Started
5. 실행 중인 Docker 컨테이너 확인:
f5@ubuntu:~/NGINX-Declarative-API/contrib/docker-compose$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e29a2f783da2 nginx-declarative-api "/deployment/env/bin…" 5 minutes ago Up 5 minutes 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp nginx-dapi
97142840eaf7 redis "docker-entrypoint.s…" 5 minutes ago Up 5 minutes 0.0.0.0:6379->6379/tcp, :::6379->6379/tcp redis
6b50c0426643 nginx-declarative-api-devportal "/deployment/src/sta…" 5 minutes ago Up 5 minutes 0.0.0.0:5001->5000/tcp, :::5001->5000/tcp devportal
6. 클라이언트 호스트에서 Postman을 실행하고 https://raw.githubusercontent.com/f5devcentral/NGINX-Declarative-API/main/contrib/postman/NGINX%20Declarative%20API.postman_collection.json 에서 NGINX Declarative API 컬렉션을 가져옵니다.
7. Postman 컬렉션 변수를 편집하여 환경에 맞게 조정하세요.
8. 다음 변수를 설정하세요.
ncg_host
- 선언적 API docker-compose가 실행되는 Linux 호스트의 호스트 이름 또는 IP 주소ncg_port
- NGINX 선언적 API에 대한 TCP 포트: 5000이 기본값입니다nim_host
- NGINX 인스턴스 관리자 기본 URL(예: https://nms.k8s.ie.ff.lan)nim_username
- NGINX Instance Manager 인증 사용자 이름nim_password
- NGINX Instance Manager 인증 비밀번호9. Postman에서 모든 변경 사항을 저장합니다.
10. Postman 컬렉션에서 Petstore API Gateway RateLimit + JWT AuthN/AuthZ + WAF를
탐색하여 요청을 엽니다.
JSON 선언은 다음과 같습니다.
{
"output": {
"type": "nms",
"nms": {
"url": "{{nim_host}}",
"username": "{{nim_username}}",
"password": "{{nim_password}}",
"instancegroup": "{{nim_instancegroup}}",
"synctime": 0,
"modules": [
"ngx_http_app_protect_module"
],
"certificates": [
{
"type": "certificate",
"name": "test_cert",
"contents": {
"content": "{{github_gitops_root}}/v4.2/testcert.crt"
}
},
{
"type": "key",
"name": "test_key",
"contents": {
"content": "{{github_gitops_root}}/v4.2/testcert.key"
}
}
],
"policies": [
{
"type": "app_protect",
"name": "production-policy",
"active_tag": "xss-blocked",
"versions": [
{
"tag": "xss-blocked",
"displayName": "Production Policy - XSS blocked",
"description": "This is a production-ready policy - XSS blocked",
"contents": {
"content": "{{github_gitops_root}}/v4.2/nap-policy-xss-blocked-bot-allowed.json"
}
},
{
"tag": "xss-allowed",
"displayName": "Production Policy - XSS allowed",
"description": "This is a production-ready policy - XSS allowed",
"contents": {
"content": "{{github_gitops_root}}/v4.2/nap-policy-xss-allowed.json"
}
}
]
}
]
}
},
"declaration": {
"http": {
"servers": [
{
"name": "Petstore API",
"names": [
"apigw.nginx.lab"
],
"resolver": "8.8.8.8",
"listen": {
"address": "0.0.0.0:443",
"http2": true,
"tls": {
"certificate": "test_cert",
"key": "test_key",
"ciphers": "DEFAULT",
"protocols": [
"TLSv1.2",
"TLSv1.3"
]
}
},
"log": {
"access": "/var/log/nginx/apigw.nginx.lab-access_log",
"error": "/var/log/nginx/apigw.nginx.lab-error_log"
},
"locations": [
{
"uri": "/petstore",
"urimatch": "prefix",
"apigateway": {
"openapi_schema": {
"content": "http://petstore.swagger.io/v2/swagger.json"
},
"api_gateway": {
"enabled": true,
"strip_uri": true,
"server_url": "https://petstore.swagger.io/v2"
},
"developer_portal": {
"enabled": true,
"uri": "/petstore-devportal.html"
},
"authentication": {
"client": [
{
"profile": "Petstore JWT Authentication"
}
],
"enforceOnPaths": true,
"paths": [
"/user/login",
"/user/logout"
]
},
"authorization": [
{
"profile": "JWT role based authorization",
"enforceOnPaths": true,
"paths": [
"/user/login",
"/user/logout"
]
}
],
"rate_limit": [
{
"profile": "petstore_ratelimit",
"httpcode": 429,
"burst": 0,
"delay": 0,
"enforceOnPaths": true,
"paths": [
"/user/login",
"/user/logout"
]
}
]
},
"log": {
"access": "/var/log/nginx/petstore-access_log",
"error": "/var/log/nginx/petstore-error_log"
},
"app_protect": {
"enabled": true,
"policy": "production-policy",
"log": {
"profile_name": "secops_dashboard",
"enabled": true,
"destination": "127.0.0.1:514"
}
}
}
]
}
],
"rate_limit": [
{
"name": "petstore_ratelimit",
"key": "$binary_remote_addr",
"size": "10m",
"rate": "2r/s"
}
],
"authentication": {
"client": [
{
"name": "Petstore JWT Authentication",
"type": "jwt",
"jwt": {
"realm": "Petstore Authentication",
"key": "{\"keys\": [{\"k\":\"ZmFudGFzdGljand0\",\"kty\":\"oct\",\"kid\":\"0001\"}]}",
"cachetime": 5
}
}
]
},
"authorization": [
{
"name": "JWT role based authorization",
"type": "jwt",
"jwt": {
"claims": [
{
"name": "roles",
"value": [
"~(devops)"
],
"errorcode": 403
}
]
}
}
]
}
}
}
출력
섹션은 다음을 정의합니다.
선언
섹션에서는 다음 사항을 설명합니다.
/petstore
기본 URIAPI Gateway 선언 섹션에서는 선언적 API가 어떻게 결과를 전달하는지 설명합니다.
OpenAPI 스키마는 전체 URL을 통해 참조됩니다.
"apigateway": {
"openapi_schema": {
"content": "http://petstore.swagger.io/v2/swagger.json"
},
NGINX API Gateway 구성을 생성하도록 요청했으며, 업스트림 서버가 정의되었습니다. NGINX가 업스트림에 대한 역방향 프록시 요청을 수행하면 /petstore
기본 URI가 제거됩니다.
"api_gateway": {
"enabled": true,
"strip_uri": true,
"server_url": "https://petstore.swagger.io/v2"
},
특정 URI에 따른 개발자 포털 생성 및 배포가 요청되었습니다.
"developer_portal": {
"enabled": true,
"uri": "/petstore-devportal.html"
},
지정된 클라이언트 인증 프로필을 기반으로 하는 클라이언트 인증은 /user/login
및 /user/logout
에 적용됩니다.
"authentication": {
"client": [
{
"profile": "Petstore JWT Authentication"
}
],
"enforceOnPaths": true,
"paths": [
"/user/login",
"/user/logout"
]
},
지정된 클라이언트 인증 프로필에 따른 클라이언트 인증은 /user/login
및 /user/logout
에 적용됩니다.
"authorization": [
{
"profile": "JWT role based authorization",
"enforceOnPaths": true,
"paths": [
"/user/login",
"/user/logout"
]
}
],
지정된 프로필에 따라 /user/login
및 /user/logout
에 대한 속도 제한이 적용됩니다.
"rate_limit": [
{
"profile": "petstore_ratelimit",
"httpcode": 429,
"burst": 0,
"delay": 0,
"enforceOnPaths": true,
"paths": [
"/user/login",
"/user/logout"
]
}
]
},
12. Postman Send
버튼을 사용하여 선언적 API에 요청을 게시합니다. 응답은 다음과 유사합니다.
{
"code": 200,
"content": {
"createTime": "2024-04-26T17:09:10.419574328Z",
"details": {
"failure": [],
"pending": [],
"success": [
{
"name": "vm-test"
}
]
},
"id": "1060ec49-120e-45ca-820b-5203c8b3538d",
"message": "Instance Group config successfully published to declarativeAPITest",
"status": "successful",
"updateTime": "2024-04-26T17:09:10.881509913Z"
},
"configUid": "eecf1da6-9d8f-4e44-89cc-a470af79379d"
}
13. 이 단계에서는 NGINX 인스턴스가 API 게이트웨이로 구성되고, WAF 보안이 시행되며, 개발자 포털이 게시됩니다.
참고: FQDN apigw.nginx.lab은
NGINX 인스턴스가 실행되는 가상 머신의 IP 주소로 확인된다고 가정합니다.
1. jwt
디렉토리로 변경:
f5@ubuntu:~$ cd ~/NGINX-Declarative-API/contrib/gitops-examples/jwt
2. 인증되지 않은 REST API 엔드포인트에 액세스:
$ curl -w '\n' -ki https://apigw.nginx.lab/petstore/store/inventory
HTTP/2 200
date: Fri, 26 Apr 2024 17:13:54 GMT
content-type: application/json
access-control-allow-origin: *
access-control-allow-methods: GET, POST, DELETE, PUT
access-control-allow-headers: Content-Type, api_key, Authorization
{"totvs":5,"aut":1,"FORsold":1,[...]
3. 속도 제한:
$ curl -w '\n' -ki https://apigw.nginx.lab/petstore/user/login;curl -w '\n' -ki
https://apigw.nginx.lab/petstore/user/login
HTTP/2 401
date: Fri, 26 Apr 2024 17:14:51 GMT
content-type: text/html
content-length: 179
www-authenticate: Bearer realm="Petstore Authentication"
<html>
<head><title>401 Authorization Required</title></head>
<body>
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx/1.25.3</center>
</body>
</html>
HTTP/2 429
date: Fri, 26 Apr 2024 17:14:51 GMT
content-type: text/html
content-length: 169
<html>
<head><title>429 Too Many Requests</title></head>
<body>
<center><h1>429 Too Many Requests</h1></center>
<hr><center>nginx/1.25.3</center>
</body>
</html>
4. 인증 및 유효한 권한 부여:
$ curl -w '\n' -ki https://apigw.nginx.lab/petstore/user/login -H "Authorization: Bearer `cat jwt.devops`"
HTTP/2 200
date: Fri, 26 Apr 2024 17:15:41 GMT
content-type: application/json
access-control-allow-origin: *
access-control-allow-methods: GET, POST, DELETE, PUT
access-control-allow-headers: Content-Type, api_key, Authorization
x-expires-after: Fri Apr 26 18:15:41 UTC 2024
x-rate-limit: 5000
{"code":200,"type":"unknown","message":"logged in user session:1714151741883"}
5. 인증 및 잘못된 권한 부여:
$ curl -w '\n' -ki https://apigw.nginx.lab/petstore/user/login -H "Authorization: Bearer `cat jwt.guest`"
HTTP/2 403
date: Fri, 26 Apr 2024 17:16:07 GMT
content-type: text/html
content-length: 153
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.25.3</center>
</body>
</html>
6. NGINX App Protect WAF 및 Cross-Site Scripting 보안 위반:
$ curl -w '\n' -ki "https://apigw.nginx.lab/petstore/store/inventory?
"
HTTP/2 200
content-type: text/html; charset=utf-8
cache-control: no-cache
pragma: no-cache
content-length: 246
<html><head><title>Request Rejected</title></head><body>The requested URL was rejected. Please consult with your administrator.<br><br>Your support ID is: 7283327928460093545<br><br><a href='javascript:history.back();'>[Go Back]</a></body></html>
7. 개발자 포털은 검색을 통해 액세스할 수 있습니다.
https://apigw.nginx.lab/petstore/petstore-devportal.html
이 게시물에서 논의된 NGINX 솔루션을 시도하려면 오늘 30일 무료 평가판을 시작하거나 사용 사례에 대해 논의하기 위해 저희에게 연락하세요 .
NGINX Agent를 다운로드하세요. 무료이며 오픈 소스입니다.
"이 블로그 게시물에는 더 이상 사용할 수 없거나 더 이상 지원되지 않는 제품이 참조될 수 있습니다. 사용 가능한 F5 NGINX 제품과 솔루션에 대한 최신 정보를 보려면 NGINX 제품군을 살펴보세요. NGINX는 이제 F5의 일부가 되었습니다. 이전의 모든 NGINX.com 링크는 F5.com의 유사한 NGINX 콘텐츠로 리디렉션됩니다."