NGINX Plus Forward Proxy 접근 제어 설정 가이드
NGINX Plus R36에서 새롭게 추가된 ngx_http_tunnel_module을 통해 Forward Proxy 기능을 손쉽게 구현할 수 있게 되었지만, 실제 운영 환경에서는 단순한 연결 지원을 넘어 보안 정책을 준수하는 것이 무엇보다 중요합니다. 이번 포스트에서는 안정적인 서비스 운영을 위한 NGINX Plus Forward Proxy 접근 제어 설정 방법을 확인해 보겠습니다.
Forward Proxy 서버의 HTTPS 트래픽의 터널링을 담당하는 CONNECT 메서드 요청에 대하여, 목적지 Port/Host/IP와 같은 조건에 따라 접속을 허용하거나 차단하는 구성 방법을 다룹니다.
CONNECT 메서드를 사용하지 않는 일반적인 HTTP 목적지의 요청 처리 구성 방법은 이전 포스트인 NGINX Plus tunnel 모듈 활용 HTTP/HTTPS Forward Proxy 구현을 참고하세요.
목차
1. Port 기반 접근 제어
2. Host 기반 접근 제어
3. IP 기반 접근 제어
4. 설정 적용 및 동작 확인
5. 결론
1. Port 기반 접근 제어
NGINX의 map 블록과 if 블록을 사용하여, 클라이언트가 Forward Proxy 서버를 통해 연결할 목적지 서버의 Port 번호 기반 접근 제어를 설정할 수 있습니다.
map $request_port $allow_port { 443 1;}server { listen 3309; access_log /var/log/nginx/forward-proxy/ac-access.log main; error_log /var/log/nginx/forward-proxy/ac-error.log debug; resolver 8.8.8.8 ipv6=off valid=60s; tunnel_connect_timeout 60s; tunnel_read_timeout 60s; tunnel_send_timeout 60s; if ($allow_port != 1) { return 403; } tunnel_pass;}
위와 같이 설정 구성 시 목적지 서버의 포트($request_port)의 값이 443일 경우 $allow_port 변수에 1이 매핑됩니다. 이에 따라 하단 if 블록 설정으로 인해 $allow_port 변수의 값이 1이 아닌 요청은 403 응답 코드와 함께 실패합니다.
map 블록에 명시되지 않은 값들은 기본적으로 빈 문자열로 매핑됩니다. 443 포트가 아닌 다른 포트 요청의 경우 $allow_port 변수의 값은 빈 문자열로 매핑됩니다.
2. Host 기반 접근 제어
NGINX의 map 블록과 if 블록을 사용하여, 클라이언트가 Forward Proxy 서버를 통해 연결할 목적지 서버의 Host 기반 접근 제어를 설정할 수 있습니다.
map $host $allow_host { hostnames; *.devopssong.site 1; devopssong.* 1; evil.devopssong.site 0;}server { listen 3309; access_log /var/log/nginx/forward-proxy/ac-access.log main; error_log /var/log/nginx/forward-proxy/ac-error.log debug; resolver 8.8.8.8 ipv6=off valid=60s; tunnel_connect_timeout 60s; tunnel_read_timeout 60s; tunnel_send_timeout 60s; if ($allow_host != 1) { return 403; } tunnel_pass;}
앞서 구성한 Port 기반 접근 제어와 동일한 방식으로 목적지 서버에 대한 접근 제어를 설정합니다. $host 변수의 값이 접두사/접미사 마스크(*)를 포함하는 호스트 이름임을 나타내기 위해 map 블록 가장 상단에 hostnames 값을 설정합니다.
위 설정은 허용할 Host의 $allow_host 변수를 1로 매핑하여 whitelist 방식의 접근 제어를 구현한 설정입니다. map 블록의 default 옵션을 사용하여 blacklist 방식의 접근 제어를 구현할 수도 있습니다.
map $host $allow_host { hostnames; default 1; evil.devopssong.site 0;}
위 설정과 같이 default 옵션을 사용하면 map 블록 내부에 명시되지 않은 Host의 $allow_host 변수 값은 기본적으로 1로 매핑됩니다. 따라서 전체적으로 Host를 허용하되, 차단을 적용할 Host의 값은 1이 아닌 값으로 매핑되도록 설정하면 blacklist 방식의 접근 제어를 구현할 수 있습니다.
3. IP 기반 접근 제어
Host 기반 접근 제어는 클라이언트가 요청한 도메인 이름을 검사하지만, 실제 연결되는 서버의 IP 주소까지는 검증하지 못합니다.
도메인 정책을 우회하여 사내망이나 허가되지 않은 IP로 연결하려는 시도를 차단하기 위해, DNS 해석이 완료된 후의 실제 목적지 IP를 기반으로 접근 제어를 설정할 수 있습니다.
IP 기반 접근 제어는 NGINX의 geo 블록과 tunnel_allow_upstream 지시문을 사용하여 구성할 수 있습니다.
geo $upstream_last_addr $allow { volatile; 192.168.40.164/32 1;}server { listen 3309; access_log /var/log/nginx/forward-proxy/ac-access.log main; error_log /var/log/nginx/forward-proxy/ac-error.log debug; resolver 8.8.8.8 ipv6=off valid=60s; tunnel_connect_timeout 60s; tunnel_read_timeout 60s; tunnel_send_timeout 60s; tunnel_pass; tunnel_allow_upstream $allow;}
$upstream_last_addr: Host 기반 제어와 달리, DNS 질의가 완료된 후 실제로 연결을 시도하려는 최종 목적지 서버의 IP 주소를 담고 있는 변수입니다. 이 변수를geo모듈의 입력값으로 사용하여 IP 대역 기반의 제어를 수행합니다.volatile: 변수의 값이 캐시되지 않도록 설정하는 옵션입니다. Forward Proxy 환경에서는 DNS 로드밸런싱이나 재시도 로직에 의해 목적지 IP가 요청 처리 도중 변경될 수 있습니다. 캐시된 이전 IP의 허용 여부를 그대로 사용하면 보안 문제가 발생할 수 있으므로, 매 연결 시도마다 IP를 새로 검사하도록 하기 위해 설정이 필요합니다.tunnel_allow_upstream: Upstream 서버와의 연결 전에 실행되는 접근 제어 지시문입니다. 지정된 변수($allow)의 값을 평가하여, 값이0이거나 비어있을 경우 연결을 중단합니다.
앞선if블록 설정의 403 Forbidden과 달리, 이 지시문을 통한 연결 중단은 502 Bad Gateway 응답을 반환합니다.
앞서 구성한 Host 기반 접근 제어의 blacklist 방식의 접근 제어와 동일하게 default 옵션을 geo 블록 내부에 사용하여 연결 서버 IP를 전체적으로 허용하되, 차단할 IP를 별도 명시하여 관리할 수 있습니다.
CIDR 표기 사용 시 default 대신 0.0.0.0/0와 ::/0를 사용할 수 있습니다.
geo $upstream_last_addr $allow { default 1; volatile; 192.168.41.0/24 0;}
4. 설정 적용 및 동작 확인
NGINX Plus Forward Proxy 접근 제어 동작 확인을 위해 앞서 소개한 3가지 설정이 모두 적용된 다음 설정 파일을 사용했습니다.
map $request_port $allow_port { 443 1;}map $host $allow_host { hostnames; *.devopssong.site 1; evil.devopssong.site 0;}geo $upstream_last_addr $allow { volatile; 192.168.40.164/32 1;}server { listen 192.168.40.225:3309; access_log /var/log/nginx/forward-proxy/ac-access.log main; error_log /var/log/nginx/forward-proxy/ac-error.log debug; resolver 8.8.8.8 ipv6=off valid=60s; tunnel_connect_timeout 60s; tunnel_read_timeout 60s; tunnel_send_timeout 60s; if ($allow_port != 1) { return 403; } if ($allow_host != 1) { return 403; } tunnel_pass; tunnel_allow_upstream $allow;}
- 443 포트를 제외한 목적지 서버의 포트는 제한됩니다.
- *.devopssong.site를 제외한 목적지 서버의 Host는 제한되며, evil.devopssong.site도 제한됩니다.
- 192.168.40.164를 제외한 목적지 서버의 IP는 제한됩니다.
443 포트가 아닌 다른 포트로 목적지 서버에 요청을 전달합니다.
$ curl -x http://192.168.40.225:3309 https://forward.devopssong.site:8443curl: (56) CONNECT tunnel failed, response 403------# Forward Proxy log192.168.40.220 - - [11/Dec/2025:15:48:03 +0900] "CONNECT forward.devopssong.site:8443 HTTP/1.1" 403 118 "-" "curl/8.5.0" "-"
8443 포트로 요청을 전송하여 403 응답과 함께 실패합니다.
443 포트로 요청 시 성공합니다.
$ curl -x http://192.168.40.225:3309 https://forward.devopssong.site:443<!DOCTYPE html><html><head><title>Welcome to nginx!</title>...------# Forward Proxy log192.168.40.220 - - [11/Dec/2025:15:49:06 +0900] "CONNECT forward.devopssong.site:443 HTTP/1.1" 200 3914 "-" "curl/8.5.0" "-"
허용되지 않은 Host인 evil.devopssong.site로 요청을 전송합니다.
$ curl -x http://192.168.40.225:3309 https://evil.devopssong.sitecurl: (56) CONNECT tunnel failed, response 403-------# Forward Proxy log192.168.40.220 - - [11/Dec/2025:15:53:11 +0900] "CONNECT evil.devopssong.site:443 HTTP/1.1" 403 118 "-" "curl/8.5.0" "-"
403 응답과 함께 실패합니다.
허용된 Host지만, 허용되지 않은 목적지 IP(192.168.40.225)인 good.devopssong.site로 요청을 전송합니다.
$ nslookup good.devopssong.siteServer: 127.0.0.53Address: 127.0.0.53#53Non-authoritative answer:Name: good.devopssong.siteAddress: 192.168.40.225
$ curl -x http://192.168.40.225:3309 https://good.devopssong.sitecurl: (56) CONNECT tunnel failed, response 502-------# Forward Proxy log192.168.40.220 - - [11/Dec/2025:15:55:06 +0900] "CONNECT good.devopssong.site:443 HTTP/1.1" 502 122 "-" "curl/8.5.0" "-"
tunnel_allow_upstream 설정에 의해 502 응답과 함께 실패합니다.
허용된 Host, IP인 forward.devopssong.site로 요청을 전송합니다.
$ nslookup forward.devopssong.siteServer: 127.0.0.53Address: 127.0.0.53#53Non-authoritative answer:Name: forward.devopssong.siteAddress: 192.168.40.164
$ curl -x http://192.168.40.225:3309 https://forward.devopssong.site<!DOCTYPE html><html><head><title>Welcome to nginx!</title>...------# Forward Proxy log192.168.40.220 - - [11/Dec/2025:15:57:07 +0900] "CONNECT forward.devopssong.site:443 HTTP/1.1" 200 3914 "-" "curl/8.5.0" "-"
5. 결론
이번 포스트에서는 NGINX Plus R36의 ngx_http_tunnel_module을 기반으로, HTTPS 터널링(CONNECT) 요청에 대한 접근 제어 설정을 구성하는 방법을 알아봤습니다.
단순히 트래픽을 중계하는 기능을 넘어, map 모듈을 활용한 Port 및 Host 필터링과 geo 모듈을 이용한 실제 목적지 IP(Upstream) 검증 과정을 결합함으로써, 기업 내부망에서 외부로 나가는 트래픽을 빈틈없이 제어할 수 있습니다.
기존에 사용 중이던 NGINX 서버에 Forward Proxy 기능을 추가해 확장해 보고 싶으신가요? NGINX STORE를 통해 문의하여 무료로 NGINX One trial을 신청하고 NGINX Plus를 체험해 보세요.