Files
computer-playbook/roles/sys-svc-webserver-core/templates/nginx.conf.j2
kevinveenbirkenbachandClaude Opus 5.5 8d6040948c fix(sys-svc-webserver-core): match token routes on the decoded path
The access-log map read $request_uri, which keeps percent-encoding and
repeated slashes. A request such as /%70assword/reset/<token>,
/password%2Freset/<token> or //password//reset/<token> missed the
token-route expression, fell through to the plain-path branch and was
logged with the token in plain text, while the backend still decoded it
to its reset handler.

The map now reads $uri: decoded, slash-merged (merge_slashes is not
overridden anywhere) and without the query string, so the existing
expressions match every spelling of a token route. Trade-off: after an
internal redirect the log shows the internal URI; with the generic
vhost's error_page (ws_generic.conf.j2) a 5xx line now carries
/500.html instead of the client's path, with the status unchanged.
Nothing in the repository parses the proxy access log.

The unit test resolves the map against the variable it reads, modelling
$uri as nginx builds it, and adds the three encoded spellings above.
Negative control: with the map back on $request_uri, exactly those three
cases fail (3 failed, 8 passed).

Reported by CodeRabbit on #1322.

Part of #1192

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 12:46:31 +01:00

70 lines
3.3 KiB
Django/Jinja

worker_processes {{ WEBSERVER_PROXY_WORKER_PROCESSES }};
worker_rlimit_nofile {{ WEBSERVER_PROXY_WORKER_RLIMIT_NOFILE }};
events
{
worker_connections {{ WEBSERVER_PROXY_WORKER_CONNECTIONS }};
}
http
{
{% if DEPLOYMENT_MODE == 'swarm' %}
resolver 127.0.0.11 valid=10s ipv6=off;
{% endif %}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
h2c close;
}
map $http_upgrade $proxy_upgrade {
default $http_upgrade;
h2c '';
}
include mime.types;
default_type text/html;
server_tokens off;
add_header 'Vary' 'Origin' always;
proxy_cache_path {{ lookup('nginx','directories.cache.general') }} levels=1:2 keys_zone=cache:20m max_size=20g inactive=14d use_temp_path=off;
proxy_cache_path {{ lookup('nginx','directories.cache.image') }} levels=1:2 keys_zone=imgcache:10m inactive=60m use_temp_path=off;
server_names_hash_bucket_size 128; # size of each bucket for server_name lookups (in bytes)
server_names_hash_max_size 512; # maximum total buckets for the server_name hash table
{% include 'roles/sys-svc-proxy/templates/headers/buffers.conf.j2' %}
map $uri $infinito_access_log_path {
"~*^(?P<infinito_access_log_token_route>[^?]*?/(?:password/reset|password-reset|reset-password|confirm-email|lostpassword/reset/form|email-login|activate-account)/)" "${infinito_access_log_token_route}[redacted]";
"~^(?P<infinito_access_log_plain_path>[^?]*)" "${infinito_access_log_plain_path}";
}
{% if MODE_DEBUG | bool %}
log_format debug '$host - $remote_addr [$time_local] '
'"$request_method $infinito_access_log_path $server_protocol" $status $body_bytes_sent '
'"User-Agent: $http_user_agent" '
'"ReqTime: $request_time" "UpstreamTime: $upstream_response_time" '
'"ReqLength: $request_length" "BytesSent: $bytes_sent" '
'"ConnRequests: $connection_requests" '
'"X-Forwarded-For: $http_x_forwarded_for" '
'"Scheme: $scheme" "Protocol: $server_protocol" "ServerName: $server_name"';
access_log /dev/stdout debug;
{% else %}
log_format redacted '$remote_addr - - [$time_local] '
'"$request_method $infinito_access_log_path $server_protocol" $status $body_bytes_sent '
'"-" "$http_user_agent"';
access_log /dev/stdout redacted;
{% endif %}
error_log /dev/stderr info;
sendfile on;
keepalive_timeout 65;
gzip on;
gzip_proxied any;
gzip_vary on;
gzip_disable "MSIE [1-6]\.(?!.*SV1)"; # Disable for Internetexplorer 6
gzip_comp_level 4;
gzip_min_length 256;
gzip_types application/atom+xml application/javascript application/xml+rss application/x-javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy text/javascript text/xml;
types_hash_max_size 4096;
{% for dir in lookup('nginx','directories.configuration.http_includes') %}
include {{ dir }}*.conf;
{% endfor %}
}
stream{
include {{ lookup('nginx','directories.configuration.streams') }}*.conf;
}