Dear all,
My issue: I implemented successfully FreshRSS behind NGINX reverse proxy. But following the discussion, I would like to go further and get the $remote_user printed in the nginx access.log when connected to the API. The recommended solution seems to look at “Authorization” and if it’s equal to “^GoogleLogin auth=([^/]+)” then modify accordingly the value of $remote_user. Unfortunately I don’t know how to do that!
I want to translate SetEnvIfNoCase “Authorization” “^GoogleLogin auth=([^/]+)” REMOTE_USER=$1 LOG_REMOTE_USER=$1 (apache language) into the nginx location block.
How I encountered the problem: FreshRSS Docker behind nginx-proxy Docker image.
Version of NGINX or NGINX adjacent software (e.g. NGINX Gateway Fabric): 1.29.3
Deployment environment: docker
Minimal NGINX config to reproduce your issue:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
# configuration file /etc/nginx/nginx.conf:
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
server_tokens off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
# configuration file /etc/nginx/mime.types:
types {
text/html html htm shtml;
text/css css;
text/xml xml;
image/gif gif;
image/jpeg jpeg jpg;
application/javascript js;
application/atom+xml atom;
application/rss+xml rss;
text/mathml mml;
text/plain txt;
text/vnd.sun.j2me.app-descriptor jad;
text/vnd.wap.wml wml;
text/x-component htc;
image/avif avif;
image/png png;
image/svg+xml svg svgz;
image/tiff tif tiff;
image/vnd.wap.wbmp wbmp;
image/webp webp;
image/x-icon ico;
image/x-jng jng;
image/x-ms-bmp bmp;
font/woff woff;
font/woff2 woff2;
application/java-archive jar war ear;
application/json json;
application/mac-binhex40 hqx;
application/msword doc;
application/pdf pdf;
application/postscript ps eps ai;
application/rtf rtf;
application/vnd.apple.mpegurl m3u8;
application/vnd.google-earth.kml+xml kml;
application/vnd.google-earth.kmz kmz;
application/vnd.ms-excel xls;
application/vnd.ms-fontobject eot;
application/vnd.ms-powerpoint ppt;
application/vnd.oasis.opendocument.graphics odg;
application/vnd.oasis.opendocument.presentation odp;
application/vnd.oasis.opendocument.spreadsheet ods;
application/vnd.oasis.opendocument.text odt;
application/vnd.openxmlformats-officedocument.presentationml.presentation
pptx;
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
xlsx;
application/vnd.openxmlformats-officedocument.wordprocessingml.document
docx;
application/vnd.wap.wmlc wmlc;
application/wasm wasm;
application/x-7z-compressed 7z;
application/x-cocoa cco;
application/x-java-archive-diff jardiff;
application/x-java-jnlp-file jnlp;
application/x-makeself run;
application/x-perl pl pm;
application/x-pilot prc pdb;
application/x-rar-compressed rar;
application/x-redhat-package-manager rpm;
application/x-sea sea;
application/x-shockwave-flash swf;
application/x-stuffit sit;
application/x-tcl tcl tk;
application/x-x509-ca-cert der pem crt;
application/x-xpinstall xpi;
application/xhtml+xml xhtml;
application/xspf+xml xspf;
application/zip zip;
application/octet-stream bin exe dll;
application/octet-stream deb;
application/octet-stream dmg;
application/octet-stream iso img;
application/octet-stream msi msp msm;
audio/midi mid midi kar;
audio/mpeg mp3;
audio/ogg ogg;
audio/x-m4a m4a;
audio/x-realaudio ra;
video/3gpp 3gpp 3gp;
video/mp2t ts;
video/mp4 mp4;
video/mpeg mpeg mpg;
video/quicktime mov;
video/webm webm;
video/x-flv flv;
video/x-m4v m4v;
video/x-mng mng;
video/x-ms-asf asx asf;
video/x-ms-wmv wmv;
video/x-msvideo avi;
}
# configuration file /etc/nginx/conf.d/default.conf:
# nginx-proxy
# Networks available to the container labeled "com.github.nginx-proxy.nginx-proxy.nginx" or the one running docker-gen
# (which are assumed to match the networks available to the container running nginx):
# reverse_proxy
map $proxy_add_x_forwarded_for $proxy_x_forwarded_for {
default $remote_addr;
'' $remote_addr;
}
# If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the
# scheme used to connect to this server
map $http_x_forwarded_proto $proxy_x_forwarded_proto {
default $scheme;
'' $scheme;
}
map $http_x_forwarded_host $proxy_x_forwarded_host {
default $host;
'' $host;
}
# If we receive X-Forwarded-Port, pass it through; otherwise, pass along the
# server port the client connected to
map $http_x_forwarded_port $_proxy_x_forwarded_port {
default $server_port;
}
map $_proxy_x_forwarded_port $proxy_x_forwarded_port {
default $_proxy_x_forwarded_port;
'' $server_port;
}
# Include the port in the Host header sent to the container if it is non-standard
map $server_port $host_port {
default :$server_port;
80 '';
443 '';
}
# If the request from the downstream client has an "Upgrade:" header (set to any
# non-empty value), pass "Connection: upgrade" to the upstream (backend) server.
# Otherwise, the value for the "Connection" header depends on whether the user
# has enabled keepalive to the upstream server.
map $http_upgrade $proxy_connection {
default upgrade;
'' $proxy_connection_noupgrade;
}
map $upstream_keepalive $proxy_connection_noupgrade {
# Preserve nginx's default behavior (send "Connection: close").
default close;
# Use an empty string to cancel nginx's default behavior.
true '';
}
# Abuse the map directive (see <https://stackoverflow.com/q/14433309>) to ensure
# that $upstream_keepalive is always defined. This is necessary because:
# - The $proxy_connection variable is indirectly derived from
# $upstream_keepalive, so $upstream_keepalive must be defined whenever
# $proxy_connection is resolved.
# - The $proxy_connection variable is used in a proxy_set_header directive in
# the http block, so it is always fully resolved for every request -- even
# those where proxy_pass is not used (e.g., unknown virtual host).
map "" $upstream_keepalive {
# The value here should not matter because it should always be overridden in
# a location block (see the "location" template) for all requests where the
# value actually matters.
default false;
}
# Apply fix for very long server names
server_names_hash_bucket_size 128;
# Default dhparam
ssl_dhparam /etc/nginx/dhparam/dhparam.pem;
# Set appropriate X-Forwarded-Ssl header based on $proxy_x_forwarded_proto
map $proxy_x_forwarded_proto $proxy_x_forwarded_ssl {
default off;
https on;
}
gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
log_format vhost escape=default '$host $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$upstream_addr" $ssl_protocol $ssl_cipher "$sent_http_content_type" $request_time';
access_log off;
ssl_protocols TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384;
ssl_conf_command Ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256;
ssl_prefer_server_ciphers off;
error_log /dev/stderr;
# HTTP 1.1 support
proxy_http_version 1.1;
proxy_set_header Host $host$host_port;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $proxy_connection;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_x_forwarded_for;
proxy_set_header X-Forwarded-Host $proxy_x_forwarded_host;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl;
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
proxy_set_header X-Original-URI $request_uri;
# Mitigate httpoxy attack (see README for details)
proxy_set_header Proxy "";
server {
server_name _; # This is just an invalid value which will never trigger on a real hostname.
server_tokens off;
access_log /var/log/nginx/access.log vhost;
http2 on;
listen 8080;
listen 8443 ssl;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_certificate /etc/nginx/certs/default.crt;
ssl_certificate_key /etc/nginx/certs/default.key;
location ^~ / {
return 503;
}
}
# xx/freshrss
upstream xx-1097b4b6ab1cd8d43e9d3d765fad295606b80016 {
# Container: freshrss-docker-web-1
# networks:
# freshrss-docker_backend (unreachable)
# reverse_proxy (reachable)
# IPv4 address: 172.28.5.3
# IPv6 address: (none usable)
# exposed ports (first ten): 80/tcp
# default port: 80
# using port: 80
server 172.28.5.3:80;
keepalive 2;
}
server {
server_name xx.fr;
access_log /var/log/nginx/access.log vhost;
listen 8080;
# Do not HTTPS redirect Let's Encrypt ACME challenge
location ^~ /.well-known/acme-challenge/ {
auth_basic off;
auth_request off;
allow all;
root /usr/share/nginx/html;
try_files $uri =404;
break;
}
location / {
if ($request_method ~ (OPTIONS|POST|PUT|PATCH|DELETE)) {
return 301 https://$host:84437$request_uri;
}
return 301 https://$host:8443$request_uri;
}
}
server {
server_name xx.fr;
access_log /var/log/nginx/access.log vhost;
http2 on;
listen 8443 ssl;
ssl_session_timeout 5m;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_certificate /etc/nginx/certs/xx.crt;
ssl_certificate_key /etc/nginx/certs/xx.key;
ssl_dhparam /etc/nginx/certs/xx.dhparam.pem;
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/certs/xx.chain.pem;
set $sts_header "";
if ($https) {
set $sts_header "max-age=31536000";
}
add_header Strict-Transport-Security $sts_header always;
include /etc/nginx/vhost.d/default;
location /freshrss {
proxy_pass http://xx-1097b4b6ab1cd8d43e9d3d765fad295606b80016;
set $upstream_keepalive true;
auth_basic "Restricted xx/freshrss";
auth_basic_user_file /etc/nginx/htpasswd/xx_1097b4b6ab1cd8d43e9d3d765fad295606b80016;
include /etc/nginx/vhost.d/xx_1097b4b6ab1cd8d43e9d3d765fad295606b80016_location;
}
include /etc/nginx/vhost.d/xx_9ae1aacb42439cb07010d9921db96191facc4f02_location_override;
location / {
return 418;
}
}
# configuration file /etc/nginx/vhost.d/xx_1097b4b6ab1cd8d43e9d3d765fad295606b80016_location:
location /freshrss/ {
# Copié collé depuis https://github.com/FreshRSS/FreshRSS/tree/edge/Docker#hosted-in-a-subdirectory
# En commenté les lignes préconisées mais dont une version identique ou proche existe dans le bloc server par défaut
proxy_pass http://172.28.5.3/;
add_header X-Frame-Options SAMEORIGIN;
add_header X-XSS-Protection "1; mode=block";
proxy_redirect off;
proxy_buffering off;
#proxy_set_header Host $host;
#proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Prefix /freshrss/;
#proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#proxy_set_header X-Forwarded-Proto $scheme;
#proxy_set_header X-Forwarded-Port $server_port;
proxy_read_timeout 90;
# Sécurisation du header https://securityheaders.com
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), fullscreen=(self), payment=(), usb=()" always;
add_header Referrer-Policy "no-referrer" always;
# Support basicauth
proxy_set_header Remote-User $remote_user;
location /freshrss/api {
proxy_pass http://172.28.5.3/api;
proxy_set_header X-Forwarded-Prefix /freshrss/api;
auth_basic off;
# Copié collé depuis https://github.com/FreshRSS/FreshRSS/tree/edge/Docker#hosted-in-a-subdirectory
# Forward the Authorization header for the Google Reader API.
proxy_set_header Authorization $http_authorization;
proxy_pass_header Authorization;
}
}
# configuration file /etc/nginx/network_internal.conf:
# Only allow traffic from internal clients
allow 127.0.0.0/8;
allow 10.0.0.0/8;
allow 192.168.0.0/16;
allow 172.16.0.0/12;
allow fc00::/7; # IPv6 local address range
deny all;
# configuration file /etc/nginx/conf.d/my_proxy.conf:
proxy_request_buffering off;
#server_tokens off;
NGINX access/error log: No error