Skip to content

Commit 4bffb45

Browse files
authored
feat: http connect proxy (#1544)
1 parent 562243e commit 4bffb45

File tree

1 file changed

+283
-0
lines changed

1 file changed

+283
-0
lines changed
Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
---
2+
description: HTTP traffic tunneling via the HTTP CONNECT method, enabling centralized egress control through a trusted NGINX Plus server.
3+
nd-docs: DOCS-441
4+
title: HTTP CONNECT forward proxy
5+
toc: true
6+
weight: 600
7+
type:
8+
- how-to
9+
---
10+
11+
In corporate networks, NGINX Plus R36 and later can be configured as a forward proxy server, facilitating client access to external resources. A forward proxy operates between internal clients and the global network, enabling centralized traffic control.
12+
13+
Unlike a reverse proxy that protects servers, upstreams, or services, forward proxy serves client requests and regulates their access to the external resources. To enable this functionality, the HTTP `CONNECT` method is used to establish a secure tunnel between the client and the proxy server (NGINX Plus). This tunnel permits the transmission of HTTPS traffic and other protocols, such as SSH or FTP, through the proxy.
14+
15+
## Enabling HTTP CONNECT proxy
16+
17+
To enable the HTTP `CONNECT` forward proxy, add the [`tunnel_pass`](https://nginx.org/en/docs/http/ngx_http_tunnel_module.html#tunnel_pass) directive that turns on the forward proxy functionality within the `server` or `location` blocks.
18+
19+
The directive can be used without any parameters. The default value is [`$host`](https://nginx.org/en/docs/http/ngx_http_core_module.html#var_host):[`$request_port`](https://nginx.org/en/docs/http/ngx_http_core_module.html#var_request_port) variables: all requests are automatically forwarded to external resources based on the host and the port which we are trying to access.
20+
21+
The `tunnel_pass` directive is a content handling directive (similar to `proxy_pass` or `grpc_pass`), and if specified for the `server` block, will not be inherited by corresponding `location` blocks. If you have a location that implements any other NGINX Plus capabilities within the forward proxy, you will need to enable the `tunnel_pass` for this `location` as well. See [Disabling other http methods](#disable-other-http-methods).
22+
23+
```nginx
24+
server {
25+
listen 10.10.1.11:3128;
26+
27+
tunnel_pass;
28+
}
29+
```
30+
31+
In this basic configuration, the client establishes a tunnel with NGINX Plus, sends the HTTP `CONNECT` method to the specified port `3128` which is standard for forward proxying, and if successful, responds with `200 OK`. The tunnel is established and the client can perform the TLS handshake and get the response.
32+
33+
34+
## Disable other HTTP methods
35+
36+
As soon as the `CONNECT` method is enabled, all other methods such as `GET`, `POST` are not applicable in this mode. Rejecting them ensures that only tunnel requests are allowed thus improving security.
37+
38+
You can create a rule with the [`if`](https://nginx.org/en/docs/http/ngx_http_rewrite_module.html#if) directive to deny all HTTP methods except CONNECT, If a client uses any other method, NGINX Plus returns the `403` error code:
39+
40+
```nginx
41+
server {
42+
listen 10.10.1.11:3128;
43+
44+
# Handle other methods
45+
location / {
46+
if ($request_method != CONNECT) {
47+
return 403 "Forbidden: allows only CONNECT method";
48+
}
49+
50+
# allow CONNECT requests
51+
tunnel_pass;
52+
}
53+
}
54+
```
55+
56+
Note that in the example the [`tunnel_pass`](https://nginx.org/en/docs/http/ngx_http_tunnel_module.html#tunnel_pass) directive is specified inside the `location` block. Since `tunnel_pass` is a content handling directive, when it is specified for the `server` block, it is not inherited by nested locations.
57+
58+
59+
## Enable logging
60+
61+
For testing, debugging and monitoring purposes you can configure error and access logs.
62+
63+
1. At the `http` level, specify the [`log_format`](https://nginx.org/en/docs/http/ngx_http_log_module.html#log_format) directive, give the log a name (for example, `http_connect`), and include the metrics:
64+
65+
```nginx
66+
log_format http_connect '$remote_addr [$time_local] '
67+
'$request_method $host:$request_port $status '
68+
'$upstream_addr $bytes_sent $upstream_connect_time '
69+
'$request_time "$http_user_agent"';
70+
```
71+
72+
See the [NGINX variables index](https://nginx.org/en/docs/varindex.html) for the list of all supported variables.
73+
74+
2. Specify the [`error_log`](https://nginx.org/en/docs/ngx_core_module.html#error_log) and [`access_log`](https://nginx.org/en/docs/http/ngx_http_log_module.html#access_log) directives on the same configuration level where the [`tunnel_pass`](https://nginx.org/en/docs/http/ngx_http_tunnel_module.html#tunnel_pass) directive is defined:
75+
76+
```nginx
77+
access_log logs/connect_access.log http_connect;
78+
error_log logs/connect_error.log;
79+
```
80+
81+
3. Test the configuration. This example enables logging for the location block which denies all HTTP methods except `CONNECT` and where the `tunnel_pass` directive is specified:
82+
83+
```nginx
84+
log_format http_connect '$remote_addr [$time_local] '
85+
'$request_method $host:$request_port $status '
86+
'$upstream_addr $bytes_sent $upstream_connect_time '
87+
'$request_time "$http_user_agent"';
88+
server {
89+
listen 10.10.1.11:3128;
90+
91+
location / {
92+
if ($request_method != CONNECT) {
93+
return 403 "Forbidden: allows only CONNECT method";
94+
}
95+
96+
tunnel_pass;
97+
98+
access_log logs/connect_access.log http_connect;
99+
error_log logs/connect_error.log;
100+
}
101+
}
102+
```
103+
104+
A test request can be sent with the `curl` command:
105+
106+
```shell
107+
curl -v -x 10.10.1.11:3128 https://example.com
108+
```
109+
The result of the command can be seen in the log (`200 OK`):
110+
111+
```none
112+
logs > connect_access.log
113+
1 10.10.1.240 [01/Dec/2025:10:46:27 +0000] CONNECT example.com:443 200 127.456.789.0:443 4275 0.014 0.258 "curl/8.7.1"
114+
```
115+
116+
## Access control
117+
118+
It is highly recommended to restrict access to the proxy servers. Access control can be managed in several ways:
119+
120+
- by ports and port ranges with the [`num_map`](https://nginx.org/en/docs/http/ngx_http_num_map_module.html) module (NGINX Plus R36)
121+
- by IP addresses with the [`geo`](https://nginx.org/en/docs/http/ngx_http_geo_module.html) module
122+
- by hostnames with the [`map`](https://nginx.org/en/docs/http/ngx_http_map_module.html) module
123+
124+
All of these access methods can be used together and combined with the the [`proxy_allow_upstream`](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_allow_upstream) directive (NGINX Plus R36).
125+
126+
### Restricting by ports and port ranges
127+
128+
The [`num_map`](https://nginx.org/en/docs/http/ngx_http_num_map_module.html) module introduced in NGINX Plus R36 allows defining ports and port ranges and uses the same approach as the `map` block. For example, if the value of the [`$request_port`](https://nginx.org/en/docs/http/ngx_http_core_module.html#var_request_port) variable is `443`, the `$allowed_port` variable is assigned the value `ssl`. It also restricts the allowed port range for the upstream connections or proxy requests:
129+
130+
```nginx
131+
num_map $request_port $allowed_port {
132+
default 0;
133+
134+
443 ssl;
135+
<=1023 less-eq;
136+
8080-8090 range;
137+
>8092 more;
138+
}
139+
```
140+
141+
### Restricting by IP addresses
142+
143+
Access by IP addresses can be restricted with the [`geo`](https://nginx.org/en/docs/http/ngx_http_geo_module.html) module:
144+
145+
```nginx
146+
geo $allowed_networks {
147+
10.10.1.0/24 allow;
148+
10.20.11.0/24 allow;
149+
default deny;
150+
}
151+
```
152+
153+
### Restricting by hostnames
154+
155+
Restriction by hostnames can be configured with the [`map`](https://nginx.org/en/docs/http/ngx_http_map_module.html) module, which works mainly with strings and regular expressions. Although it is possible to match port numbers using `map`, it is recommended to use the [`num_map`](https://nginx.org/en/docs/http/ngx_http_num_map_module.html) module for more port-based restrictions, as it provides additional functionality such as defining ranges.
156+
157+
In this example, access is allowed to `example.com` and its subdomains only:
158+
159+
```nginx
160+
map $host $allowed_host {
161+
hostnames;
162+
default 0;
163+
example.com 1;
164+
*.example.com 1;
165+
}
166+
```
167+
168+
### Combining access control methods
169+
170+
These access methods can be combined in the [`tunnel_allow_upstream`](https://nginx.org/en/docs/http/ngx_http_tunnel_module.html#tunnel_allow_upstream) directive, which performs access checks when the upstream server to which the request will be sent is selected. If any variable passed to [`tunnel_allow_upstream`](https://nginx.org/en/docs/http/ngx_http_tunnel_module.html#tunnel_allow_upstream) evaluates to non-zero or non-empty value, it will be calculated as `1` and access is granted. If all variables evaluate to `0` or an empty value, access is denied.
171+
172+
### Access control example
173+
174+
```nginx
175+
log_format connect_debug '$remote_addr [$time_local] '
176+
'$request_method $host:$request_port $status '
177+
'$upstream_addr $bytes_sent $upstream_connect_time '
178+
'$request_time "$http_user_agent"';
179+
180+
num_map $request_port $allowed_port {
181+
default 0;
182+
183+
443 ssl;
184+
<=1023 less-eq;
185+
8080-8090 range;
186+
>8092 more;
187+
}
188+
189+
geo $allowed_networks {
190+
10.10.1.0/24 allow;
191+
10.20.11.0/24 allow;
192+
default deny;
193+
}
194+
195+
map $host $allowed_host {
196+
hostnames;
197+
default 0;
198+
example.com 1;
199+
*.example.com 1;
200+
}
201+
202+
server {
203+
listen 10.10.1.11:3128;
204+
205+
#Allow CONNECT only to certain ports/nets/hosts
206+
tunnel_allow_upstream $allowed_networks $allowed_port $allowed_host;
207+
208+
error_page 403;
209+
210+
satisfy all;
211+
212+
allow 10.10.0.0/16;
213+
allow 127.0.0.1;
214+
deny all;
215+
216+
tunnel_pass;
217+
218+
location ^~ /errors/ {
219+
internal;
220+
root html;
221+
allow all;
222+
}
223+
224+
access_log logs/connect_access.log connect_debug;
225+
error_log logs/connect_debug.log debug;
226+
}
227+
```
228+
229+
Access can also be limited using other modules, for example with the [`satisfy`](https://nginx.org/en/docs/http/ngx_http_core_module.html#satisfy) directive, or the [`allow`/`deny`](https://nginx.org/en/docs/http/ngx_http_access_module.html#allow) directives.
230+
231+
232+
## mTLS authenticaton
233+
234+
For proxy functionality, mutual mTLS authentication is supported:
235+
236+
```nginx
237+
log_format http_connect '$remote_addr [$time_local] '
238+
'$request_method $host:$request_port $status '
239+
'$upstream_addr $bytes_sent $upstream_connect_time '
240+
'$request_time "$http_user_agent"';
241+
242+
243+
map $ssl_client_verify $ssl_ok {
244+
default 0;
245+
SUCCESS 1;
246+
}
247+
248+
map $host $allowed_host {
249+
hostnames;
250+
default 0;
251+
example.com 1;
252+
*.example.com 1;
253+
}
254+
255+
map "$ssl_ok:$allowed_host" $connect_ok {
256+
default 0;
257+
"1:0" 1;
258+
"1:1" 1;
259+
"0:1" 1;
260+
}
261+
262+
server {
263+
listen 10.10.1.11:3128; ssl;
264+
265+
ssl_certificate tls/proxy.cert;
266+
ssl_certificate_key tls/proxy.key;
267+
268+
ssl_client_certificate tls/ca.cert;
269+
ssl_verify_client on;
270+
ssl_verify_depth 1;
271+
272+
ssl_protocols TLSv1.2 TLSv1.3;
273+
ssl_prefer_server_ciphers on;
274+
275+
tunnel_allow_upstream $connect_ok;
276+
277+
tunnel_pass;
278+
279+
access_log logs/connect_access.log http_connect;
280+
error_log logs/connect_error.log;
281+
}
282+
```
283+

0 commit comments

Comments
 (0)