블로그 | NGINX

선언적 API를 사용하여 OpenAPI에서 API 게이트웨이로 NGINX로

NGINX-F5-수평-검정-유형-RGB의 일부
파브리지오 피오루치 썸네일
파브리지오 피오루치
2024년 6월 17일 게시

NGINX는 오랫동안 고성능 웹 서버와 로드 밸런서를 구동하는 최고의 선택 중 하나로 인정받아 왔습니다. 그러나 마이크로서비스 아키텍처의 등장과 효율적인 API 관리에 대한 필요성으로 인해 NGINX도 API 게이트웨이를 구축하는 데 인기 있는 선택이 되었습니다.

이 블로그에서는 선언적 API 접근 방식을 사용하여 OpenAPI 스키마 정의를 웹 애플리케이션 방화벽 보안과 개발자 포털을 갖춘 API 게이트웨이로 실행되는 완전한 기능의 NGINX 구성으로 변환하는 방법을 살펴보겠습니다.

NGINX Plus를 활용하여 API 관리 프로세스를 간소화하고 애플리케이션의 최적 성능을 보장하는 방법에 대한 단계별 지침과 통찰력을 제공합니다.

API 게이트웨이 소개

API 게이트웨이는 클라이언트와 백엔드 서비스 간의 통신을 관리하고 보호하는 중앙 허브 역할을 합니다. 이는 클라이언트와 백엔드 서버 사이에 위치하여 들어오는 요청을 라우팅하고 적절한 서비스에 분산시키는 역방향 프록시 역할을 합니다. 이를 통해 클라이언트와 서비스 간의 효율적인 통신이 가능해지고, API 게이트웨이가 인증 , 권한 부여 , 속도 제한 , 캐싱 등의 작업을 처리할 수 있습니다.

또한 API 게이트웨이는 보안 계층 역할을 하여 백엔드 서비스를 잠재적인 위협과 공격으로부터 보호할 수 있습니다. 암호화, 토큰 기반 인증, 액세스 제어와 같은 보안 조치를 시행하여 권한이 있는 사용자만 서비스에 액세스할 수 있도록 보장합니다. API 게이트웨이는 이러한 보안 기능을 한곳에서 통합하고 관리함으로써 시스템의 전반적인 보안 아키텍처를 단순화하고 여러 서비스에 걸쳐 보안 조치를 구현하는 데 따른 복잡성을 줄이는 데 도움이 됩니다.

NGINX 선언적 API 프로젝트

커뮤니티가 지원하는 NGINX Declarative API 프로젝트는 NGINX Instance Manager를 위한 선언적 REST API 세트를 제공합니다.

NGINX Plus 구성 라이프사이클을 관리하고 JSON 서비스 정의를 사용하여 NGINX Plus 구성을 생성하는 데 사용할 수 있습니다. NGINX Instance Manager와 함께 사용하면 GitOps 통합이 지원됩니다. 참조된 개체 업데이트에 대한 진실 출처를 확인하고 NGINX 구성은 자동으로 동기화됩니다.

OpenAPI 스키마를 사용하면 NGINX를 API 게이트웨이로 자동으로 구성할 수 있습니다. 개발자 포털 생성은 Redocly를 통해 지원됩니다.

필수 조건

이 블로그의 내용을 실행하려면 다음이 필요합니다.

  • NGINX Instance Manager 의 실행 중인 인스턴스
  • NGINX PlusNGINX App Protect WAF에 대한 구독(또는 30일 무료 체험판 ). NGINX 에이전트 NGINX Instance Manager에 연결하고 일부가 되려면 설치해야 합니다. 선언적API테스트 인스턴스 그룹
  • NGINX 선언적 API 프로젝트를 실행하기 위한 docker 및 docker-compose가 있는 Linux 호스트(베어 메탈 또는 VM)
  • Postman이 선언적 API 요청을 제출합니다.
  • Postman을 실행할 클라이언트 호스트

