Page MenuHomePhabricator

Wikibase: Quick-statements behind reverse proxy
Open, In Progress, Needs TriagePublic

Description

Hi,

I'm referring to the wikibase-release pipline. My deployment is the docker compose setup and to enable and expose all services I use nginx (which redirects the public urls to HTTPS and to the corresponding docker services). I have the following problem.

First when setting up quickstatements in the docker-compose.extra.yml there is:

WB_PUBLIC_SCHEME_HOST_AND_PORT=http://${WIKIBASE_HOST}:${WIKIBASE_PORT}

which means that there is a redirect not to https. This should be fixed.

If I ignore this and try to login via oauth to quickstatements, I will be redirected, I log in to the wikibase, I give the rights and when I'm back to quickstatements I'm NOT logged in. I can avoid this by unsetting in docker-compose.yml the following:

aliases:

    1. ${WIKIBASE_HOST}
  • wikibase-docker.svc

Then quickstatements is working as expected. I really do not remember the reason for that. Arggg....

Anyway.

  1. If I take the configurations out of the box (with the ajustments above) quickstatements is working but the type as you search is not working. The reason is that apis are called under http://my.host (so not httpS). In particular the type as you search is not working because the browser complains:
XMLHttpRequest cannot load http://my.host/w/api.php?action=wbsearchentities&search=test&format=json&errorformat=plaintext&language=en&uselang=en&type=item due to access control checks.

To fix this problem I set

$wgServer = 'https://my.host'

then the type as you search is working.

  1. If I enable $wgServer = 'https://my.host' then quickstatement is not working. The reason is connected to the OAuth extension. When logging in via quick statements I get then:
<br />
<b>Fatal error</b>:  Uncaught Exception: Error retrieving token1: {&amp;quot;error&amp;quot;:&amp;quot;mwoauth-oauth-exception&amp;quot;,&amp;quot;message&amp;quot;:&amp;quot;An error occurred in the OAuth protocol: Invalid signature&amp;quot;,&amp;quot;callback&amp;quot;:&amp;quot;https:\/\/quickstatements.wikibase.the-qa-company.com\/api.php&amp;quot;} in /var/www/html/magnustools/public_html/php/oauth.php:289
Stack trace:
#0 /var/www/html/quickstatements/public_html/api.php(123): MW_OAuth-&gt;doAuthorizationRedirect('https://quickst...')
#1 {main}
  thrown in <b>/var/www/html/magnustools/public_html/php/oauth.php</b> on line <b>289</b><br />

basically the problem is "Invalid signature". This I think (60% sure) is due to the fact that the host knows it is 'https://my.host' but we are calling it over 'wikibase-docker.svc'. By comparing the signatures OAuth complains.

So I changed in docker-compose.extra.yml the quickstatement section to

WIKIBASE_SCHEME_AND_HOST=https://my.host

With this I'm redirected to the wikibase, I can login, give the rights but when coming back I get:

<br />
<b>Fatal error</b>:  Uncaught Exception: Curl error:  in /var/www/html/magnustools/public_html/php/oauth.php:164
Stack trace:
#0 /var/www/html/magnustools/public_html/php/oauth.php(53): MW_OAuth-&gt;fetchAccessToken()
#1 /var/www/html/quickstatements/public_html/quickstatements.php(110): MW_OAuth-&gt;__construct(Array)
#2 /var/www/html/quickstatements/public_html/api.php(49): QuickStatements-&gt;getOA()
#3 {main}
  thrown in <b>/var/www/html/magnustools/public_html/php/oauth.php</b> on line <b>164</b><br />

I checked in the code and there is an API call to:

https://my.host/w/index.php?title=Special:OAuth/token

Which looks fine, but this call is passing via nginx, then proxied to localhost:8080 and the Wikibase responds with 302 Found and redirects to the exact same URL. The reason (I guess) is that over the revers proxy the call is going to localhost:8080 and so the OAuth says, try again over 'https://my.host'. But this is exactly what we are calling. I tried in nginx to set all these headers:

proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-Proto: https;

but it is not working either. The wikibase still replies 302 and points to the same redirect. Strangely this is only happening for 'https://my.host/w/index.php?title=Special:OAuth' Others paths are fine.

Basically my question is. Is there a way to properly setting up quickstatements with the docker release pipeline over a public url (using nginx). I'm currently not able to make this work and I would really be greatfull for help.

Thank you
D063520

PS: if it helps I can provide access to the above setup or show the things in a call

Event Timeline

@DD063520 do you have a minimum example docker-compose file that you might be able to provide for your setup?

