Security

General

Development life cycle

At Open Analytics we are continuously improving the development life cycle to prevent that malicious code would sneak into the jar files we distribute. We use continuous integration and a deployment process to enable automated security checks in the recipe and guarantee fresh JAR delivery. Should you discover security issues in the code, please contact us ASAP, ideally via e-mail. One can also file an issue on our Github repository; just ensure not to share sensitive information (IP, names, login, passwords,…).

Secure your underlying infrastructure

ShinyProxy is relying on different infrastructure components: docker engine, authentication back-end, operating system,… The security of those components should be guaranteed and ShinyProxy features have been added to enable our users to build secure infrastructure. In particular for the Docker host, there is a well-known issue should a Docker host API be publicly accessible without appropriate access controls. The host could be remotely compromised or arbitrary docker instances could run on it.

To protect a docker daemon the below security controls are mandatory:

  • isolate docker host from public/untrusted network
  • never bind the docker daemon API on 0.0.0.0, only on the loopback interface (127.0.0.1)
  • should the docker API be exposed (in case of a swarm or cloud deployment), ensure to use TLS mutual authentication for enabling communication with the docker API. It’s a more heavy setup as it would require a proper PKI or certificate management/creation solution but exposing the docker API should be avoided at all costs.

Using the general Configuration, it is possible to

  • secure the communication between ShinyProxy and the LDAP directory
  • secure the communication between ShinyProxy and the Docker daemon

HTTPS (SSL / TLS)

From an architectural point of view it is recommended to support the off-loading of SSL certificates to a separate reverse proxy. Nginx works perfectly fine with ShinyProxy and below an example configuration is given:

server {
  listen                80;
  server_name           shinyproxy.yourdomain.com;
  rewrite     ^(.*)     https://$server_name$1 permanent;
}

server {
  listen                443;
  server_name           shinyproxy.yourdomain.com;
  access_log            /var/log/nginx/shinyproxy.access.log;
  error_log             /var/log/nginx/shinyproxy.error.log error;

  ssl on;
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

  ssl_certificate       /etc/ssl/certs/yourdomain.com.crt;
  ssl_certificate_key   /etc/ssl/private/yourdomain.com.key;

   location / {
       proxy_pass          http://127.0.0.1:8080/;

       proxy_http_version 1.1;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection "upgrade";
       proxy_read_timeout 600s;

       proxy_redirect    off;
       proxy_set_header  Host              $http_host;
       proxy_set_header  X-Real-IP         $remote_addr;
       proxy_set_header  X-Forwarded-For   $proxy_add_x_forwarded_for;
       proxy_set_header  X-Forwarded-Proto $scheme;
     }

}

The configuration:

  • redirects http traffic (port 80) to https (port 443)
  • sends all traffic to ShinyProxy which is running on port 8080
  • includes all nginx settings to correctly proxy web socket traffic as required by Shiny apps

Note that nginx will pass requests to port 8080 on the loopback interface (127.0.0.1) as indicated in the proxy_pass directive above). It is therefore also a good security measure to restrict ShinyProxy to bind only on 127.0.0.1 (and not to 0.0.0.0 which is the default). This can be achieved by setting the bind-address in the application.yml configuration file:

proxy:
  # ...
  bind-address: 127.0.0.1

