R Shiny with user authentication over HTTPS
I want to use R Shiny to create dashboards to look at health record data from OpenMRS. Don't worry, this tutorial is not specific to OpenMRS. I'll explain how to get R Shiny working with user authentication via ShinyProxy and KeyCloak, all dockerized, all over HTTPS.
We're going to use ShinyProxy with "simple" (insecure) auth available only on the LAN, and set up an instance in the cloud with KeyCloak authentication, with a KeyCloak server we'll also set up in the cloud.
I'm just going to document the fully secured setup -- the insecure setup is a subset of the work.
So far, I've only gotten KeyCloak to work with dockerized MySQL. Getting it to work with a preexisting MySQL database (such as the one used by OpenMRS) is a TODO.
Shiny is an R library for producing webapps
ShinyProxy is an authorization and orchestration layer. It checks whether a user is authenticated, and if not, sends them to KeyCloak for authentication. It then presents an interface for launching the Shiny apps that the user is authorized to use.
KeyCloak is a crazy powerful user/auth management application, which provides a management console for adding users, a login screen, and a million options.
MySQL will hold the user data, but could also be used for other things (e.g. by the Shiny apps).
https-portal will act as a reverse proxy, securing everything over HTTPS. It runs nginx under the hood and uses letsencrypt for TLS certificates.
+----------------------------------------------------------+ | _____ | | Docker +----------+ / \ | | auth | | + + | | +-------> | KeyCloak +---------> | MySQL | | | | | | + + | | | +----------+ \_____/ | | | | +--------+ +--------------+ | | | https | | | | Client +---------> | https-portal | | | | | | +------------+ +-------+ | +--------+ +--------------+ | | | | | |--| | +-------> | ShinyProxy +-------> | Shiny | | +--------+ | authenticated | | | | | +--------+ | app data +------------+ +-------+ | | | | | +----------------------------------------------------------+
Spin up a server. I'm working on Debain Stretch. I like that it doesn't have the AppArmor weirdness mentioned below.
Configure daemon to be available at port 2375. Use the snippet from the ShinyProxy instructions, but use
sudo systemctl edit docker.serviceto manage the override file.
(On Ubuntu 18.04, I had to disable AppArmor because otherwise Docker containers were unkillable. But this will make snaps fail to start. Good luck.)
Add your user to the docker group.
sudo usermod -aG docker $(whoami)
Create the network that your docker stuff will operate on:
sudo docker network create sp-net
ShinyProxy Initial Setup
We're going to build a docker image for ShinyProxy exactly as described here, except that our network is called
sp-net, and we're going to call our image
shinyproxy rather than
shinyproxy-example. Don't worry about the contents of
application.yml just yet.
Get Everything Initially Running
You'll need two (sub)domains, one for ShinyProxy and one for KeyCloak. Mine were
sp-keycloak.domain.com. Add A records from these (sub)domains to your server's IP address.
Take a look at the KeyCloak Docker documentation. Get mysql up and running (note that the database name, user name, and password all matter — see note about env vars below):
docker run --name mysql -d --net sp-net -e MYSQL_DATABASE='keycloak' -e MYSQL_USER='keycloak' -e MYSQL_PASSWORD='password' -e MYSQL_ROOT_PASSWORD="$MYSQL_ROOT_PASS" mysql:5.6
(This could probably be integrated into docker-compose as well.)
Create a docker-compose.yml like the following:
version: '3' services: https-portal: image: steveltn/https-portal:1 ports: - '80:80' - '443:443' networks: - sp-net restart: always environment: DOMAINS: 'sp-keycloak.domain.com -> http://dockerhost:8010, sp.domain.com -> http://dockerhost:8020' # change 'domain.com' to your domain STAGE: local # change this to 'production' once you're sure DNS is working keycloak: image: jboss/keycloak ports: - '8010:8080' networks: - sp-net environment: PROXY_ADDRESS_FORWARDING: 'true' shinyproxy: image: shinyproxy ports: - '8020:8080' networks: - sp-net volumes: - /var/run/docker.sock:/var/run/docker.sock networks: sp-net: external: true
And run it with
docker-compose up -d
This will spin up HTTPS-PORTAL exposed at ports 80 and 443 (make sure your firewall is configured such that these ports are exposed; and that they are the only ports exposed), ShinyProxy on localhost:8020, and KeyCloak on localhost:8010. HTTPS-PORTAL will route to either ShinyProxy or KeyCloak depending on the URL at which it is accessed.
I wasn't able to get the KeyCloak environment variables for setting database address or database password to work. We need to do so if we want to use an un-dockerized or differently-named MySQL instance (or a different database entirely), or if we want another layer of security in between the system and the data.
Start by creating the initial admin user:
docker exec $CONTAINER_ID keycloak/bin/add-user-keycloak.sh -u admin -p admin123
KeyCloak wisely disallows admin console access from anywhere outside localhost. So, from your dev machine, open a tunnel:
ssh -L 4000:localhost:8010 $MY_SERVER
Navigate to localhost:4000 in your browser and sign in with the initial admin credentials from above.
There's a zillion options! Fortunately we only need a few of them.
- First, hover over "Master" in the upper-left corner and click "Add Realm." Name your realm. I used "shinyproxy".
- Create your first user!
- Click "Users" in the left sidebar. Create a user. Only Username is required.
- Click on your newly created user. Click the "Credentials" tab. Set a temporary password. So you can log in as this user.
- Click "Clients" in the left sidebar.
- Click "Create." Name your client application. I used "shinyproxy" again.
- On the main Settings page:
- Turn "Authorization Enabled" ON. (not sure if strictly necessary)
- Add https://sp.domain.com/* to "Valid Redirect URIs" (not sure if strictly necessary)
- Click "Save"
- Click the "Credentials" tab in the top tab bar. On this page:
- Copy the Secret
Configure ShinyProxy to use KeyCloak
We're going to edit ShinyProxy's
application.yml and rebuild the docker image. With the names I used to configure KeyCloak in the admin console earlier, my
application.yml ended up looking like
proxy: port: 8080 authentication: keycloak useForwardHeaders: true # not sure if necessary or not admin-groups: admins keycloak: realm: shinyproxy auth-server-url: https://sp-keycloak.domain.com/auth resource: shinyproxy credentials-secret: ****Secret**** # the secret we copied earlier docker: internal-networking: true specs: - id: 01_hello display-name: Hello Application description: Application which demonstrates the basics of a Shiny app container-cmd: ["R", "-e", "shinyproxy::run_01_hello()"] container-image: openanalytics/shinyproxy-demo container-network: sp-net - id: 06_tabsets container-cmd: ["R", "-e", "shinyproxy::run_06_tabsets()"] container-image: openanalytics/shinyproxy-demo container-network: sp-net logging: file: shinyproxy.log
docker build . -t shinyproxy and
docker-compose up -d.
One last piece before we're done: execute
docker pull openanalytics/shinyproxy-demo to pull the Shiny demo apps that ShinyProxy references in the
You should now be able to navigate to
sp.domain.com, be redirected to
sp-keycloak.domain.com, log in as the user you created, be redirected back to
sp.domain.com, and launch a Shiny App.
Hopefully this all works for you!