랩 개요

모든 필수 구성 요소를 설치하고 실행한 후 NGINX Instance Manager는 NGINX Plus 인스턴스가 NGINX App Protect WAF를 통해 온라인 상태로 표시됩니다.

NGINX Plus 인스턴스는 declarativeAPITest 인스턴스 그룹의 일부입니다.

선언적 API 배포

NGINX 선언적 API 프로젝트는 NGINX Instance Manager가 제공하는 REST API에 의존하며 선언적이고 JSON 기반 추상화를 제공합니다. 선언적 API 프로젝트를 실행하려면 다음 지침을 따르세요.

1. Docker가 실행 중인지 확인하려면 docker ps를 실행하세요.


f5@ubuntu:~$ docker ps
컨테이너 ID 이미지 명령 생성 상태 포트 이름


2. Linux 호스트에서 Github 저장소를 복제합니다.


f5@ubuntu:~$ git clone https://github.com/f5devcentral/NGINX-Declarative-API/
'NGINX-Declarative-API'로 복제 중...
remote: 객체 열거: 4072, 완료.
원격: 사물 세기: 100% (1982/1982), 완료.
원격: 객체 압축: 100% (1332/1332), 완료.
원격: 총 4072개(델타 668개), 재사용 876개(델타 609개), 팩 재사용 2090개
수신 객체: 100% (4072/4072), 19.05 MiB | 4.88 MiB/s, 완료.
델타 해결: 100%(1154/1154) 완료되었습니다.
f5@우분투:~$


3. docker-compose 디렉토리로 변경:


f5@ubuntu:~$ cd NGINX-선언적-API/contrib/docker-compose/


4. nginx-dapi.sh 스크립트를 사용하여 docker-compose를 통해 모든 컨테이너를 시작합니다. 초기 시작 시 모든 Docker 이미지가 자동으로 빌드됩니다.


f5@ubuntu:~/NGINX-Declarative-API/contrib/docker-compose$ ./nginx-dapi.sh -c start
-> docker 이미지 업데이트
[+] 11/11 가져오기
[...]
-> NGINX Declarative API 배포
[+] 4/4 실행
✔ 네트워크 nginx-dapi_dapi-network 생성됨 0.1초 
✔ 컨테이너 redis 시작됨 1.5초 
✔ 컨테이너 devportal 시작됨 1.5초 
✔ 컨테이너 nginx-dapi 시작됨


5. 실행 중인 Docker 컨테이너 확인:


f5@ubuntu:~/NGINX-Declarative-API/contrib/docker-compose$ docker ps
컨테이너 ID 이미지 명령 생성 상태 포트 이름
e29a2f783da2 nginx-declarative-api "/deployment/env/bin…" 5분 전 Up 5분 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp nginx-dapi
97142840eaf7 redis "docker-entrypoint.s…" 5분 전 Up 5분 0.0.0.0:6379->6379/tcp, :::6379->6379/tcp redis
6b50c0426643 nginx-declarative-api-devportal "/deployment/src/sta…" 5분 전 Up 5분 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 선언은 다음과 같습니다.


