4631: Dashboard isn't properly redirecting behind a proxy

kbjerke

What version are you running?

3.0.2, but the issue was present in 2.5.16

What's the URL of the page containing the problem?

/dashboard/

What steps will reproduce the problem?

  1. Run Review Board behind a proxy at a path e.g.: /rb/
  2. Login and go to Dashboard
  3. Click any submenu item under Outgoing/Incoming (All, Open, Open, To me)

What is the expected output? What do you see instead?

Expected: Redirect to /rb/dashboard/?view={to-me, outgoing}
Actual result: Redirect to /dashboard/?view={to-me, outgoing}

What operating system are you using? What browser?

CentOS 7 (Inside Docker container)
Chrome, Firefox is not even displaying the <div id="page_sidebar"> in the rendered HTML

Please provide any additional information below.

So I am running all of this in Docker, I have 3 containers (reviewboard, memcached and apache httpd) and a remote PostgreSQL db. After doing some research, I believe my proxy settings are correct, but I may be mistaken.

I'm including 2 files: 1. view-source of /rb/dashboard/ and 2. VirtualHost entry.

#1 kbjerke
  • -
    <VirtualHost *:443>
      RequestHeader set X-Forwarded-Proto "https"
      ProxyRequests Off
      ProxyPreserveHost On
      ServerName www.example.com
      <Location /rb/>
        ProxyPass http://rb:8000/
        ProxyPassReverse /
        SetOutputFilter proxy-html
        ProxyHTMLURLMap ^/(.*)$ /rb/$1 R
        RequestHeader unset Accept-Encoding
      </Location>
    </VirtualHost>
#2 kbjerke
  • -
    <html><head><meta http-equiv="X-UA-Compatible" content="IE=10; IE=9; IE=8; IE=7; IE=EDGE"><title>My Dashboard | Review Board</title><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"><script type="text/javascript">
        var AJAX_SERIAL = "1516018794",
            TEMPLATE_SERIAL = "1516008003",
            SITE_ROOT = "/",
            MANUAL_URL = 'https://www.reviewboard.org/docs/manual/3.0/',
            STATIC_URLS = {
                'rb/images/favicon_notify.ico': '/static/rb/images/favicon_notify.43aac64f3b61.ico',
                'rb/images/resize-grip.png': '/static/rb/images/resize-grip.b822a7e06419.png',
                'rb/images/logo.png': '/static/rb/images/logo.cc81d3ae01b2.png'
            };
      </script><link rel="shortcut icon" type="image/x-icon" href="/rb/static/rb/images/favicon.3161c840d49e.ico"><link rel="apple-touch-icon-precomposed" type="image/png" href="/rb/static/rb/images/apple-home-icon.fd8758a2ebe3.png"><link href="/rb/static/rb/css/common.mi
#4 kbjerke

I was able to fix the icon img by adding srcset to the rule for img tags in mod_proxy_html:
ProxyHTMLLinks img src srcset

original:
<img id="logo" src="/rb/static/rb/images/logo.cc81d3ae01b2.png" srcset="/static/rb/images/logo.cc81d3ae01b2.png 1x, /static/rb/images/logo@2x.4e25cc3cacef.png 2x" alt="" border="0" width="60" height="57">
result:
<img id="logo" src="/rb/static/rb/images/logo.cc81d3ae01b2.png" srcset="/rb/static/rb/images/logo.cc81d3ae01b2.png 1x, /static/rb/images/logo@2x.4e25cc3cacef.png 2x" alt="" border="0" width="60" height="57">

Although the second url in srcset is not rewritten.

However, the onclick event in list item elements of the page_sidebar is quite a bit different.

<li class="item has-url has-count" onclick="javascript:window.location=&quot;/dashboard/?view=mine&quot;; return false;">
<div class="page-sidebar-row">
<div class="rb-icon "></div>
<span class="label"><a href="/rb/dashboard/?view=mine">All</a></span>
<span class="count count-zero">0</span>
</div>
</li>

chipx86
#5 chipx86

Ah, mod_proxy_html. That would explain it. We do not support any modules that rewrite our HTML, unfortunately. They cannot get it right for all our uses, and will fail. You'll have to disable that to properly use Review Board.

chipx86
#6 chipx86

Reason for that is that we have links that we build dynamically in JavaScript, and in payloads unparseable by the module, so you're going to have links coming from different places that will be in the other format.

What are you needing the module for?

#7 kbjerke

Our requirement is that the application isn't served at the root path, so I've been trying to get it to work at e.g.: /rb/ using Apache as a reverse proxy. If there's another, better way to solve this I'm open to suggestions.

chipx86
#8 chipx86

