How to set up Sandstorm behind a reverse proxy, keeping your sandcats.io domain and certificates

I’m having some fun tinkering with my new Intel NUC home server (with Linux, of course) and moving everything that I had in the “cloud” to it (except backups). Last night I set up Sandstorm behind an nginx reverse proxy accepting https connections (for this and other services) while keeping Sandstorm working with my registered sandcats.io domain and free auto-renewable https certificates. Since this is something I couldn’t find any documentation about I’ll add how to get it done in this article.

This was not obvious to do because:

  • Sandcats.io certificates only seem to work for the 443 port (or maybe is a problem with the renewal, I don’t know).

  • Sandstorm need a wildcard domain or subdomain to run and letscrypt doesn’t provide wildcard certificates. So to reverse proxy Sandstorm under your own domain you would need a paid wildcard cert (and those are not cheap) or run on unencrypted http (and you don’t want that).

  • Self signed wildcard certificates don’t work very well. Usually they make the images don’t load correctly even if you accept the certificate (happened to me on Chrome, Opera, in Firefox grains wouldn’t even load) or require you to create a rootCA cert and install it on every machine where you want to use Sandstorm. Which defeats the purpose of having web apps and it’s not always easy to do on some phones or other non-PC devices.

The official documentation advices to use sniproxy if you want to share the 443 port with other applications. I did that for a while, having a dockerized sniproxy in front of both nginx and Sandstorm redirecting to the correct one based on the ssl handshake. I run like this for a while, but I had two problems. First, there was a noticeable performance penalty in requests/second compared to unproxied nginx. The second problem is that I saw that there was some leaking something between domains. For example using some web tool to analyze site performance that showed all requests I saw that, oddly, there were some requests for parts of my site (on another domain) that landed on my name.sandcats.io domain, and they delayed the page loading. Also, with sniproxy I couldn’t enable http2.

Finally after some tinkering and a little scripting I found a better and working solution. Follow these steps:

Install and configure Sandstorm

Do the initial installation and configuration of Sandstorm, choosing to enable the Sandcats service. Don’t start the service yet. Now edit your sandstorm.conf file and configure it to also use HTTPS by adding or uncommenting the line:

HTTPS_PORT=443

Its important to keep the port number of the HTTPS_PORT option at 443 at least when retrieving of renewing the certificates. If you are running your Sandstorm service inside a container or VM you can leave this option enabled since this way the short-lived certificates will continue auto renewing themselves and we’ll not export the 443 port outside the container so it won’t conflict with your reverse proxy.

If you aren’t using running Sandstorm inside a container, you’ll need to block or redirect the port (with iptables?). Or just use Docker, it’s super easy with Sandstorm

Next, change BASE_URL to use start with https://:

# BASE_URL=http://yourname.sandcats.io
BASE_URL=https://yourname.sandcats.io

After this:

  • Stop your reverse proxy so the 443 port is free

  • Run the sandstorm service, making sure that the 443 port is accessible from the outside (unfirewall it, enable it on docker with the -p 443:443 option, whatever)

  • Check with your browser that you can access your https URL and Sandstorm works.

  • Check the logs on [sandstorm_dir]/data/var/log/sandstorm/log) to see that it fetched the certificates.

  • Double check that the files are on [sandstorm_dir]/data/var/sandcats/https/yourname.sandcats.io (they are like timestamps with .csr or response-json extensions or no extension, with the timestamp being the expiration date)

  • Finally, stop sandstorm, and if you run it dockerized unexport the 443 port but expose the HTTP port (see below in the network section) or if you are running it uncontained change or disable the HTTPS_PORT setting as needed.

  • Wake up both your reverse proxy and sandstorm services.

Run the script on this repo

That script, written by some random handsome programmer, will get Sandcats.io certs in your reverse proxy directory (for nginx this would tipically be /etc/nginx/ssl). Since Sancats.io certificates must be renewed weekly I suggest to add this to your cron. A typical call could be:

python get_sandcats_certs.py \
  --certs_origin_dir='/opt/sandstorm/var/sandcats/https/myname.sandcats.io' \
  --certs_dest_dir='/etc/nginx/conf/ssl' \
  --key_filename='sandstorm.key' \
  --cert_filename='sandstorm.pem'

Please note that the key_filename and cert_filename parameters are the desired filenames on the DESTINATION path (the ones that your reverse proxy will use), not the original ones on the Sandstorm directory; for those the script will take care of finding the most recent ones an extracting them from the JSON files they’re stored in.

Configure Sandstorm network parameters

Next you need to configure your Sandstorm container/VM so it serves on the 443 port of the container and can be linked to other containers but it’s not published on the host machine (where it would conflict with your reverse proxy).

In the case of the docker container just remove the -p parameter for the docker run command and add the port as a --expose like:

sudo docker run --name sandstorm \
  --privileged --sig-proxy=true --expose 443 \
  -v /home/you/docker/sandstorm/data:/opt/sandstorm \
  -d buildpack-deps \
   bash -c 'useradd --system --user-group sandstorm && ' \
           '/opt/sandstorm/sandstorm start && ' \
           'tail -f /opt/sandstorm/var/log/sandstorm.log && ' \
           'sleep infinity' 

With --expose only linked containers, your reverse proxy in this case, will be able to see the port. If you’re of the paranoid type like I’m you can also add a rule to your firewall blocking the port to outside connections.

You could be thinking on exporting the non secure port and connecting the already secured reverse proxy to it using plain unencrypted http to avoid unnecessary SSL baggage but if you do this Sandstorm will try to upgrade the connection to https, which will call your reverse proxy again, etc, so it will enter a redirect loop. This won’t happen if you disable the HTTPS_PORT option, but then the certificates won’t auto renew so… this just works.

Configure the reverse proxy

The reverse proxy will receive the HTTPS connections securely at the https://yourname.sandcats.io domain on the external 443 port, using Sandstorm own certificates, and will proxy them to your Sandstorm container “virtual” 443 port, also using HTTPS.

For example with nginx this should work:

map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

server {
  server_name ~^(.*)>yourdomain\.sandcats\.io$ ~yourdomain\.sandcats\.io$;
  return 301 https://$host$request_uri;
}

server {
  listen 443 ssl http2;
  server_name ~^(.*)>yourdomain\.sandcats\.io$ ~yourdomain\.sandcats\.io$;

  ssl_certificate     /etc/nginx/ssl/sandstorm.pem;
  ssl_certificate_key /etc/nginx/ssl/sandstorm.key;
  include /etc/nginx/ssl_params.conf;

  client_max_body_size 10G; # change this value it according to $UPLOAD_MAX_SIZE

  location / {
    proxy_pass https://sandstorm;
    include /etc/nginx/proxy_params;

    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
  }
}

Don’t forget to point the ssl_certificate and ssl_certificate_key options to the directory where the script copied the certificates. Then restart the reverse proxy checking the logs so see if there is any problem with your config and restart sandstorm. The service should be accessible using your normal https://yourname.sandcats.io domain.

tags: [ software libre · linux · howtos ]
comments powered by Disqus