This I think (60% sure) is due to the fact that the host knows it is 'https://my.host' but we are calling it over 'wikibase-docker.svc'

Yes, for a deployment of wikibase and quickstatements, calls for OAuth should use the real protocol and domain name to avoid signature errors.

The other route around this is making things like quickstatements call your reverse proxy with a Host header set, so the proxy knows where the call should go.
This requires code modifications, for quickstatements this means magnustools. This was done for wbstack and wikibase.cloud in a fork that should be upstreamed if it is useful for others.
The evil hack is https://github.com/wbstack/magnustools/blob/e0fadb9a7149f0b06ee419e554c2f78341c4f5b7/public_html/php/WbstackMagnusOauth.php which overrides some stuff to do with config parsing etc.

You may benefit from looking at the old reverse proxy setup for wbstack.com
https://github.com/wbstack/deploy/blob/1f5853eb832e40e3a39c7a9e54ab9a3bf555c9d0/k8s/helm/platform-nginx/nginx.conf
Infact the whole of wbstack.com was and wikibase.cloud still is behind a reverse proxy
(current wikibase.cloud config is not yet public)

In the wbstack nginx config you can find

real_ip_header X-Forwarded-For;
proxy_set_header X-Forwarded-For "$http_x_forwarded_for, $realip_remote_addr";
proxy_set_header Host                   $http_host;

but it is not working either. The wikibase still replies 302 and points to the same redirect. Strangely this is only happening for 'https://my.host/w/index.php?title=Special:OAuth' Others paths are fine.

Could you in a single line / bullet points outline the various redirect steps? I found this part a little hard to follow above.

Hi,

thank you for the response! The nginx config look like this:

server {

    server_name wikibase.my.host;

    client_max_body_size 2M;

    location / {
        proxy_set_header Host $host:$server_port;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-Proto: https;
        proxy_pass http://127.0.0.1:8080;

    }

    listen 443 ssl; 
    ssl_certificate /etc/letsencrypt/live/wikibase.my.host/fullchain.pem; 
    ssl_certificate_key /etc/letsencrypt/live/wikibase.my.host/privkey.pem; 
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

}

server {
    if ($host = wikibase.my.host) {
        return 301 https://$host$request_uri;
    }

    listen 80;

    server_name wikibase.my.host
    return 404; 


}

Very similar for the different services. My idea, instead of making quickstatements setting the "Host" header, was to put it in the nginx configuration. Basically we want always, for every request going to the wikibase, to say that the $host is called. Like this we do not have to change the code in quickstatements. The problem is the one I described above:

"the Wikibase responds with 302 Found and redirects to the exact same URL. The reason (I guess) is that over the revers proxy the call is going to localhost:8080 and so the OAuth says, try again over 'https://my.host'."

(the Host header is ignored! and only for Special:OAuth urls as far as I can see )

Concretely as an example you can try here:

curl 'https://wikibase.the-qa-company.com/w/index.php?title=Special:OAuth/token&format=json&oauth_verifier=d9fc52fe7492a7f4626a680b43e389ff&oauth_consumer_key=177700dc97c6e471e106234ba3275972&oauth_token=8060d58cf24aece2d0b486c7b0705820&oauth_version=1.0&oauth_nonce=0787eb5ff478a613545670de5196d3c3&oauth_timestamp=1661179411&oauth_signature_method=HMAC-SHA1&oauth_signature=5RrE2SKUstzSsbGrw2HjS9pmvCQ%3D' -v -L

For the minimal docker compose see below. I marked with

CHANGE FROM OFFICIAL RELEASE

the changes from the official release.

If you wish I can share the access to the instance and show you. Might be quicker.

cat docker-compose.yml

# Example Wikibase docker-compose setup
version: '3.4'

x-common-variables: &wikibase_variables
  DB_SERVER: mysql.svc:3306
  MW_ADMIN_NAME: ${MW_ADMIN_NAME}
  MW_ADMIN_PASS: ${MW_ADMIN_PASS}
  MW_ADMIN_EMAIL: ${MW_ADMIN_EMAIL}
  MW_WG_SECRET_KEY: ${MW_SECRET_KEY}
  # Disable jobs running after requests when wikibase_jobrunner is defined
  MW_WG_JOB_RUN_RATE: 0
  DB_USER: ${DB_USER}
  DB_PASS: ${DB_PASS}
  DB_NAME: ${DB_NAME}
  WIKIBASE_HOST: ${WIKIBASE_HOST}

