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
orresponse-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 will copy the 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:
extract_certs.sh
cp sandstorm.key /etc/nginx/ssl
cp sandstorm.pem /etc/nginx/ssl
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;
<span class="kn">proxy_http_version</span> <span class="mi">1</span><span class="s">.1</span><span class="p">;</span>
<span class="kn">proxy_set_header</span> <span class="s">Upgrade</span> <span class="nv">$http_upgrade</span><span class="p">;</span>
<span class="kn">proxy_set_header</span> <span class="s">Connection</span> <span class="nv">$connection_upgrade</span><span class="p">;</span>
}
}
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.