HTTPS rewrites for modern admin UI (Nginx and Apache)

Background

When Funnelback is installed with either Nginx or Apache as the front-facing web server it is necessary to proxy certain paths on HTTPS in order for the Modern Admin UI to function properly. This article explains which HTTPS-specific rules are needed.

Nginx

For nginx (or openresty, the Lua variant) several config files are needed to support the HTTPS rewrites (HTTP entries have been left out for this article to avoid confusion).

upstream.conf

# This upstream is for public-https traffic (assumed to be bound on 9443 in the example case)
upstream https-upstream {
  server 127.0.0.1:9443;
}

# This upstream is for the admin-https traffic (assumed to be bound on 8443 in the example case)
upstream admin-upstream {
  server 127.0.0.1:8443;
}

https.conf

# Main HTTPS proxy configuration
#
server {
  listen                  443 ssl;
  server_name             client.funnelback.co.uk ;

  ssl                     on;
  ssl_certificate         /etc/openresty/star.funnelback.co.uk.crt;
  ssl_certificate_key     /etc/openresty/star.funnelback.co.uk.key;
  ssl_session_timeout     5m;

  ssl_protocols           TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers             'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK';
  ssl_prefer_server_ciphers    on;
  ssl_verify_client            off;
  underscores_in_headers on;
  # Limit the number of requests, using the all zone and setting a burst rate of 100
  #limit_req      zone=all burst=100 nodelay;

  #Send all requests to https-upstream
  location / {
    proxy_pass      https://https-upstream;
  }

  # The following locations should all be served by the admin API
  location /a {
    proxy_pass      https://admin-upstream;
  }

  location /data-api {
    proxy_pass      https://admin-upstream;
  }

  location /admin-api {
    proxy_pass      https://admin-upstream;
  }

  location /push-api {
    proxy_pass      https://admin-upstream;
  }

  location /search/admin {
    proxy_pass      https://admin-upstream;
  }

  location /s/content-auditor.html {
    proxy_pass      https://admin-upstream;
  }

  location /s/seo-auditor.html {
    proxy_pass      https://admin-upstream;
  }

nginx example - URL rewrites for cached copies and canned search views

A commonly occurring scenario where Funnelback is used to render detail versions of items in its index (typically from a DB collection) at human-friendly URLs:

https.conf

server {
    ...
    # Item detail URL rewrite
    rewrite ^/service/(.*?)$ /s/cache.html?collection=COLLECTION&form=detail_view&url=www.services.gov/service/$1 last;
    # Category index URL rewrite
    rewrite ^/category/(.*?)$ /s/search.html?collection=COLLECTION&profile=canned-lists&form=index_view&smeta_category_phrase=$1 last;
    ...
    # Location rewrites
}

In this example:

  • The source collection is 'crm'

  • There’s a custom cache controller (detail_view) render out cached copies of items via Freemarker

  • There’s a custom form (index_view) to render out canned views of content with QPOs from a separate profile

  • Items end up being accessible from www.services.gov/service/SERVICEID

  • Lists of items in a given category are accessible from www.services.gov/category/CATEGORY

Apache2

The apache configuration only needs editing in one ssl.conf:

# These should be in the main SSL virtualhost config
# Again assumes admin UI has been bound to 8443 and public-https to 9443.
# Check your ~/conf/global.cfg for your jetty.admin_port for admin, and jetty.search_port_https for public-https
<Location /push-api>
  ProxyPreserveHost on
  ProxyPass https://localhost:8443/push-api
  ProxyPassReverse https://localhost:8443/push-api
</Location>

<Location /admin-api>
  ProxyPreserveHost on
  ProxyPass https://localhost:8443/admin-api
  ProxyPassReverse https://localhost:8443/admin-api
</Location>

<Location /data-api>
  ProxyPreserveHost on
  ProxyPass https://localhost:8443/data-api
  ProxyPassReverse https://localhost:8443/data-api
</Location>

<Location /search/admin>
  ProxyPreserveHost on
  ProxyPass https://localhost:8443/data-api
  ProxyPassReverse https://localhost:8443/data-api
</Location>

<Location /s/seo-auditor>
  ProxyPreserveHost on
  ProxyPass https://localhost:8443/s/seo-auditor
  ProxyPassReverse https://localhost:8443/s/seo-auditor
</Location>

<Location /s/content-auditor.html>
  ProxyPreserveHost on
  ProxyPass https://localhost:8443/s/content-auditor.html
  ProxyPassReverse https://localhost:8443/s/content-auditor.html
</Location>

# If we put this one first we could lose /admin-api but this is clearer documented
<Location /a>
  ProxyPreserveHost on
  ProxyPass https://localhost:8443/a
  ProxyPassReverse https://localhost:8443/a
</Location>

# Finally our public binding, which captures anything remaining under /s
<Location /s>
  ProxyPreserveHost on
  ProxyPass https://localhost:9443/s
  ProxyPassReverse https://localhost:9443/s
</Location>