{
"출력": {
"유형": "nms",
"nms": {
"url": "{{nim_host}}",
"사용자 이름": "{{nim_username}}",
"비밀번호": "{{nim_password}}",
"인스턴스 그룹": "{{nim_instancegroup}}",
"동기화 시간": 0,
"모듈": [
"ngx_http_app_protect_module"
],
"인증서": [
{
"유형": "인증서",
"이름": "테스트_인증서",
"내용": {
"내용": "{{github_gitops_root}}/v4.2/testcert.crt"
}
},
{
"유형": "키",
"이름": "테스트_키",
"내용": {
"내용": "{{github_gitops_root}}/v4.2/testcert.key"
}
}
],
"정책": [
{
"유형": "앱_보호",
"이름": "생산 정책",
"활성_태그": "xss 차단",
"버전": [
{
"태그": "xss-차단됨",
"디스플레이 이름": "생산 정책 - XSS 차단됨",
"설명": "이것은 프로덕션 준비 정책입니다. XSS 차단됨",
"contents": {
"content": "{{github_gitops_root}}/v4.2/nap-policy-xss-blocked-bot-allowed.json"
}
},
{
"tag": "xss-allowed",
"displayName": "생산 정책 - XSS 허용",
"설명": "이것은 프로덕션 준비 정책입니다. XSS 허용",
"contents": {
"content": "{{github_gitops_root}}/v4.2/nap-policy-xss-allowed.json"
}
}
]
}
]
}
},
"declaration": {
"http": {
"servers": [
{
"name": "펫스토어 API",
"이름": [
"apigw.nginx.lab"
],
"리졸버": "8.8.8.8",
"listen": {
"주소": "0.0.0.0:443",
"http2": 참,
"tls": {
"인증서": "테스트_인증서",
"키": "테스트_키",
"암호": "기본값",
"프로토콜": [
"TLSv1.2",
"TLSv1.3"
]
}
},
"로그": {
"액세스": "/var/log/nginx/apigw.nginx.lab-액세스_로그",
"오류": "/var/log/nginx/apigw.nginx.lab-오류_로그"
},
"위치": [
{
"uri": "/petstore",
"urimatch": "접두사",
"apigateway": {
"openapi_schema": {
"콘텐츠": "http://petstore.swagger.io/v2/swagger.json"
},
"api_gateway": {
"활성화됨": 참,
"strip_uri": 참,
"server_url": "https://petstore.swagger.io/v2"
},
"개발자_포털": {
"활성화됨": 참,
"uri": "/petstore-devportal.html"
},
"인증": {
"클라이언트": [
{
"프로필": "펫스토어 JWT 인증"
}
],
"enforceOnPaths": true,
"경로": [
"/user/login",
"/user/logout"
]
},
"권한 부여": [
{
"프로필": "JWT 역할 기반 권한 부여",
"enforceOnPaths": true,
"경로": [
"/user/login",
"/user/logout"
]
}
],
"rate_limit": [
{
"profile": "petstore_ratelimit",
"httpcode": 429,
"버스트": 0,
"지연": 0,
"enforceOnPaths": 참,
"경로": [
"/user/login",
"/user/logout"
]
}
]
},
"로그": {
"액세스": "/var/log/nginx/petstore-액세스_로그",
"오류": "/var/log/nginx/petstore-오류_로그"
},
"app_protect": {
"활성화": 참,
"정책": "생산 정책",
"로그": {
"프로필_이름": "secops_대시보드",
"활성화": 참,
"대상": "127.0.0.1:514"
}
}
}
]
}
],
"rate_limit": [
{
"name": "petstore_ratelimit",
"key": "$binary_remote_addr",
"size": "10m",
"속도": "2r/s"
}
],
"인증": {
"클라이언트": [
{
"이름": "펫스토어 JWT 인증",
"유형": "jwt",
"jwt": {
"영역": "펫스토어 인증",
"키": "{\"키\": [{\"k\":\"ZmFudGFzdGljand0\",\"kty\":\"oct\",\"kid\":\"0001\"}]}",
"캐시타임": 5
}
}
]
},
"authorization": [
{
"name": "JWT 역할 기반 권한 부여",
"유형": "jwt",
"jwt": {
"클레임": [
{
"이름": "역할",
"값": [
"~(devops)"
],
"오류 코드": 403
                            }
                        ]
                    }
                }
            ]
        }
    }
}


출력 섹션은 다음을 정의합니다.

  • NGINX Instance Manager 서버 선언 API는 NGINX 구성을 게시합니다.
  • TLS 인증서 및 키 - 이는 저장된 진실의 출처 URL로 참조될 수 있습니다.
  • NGINX 앱 보호 WAF 보안 정책 - 이는 저장된 진실의 출처 URL로 참조될 수 있습니다.

