Connect to RStudio Server over HTTPS for free, CentOS 7


The aim is to use the capability of RStudio Server available in professional version using free version.
The scenario is: researchers are using RStudio to perform research within academic network. The network is protected by firewall but as there is a large number of hosts connected there is no way to ensure, that there would not be an attacker from within or from the outside (while, for example, using vulnerability of one of the network clients).
In order to encrypt the communication between the RStudio server and the client machine, I used a simple, yet effective trick of proxying SSL traffic.
In my case I use certificates that I generated then signed them with our organisation wildcard key. You can use self-signed certificates or Let's Encrypt. Configuration needed to use Let's Encrypt is shown in  this example:
https://certbot.eff.org/lets-encrypt/centosrhel7-nginx
The the following diagram describes the idea behind this scenario:

RStudio server <-->  nginx as an SSL proxy <-->  client machines (web browser)


I achieved similar goal using APACHE 2 with its proxy module, this guide is fairly good, shows the set-up for port 443 though:
To set this up on a standard HTTPS port (443) is easy enough and does not require much attention here as it might be discovered on the web, it was described in few places, however, I wanted to redirect RStudio server to non-standard port, as I wanted to utilise port 443 for other services.
 Basic set-up is well described here:
and here:
Once we have RStudio Server and nginx installed we need to look at the config files:
/etc/rstudio/rserver.conf

And add this line in order to make RStudio available only on localhost and not available from the network through http call:



www-address=127.0.0.1

Then we need to go into nginx config directory and open its config file located here:
/etc/nginx/nginx.conf

server {
# define the port on which you will connect from client browser:
listen 9999 ssl http2 default_server;
listen [::]:9999 ssl http2 default_server;
server_name our-example-server.com;
root /usr/share/nginx/html;
# use your certificates, use TLS, do not use SSL
ssl_certificate "/etc/pki/tls/certs/our-example-server.com.crt"; ssl_certificate_key "/etc/pki/tls/private/our-example-server.com.key"; ssl_session_cache shared:SSL:1m; ssl_session_timeout 10m; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on;

# Load configuration files for the default server block.

include /etc/nginx/default.d/*.conf;

location / {
proxy_pass http://localhost:8787/;
# below is the real trick that makes it work on non standard port on the whole site:
proxy_redirect http://localhost:8787/ https://our-example-server.com:9999/;
proxy_http_version 1.1;
proxy_ssl_certificate "/etc/pki/tls/certs/our-example-server.com.crt";
proxy_ssl_certificate_key "/etc/pki/tls/private/our-example-server.com.key";
# Each new SSL connection requires a full SSL handshake between the client and server, which is quite CPU-intensive.
# To have NGINX proxy previously negotiated connection parameters and use a so-called abbreviated handshake:

proxy_ssl_session_reuse on;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 20d;
}

error_page 404 /404.html;
location = /40x.html {
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
# this is also very important
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

 Since I still use original iptables (I found them easier to manage with my very complicated Docker Swarm Mode cluster), you would need to open that non-standard port for proxy. Put this in your INPUT chain:


-A INPUT -p tcp -m state -m tcp -s 192.168.10.0/24 --dport 9999 --state NEW -j ACCEPT

This way, you can also implement another feature which is not available in free version of RStudio Server, which is client IP restriction, the above example just shows one rule which is applied to the whole subnet 192.168.10.0, but you can widen it or narrow down even to single IP addresses, like for example 192.168.10.12/32.
The firewalld would look like this:



firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.10.0" service name="distinct" accept'
where name="distinct" is the name matched to the port according to /etc/services file.



The last thing you need to do, is to permit nginx to work on port 9999, or whatever port you chose. Otherwise SELinux would not allow it. Since setting SELinux to permissive is a very bad idea and setting it to disabled is even worse, you would like to execute the following command:

semanage port -a -t http_port_t -p tcp 9999



semanage port -l | grep http_port_t



Please keep in mind, that presented ports, IP addresses and domain names need to be replaced to your actual ones in order to make above example work. You would also need to generate encryption keys and replace the example ones to avoid errors. 
Good luck!

No comments:

Post a Comment