services:
  wikibase:
    image: "${WIKIBASE_BUNDLE_IMAGE_NAME}"
    links:
      - mysql
    depends_on:
      - mysql
    restart: unless-stopped
    ports:
      - "${WIKIBASE_PORT}:80"
    volumes:
      ## This shares the configuration with jobrunner
      - shared:/var/www/html/:rw

      ## Uncomment this next line to mount your own LocalSettings.php file
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
CHANGE FROM OFFICIAL RELEASE
      - ./LocalSettings.php:/var/www/html/LocalSettings.d/LocalSettings.override.php
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------

    networks:
      default:
        aliases:
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
CHANGE FROM OFFICIAL RELEASE
          #- ${WIKIBASE_HOST}
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
         - wikibase-docker.svc
    environment:
      <<: *wikibase_variables
      WIKIBASE_PINGBACK:
      MW_WG_ENABLE_UPLOADS:

  wikibase_jobrunner:
    image: "${WIKIBASE_BUNDLE_IMAGE_NAME}"
    entrypoint: /bin/bash
    command:  /jobrunner-entrypoint.sh
    links:
      - mysql
    depends_on:
      - mysql
    restart: always
    volumes:
      - shared:/shared/:ro
      - ./jobrunner-entrypoint.sh:/jobrunner-entrypoint.sh
    networks:
      default:
        aliases:
          - wikibase-jobrunner.svc
    environment:
      <<: *wikibase_variables
      MAX_JOBS: ${MAX_JOBS}

  mysql:
    image: "${MYSQL_IMAGE_NAME}"
    restart: unless-stopped
    volumes:
      - mediawiki-mysql-data:/var/lib/mysql
    environment:
      # CONFIG - Change the default values below (should match values passed to wikibase)
      MYSQL_DATABASE: ${DB_NAME}
      MYSQL_USER: ${DB_USER}
      MYSQL_PASSWORD: ${DB_PASS}
      MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
    networks:
      default:
        aliases:
         - mysql.svc

volumes:
  shared:
  mediawiki-mysql-data:

cat docker-compose.extra.yml

# Additional services example
version: '3.4'

x-common-variables: &wikibase_extra_variables
  MW_ELASTIC_HOST: ${MW_ELASTIC_HOST}
  MW_ELASTIC_PORT: ${MW_ELASTIC_PORT}

services:

  wikibase:
    volumes:
      - quickstatements-data:/quickstatements/data
      - ./extra-install.sh:/extra-install.sh
    environment:
      <<: *wikibase_extra_variables
      QS_PUBLIC_SCHEME_HOST_AND_PORT:

  wikibase_jobrunner:
    environment:
      <<: *wikibase_extra_variables

  elasticsearch:
    image: "${ELASTICSEARCH_IMAGE_NAME}"
    restart: unless-stopped
    volumes:
      - elasticsearch-data:/usr/share/elasticsearch/data
    networks:
      default:
        aliases:
         - elasticsearch.svc
    environment:
      discovery.type: single-node
      ES_JAVA_OPTS: "-Xms512m -Xmx512m -Dlog4j2.formatMsgNoLookups=true"

  wdqs-frontend:
    image: "${WDQS_FRONTEND_IMAGE_NAME}"
    restart: unless-stopped
    ports:
     - "${WDQS_FRONTEND_PORT}:80"
    depends_on:
      - wdqs-proxy
    networks:
      default:
        aliases:
         - ${WDQS_FRONTEND_HOST}
    environment:
      - WIKIBASE_HOST=${WIKIBASE_HOST}
      - WDQS_HOST=wdqs-proxy.svc
  wdqs:
    image: "${WDQS_IMAGE_NAME}"
    restart: unless-stopped
    command: /runBlazegraph.sh
    volumes:
      - query-service-data:/wdqs/data
    networks:
      default:
        aliases:
         - wdqs.svc
    environment:
      - WIKIBASE_HOST=${WIKIBASE_HOST}
      - WDQS_HOST=wdqs.svc
      - WDQS_PORT=9999
    expose:
      - 9999

  wdqs-proxy:
    image: "${WDQS_PROXY_IMAGE_NAME}"
    restart: unless-stopped
    environment:
      - PROXY_PASS_HOST=wdqs.svc:9999
    depends_on:
      - wdqs
    networks:
      default:
        aliases:
         - wdqs-proxy.svc

  wdqs-updater:
    image: "${WDQS_IMAGE_NAME}"
    restart: unless-stopped
    command: /runUpdate.sh
    depends_on:
    - wdqs
    - wikibase
    networks:
      default:
        aliases:
         - wdqs-updater.svc
    environment:
     - WIKIBASE_HOST=${WIKIBASE_HOST}
     - WDQS_HOST=wdqs.svc
     - WDQS_PORT=9999
     # CONFIG - WIKIBASE_SCHEME can be set to 'https' if the updater should expect https concept uris

  quickstatements:
    image: "${QUICKSTATEMENTS_IMAGE_NAME}"
    restart: unless-stopped
    volumes:
      - quickstatements-data:/quickstatements/data
    ports:
      - "${QUICKSTATEMENTS_PORT}:80"
    depends_on:
      - wikibase
    networks:
      default:
        aliases:
         - ${QUICKSTATEMENTS_HOST}
    environment:
      - QUICKSTATEMENTS_HOST
      - QS_PUBLIC_SCHEME_HOST_AND_PORT
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
CHANGE FROM OFFICIAL RELEASE
      - WIKIBASE_SCHEME_AND_HOST=https://my.host
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
      - WB_PUBLIC_SCHEME_HOST_AND_PORT=http://${WIKIBASE_HOST}:${WIKIBASE_PORT}
      - WB_PROPERTY_NAMESPACE=122
      - "WB_PROPERTY_PREFIX=Property:"
      - WB_ITEM_NAMESPACE=120
      - "WB_ITEM_PREFIX=Item:"
      - OAUTH_CONSUMER_KEY=${OAUTH_CONSUMER_KEY}
      - OAUTH_CONSUMER_SECRET=${OAUTH_CONSUMER_SECRET}