선언 섹션에서는 다음 사항을 설명합니다.

  • 생성할 NGINX 서버
  • TLS 오프로드가 수행되는지 여부
  • 액세스 및 오류 항목을 기록할 위치
  • API Gateway 구성이 배포되고 클라이언트가 액세스할 수 있게 되는 /petstore 기본 URI
  • API Gateway 구성 및 개발자 포털이 공개됩니다.
  • NGINX App Protect WAF가 어떻게 활성화되는지, 어떤 보안 정책을 사용해야 하는지, 보안 위반을 어디에 기록해야 하는지
  • 클라이언트 요청을 인증하고 승인하는 방법

API Gateway 선언 섹션에서는 선언적 API가 어떻게 결과를 전달하는지 설명합니다.

OpenAPI 스키마는 전체 URL을 통해 참조됩니다.


"apigateway": {
"openapi_schema": {
"content": "http://petstore.swagger.io/v2/swagger.json"
},


NGINX API Gateway 구성을 생성하도록 요청했으며, 업스트림 서버가 정의되었습니다. NGINX가 업스트림에 대한 역방향 프록시 요청을 수행하면 /petstore 기본 URI가 제거됩니다.


"api_gateway": {
"활성화됨": 참,
"스트립_uri": 참,
"서버_url": "https://petstore.swagger.io/v2"
},




특정 URI에 따른 개발자 포털 생성 및 배포가 요청되었습니다.


"개발자_포털": {
"활성화됨": 참,
"uri": "/petstore-devportal.html"
},


지정된 클라이언트 인증 프로필을 기반으로 하는 클라이언트 인증은 /user/login/user/logout 에 적용됩니다.


"인증": {
"클라이언트": [
{
"프로필": "펫스토어 JWT 인증"
}
],
"enforceOnPaths": true,
"경로": [
"/user/login",
"/user/logout"
]
},


지정된 클라이언트 인증 프로필에 따른 클라이언트 인증은 /user/login/user/logout 에 적용됩니다.


"authorization": [
{
"profile": "JWT 역할 기반 권한 부여",
"enforceOnPaths": true,
"경로": [
"/user/login",
"/user/logout"
]
}
],




지정된 프로필에 따라 /user/login/user/logout 에 대한 속도 제한이 적용됩니다.


"rate_limit": [
{
"profile": "petstore_ratelimit",
"httpcode": 429,
"버스트": 0,
"지연": 0,
"enforceOnPaths": 참,
"경로": [
"/사용자/로그인",
"/사용자/로그아웃"
]
}
]
},


12. Postman Send 버튼을 사용하여 선언적 API에 요청을 게시합니다. 응답은 다음과 유사합니다.


{
"코드": 200,
"내용": {
"생성시간": "2024-04-26T17:09:10.419574328Z",
"세부 정보": {
"실패": [],
"보류": [],
"성공": [
{
"이름": "vm-테스트"
}
]
},
"ID": "1060ec49-120e-45ca-820b-5203c8b3538d",
"메시지": "인스턴스 그룹 구성이 declarativeAPITest에 성공적으로 게시되었습니다",
"상태": "성공",
"업데이트 시간": "2024-04-26T17:09:10.881509913Z"
},
"configUid": "eecf1da6-9d8f-4e44-89cc-a470af79379d"
}


13. 이 단계에서는 NGINX 인스턴스가 API 게이트웨이로 구성되고, WAF 보안이 시행되며, 개발자 포털이 게시됩니다.

API 게이트웨이 테스트

참고: FQDN apigw.nginx.lab은 NGINX 인스턴스가 실행되는 가상 머신의 IP 주소로 확인된다고 가정합니다.

1. jwt 디렉토리로 변경:


f5@ubuntu:~$ cd ~/NGINX-선언-API/contrib/gitops-examples/jwt


2. 인증되지 않은 REST API 엔드포인트에 액세스:


