Permalink
Cannot retrieve contributors at this time
executable file
679 lines (600 sloc)
19.9 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| # -*- awk -*- | |
| # eotk (c) 2017 Alec Muffett | |
| # EMACS awk mode works quite well for nginx configs | |
| # ---- BEGIN HARD/CLASSIC SWITCH ---- | |
| %%IF %HARD_MODE% | |
| # *HARD* configuration | |
| # swap domain names for onions via brute-force, with whitelisted repairs... | |
| %%ELSE | |
| # *CLASSIC* configuration | |
| # swap domain names for onions via targeted regular expressions... | |
| %%ENDIF | |
| # ---- END HARD/CLASSIC SWITCH ---- | |
| # logs and pids | |
| pid %PROJECT_DIR%/nginx.pid; | |
| error_log %LOG_DIR%/nginx-error.log %NGINX_SYSLOG%; | |
| # TODO: notes for custom 403 error-handling pages: | |
| # https://www.cyberciti.biz/faq/unix-linux-nginx-custom-error-403-page-configuration/ | |
| # https://nginx.org/en/docs/http/ngx_http_core_module.html#error_page | |
| # performance | |
| %%IF %IS_SOFTMAP% | |
| worker_processes %SOFTMAP_NGINX_WORKERS%; # softmap | |
| %%ELSE | |
| worker_processes %NGINX_WORKERS%; # hardmap | |
| %%ENDIF | |
| worker_rlimit_nofile %NGINX_RLIM%; | |
| events { | |
| worker_connections %NGINX_RLIM%; | |
| } | |
| http { | |
| # nginx fails without large enough buckets (sigh) | |
| map_hash_bucket_size %NGINX_HASH_BUCKET_SIZE%; | |
| # dns for proxy (sigh) | |
| resolver %NGINX_RESOLVER% valid=%NGINX_TIMEOUT%s; | |
| resolver_timeout %NGINX_TIMEOUT%s; | |
| # we walk a line between keeping it small and flooding resources... | |
| proxy_buffering on; | |
| # for initial; impacts SSL header | |
| proxy_buffer_size %NGINX_BLOCK_SIZE%; | |
| # for rest of response | |
| proxy_buffers %NGINX_BLOCK_COUNT% %NGINX_BLOCK_SIZE%; | |
| # how much can be busy sending to client? | |
| proxy_busy_buffers_size %NGINX_BLOCK_BUSY_SIZE%; | |
| # where to stash oversize requests? | |
| client_body_temp_path /tmp/nginx-body-%PROJECT%; | |
| client_max_body_size 4m; | |
| # in case we want to start spooling responses locally | |
| proxy_temp_path /tmp/nginx-proxy-%PROJECT%; | |
| proxy_max_temp_file_size %NGINX_TMPFILE_SIZE%; | |
| proxy_temp_file_write_size %NGINX_BLOCK_SIZE%; | |
| %%IF %NGINX_CACHE_SECONDS% | |
| # nginx caching static responses for %NGINX_CACHE_SECONDS% seconds | |
| # - this is a lightweight cache to reduce "storms", hence the global | |
| # approch of "cache everything for a small number of seconds" | |
| # https://nginx.org/en/docs/http/ngx_http_proxy_module.html | |
| proxy_cache_path /tmp/nginx-cache-%PROJECT% levels=1:2 keys_zone=%PROJECT%:%NGINX_CACHE_SIZE%; | |
| proxy_cache %PROJECT%; | |
| proxy_cache_min_uses %NGINX_CACHE_MIN_USES%; | |
| proxy_cache_revalidate on; | |
| proxy_cache_use_stale timeout updating; | |
| proxy_cache_valid any %NGINX_CACHE_SECONDS%s; # "any" includes 404s, etc | |
| # content-types to not cache | |
| map $http_content_type $no_cache_content_type { | |
| %%CSV %NO_CACHE_CONTENT_TYPE% | |
| %1% 1; | |
| %%ENDCSV | |
| default 0; | |
| } | |
| # hosts not to cache | |
| map $http_host $no_cache_host { | |
| hostnames; | |
| %%CSV %NO_CACHE_HOST% | |
| %1% 1; | |
| %%ENDCSV | |
| default 0; | |
| } | |
| # so, should we skip caching this stuff for some reason? | |
| proxy_no_cache $no_cache_content_type $no_cache_host; | |
| proxy_cache_bypass $no_cache_content_type $no_cache_host; | |
| %%ELSE | |
| # nginx caching disabled | |
| %%ENDIF | |
| # logs | |
| access_log %LOG_DIR%/nginx-access.log; | |
| # global settings | |
| server_tokens off; | |
| # allow/deny (first wins) | |
| allow "unix:"; | |
| deny all; | |
| # rewrite these content types; text/html is implicit | |
| subs_filter_types | |
| application/javascript | |
| application/json | |
| application/x-javascript | |
| text/css | |
| text/javascript | |
| text/xml | |
| %%IF %EXTRA_SUBS_FILTER_TYPES% | |
| # extra_subs_filter_types | |
| %EXTRA_SUBS_FILTER_TYPES% | |
| %%ELSE | |
| # no extra_subs_filter_types | |
| %%ENDIF | |
| ; | |
| #================================================================== | |
| %%IF %HARD_MODE% | |
| # ---- BEGIN HARD MODE CODE ---- | |
| %%IF %PRESERVE_CSV% | |
| # preserve subs (save-phase): 1=description,2=re,3=i_or_empty,4=replacement | |
| %%CSV %PRESERVE_CSV% | |
| # saving regexp '%2%' as '%1%' for replacement with '%4%' (%3%) | |
| subs_filter | |
| (%PRESERVE_PREAMBLE%)(%2%)\\b | |
| $1%PRESERVE_BEFORE%%1%%PRESERVE_AFTER% | |
| g%3%r | |
| ; | |
| %%ENDCSV | |
| %%ELSE | |
| # no preserve subs (save-phase) | |
| %%ENDIF | |
| %%BEGIN | |
| # HARD-MODE: %DNS_DOMAIN% -> %ONION_ADDRESS% | |
| subs_filter | |
| \\b%DNS_DOMAIN_RE2%\\b | |
| %ONION_ADDRESS% | |
| gir | |
| ; | |
| %%IF %HARD_MODE% > 1 | |
| # HARD-MODE-EXTRA: %DNS_DOMAIN_RE% -> %ONION_ADDRESS_RE% | |
| subs_filter | |
| \\b%DNS_DOMAIN_RERE2%\\b | |
| %ONION_ADDRESS_RE2% | |
| gir | |
| ; | |
| %%ENDIF | |
| %%END | |
| %%IF %FOREIGNMAP_CSV% | |
| # foreignmap subs: 1=onion,2=re,3=re2,4=dns,5=re,6=re2 | |
| %%CSV %FOREIGNMAP_CSV% | |
| # for %4% -> %1% | |
| subs_filter | |
| \\b%6%\\b | |
| %1% | |
| gir | |
| ; | |
| %%ENDCSV | |
| %%ELSE | |
| # no foreignmap subs | |
| %%ENDIF | |
| %%IF %PRESERVE_CSV% | |
| # preserve subs (restore-phase): 1=description,2=re,3=i_or_empty,4=replacement | |
| %%CSV %PRESERVE_CSV% | |
| # restoring '%1%' with '%4%' | |
| subs_filter | |
| %PRESERVE_BEFORE%%1%%PRESERVE_AFTER% | |
| %4% | |
| g | |
| ; | |
| %%ENDCSV | |
| %%ELSE | |
| # no preserve subs (restore-phase) | |
| %%ENDIF | |
| # ---- END HARD MODE CODE ---- | |
| #------------------------------------------------------------------ | |
| %%ELSE | |
| #------------------------------------------------------------------ | |
| # ---- BEGIN CLASSIC MODE CODE ---- | |
| # subs_filter: these patterns bear some explanation; the goal is to | |
| # work regular expressions really hard in order to minimise the | |
| # number of expressions which are used in the basic config, so the | |
| # basic pattern is to capture zero/more "sub." in "//sub.foo.com" | |
| # and interpolate that into "//sub.xxxxxxxx.onion"; so far? | |
| # but it turns out that some JSON libraries like to "escape" the | |
| # forward slashes in JSON content, leading to input like (literal) | |
| # "http:\/\/sub.foo.com\/foo.html" - so you need to add the | |
| # backslashes, but then you need to escape the backslashes, except | |
| # they need double-escaping in the regexp because of string | |
| # interpolation; hence 4x backslash -> 1x matched character | |
| # likewise we use the "_RE2" form of the re-escaped domain name in | |
| # order to coerce the regexp to match literal dots, not wildcards. | |
| # there seems to be some sort of shortcut at play here; the trailing | |
| # "\\b" also seems to work as "\b" however that would apparently | |
| # break the double-escaping that is necessary/works everywhere else | |
| # in subs_filter. | |
| # also, regrettably, named capture groups appear not to work, we're | |
| # fortunate that there appear not to be more than 9 capture groups | |
| # by default, lest "$1" bleed into the subsequent digits of an onion | |
| # address: $1234567abcdefghij.onion | |
| # finally: some sites encode // with %-encoded "2F" in URIs... | |
| %%BEGIN | |
| # for %DNS_DOMAIN% -> %ONION_ADDRESS% anchored by // or \/\/ | |
| subs_filter | |
| (/|\\\\/\\\\)/(([-0-9a-z]+\\.)+)?%DNS_DOMAIN_RE2%\\b | |
| $1/$2%ONION_ADDRESS% | |
| gir | |
| ; | |
| # for %DNS_DOMAIN% -> %ONION_ADDRESS% anchored with hex-encoded slashes | |
| subs_filter | |
| %%2F%%2F(([-0-9a-z]+\\.)+)?%DNS_DOMAIN_RE2%\\b | |
| %%2F%%2F$1%ONION_ADDRESS% | |
| gir | |
| ; | |
| %%END | |
| %%IF %FOREIGNMAP_CSV% | |
| # foreignmap subs: 1=onion,2=re,3=re2,4=dns,5=re,6=re2 | |
| %%CSV %FOREIGNMAP_CSV% | |
| # for %4% -> %1% anchored by // or \/\/ | |
| subs_filter | |
| (/|\\\\/\\\\)/(([-0-9a-z]+\\.)+)?%6%\\b | |
| $1/$2%1% | |
| gir | |
| ; | |
| # for %4% -> %1% anchored with hex-encoded slashes | |
| subs_filter | |
| %%2F%%2F(([-0-9a-z]+\\.)+)?%6%\\b | |
| %%2F%%2F$1%1% | |
| gir | |
| ; | |
| %%ENDCSV | |
| %%ELSE | |
| # no foreignmap subs | |
| %%ENDIF | |
| # ---- END CLASSIC MODE CODE ---- | |
| %%ENDIF | |
| #================================================================== | |
| # o_to_d_lookup -> if cannot remap, return input. note: old versions | |
| # of lua-plugin cannot cope with code like o_to_d_mappings[o[1]] | |
| # because of `long bracket syntax`; the `[o[` freaks it out. | |
| # See: https://github.com/openresty/lua-nginx-module/issues/748 | |
| init_by_lua_block { | |
| -- helper functions for elsewhere | |
| slog = function (s) -- in case of manual debugging | |
| ngx.log(ngx.ERR, s) | |
| return | |
| end | |
| has_suffix = function (s, x) | |
| return string.sub(s, -string.len(x)) == x | |
| end | |
| -- mapping onions to dns | |
| o_to_d_mappings = {} | |
| %%BEGIN | |
| o_to_d_mappings["%ONION_ADDRESS%"] = "%DNS_DOMAIN%" | |
| %%END | |
| o_to_d_lookup = function (m) | |
| local k = m[1] -- see note above re: array syntax | |
| return ( o_to_d_mappings[k] or k ) | |
| end | |
| onion_to_dns = function (i) | |
| if i == nil or i == "" then | |
| return i | |
| end | |
| if (type(i) == "table") then | |
| local j, k, result | |
| result = {} | |
| for j, k in ipairs(i) do | |
| table.insert(result, onion_to_dns(k)) | |
| end | |
| return result | |
| end | |
| local o, num, errs = ngx.re.gsub(i, "\\b([a-z2-7]{16}(?:[a-z2-7]{40})?\\.onion)\\b", o_to_d_lookup, "io") | |
| return o | |
| end | |
| -- mapping dns to onions, for experimentation | |
| d_to_o_mappings = {} | |
| %%BEGIN | |
| d_to_o_mappings["%DNS_DOMAIN%"] = "%ONION_ADDRESS%" | |
| %%END | |
| d_to_o_lookup = function (m) | |
| local k = m[1] -- see note above re: array syntax | |
| return ( d_to_o_mappings[k] or k ) | |
| end | |
| dns_to_onion = function (i) | |
| if i == nil or i == "" or i == "*" then | |
| return i | |
| end | |
| if (type(i) == "table") then | |
| local j, k, result | |
| result = {} | |
| for j, k in ipairs(i) do | |
| table.insert(result, dns_to_onion(k)) | |
| end | |
| return result | |
| end | |
| local num, errs | |
| %%BEGIN | |
| i, num, errs = ngx.re.gsub(i, "\\b%DNS_DOMAIN_RE2%\\b", "%ONION_ADDRESS%", "io") | |
| %%END | |
| return i | |
| end | |
| -- a note for future maintainers; if we were being strictly orthogonal then | |
| -- the replacement with ONION_ADDRESS in much of this Lua block would have to | |
| -- be double-escaped for potential backslashes, because double-quotes; | |
| -- however this is not needed because DNS forbids backslash; the only code | |
| -- where this becomes evident/necessary is here, with "_RE2": | |
| dnsre_to_onionre = function (i) | |
| local num, errs | |
| -- TODO: BRING THIS INTO LINE WITH dns_to_onion -- | |
| %%BEGIN | |
| i, num, errs = ngx.re.gsub(i, "\\b%DNS_DOMAIN_RERE2%\\b", "%ONION_ADDRESS_RE2%", "io") | |
| %%END | |
| return i | |
| end | |
| } | |
| # filter the response headers en-route back to the user | |
| header_filter_by_lua_block { | |
| local k, v | |
| -- ================================================================== | |
| %%IF ! %HARD_MODE% | |
| -- ---- BEGIN CLASSIC MODE CODE ---- | |
| -- is this javascript/json? if so, extra processing: | |
| -- 1) set a processing flag to pick up in body_filter_by_lua_block | |
| -- 2) invalidate content-length, because we will change it | |
| k = "Content-Type" | |
| v = ngx.header[k] | |
| if v == "application/javascript" or | |
| v == "application/json" or | |
| v == "application/x-javascript" or | |
| v == "text/css" or | |
| v == "text/javascript" then | |
| ngx.ctx.needs_extra_processing = 1 | |
| ngx.header.content_length = nil | |
| end | |
| %%IF %EXTRA_PROCESSING_CSV% | |
| -- run on `v` for further extra_processing_csv checks | |
| %%CSV %EXTRA_PROCESSING_CSV% | |
| if v == "%1%" then | |
| local m, err = ngx.re.match(ngx.var.uri, "%2%", "io") | |
| if m then | |
| ngx.ctx.needs_extra_processing = 1 | |
| ngx.header.content_length = nil | |
| end | |
| end | |
| %%ENDCSV | |
| %%ELSE | |
| -- no extra_processing_csv checks | |
| %%ENDIF | |
| -- ---- END CLASSIC MODE CODE ---- | |
| %%ENDIF | |
| -- ================================================================== | |
| local origin_rewrites = { | |
| "Access-Control-Allow-Origin", | |
| %%IF %SUPPRESS_HEADER_CSP% | |
| -- CSP headers are suppressed via SUPPRESS_HEADER_CSP | |
| %%ELSE | |
| "Content-Security-Policy", | |
| "Content-Security-Policy-Report-Only", | |
| %%ENDIF | |
| "Link", | |
| "Location", | |
| "Set-Cookie" | |
| } | |
| local i, k | |
| for i, k in ipairs(origin_rewrites) do | |
| local v = ngx.header[k] | |
| if v then | |
| ngx.header[k] = dns_to_onion(v) | |
| end | |
| end | |
| } | |
| # filter the response body en-route back to the user | |
| body_filter_by_lua_block { | |
| -- ================================================================== | |
| %%IF ! %HARD_MODE% | |
| -- ---- BEGIN CLASSIC MODE CODE ---- | |
| -- rather than blindly replacing "foo.com" with "foo.onion" everywhere, | |
| -- instead we restrict such brute-force replacement to content that was | |
| -- flagged in header_filter_by_lua_block | |
| if ngx.ctx.needs_extra_processing == 1 then | |
| -- the flag was set; this content deserves brute-force search & replace | |
| local chunk = ngx.arg[1] | |
| -- subs_filter picked up the "//"-anchored strings; now we sub the rest | |
| chunk = dns_to_onion(chunk) | |
| -- and we sub the basic "foo\.com" regular-expressions, too | |
| chunk = dnsre_to_onionre(chunk) | |
| -- more complex regular expressions are out of scope. | |
| ngx.arg[1] = chunk | |
| end | |
| -- ---- END CLASSIC MODE CODE ---- | |
| %%ENDIF | |
| -- ================================================================== | |
| %%IF %DEBUG_TRAP% | |
| -- debug traps | |
| local i = ngx.arg[1] | |
| local ct = ngx.header["Content-Type"] | |
| local uri = ngx.var.uri | |
| local iterator, err | |
| %%CSV %DEBUG_TRAP% | |
| iterator, err = ngx.re.gmatch(i, ".{0,32}(%1%).{0,32}") | |
| if not iterator then | |
| ngx.log(ngx.ERR, "gmatch error: ", err) | |
| else | |
| while true do | |
| local m, err = iterator() | |
| if err then | |
| ngx.log(ngx.ERR, "iterator error: ", err) | |
| break | |
| end | |
| if not m then | |
| break | |
| end | |
| slog(string.format("TRAP <<%s>> CONTEXT <<%s>> TYPE <<%s>> URI %s", m[1], m[0], ct, uri)) | |
| end -- while true | |
| end -- if iterator | |
| %%ENDCSV | |
| %%ELSE | |
| -- no debug traps | |
| %%ENDIF | |
| } | |
| %%IF %SUPPRESS_HEADER_CSP% | |
| # csp suppression | |
| proxy_hide_header "Content-Security-Policy"; | |
| proxy_hide_header "Content-Security-Policy-Report-Only"; | |
| %%ELSE | |
| # csp not suppressed, will be rewritten instead, see below | |
| %%ENDIF | |
| %%IF %SUPPRESS_HEADER_HSTS% | |
| # hsts suppression | |
| proxy_hide_header "Strict-Transport-Security"; | |
| %%ELSE | |
| # hsts not suppressed | |
| %%ENDIF | |
| %%IF %SUPPRESS_HEADER_HPKP% | |
| # hpkp suppression | |
| proxy_hide_header "Public-Key-Pins"; | |
| proxy_hide_header "Public-Key-Pins-Report-Only"; | |
| %%ELSE | |
| # hpkp not suppressed | |
| %%ENDIF | |
| # global proxy settings | |
| proxy_read_timeout %NGINX_TIMEOUT%; | |
| proxy_connect_timeout %NGINX_TIMEOUT%; | |
| # SSL config | |
| ssl_certificate %SSL_DIR%/%CERT_PREFIX%.cert; | |
| ssl_certificate_key %SSL_DIR%/%CERT_PREFIX%.pem; | |
| ssl_buffer_size 4k; | |
| #ssl_ciphers 'EECDH+CHACHA20:EECDH+AESGCM:EECDH+AES256'; ## LibreSSL, OpenSSL 1.1.0+ | |
| ssl_ciphers 'EECDH+AESGCM:EECDH+AES256'; ## OpenSSL 1.0.1% to 1.0.2% | |
| ssl_ecdh_curve prime256v1; | |
| #ssl_ecdh_curve secp384r1:prime256v1; ## NGINX nginx 1.11.0 and later | |
| ssl_prefer_server_ciphers on; | |
| ssl_protocols TLSv1 TLSv1.1 TLSv1.2; | |
| ssl_session_cache shared:SSL:10m; | |
| ssl_session_timeout 10m; | |
| # websockets: on the basis of http_upgrade, set connection_upgrade: | |
| # empty -> empty | |
| # default -> "upgrade" | |
| map $http_upgrade $connection_upgrade { | |
| default "upgrade"; | |
| "" ""; | |
| } | |
| %%BEGIN | |
| %%IF %FORCE_HTTPS% | |
| # FORCE_HTTPS is in use; set up separate server for port 80 & force redirects | |
| server { | |
| %%IF %IS_SOFTMAP% | |
| %%RANGE I 1 %SOFTMAP_TOR_WORKERS% | |
| listen unix:%PROJECT_DIR%/%TOR_WORKER_PREFIX%-%I%.d/port-80.sock; | |
| %%ENDRANGE | |
| %%ELSE | |
| listen unix:%PROJECT_DIR%/%ONION_ADDRESS%.d/port-80.sock; | |
| %%ENDIF | |
| # subdomain regexp captures trailing dot, use carefully; does not need "~*" | |
| # NB: this regexp should be kept in-sync with the other FORCE_HTTPS copy | |
| server_name | |
| %ONION_ADDRESS% | |
| ~^(?<servernamesubdomain>([-0-9a-z]+\\.)+)%ONION_ADDRESS_RE2%$ | |
| ; | |
| %%IF %SUPPRESS_TOR2WEB% | |
| # suppress tor2web traffic; "let them use clearnet" | |
| if ( $http_x_tor2web ) { | |
| return 403 "%BLOCK_ERR%"; | |
| } | |
| %%ELSE | |
| # tor2web not suppressed | |
| %%ENDIF | |
| # tell the client to try again as HTTPS without ever leaving the onion | |
| # use 307 / temporary redirect because your URIs may change in future | |
| # use $host (not $server) to copy-over subdomains, etc, transparently | |
| # SEND BACK ORIGINAL PARAMS, FIX THEM ONLY UPON FORWARD TO THE PROXY. | |
| return 307 https://$host$request_uri; | |
| } | |
| %%ELSE | |
| # FORCE_HTTPS is not in use, cleartext data may traverse the internet | |
| %%ENDIF | |
| # for %ONION_ADDRESS% -> %DNS_DOMAIN% | |
| server { | |
| %%IF %IS_SOFTMAP% | |
| %%RANGE I 1 %SOFTMAP_TOR_WORKERS% | |
| %%IF not %FORCE_HTTPS% | |
| # FORCE_HTTPS is not in use, cleartext data may traverse the internet | |
| listen unix:%PROJECT_DIR%/%TOR_WORKER_PREFIX%-%I%.d/port-80.sock; | |
| %%ENDIF | |
| listen unix:%PROJECT_DIR%/%TOR_WORKER_PREFIX%-%I%.d/port-443.sock ssl; | |
| %%ENDRANGE | |
| %%ELSE | |
| # hardmap | |
| # unix sockets; use <ONION_ADDRESS>.d as a naming convention | |
| %%IF not %FORCE_HTTPS% | |
| # FORCE_HTTPS is not in use, cleartext data may traverse the internet | |
| listen unix:%PROJECT_DIR%/%ONION_ADDRESS%.d/port-80.sock; | |
| %%ENDIF | |
| listen unix:%PROJECT_DIR%/%ONION_ADDRESS%.d/port-443.sock ssl; | |
| %%ENDIF | |
| # subdomain regexp captures trailing dot, use carefully; does not need "~*" | |
| # NB: this regexp should be kept in-sync with the other FORCE_HTTPS copy | |
| server_name | |
| %ONION_ADDRESS% | |
| ~^(?<servernamesubdomain>([-0-9a-z]+\\.)+)%ONION_ADDRESS_RE2%$ | |
| ; | |
| %%INCLUDE templates.d/nginx-generated-blocks.conf | |
| %%IF %COOKIE_LOCK% | |
| # if we are visiting the magic path, open the cookie-lock | |
| location "%COOKIE_LOCK%" { | |
| add_header Set-Cookie "eotk_lock=%COOKIE_LOCK%;Domain=.%ONION_ADDRESS%;Path=/;Max-Age=604800"; | |
| return 200 "OK"; | |
| } | |
| %%ELSE | |
| # no cookie_lock cookie setting | |
| %%ENDIF | |
| %%IF %NGINX_HELLO_ONION% | |
| # for test & to help SSL certificate acceptance | |
| location ~* ^/hello[-_]onion/?$ { | |
| return 200 "Hello, Onion User!"; | |
| } | |
| %%ELSE | |
| # no "hello-onion" endpoint | |
| %%ENDIF | |
| %%IF %HARDCODED_ENDPOINT_CSV% | |
| # hardcoded_endpoints: 1=path_re,2=response | |
| %%CSV %HARDCODED_ENDPOINT_CSV% | |
| location "%1%" { | |
| return 200 %2%; | |
| } | |
| %%ENDCSV | |
| %%ELSE | |
| # no hardcoded_endpoints | |
| %%ENDIF | |
| # for traffic | |
| location / { | |
| %%INCLUDE templates.d/nginx-generated-checks.conf | |
| %%IF %COOKIE_LOCK% | |
| # check for cookie-lock | |
| if ( $cookie_eotk_lock != "%COOKIE_LOCK%" ) { %NGINX_ACTION_ABORT%; } | |
| %%ELSE | |
| # no cookie-lock checks | |
| %%ENDIF | |
| # deonionify the request_uri for forwarding (both path and args) | |
| set_by_lua_block $request_uri2 { | |
| local old = ngx.var.request_uri | |
| -- onion_to_dns is potentially expensive at scale, so do a cheap test | |
| local m, err = ngx.re.match(old, "\\b[a-z2-7]{16}(?:[a-z2-7]{40})?\\.onion\\b", "o") | |
| if not m then -- nothing to attempt to rewrite, quick return | |
| return old | |
| end | |
| return onion_to_dns(old) | |
| } | |
| # note use of both $scheme and the deonionified uri (both path and args) | |
| set $new_url "$scheme://${servernamesubdomain}%DNS_DOMAIN%$request_uri2"; | |
| proxy_pass $new_url; | |
| proxy_http_version 1.1; | |
| # a note on proxy_set_header, add_header, similar methods, etc; | |
| # if you override *any* header then you will lose the other | |
| # headers inherited from the parent contexts: | |
| # https://blog.g3rt.nl/nginx-add_header-pitfall.html | |
| proxy_set_header X-From-Onion %X_FROM_ONION_VALUE%; | |
| proxy_set_header Host "${servernamesubdomain}%DNS_DOMAIN%"; | |
| proxy_set_header Accept-Encoding "identity"; | |
| proxy_set_header Connection $connection_upgrade; # SSL | |
| proxy_set_header Upgrade $http_upgrade; # SSL | |
| proxy_ssl_server_name on; # SSL | |
| # rewrite request referer | |
| set_by_lua_block $referer2 { return onion_to_dns(ngx.var.http_referer) } | |
| proxy_set_header Referer $referer2; | |
| # rewrite request origin | |
| set_by_lua_block $origin2 { return onion_to_dns(ngx.var.http_origin) } | |
| proxy_set_header Origin $origin2; | |
| # rewrite request cookies | |
| set_by_lua_block $cookie2 { return onion_to_dns(ngx.var.http_cookie) } | |
| proxy_set_header Cookie $cookie2; | |
| %%IF %SUPPRESS_METHODS_EXCEPT_GET% | |
| # suppress non-GET methods (e.g.: POST) | |
| limit_except GET { | |
| deny all; | |
| } | |
| %%ELSE | |
| # non-GET methods (e.g.: POST) are not suppressed | |
| %%ENDIF | |
| } | |
| } | |
| %%END | |
| # header purge | |
| more_clear_headers "Age"; | |
| more_clear_headers "Server"; | |
| more_clear_headers "Via"; | |
| more_clear_headers "X-From-Nginx"; | |
| more_clear_headers "X-NA"; | |
| more_clear_headers "X-Powered-By"; | |
| more_clear_headers "X-Request-Id"; | |
| more_clear_headers "X-Runtime"; | |
| more_clear_headers "X-Varnish"; | |
| } |