Set up RStudio server with Let's Encrypt SSL certificate

For a while now I’ve been struggling with various installation setup related to the open source versions of RStudio server, Shiny server (and dockerized versions of them).

After browsing internet tutorials on-and-off for the last couple of weeks I’ve come to the conclusion that there is a small gap when it comes to setting up RStudio server and shiny server securely, i.e., with SSL certificates.

I’ve put together this step-by-step post to close this gap (in part for self-documentation). I don’t cover dockerized versions here.

Step 1 (or actually steps 1-10)

Install rstudio server and shiny server on your choice of cloud provider. Common choices are AWS (EC2 or Lightsail, Digital Ocean, Azure, and Google. Anything works actually).

Installation instructions are covered in detail in Dean Attali’s post here which uses Digital Ocean.

No use repeating everything in its entirety. I will however highlight two things I’ve found helpful and are a bit different from Dean’s post.

  1. Selection of instance size (resources)
  2. AWS Lightsail’s deafult firewall settings

Selection of instance size

I’ve found that opening an instance with low memory (i.e., 0.5-1Gb) is problematic. Its cheap, but if you’re installing packages such as dplyr (included in tidyverse), compiling the package within your server, will require more memory than what you have available. You can workaround this by increasing your swap file size (step 6 in Dean’s post), but eventually even R processes might give you a hard time in the future. I’ve been using a 4GB server, which seems to be ok for my purposes (and at the time of writing this post costs about $20 USD per month).

AWS Lightsail’s firewall settings

When using AWS Lightsail (which I’ve been using), a default firewall rule is applied. This means that when you complete steps 7-8 in Dean’s post, your server will not work if you’re using AWS Lightsail. The reason for that is that AWS Lightsail built-in firewall blocks all ports except 22 and 80 by default (rstudio server uses 8787 and shiny server uses 3838). It doesn’t matter much, because by the end of this post we’ll be using port 443 (for secure SSL connection), but you can also make the non-secure version work out - useful for checking that your server is working. Let’s go ahead and open port 443 in your Lightsail instance (and explain how to also temporarily open 8787 and 3838).

Click on the instance you’ve opened, and then go to the Networking tab. In this page you’ll see a “Firewall” title and beneath it a table with the currently open ports. Under the table click on “+ Add another”. Add HTTPS (under application), TCP (under protocol) and port 443, like in the following screenshot:

If you want to temporarily enable direct access to port 8787 and 3838 (to check that your installation went well), you can do that as well (use “Custom” under application, TCP for protocol, and port 8787 (repeat for 3838 in an additional line).

Secure you server with SSL encryption

Since you will probably be passing data back and forth between your remote server and local computer, you want to secure this data transfer with encryption. The best way to do this is using SSL certificates (it’s actually called TSL, the name SSL refers to a deprecated protocol, but everyone seems to still be using the SSL initials, see here).

There is a free service called Let’s Encrypt which provides SSL certificates, which is what we’re going to use here. I’m actually adopting the approach of this tutorial.

Install Let’s Encrypt and get certficates

Install the following software on your linux server

sudo apt instal letsencrypt

Update your nginx configuration as preperation for obtaining the let’s encrypt certificate. This step is needed because when requesting a certificate from let’s encrypt, the let’s encrypt server will try to authenticate your server.

Use

sudo nano /etc/nginx/sites-enabled/default

And add the following (replace r.example.com with your domain):

server {
        listen 80;
        listen [::]:80;
        root /var/www/r.example.com/html;

        # Add index.php to the list if you are using PHP
        index index.html index.htm index.nginx-debian.html;

        server_name r.example.com;
}

Get your SSL certificates using the following line, just replace r.example.com with your subdomain.

letsencrypt certonly -a webroot --webroot-path=/var/www/r.example.com/html/ -d r.example.com

Update your nginx settings again

sudo nano /etc/nginx/sites-enabled/default

To have the following setup (remember to replace r.example.com with your domain):

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

# listens on port 80 and redirects traffic to secure alternative
server {
        listen 80 default_server;
        listen [::]:80 default_server;
        server_name r.example.com;
        return 301 https://r.example.com$request_uri;
}

server {
        
        # SSL configuration
        
        listen 443 ssl;
        ssl_certificate /etc/letsencrypt/live/r.example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/r.example.com/privkey.pem;
        ssl_protocols TLSv1.2;

        ssl_ciphers EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
        ssl_prefer_server_ciphers On;
        ssl_session_cache shared:SSL:128m;
        add_header Strict-Transport-Security "max-age=31557600; includeSubDomains";
        ssl_stapling on;
        ssl_stapling_verify on;

        root /var/www/r.example.com/html;

        server_name _;

        # Reroute traffic to shiny server (i.e., reverse proxy for port 3838)
        location /shiny/ {
           proxy_pass http://127.0.0.1:3838/;
           proxy_http_version 1.1;
           proxy_set_header Upgrade $http_upgrade;
           proxy_set_header Connection $connection_upgrade;
           rewrite ^(/shiny/[^/]+)$ $1/ permanent;
        }

        # Reroute traffic to rstudio server (i.e., reverse proxy for port 8787)
        location /rstudio/ {
           proxy_pass http://127.0.0.1:8787/;
           proxy_http_version 1.1;
           proxy_set_header Upgrade $http_upgrade;
           proxy_set_header Connection $connection_upgrade;
        }
}

Your server should be working now, but since Let’s Encrypt certificates only last 90 days, lets put an automatically renewal process in place.

sudo nano /opt/renewCerts.sh

Paste the following text:

#!/bin/sh
# This script renews all the Let's Encrypt certificates with a validity < 30 days
if ! letsencrypt renew > /var/log/letsencrypt/renew.log 2>&1 ; then
    echo Automated renewal failed:
    cat /var/log/letsencrypt/renew.log
    exit 1
fi
nginx -t && nginx -s reload

Make sure the script is owned and executable by root:

chown root.root /opt/renewCerts.sh
chmod u+x /opt/renewCerts.sh

Add it to cron for auto execution:

sudo crontab -e

Add

@weekly /opt/renewCerts.sh

All should be set!

Go ahead and browse to your domain (e.g., https://r.example.com/rstudio). Check that you’re able to login properly and that all pages are secure on https.

Partner and Head of Operations Research Department