You can continue to serve Review Board out of /rb/. The trick is the links, and fortunately Review Board has you covered here. There's a SITE_ROOT variable that specifies what the prefix should be to the URL. This is normally asked during rb-site install, but you can set it manually in $sitedir/conf/settings_local.py by setting:

SITE_ROOT = '/rb/'

You should see it already in there as SITE_ROOT = '', meaning no prefix. Give that a try, it should take care of the URLs.

#9 kbjerke

Thank you, I just stumbled upon this while researching django's equivalent to tomcat's context path. I guess this will solve all my issues. I haven't worked with django applications before.

#10 kbjerke

Ok, so I've been stuck here for a while now. I totally dropped Apache, even though I eventually want to go back to it, but I attempted removing SSL for now from configuration and running with Nginx instead. I get to login screen at both http://my-domain/rb/ and http://my-ip/rb/, but I am only able to login from http://my-ip/rb/. Logging in from http://my-domain/rb/ just reloads the login view.

I tried adding the --domain-name="my-domain" flag on site install.
Any previous experience on this issue?

#11 kbjerke

Figured out this only applies to newer versions of Google Chrome, whereas Firefox and Edge both works fine. I think you better look into this.
ReviewBoard on a non-root path behind a reverse proxy.

chipx86
#12 chipx86

It sounds more like the reverse proxy. We test with reverse proxies and the latest versions of Google Chrome, and I'm confident this is a setup issue and not a bug in Review Board.

What reverse proxy are you using, and what's the configuration? How do you have things set up with Nginx?

#13 kbjerke

Don't you think it's strange that this works for Firefox, Edge and even Chrome logging in using IP

  • +
    user  nginx;
    worker_processes  1;
    error_log  /var/log/nginx/error.log warn;
    pid        /var/run/nginx.pid;
    events {
        worker_connections  1024;
    }
    http {
        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;
        upstream reviewboard {
          server rb:8000;
        }
        server {
          listen 80;
          server_name our.domain.com;
          location /rb/ {
            proxy_pass       http://reviewboard/rb/;
            proxy_redirect   off;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_
chipx86
#14 chipx86

I do, but a lot can go wrong with reverse proxies and it can be browser-dependent to a degree. For instance, if some header isn't being sent or handled just right, or an extension is getting in the way. Or if caching of some sort is coming into play. Chrome, for instance, will try to remember if a site was accessible over HTTPS before, and continue to attempt using it even if it's not set up correctly anymore (this is not always true, but depends on some factors).

Can you access the login page in Chrome, view the source, and find the <form ...> line for the login form, and then provide that?

#15 kbjerke

<form method="post" action="." class="auth-section main-auth-section" id="login_form">
<input type="hidden" name="next" value="/rb/">
<input type="hidden" name="csrfmiddlewaretoken" value="ocjTRLVkW6VbwqXJeLwoZFUTnYZoKja7">
<div class="auth-form-row auth-field-row">
<label for="id_username">Username:</label>
<input autofocus="autofocus" id="id_username" name="username" type="text">
</div>
<div class="auth-form-row auth-field-row">
<label for="id_password">Password:</label>
<input id="id_password" name="password" type="password">
</div>
<div class="auth-form-row">
<div class="auth-button-container">
<input type="submit" class="primary" value="Log in">
</div>
</div>
<div class="auth-form-row login-links">
<p>
<a href="/rb/account/recover/">Forgot your password?</a>
</p>
</div>
</form>

chipx86
#16 chipx86

Can you go into the Admin UI -> General Settings and make sure you see your desired hostname there for the server URL? One suspicion is that cookies may not be correct for the hostname. It's also possible the problem is on the Apache end (I'm guessing you're back to having that set up, given the upstream rule in nginx?).

#17 kbjerke

The hostname there is correct. The upstream rule refers to a remote server, or rather the docker container that's running Review Board, and I only have Nginx as reverse proxy, no Apache. A short update, I tried to go back to just using Apache again, with my default SSL settings etc.
This is the VirtualHost:
<VirtualHost *:443>
ServerName our.domain.com
ProxyRequests Off
ProxyPreserveHost On

ProxyPass /rb/ http://rb:8000/rb/
ProxyPassReverse /rb/ http://rb:8000/rb/
RequestHeader set X-Forwarded-Proto "https"

RequestHeader set X-Forwarded-Port "443"

<Location /rb/>
require all granted
</Location>
</VirtualHost>

And now this works. I'm totally confused, because I'm sure I've had this setup prior to testing on Nginx.

#18 kbjerke

that line RequestHeader set X-Forwarded-Port "443" is commented out with #, that's why it's bold.. Should have just removed that line.