$ curl -w '\n' -ki https://apigw.nginx.lab/petstore/store/inventory
HTTP/2 200 
날짜: 금, 2024년 4월 26일 17:13:54 GMT
content-type: application/json
access-control-allow-origin: *
access-control-allow-methods: GET, POST, DELETE, PUT
액세스 제어 허용 헤더: 콘텐츠 유형, API 키, 권한 부여

{"totvs":5,"aut":1,"FORsold":1,[...]



3. 속도 제한:

    
    $ curl -w '\n' -ki https://apigw.nginx.lab/펫스토어/사용자/로그인;curl -w '\n' -ki 
https://apigw.nginx.lab/펫스토어/사용자/로그인
HTTP/2 401 
날짜: 금, 2024년 4월 26일 17:14:51 GMT
content-type: text/html
content-length: 179
www-인증: Bearer realm="Petstore 인증"
<html>
<head><title>401 권한 부여 필요</title></head>
<body>
<center><h1>401 권한 부여 필요</h1></center>
<hr><center>nginx/1.25.3</center>
</body>
</html>
HTTP/2 429 
날짜: 금, 2024년 4월 26일 17:14:51 GMT
content-type: text/html
content-length: 169
<html>
<head><title>429 요청이 너무 많음</title></head>
<body>
<center><h1>429 요청이 너무 많음</h1></center>
<hr><center>nginx/1.25.3</center>
</body>
</html>
    

4. 인증 및 유효한 권한 부여:


$ curl -w '\n' -ki https://apigw.nginx.lab/petstore/user/login -H "권한 부여: 전달자 `cat jwt.devops`"
HTTP/2 200 
날짜: 금, 2024년 4월 26일 17:15:41 GMT
content-type: application/json
access-control-allow-origin: *
access-control-allow-methods: GET, POST, DELETE, PUT
액세스 제어 허용 헤더: 콘텐츠 유형, API 키, 권한
x-만료 후: 금 4월 26일 18:15:41 UTC 2024
x-rate-limit: 5000

{"코드":200,"유형":"알 수 없음","메시지":"로그인한 사용자 세션:1714151741883"}



5. 인증 및 잘못된 권한 부여:


$ curl -w '\n' -ki https://apigw.nginx.lab/petstore/user/login -H "권한 부여: 전달자 `cat jwt.guest`"
HTTP/2 403 
날짜: 금, 2024년 4월 26일 17:16:07 GMT
content-type: text/html
content-length: 153
<html>
<head><title>403 금지됨</title></head>
<body>
<center><h1>403 금지됨</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/반려동물스토어/스토어/인벤토리?
"
HTTP/2 200 콘텐츠 유형: text/html; 문자 집합=utf-8 캐시 제어: no-cache 프래그마: no-cache 콘텐츠 길이: 246
<html><head><title>요청 거부됨</title></head><body>요청된 URL이 거부되었습니다. 관리자에게 문의하세요.귀하의 지원 ID는 다음과 같습니다. 7283327928460093545[뒤로가기]

7. 개발자 포털은 검색을 통해 액세스할 수 있습니다.


https://apigw.nginx.lab/펫스토어/펫스토어-devportal.html


시작하기

이 게시물에서 논의된 NGINX 솔루션을 시도하려면 오늘 30일 무료 평가판을 시작하거나 사용 사례에 대해 논의하기 위해 저희에게 연락하세요 .

NGINX Agent를 다운로드하세요. 무료이며 오픈 소스입니다.


"이 블로그 게시물에는 더 이상 사용할 수 없거나 더 이상 지원되지 않는 제품이 참조될 수 있습니다. 사용 가능한 F5 NGINX 제품과 솔루션에 대한 최신 정보를 보려면 NGINX 제품군을 살펴보세요. NGINX는 이제 F5의 일부가 되었습니다. 이전의 모든 NGINX.com 링크는 F5.com의 유사한 NGINX 콘텐츠로 리디렉션됩니다."