20.5. Configuring an Apache VirtualHost running Couchdb for a domain of perspectives models

In this paragraph we explore the issue of configuring Apache for a particular domain of Perspectives models. We assume the server runs Couchdb, which is available as an http server on localhost. Apache will forward requests for resources to this local couchdb server.

Note
This is different from the situation where a domain is hosted on a server that does not have a Couchdb installation. The VirtualHost configuration on such a server must forward requests to another, external server. See the next paragraph for that.

As an example we’ll use the namespace of the System model, perspectives.domains (like model://perspectives.domains#System). For this domain, we will configure a VirtualHost in Apache on the relevant server. The VirtualHost starts out like this:

<VirtualHost *:443>
    ServerName https://perspectives.domains
    ServerAlias *.perspectives.domains
</VirtualHost>

All requests to https://perspectives.domains will now be captured by this virtualhost (assuming, of course, that the DNS refers this domain name to our server).

Note
this topic is related to Booting the Perspectives Universe

20.5.1. Forwarding requests to Couchdb

In principle, we just want to pass all requests in the perspectives.domains namespace to Couchdb. We can use ProxyPass for that. However, we have reason to except some requests (we’ll come back to that in the next paragraph), so we use ProxyPassMatch.

  ProxyPassMatch "^(?!.*index\.html)(.*)$" "http://localhost:5984/$1"
  Header edit Set-Cookie (.*) "$1; Secure; Domain=perspectives.domains; SameSite=None;"

Notice that we edit the cookie. Couchdb will return an AuthSession cookie when we request it at the _session endpoint, presenting our credentials. In order to make the browser keep it and return it with subsequent requests, we have to add some attributes:

  • as we forward internally over http, we have to add the Secure attribute manually in Apache;

  • Couchdb does not include the Domain attribute. It seems wise (but has not proven to be necessary) to include the domain;

  • we absolutely must include the SameSite attribute; otherwise the browser will ignore the cookie.

20.5.2. Preflight requests

Given the fact that the PDR runs in the mycontexts.com domain and we request resources from the perspectives.domains domain, we have to deal with CORS. A problem to solve is that the browser sends a preflight request for .json resources, before it sends a PUT or POST request to store some information in the database on our server. However, Couchdb doesn’t accept the OPTIONS method (which goes into the preflight request). So we have to have Apache handle that. We do so using url rewriting:

  RewriteEngine On
  RewriteCond %{REQUEST_METHOD} ^(OPTIONS)$
  RewriteRule ^/.*$  index.html  [R=204]

Every request using the OPTIONS method is rewritten as a request to a standard resource index.html (its content is of no importance). Referring back to the previous paragraph, this explains the condition for ProxyPassMatch:

  • we have Apache apply the url rewriting first;

  • and only then do we forward requests to the local Couchdb - in principle any request, except a request for index.html!

We force the response code 204 (No content), described by There is no content to send for this request, but the headers may be useful.. So what headers do we return?

  Header always set Access-Control-Allow-Credentials "true"
  Header always set Access-Control-Allow-Origin https://mycontexts.com
  Header always set Access-Control-Allow-Headers "content-type"

Notice that these should be read as server instructions to the browser as to what headers it (the server) will allow in requests from the browser. We should also inform the browser what methods are allowed, but here we need a refinement for the PDR. This is the subject of the next paragraph.

First, however, we have to make absolutely sure that we control these headers, by unsetting them first:

  Header unset Access-Control-Allow-Credentials
  Header unset Access-Control-Allow-Methods
  Header unset Access-Control-Allow-Origin
  Header unset Access-Control-Allow-Headers

20.5.3. Certificates

To complete the treatment, we should of course include references to the necessary certificate and key file for Apache to handle SSL.

  SSLEngine on
  SSLCertificateFile /path/to/certs/perspectives.domains-2023-certificate.crt
  SSLCertificateKeyFile /path/to/keys/perspectives.domains-2023-certificate.key

20.5.4. Putting it all together

In the right order, we now have:

<VirtualHost *:443>
  ServerName https://perspectives.domains
  ServerAlias *.perspectives.domains

  RewriteEngine On
  RewriteCond %{REQUEST_METHOD} ^(OPTIONS)$
  RewriteRule ^/.*$  index.html  [R=204]

  ProxyPassMatch "^(?!.*index\.html)(.*)$" "http://localhost:5984/$1"
  Header edit Set-Cookie (.*) "$1; Secure; Domain=perspectives.domains; SameSite=None;"

  Header unset Access-Control-Allow-Credentials
  Header unset Access-Control-Allow-Methods
  Header unset Access-Control-Allow-Origin
  Header unset Access-Control-Allow-Headers

  Header always set Access-Control-Allow-Credentials "true"
  Header always set Access-Control-Allow-Origin https://mycontexts.com
  Header always set Access-Control-Allow-Headers "content-type"
  Header always Set Access-Control-Allow-Methods "GET, PUT, POST, DELETE, OPTIONS"

  SSLEngine on
  SSLCertificateFile /path/to/certs/perspectives.domains-2023-certificate.crt
  SSLCertificateKeyFile /path/to/keys/perspectives.domains-2023-certificate.key
</VirtualHost>

Finally, notice that it is useful to add some more instructions like ServerAdmin, DocumentRoot, LogLevel, ErrorLog and CustomLog.


1. If no authoring role is provided by the API caller, we take it to be the System User.