volumes:
  LocalSettings:
  query-service-data:
  elasticsearch-data:
  quickstatements-data:
  mediawiki-mysql-data:

cat .env

## Example / Template .env file for Wikibase release pipeline docker-compose example
# WARNING: Do not add comments on the same line as env vars, as in some environments they will be included in the var!

## Image Configuration
WIKIBASE_IMAGE_NAME=wikibase/wikibase:1.36.3-wmde.5
WDQS_IMAGE_NAME=wikibase/wdqs:0.3.97-wmde.5
WDQS_FRONTEND_IMAGE_NAME=wikibase/wdqs-frontend:wmde.5
ELASTICSEARCH_IMAGE_NAME=wikibase/elasticsearch:6.8.23-wmde.5
WIKIBASE_BUNDLE_IMAGE_NAME=wikibase/wikibase-bundle:1.36.3-wmde.5
QUICKSTATEMENTS_IMAGE_NAME=wikibase/quickstatements:wmde.5
WDQS_PROXY_IMAGE_NAME=wikibase/wdqs-proxy:wmde.5
MYSQL_IMAGE_NAME=mariadb:10.3

## Mediawiki Configuration
## Admin password
## Passwords must be at least 10 characters.
## Your password must be different from your username.
## Your password must not appear within your username.
## The password must not be in a list of very commonly used passwords. Please choose a unique password.
MW_ADMIN_PASS=change-this-password
MW_ADMIN_NAME=admin
MW_ADMIN_EMAIL=admin@example.com
MW_SECRET_KEY=some-secret-key
MW_WG_ENABLE_UPLOADS=false

## Jobrunner Configuration
MAX_JOBS=1

## Database Configuration
DB_NAME=my_wiki
DB_USER=sqluser
DB_PASS=change-this-sqlpassword

## Wikibase Configuration
WIKIBASE_PINGBACK=false
# wikibase.svc is the internal docker hostname, change this value to the public hostname
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
CHANGE FROM OFFICIAL RELEASE
WIKIBASE_HOST=wikibase.my.host
WIKIBASE_PORT=8080
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------

## WDQS-frontend Configuration
# wdqs-frontend.svc is the internal docker hostname, change this value to the public hostname
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
CHANGE FROM OFFICIAL RELEASE
WDQS_FRONTEND_HOST=query.my.host
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
WDQS_FRONTEND_PORT=8834

## Quickstatements Configuration
# quickstatements.svc is the internal docker hostname, change this value to the public or local hostname
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
CHANGE FROM OFFICIAL RELEASE
QS_PUBLIC_SCHEME_HOST_AND_PORT=https://quickstatements.wikibase.my.host
QUICKSTATEMENTS_HOST=quickstatements.wikibase.my.host
--------------------------------------------------------------------
--------------------------------------------------------------------
--------------------------------------------------------------------
QUICKSTATEMENTS_PORT=8840

## ElasticSearch
## Comment out MW_ELASTIC_HOST to disable ElasticsSearch
## See https://github.com/wmde/wikibase-release-pipeline/blob/wmde.5/Docker/build/WikibaseBundle/LocalSettings.d.template/WikibaseCirrusSearch.php#L6
MW_ELASTIC_HOST=elasticsearch.svc
MW_ELASTIC_PORT=9200

cat LocalSettings.php

<?php

