/
http.conf
380 lines (353 loc) · 16.8 KB
/
http.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
include mime.types;
{% include "OPNsense/Nginx/ruleset.conf" ignore missing with context %}
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
log_format main_ext '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'"$host" sn="$server_name" '
'rt=$request_time '
'ua="$upstream_addr" us="$upstream_status" '
'ut="$upstream_response_time" ul="$upstream_response_length" '
'cs=$upstream_cache_status';
log_format handshake '"$http_user_agent" "$ssl_ciphers" "$ssl_curves"';
log_format anonymized ':: - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
#tcp_nopush on;
# https intercept detection
js_import /usr/local/opnsense/scripts/nginx/ngx_functions.js;
js_set $tls_intercepted ngx_functions.check_intercept;
# 200M should be big enough for file servers etc.
client_max_body_size 200M;
brotli_static on;
brotli on;
gzip_static on;
gzip on;
server_tokens off;
sendfile {% if OPNsense.Nginx.http.sendfile is defined and OPNsense.Nginx.http.sendfile == '1' %}On{% else %}Off{% endif %};
{% if OPNsense.Nginx.http.default_type is defined and OPNsense.Nginx.http.default_type != '' %}
default_type {{ OPNsense.Nginx.http.default_type }};
{% else %}
default_type application/octet-stream;
{% endif %}
{% if OPNsense.Nginx.http.server_names_hash_max_size is defined and OPNsense.Nginx.http.server_names_hash_max_size != '' %}
server_names_hash_max_size {{ OPNsense.Nginx.http.server_names_hash_max_size }};
{% endif %}
{% if OPNsense.Nginx.http.server_names_hash_bucket_size is defined and OPNsense.Nginx.http.server_names_hash_bucket_size != '' %}
server_names_hash_bucket_size {{ OPNsense.Nginx.http.server_names_hash_bucket_size }};
{% endif %}
{% if OPNsense.Nginx.http.keepalive_timeout is defined and OPNsense.Nginx.http.keepalive_timeout != '' %}
keepalive_timeout {{ OPNsense.Nginx.http.keepalive_timeout }};
{% endif %}
{% if OPNsense.Nginx.http.reset_timedout is defined and OPNsense.Nginx.http.reset_timedout == '1' %}
reset_timedout_connection on;
{% endif %}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# Map used in location.conf for proxy_ssl_name
map $ssl_server_name $upstream_sni_name {
default $ssl_server_name;
'' $host;
}
include http_post/*.conf;
# TODO add when core is ready for allowing nginx to serve the web interface
# include nginx_web.conf;
{% for zone in helpers.toList('OPNsense.Nginx.limit_zone') %}
limit_req_zone ${{ zone.key }} zone={{ zone['@uuid'].replace('-', '') }}:{{ zone.size }}m rate={{ zone.rate }}{{ zone.rate_unit }};
{% endfor %}
{% for cache_path in helpers.toList('OPNsense.Nginx.cache_path') %}
proxy_cache_path {{ cache_path.path }} levels=1:2 keys_zone={{ cache_path['@uuid'].replace('-', '') }}:{{ cache_path.size
}}m{% if cache_path.max_size is defined and cache_path.max_size != ''
%} max_size={{ cache_path.max_size }}g{% endif%}{% if cache_path.inactive is defined and cache_path.inactive != ''
%} inactive={{ cache_path.inactive }}m{% endif%} use_temp_path={%
if cache_path.use_temp_path is defined and cache_path.use_temp_path == '1'
%}on{% else %}off{%endif%};
{% endfor %}
{% include "OPNsense/Nginx/upstream.conf" ignore missing with context %}
include opnsense_http_vhost_plugins/*.conf;
{% set listen_list = [] %}
{% if OPNsense.Nginx.general.enabled is defined and OPNsense.Nginx.general.enabled == '1' %}
{% for server in helpers.toList('OPNsense.Nginx.http_server') %}
{% set single_servername = server.servername.split(",")[0] %}
{% if server.naxsi_whitelist_srcip is defined and server.naxsi_whitelist_srcip != '' %}
geo $naxsiwl{{ server['@uuid'].replace('-', '') }} {
default 0;
# Disable naxsi if client ip is trusted
{% for whitelist_srcip in server.naxsi_whitelist_srcip.split(',') %}
{{ whitelist_srcip }} 1;
{% endfor %}
}
{% endif %}
server {
{% set our_headers = [] %}
{% do our_headers.append('X-Powered-By') %}
{% if server.listen_http_address is defined and server.listen_http_address != '' %}
{% for listen_address in server.listen_http_address.split(',') %}
listen {{ listen_address }}{% if server.proxy_protocol is defined and server.proxy_protocol == '1' %} proxy_protocol{% endif %}{% if server.default_server is defined and server.default_server == '1' %} default_server{% endif %};
{% endfor %}
{% endif %}
{% if server.listen_https_address is defined and server.listen_https_address != '' %}
{% for listen_address in server.listen_https_address.split(',') %}
listen {{ listen_address }} http2 ssl{% if server.proxy_protocol is defined and server.proxy_protocol == '1' %} proxy_protocol{% endif %}{% if server.default_server is defined and server.default_server == '1' %} default_server{% endif %};
{% endfor %}
{% if server.tls_reject_handshake is defined and server.tls_reject_handshake == '1'%}
ssl_reject_handshake on;
{% endif %}
{% if server.certificate is defined %}
{% if server.ca is defined %}
ssl_client_certificate /usr/local/etc/nginx/key/{{ single_servername }}_ca.pem;
ssl_verify_client {{ server.verify_client }};
{% endif %}
{% if server.zero_rtt == '1' %}
ssl_early_data on;
{% endif %}
ssl_certificate_key /usr/local/etc/nginx/key/{{ single_servername }}.key;
ssl_certificate /usr/local/etc/nginx/key/{{ single_servername }}.pem;
ssl_protocols {{ server.tls_protocols.replace(',', ' ') }};
ssl_dhparam /usr/local/opnsense/data/OPNsense/Nginx/dh-parameters.4096.rfc7919;
{% if server.tls_ciphers is defined and server.tls_ciphers != '' %}
ssl_ciphers {{ server.tls_ciphers }};
{% endif %}
{% if server.tls_ecdh_curve is defined and server.tls_ecdh_curve != '' %}
ssl_ecdh_curve {{ server.tls_ecdh_curve }};
{% endif %}
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_prefer_server_ciphers {% if server.tls_prefer_server_ciphers is defined and server.tls_prefer_server_ciphers == '0'%}off{% else %}on{% endif %};
{% if server.ocsp_stapling is defined and server.ocsp_stapling == '1'%}
ssl_stapling on;
ssl_stapling_verify {% if server.ocsp_verify is defined and server.ocsp_verify == '1' %}On{% else %}Off{% endif %};
{% else %}
ssl_stapling off;
{% endif %}
{% endif %}
{% endif %}
sendfile {% if server.sendfile is defined and server.sendfile == '1' %}On{% else %}Off{% endif %};
server_name {{ server.servername.replace(',', ' ') }};
{% if server.real_ip_source is defined and server.real_ip_source != '' %}
real_ip_header {{ server.real_ip_source }};
{% if server.trusted_proxies is defined and server.trusted_proxies != '' %}
{% for trusted_proxy in server.trusted_proxies.split(',') %}
set_real_ip_from {{ trusted_proxy }};
{% endfor %}
{% endif %}
{% endif %}
client_header_buffer_size {{ server.client_header_buffer_size }}k;
large_client_header_buffers {{ server.large_client_header_buffers_number }} {{ server.large_client_header_buffers_size }}k;
{% if server.trusted_proxies_alias is defined and server.trusted_proxies_alias != '' %}
{% for trusted_proxy_uuid in server.trusted_proxies_alias.split(',') %}
{% set trusted_proxy_alias = helpers.getUUID(trusted_proxy_uuid) %}
{% if trusted_proxy_alias is defined and trusted_proxy_alias.content is defined %}
{% for alias_line in trusted_proxy_alias.content.split("\n") %}
set_real_ip_from {{ alias_line }};
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}
{% if server.charset is defined %}
charset {{ server.charset }};
{% endif %}
{% if server.access_log_format != 'disabled' %}
access_log /var/log/nginx/{{ server.servername }}.access.log {{ server.access_log_format }};
{% endif %}
{% if server.syslog_targets is defined and server.access_log_format != 'disabled' %}
{% set syslog_targets = server.syslog_targets.split(',') %}
{% include "OPNsense/Nginx/syslog_targets.conf" %}
{% endif %}
access_log /var/log/nginx/tls_handshake.log handshake;
error_log /var/log/nginx/{{ server.servername }}.error.log{% if server.error_log_level is defined %} {{ server.error_log_level }}{% endif %};
{% if server.root is defined and server.root != '' %}
root "{{server.root}}";
{% endif %}
{% if server.max_body_size is defined %}
client_max_body_size {{ server.max_body_size }};
{% endif %}
{% if server.body_buffer_size is defined %}
client_body_buffer_size {{ server.body_buffer_size }};
{% endif %}
{% if server.satisfy is defined %}
satisfy {{ server.satisfy }};
{% endif %}
#include tls.conf;
{% set used_errorpages = [] %}
{% if server.errorpages is defined and server.errorpages != '' %}
{% for errorpage_uuid in server.errorpages.split(',') %}
{% do used_errorpages.append(errorpage_uuid) %}
{% set errorpage = helpers.getUUID(errorpage_uuid) %}
error_page {{ errorpage.statuscodes.replace(',', ' ') }} {% if errorpage.response is defined and errorpage.response != '' %}={{ errorpage.response }} {% endif %}{% if errorpage.redirect is defined and errorpage.redirect != '' %}{{ errorpage.redirect }}{% else %}/error_{{ errorpage_uuid.replace('-', '') }}.html{% endif %};
{% if errorpage.redirect is not defined or errorpage.redirect == '' %}
location = /error_{{ errorpage_uuid.replace('-', '') }}.html {
internal;
root /usr/local/etc/nginx/views;
}
{% endif %}
{% endfor %}
{% else %}
error_page 403 /opnsense_error_403.html;
error_page 404 /opnsense_error_404.html;
error_page 405 /waf_denied.html;
error_page 500 501 502 503 504 /opnsense_server_error.html;
location = /opnsense_error_403.html {
internal;
root /usr/local/etc/nginx/views;
}
location = /opnsense_error_404.html {
internal;
root /usr/local/etc/nginx/views;
}
location = /opnsense_server_error.html {
internal;
root /usr/local/etc/nginx/views;
}
{% endif %}
{% if server.security_header is defined and server.security_header != '' %}
{% set security_rule = helpers.getUUID(server.security_header) %}
{% if security_rule is defined %}
{% include "OPNsense/Nginx/security_rule.conf" ignore missing with context %}
{% endif %}
{% endif %}
{% if server.limit_request_connections is defined %}
{% set limit_request = server.limit_request_connections %}
{% include "OPNsense/Nginx/httprequestlimit.conf" ignore missing with context %}
{% endif %}
# location to ban the host permanently
set $naxsi_extensive_log {% if server.naxsi_extensive_log is defined and server.naxsi_extensive_log == '1' %}1{% else %}0{% endif %};
location @permanentban {
access_log /var/log/nginx/permanentban.access.log main;
internal;
add_header "Content-Type" "text/plain; charset=UTF-8" always;
return {% if OPNsense.Nginx.http.ban_response is defined and OPNsense.Nginx.http.ban_response != '403' %}{{OPNsense.Nginx.http.ban_response}}{% else %}403 "You got banned permanently from this server."{% endif %};
}
error_page 418 = @permanentban;
location = /waf_denied.html {
root /usr/local/etc/nginx/views;
access_log /var/log/nginx/waf_denied.access.log main;
}
{% if server.enable_acme_support is defined and server.enable_acme_support == '1' %}
location ^~ /.well-known/acme-challenge/ {
default_type "text/plain";
{% if helpers.exists('OPNsense.AcmeClient.settings.challengePort') and OPNsense.AcmeClient.settings.challengePort|default("") != "" %}
proxy_pass http://127.0.0.1:{{OPNsense.AcmeClient.settings.challengePort}};
{% else %}
proxy_pass http://127.0.0.1:43580;
{% endif %}
}
{% endif %}
{% if server.disable_bot_protection is not defined or server.disable_bot_protection != '1' %}
# block based on User Agents - stuff I have found over the years in my server log
if ($http_user_agent ~* Python-urllib|Nmap|python-requests|libwww-perl|MJ12bot|Jorgee|fasthttp|libwww|Telesphoreo|A6-Indexer|ltx71|okhttp|ZmEu|sqlmap|LMAO/2.0|l9explore|l9tcpid|Masscan|zgrab|Ronin/2.0|Hakai/2.0) {
return 418;
}
{# MSIE 7 cannot be blocked - used for compatibility mode - https://blogs.msdn.microsoft.com/ieinternals/2013/09/21/internet-explorer-11s-many-user-agent-strings/ #}
if ($http_user_agent ~ "Indy\sLibrary|Morfeus Fucking Scanner|MSIE [0-6]\.\d+")
{
return 418;
}
if ($http_user_agent ~ ^Mozilla/[\d\.]+$)
{
return 418;
}
{% endif %}
{% if server.ip_acl is defined %}
{% set ip_acl = server.ip_acl %}
{% include "OPNsense/Nginx/ipacl.conf" %}
{% endif %}
location = /opnsense-report-csp-violation {
include fastcgi_params;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param SCRIPT_FILENAME /usr/local/opnsense/scripts/nginx/csp_report.php;
fastcgi_param TLS-Cipher $ssl_cipher;
fastcgi_param TLS-Protocol $ssl_protocol;
fastcgi_param TLS-SNI-Host $ssl_server_name;
fastcgi_param SERVER-UUID "{{ server['@uuid'] }}";
fastcgi_intercept_errors on;
fastcgi_pass unix:/var/run/php-webgui.socket;
}
location /opnsense-auth-request {
internal;
fastcgi_pass unix:/var/run/php-webgui.socket;
fastcgi_index index.php;
fastcgi_param TLS-Cipher $ssl_cipher;
fastcgi_param TLS-Protocol $ssl_protocol;
fastcgi_param TLS-SNI-Host $ssl_server_name;
fastcgi_param Original-URI $request_uri;
fastcgi_param Original-HOST $host;
fastcgi_param SERVER-UUID "{{ server['@uuid'] }}";
fastcgi_param SCRIPT_FILENAME /usr/local/opnsense/scripts/nginx/ngx_auth.php;
{% if server.advanced_acl_server is defined and server.advanced_acl_server != '' %}
fastcgi_param AUTH_SERVER "{{ server.advanced_acl_server.replace("\\", "\\\\").replace("$", "\\$").replace("\"", "\\\"") }}";
{% endif %}
fastcgi_intercept_errors on;
include fastcgi_params;
}
{% if server.block_nonpublic_data is defined and server.block_nonpublic_data == '1' %}
# apache htpasswd and htaccess
location ~ /\.ht {
return 403;
}
# those files may expose file system stuff
location ~ \.DS_Store$ {
return 403;
}
{% endif %}
{% if server.https_only is defined and server.https_only == '1' %}
if ($scheme != "https") {
return 302 https://$host$request_uri;
}
{% endif %}
include {{ server['@uuid'] }}_pre/*.conf;
{% if server.naxsi_whitelist_srcip is defined and server.naxsi_whitelist_srcip != '' %}
if ($naxsiwl{{ server['@uuid'].replace('-', '') }}) {
set $naxsi_flag_enable 0;
}
{% endif %}
{% if server.rewrites is defined %}
{% for rewrite_uuid in server.rewrites.split(',') %}
{% set rewrite = helpers.getUUID(rewrite_uuid) %}
rewrite {{ rewrite.source }} {{ rewrite.destination }}{% if rewrite.flag is defined%} {{ rewrite.flag }}{% endif %};
{% endfor %}
{% endif %}
{% if server.locations is defined %}
{% set location_errorpages = [] %}
{% for location_uuid in server.locations.split(',') %}
{% set location = helpers.getUUID(location_uuid) %}
{% if location.urlpattern is defined %}
{% include "OPNsense/Nginx/location.conf" ignore missing with context %}
{# Find used error pages in secrules_errorpage #}
{% if location.secrules_errorpage is defined and location.secrules_errorpage != '' %}
{% if location.secrules_errorpage not in location_errorpages %}
{% do location_errorpages.append(location.secrules_errorpage) %}
{% endif %}
{% endif %}
{# Find custom error pages used in locations #}
{% if location.errorpages is defined and location.errorpages != '' %}
{% for errorpage_uuid in location.errorpages.split(',') %}
{% if errorpage_uuid not in location_errorpages %}
{% do location_errorpages.append(errorpage_uuid) %}
{% endif %}
{% endfor %}
{% endif %}
{% endif %}
{% endfor %}
{# Error pages used in locations #}
{% for errorpage_uuid in location_errorpages %}
{% if errorpage_uuid not in used_errorpages %}
{% set errorpage = helpers.getUUID(errorpage_uuid) %}
{% if errorpage.redirect is not defined or errorpage.redirect == '' %}
location = /error_{{ errorpage_uuid.replace('-', '') }}.html {
internal;
root /usr/local/etc/nginx/views;
}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
include {{ server['@uuid'] }}_post/*.conf;
}
{% endfor %}
{% endif %}