A similar setup can be achieved using Apache HTTPD:

  • first change the global httpd.conf to contain at least the following configuration directives:

    Listen 443
    LoadModule proxy_module modules/mod_proxy.so
    LoadModule proxy_http_module modules/mod_proxy_http.so
    LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
    LoadModule ssl_module modules/mod_ssl.so
    LoadModule rewrite_module modules/mod_rewrite.so
    

    Note: depending on the Linux distribution you are using, the exact method to enable these modules may depend. For example, on ubuntu this can be achieved by running the following commands (no changes to configuration files are needed):

    sudo a2enmod proxy
    sudo a2enmod proxy_http
    sudo a2enmod proxy_wstunnel
    sudo a2enmod proxy_ssl
    sudo a2enmod ssl
    sudo a2enmod rewrite
    sudo a2enmod headers
    sudo systemctl restart apache2
    
  • create an empty directory:

    mkdir /srv/shinyproxy
    
  • the next step is to create two VirtualHosts for ShinyProxy. The first one is needed to redirect the traffic from HTTP to HTTPS, the second one is needed for proxying the traffic to ShinyProxy.

    <VirtualHost *:80>
      ServerName shinyproxy.yourdomain.com
    
      RewriteEngine On
    
      RewriteCond %{SERVER_NAME} =shinyproxy.yourdomain.com
      RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,QSA,R=permanent]
    
    </VirtualHost>
    
    <VirtualHost *:443>
      DocumentRoot "/srv/shinyproxy"
      ServerName shinyproxy.yourdomain.com
      ServerAlias shinyproxy.yourdomain.com
    
      ProxyRequests Off
      ProxyPreserveHost On
      ProxyPass / http://127.0.0.1:8080/
      ProxyPassReverse / http://127.0.0.1:8080/
    
      RewriteEngine on
      RewriteCond %{HTTP:Upgrade} =websocket
      RewriteRule /(.*) ws://127.0.0.1:8080/$1 [P,L]
    
      SSLEngine on
      SSLCertificateFile      "/etc/ssl/certs/yourdomain.com.crt"
      SSLCertificateKeyFile   "/etc/ssl/private/yourdomain.com.key"
      #SSLCertificateChainFile "/etc/ssl/certs/yourdomain.com.chain.pem" # this is commonly needed, but depends on your certificates
    
      # Add X-Forwarded-Proto header, the other headers are added automatically
      RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
    
    </VirtualHost>
    
  • finally, restart apache:

    sudo systemctl restart apache2
    

Forward Headers

When only https is available (i.e. no redirects from http configured in nginx), it is required to configure ShinyProxy to use “forward headers”. This configuration may also be required in setups using OIDC or SAML in order for ShinyProxy to use the correct scheme for redirects URIs.

server:
  forward-headers-strategy: native

This will make sure ShinyProxy does not redirect to http. The FAQ contains more information on what to do when this feature isn’t correctly working.

Firewalling

If no reverse proxy is put in front of ShinyProxy, the port that needs to be accessible to the outside world is the proxy port specified in the application.yml file (port field) which, by default, will be port 8080. In principle, all other ports can be filtered in the firewall.

ShinyProxy itself will connect to the ports on the docker host that are mapped to the individual containers. By default the ports will start at 20000 for the first container and are incremented for every new container that is spin up (20001 for the second one etc.). This is specified in the application.yml as port-range-start: 20000 as documented here). These ports on the docker host should be available to ShinyProxy, but it is recommended not to make these ports accessible to the outside world using appropriate firewall rules.

Sensitive Configuration

Configuration parameters that contain sensitive information and cannot be stored in the application.yml file can be provided differently. Note that best practice is to sufficiently protect application.yml with access rights, but in some cases (e.g. where the configuration is partly maintained via source control) it is useful to externalize sensitive configuration.

  • Using a Java System property:
unset HISTFILE
java -jar shinyproxy-3.1.1.jar -Dspring.mail.password=abc

Use period as a separator to descend the parameter hierarchy in the application.yml file.

  • Using environment variables:
unset HISTFILE
export SPRING_MAIL_PASSWORD=abc
java -jar shinyproxy-3.1.1.jar

Since most operating systems disallow period-separated names, the naming syntax differs slightly. To set the environment variable corresponding to a java system property, use uppercase and replace all periods with underscores.

Secure Dependencies

ShinyProxy makes uses of software libraries such as the Spring Boot framework. It is important that these libraries are updated when they contain a security vulnerability. In order to know which libraries contain vulnerabilities we use the OWASP Dependency Check plugin for Maven.

Checking for vulnerable dependencies:

git clone https://github.com/openanalytics/shinyproxy
cd shinyproxy
mvn intall -Powasp-dependency-check

The last command first downloads the NVD CVE database (this may take some time). Next, it generates a HTML report target/dependency-check-report.html that lists all known CVE’s in the dependencies.

Configure Security Options

ShinyProxy contains some configuration options to enable certain security related features. When installing ShinyProxy, make sure to set the correct values for these options. See the documentation.