如何让ruby注销link在nginx反向代理环境下工作

How to make ruby logout link work in nginx reverse proxy environment

首先,我已经在serverfault上问过同样的问题,但似乎并没有引起太多关注,因此在这里重新发布。如果这在概念上是错误的,请告诉我,我会很乐意采取建议的措施。

我有一些内部网络服务,准确地说是 redmine 服务器,我想将其公开给其他子网和互联网。我相信架构的 VirtualBox/docker 方面并不直接相关,因为所有 linking 和端口转发似乎都有效,但这是网络的样子。

Router/external-dynamic-ip/ddns-hostname:8090 -> 
Lan Host/192.168.0.6/10.0.2.2:8090 -> VirtualBox/10.0.2.15
vbox:8090 -> docker-nginx(NAT)/172.17.0.6:80

vbox:8080 -> docker-redmine-tech(NAT)/172.17.0.4:3000

vbox:8081 -> docker-redmine-tech(NAT)/172.17.0.5:3000

使用下面的 nginx 配置,我可以在局域网中的另一台机器上通过网络浏览器访问 docker-redmine-tech 服务作为 http://192.168.0.6:8090/tech/。登录后的主页、图片、脚本和所有导航似乎工作正常,地址栏基数 url 始终保持为 http://192.168.0.6:8090/tech/,因此,重定向对用户不可见。

http {
    include /etc/nginx/mime.types;
    log_format upstreamlog '[$time_local] $request_uri $http_referer $http_host $host $server_port $proxy_host $proxy_port $remote_addr - $remote_user - $server_name to: $upstream_addr $http_x_forwarded_for: $status / upstream $upstream_status $request upstream_response_time $upstream_response_time msec $msec request_time $request_time body: $request_body';

    access_log      /var/log/nginx/access.log upstreamlog;
    error_log       /var/log/nginx/error.log debug;
    rewrite_log on;
    underscores_in_headers on;

    upstream redmine-tech {
            server 10.0.2.15:8080;
    }

    server {
            listen 80 default_server;

            location /tech/ {
                    proxy_set_header Host $http_host/tech;
                    proxy_set_header X-Real-IP $remote_addr;
                    proxy_set_header X-Scheme $scheme;
                    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                    proxy_set_header X-Forwarded-Proto $scheme;
                    proxy_redirect    off;
                    add_header Pragma "no-cache";
                    add_header Cache-Control "no-cache";

                    proxy_pass http://redmine-tech/;
                    sub_filter 'action="/'  'action="/tech/';
                    sub_filter 'href="/'  'href="/tech/';
                    sub_filter 'src="/'  'src="/tech/';
                    sub_filter_once off;
            }
    }
}

redmine 网页似乎使用相对 urls ,因此 sub_filter 重写。这也不会破坏像 http://www.redmine.org/guide 这样的任何绝对 url。

到目前为止一切顺利。我遇到的唯一问题是 logout。当我点击注销时,这会将我带到 http://192.168.0.6,但我希望改为 http://192.168.0.6:8090/tech/

注销表单的html代码为

<form action="/tech/logout" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="&#x2713;" /><input type="hidden" name="authenticity_token" value="cJAHHZxZ0ddFKXSz+vzDze3N4V3yhcxfH/bM0u2IcZJXpsnBBpAr9I4v8U9yLopSpra3WkhB4oOMIqgM7iXE9w==" />

这是点击logout后的nginx日志。

[08/Nov/2016:13:41:23 +0000] /tech/logout http://192.168.0.6:8090/tech/logout 192.168.0.6:8090 192.168.0.6 80 redmine-tech 80 10.0.2.2 - - -  to: 10.0.2.15:8080 -: 302 / upstream 302 POST /tech/logout HTTP/1.1 upstream_response_time 0.011 msec 1478612483.524 request_time 0.011 body: _method=post&authenticity_token=Hb1wtuM6oSTWbgTG%2B2Ldr%2BXZZYbv6awGzDIFa7vkv6xqQDvRtuWAnCr4vDrb1SpQP8kl6qVvcuP03LGY%2ByX9rQ%3D%3D

我在 rails 上使用带有 ruby 的 redmine,/logout link 似乎是由 ruby 自动生成的。 find . -name "*.rb" -exec grep -Hn "You are being" {} \;搜索returns以下结果

./usr/local/bundle/gems/actionpack-4.2.7.1/lib/action_dispatch/routing/redirection.rb:40:        body = %(<html><body>You are being <a href="#{ERB::Util.unwrapped_html_escape(uri.to_s)}">redirected</a>.</body></html>)
./usr/local/bundle/gems/actionpack-4.2.7.1/lib/action_controller/metal/redirecting.rb:76:      self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(location)}\">redirected</a>.</body></html>"

我不确定在这种情况下哪个 rb 文件实际运行,但似乎是 uri/location 转换出错了,我不太清楚为什么。也许 POST 在 nginx conf 中需要另一个 http header 设置,但是 login/search/register 新帐户等表单也可以正常工作,也可以使用 `POST.

我不确定 curl 语句 curl -i http://192.168.0.6:8090/tech/logout -H "Host: 192.168.0.6" 是否有意义,因为我无法像在 proxy_set_header Host $http_host/tech; 中那样将端口和 /tech/ 附加到主机字段nginx conf 但这是输出

HTTP/1.1 302 Found 
Server: nginx/1.11.5
Date: Tue, 08 Nov 2016 13:20:10 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Location: http://192.168.0.6/tech/
Cache-Control: no-cache
X-Request-Id: 66707620-ebd2-4c66-af9f-3ba6633d5e7d
X-Runtime: 0.003215
Pragma: no-cache
Cache-Control: no-cache

<html><body>You are being <a href="http://192.168.0.6/tech/">redirected</a>.</body></html>

所以,我希望有人能给出一些说明,非常感谢你。

进一步挖掘并从 this post 获得提示后,我将 proxy_redirect http://$host/ http://$http_host/tech/; 行添加到配置中,它成功了。更新后的配置如下。

    upstream redmine-tech {
            server 10.0.2.15:8080;
    }

    server {
            listen 80 default_server;
            proxy_redirect http:// $scheme://;
            proxy_buffering off;

            location /tech {
                    rewrite ^/tech$ $scheme://$http_host/tech/ break;
                    proxy_set_header Host $http_host/tech;
                    proxy_set_header X-Real-IP $remote_addr;
                    proxy_set_header X-Scheme $scheme;
                    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                    proxy_set_header X-Forwarded-Proto $scheme;
                    proxy_redirect http://$host/ http://$http_host/tech/;
                    #proxy_redirect    off;
                    add_header Pragma "no-cache";
                    add_header Cache-Control "no-cache";

                    proxy_pass http://redmine-tech/;
                    sub_filter 'action="/'  'action="/tech/';
                    sub_filter 'href="/'  'href="/tech/';
                    sub_filter 'src="/'  'src="/tech/';
                    sub_filter_once off;
            }
    }