/*******************************/
/* Enable Federated properties */
/*******************************/
#$wgWBRepoSettings['federatedPropertiesEnabled'] = true;

/*******************************/
/* Enables ConfirmEdit Captcha */
/*******************************/
#wfLoadExtension( 'ConfirmEdit/QuestyCaptcha' );
#$wgCaptchaQuestions = [
#  'What animal' => 'dog',
#];

#$wgCaptchaTriggers['edit']          = true;
#$wgCaptchaTriggers['create']        = true;
#$wgCaptchaTriggers['createtalk']    = true;
#$wgCaptchaTriggers['addurl']        = true;
#$wgCaptchaTriggers['createaccount'] = true;
#$wgCaptchaTriggers['badlogin']      = true;

/*******************************/
/* Disable UI error-reporting  */
/*******************************/
#ini_set( 'display_errors', 0 );
#
--------------------------------------------------------------------
CHANGE FROM OFFICIAL RELEASE
$wgServer = 'https://my.host';
-------------------------------------------------------------------
#$wgUseCdn = true;
#$wgCdnServers = array();
#$wgCdnServers[] = "192.168.128.1";
Addshore subscribed.

Hi!

Thank you for looking into this. I do not understand why this is closed though. Basically you are saying that you provide quickstatement as part of Wikibase, but at the same time you are saying that it does not work on any deployment that is not using localhost. This is very strange to me. We are currently working with Smithsonian where this feature is a must have on the long term ....

Cordially
D063520

Hi D063520 !

In a release coming out in the next week or two there is a fix to the QuickStatements Docker image distribution, but not the QuickStatements configuration in the Docker Compose example configuration. The example configuration will receive substantial updates, including more clear documentation in a release which can be expected not long after this one.

The changes you show above from the provided example are all astute, and in fact the right way to make things work. These same changes, and more, will be reflected in the updated example configuration once it is released.

The "fix" in the release about to go out (WMDE15, 16, and 17) simply deprecates the WIKIBASE_SCHEME_AND_HOST variable, and corrects the documentation to reflect the working configuration. However, without waiting for or upgrading to these releases, QuickStatements should OAuth correctly in the current releases with the following configuration:

  1. Set the WB_PUBLIC_SCHEMA_AND_HOST and WIKIBASE_SCHEME_AND_HOST both to the publicly accessible Wikibase URL. It seems from above that you have already gotten there.
  1. Remove the network alias to WIKIBASE_HOST from the wikibase service, which again as you have already done.

The issue you highlight with the reverse proxy returning a 302 is from what I see, the only remaining issue keeping OAuth from working properly in your setup.

As noted, the provided nginx configuration does appear to respond with a 301 when proxying to Wikibase. That stands out to me, and I'm curious why it is setup that way. The reverse proxy should be effectively transparent, passing along the HTTP status codes and headers from the actual Wikibase server. Is there a reason you're needing this 301 response from nginx?

I'm not seeing the 302 in the proxy configuration provided, but wherever that is coming from, it will definitely cause an issue for OAuth. If the 302 or 301 in the proxy or any other layer in-front of Wikibase is necessary for some reason, then I don't think there is a workable configuration to OAuth QuickStatements to Wikibase without something along the lines of what Adam suggested with custom headers and modifications to the core Magnus Tools / QuickStatements code.

However, if the 301 or 302 responses to the Wikibase public URL can be and are eliminated, then I think OAuth is going to work.

Is there a reason that the 301 or 302 responses or needed? I apologize ahead of time if I'm missed the answer to that in the details above.

Hi @lojo_wmde,

thank you for your answer!

About the 302 code. We do not configure anything actively in nginx. In fact I think it is the wikibase itself. We set in the Localsettings.php:

$wgServer = 'https://si-wikinames.si.edu';

when we go on the container of the Wikibase and call:

curl 'http://localhost:80/w/index.php?title=Special:OAuth/token&amp;format=js ......

we get:

HTTP/1.1 302 Found
Location: https://si-wikinames.si.edu/w/index.php?title=Special....

note, this is happening only for this particular path, for example:

curl localhost/wiki/Main_Page -v

is directly responding with the corresponding page, no redirect. That is why the wikibase is working fine in general.

So I'm pretty sure that it is the wikibase that creates the 302. The problem I guess is that with nginx proxy_pass nginx is calling de facto localhost, this is what the wikibase sees.

Any idea?

Maybe I could reformulate the problem as following. How is it possible to make the OAuth extention work behind nginx using the proxy_pass clause?

Got it. Thank you for your patience and the additional clarifications @DD063520. I think we have everything we need here to reproduce your setup and get to the bottom of this. We'll be back with you shortly!