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 shared jar files. We use continuous integration and a deployment process to enable automated security checks in the recipe and guarantee fresh JARs 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-Protocol $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

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-1.0.2.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-1.0.2.jar

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