openanalytics / shinyproxy

ShinyProxy - Open Source Enterprise Deployment for Shiny and data science apps
https://www.shinyproxy.io
Apache License 2.0
513 stars 152 forks source link

traefik https redirect does not work on docker swarm with shinyproxy #273

Closed hcstubbe closed 1 year ago

hcstubbe commented 3 years ago

I was following this great tutorial to set up a shinyproxy (v.2.4.1) on docker-swarm using traefik (v.2.2) with keycloak for authentication on an ubuntu 20.04 LTS server. For some reason, I do not get to work the https redirect for the shinyproxy. I tried this with and without the keycloak authentication.

https redirect works for keycloak.app.example.com and manually adding https in to my url in the browser after log-in works (e.g. replacing http by https in the adressbar: https://shinyproxy.example.com). Unfortunately, the automatic redirect from http to https does not work (http://shinyproxy.example.com -> https://shinyproxy.example.com).

I tried the following, but it did not work:

server: forward-headers-strategy: "native"

Any idea why this does not work and how I could fix this?

Thank you so much in advance!

Here is my application.yml:

version: '3'

services:

  shinyproxy:
    image: mydocker/shinyproxy
    # The labels section is where you specify configuration values for Traefik.
    # Docker labels don’t do anything by themselves, but Traefik reads these so
    # it knows how to treat containers.
    ports:
      - 8090:8090
    networks:
      - traefik-public
      - sp-net
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure
      placement:
        constraints:
          - node.role==manager
      labels:
          - "traefik.enable=true" # enable traefik
          - "traefik.docker.network=traefik-public" # put it in the same network as traefik
          - "traefik.constraint-label=traefik-public" # assign the same label as traefik so it can be discovered
          - "traefik.http.routers.shinyproxy.rule=Host(`shinyproxy.example.com`)" # listen to port 80 for request to APP_DOMAIN (use together with the line below)
          - "traefik.http.routers.shinyproxy.entrypoints=http"
          - "traefik.http.middlewares.shinyproxy-https-redirect.redirectscheme.scheme=https" # redirect traffic to https
          - "traefik.http.middlewares.shinyproxy-https-redirect.redirectscheme.permanent=true" # redirect traffic to https
          - "traefik.http.routers.shinyproxy-secured.rule=Host(`shinyproxy.example.com`)" # listen to port 443 for request to APP_DOMAIN (use together with the line below)
          - "traefik.http.routers.shinyproxy-secured.entrypoints=https"
          - "traefik.http.routers.shinyproxy-secured.tls.certresolver=le" # use the Let's Encrypt certificate we set up earlier
          - "traefik.http.services.shinyproxy-secured.loadbalancer.server.port=8090" # ask Traefik to search for port 8090 of the shinyproxy service container

    volumes:
      - ./application/application_keycloak.yml:/opt/shinyproxy/application.yml
      - /var/run/docker.sock:/var/run/docker.sock

networks:
  traefik-public:
    external: true
  sp-net:
    external: true

This is the traefik.yml:

version: '3.3'

services:

  traefik:
    # Use the latest v2.2.x Traefik image available
    image: traefik:v2.2
    ports:
      # Listen on port 80, default for HTTP, necessary to redirect to HTTPS
      - 80:80
      # Listen on port 443, default for HTTPS
      - 443:443
    deploy:
      placement:
        constraints:
          # Make the traefik service run only on the node with this label
          # as the node with it has the volume for the certificates
          - node.labels.traefik-public.traefik-public-certificates == true
      labels:
        # Enable Traefik for this service, to make it available in the public network
        - traefik.enable=true
        # Use the traefik-public network (declared below)
        - traefik.docker.network=traefik-public
        # Use the custom label "traefik.constraint-label=traefik-public"
        # This public Traefik will only use services with this label
        # That way you can add other internal Traefik instances per stack if needed
        - traefik.constraint-label=traefik-public
        # admin-auth middleware with HTTP Basic auth
        # Using the environment variables USERNAME and HASHED_PASSWORD
        - traefik.http.middlewares.admin-auth.basicauth.users=${USERNAME?Variable not set}:${HASHED_PASSWORD?Variable not set}
        # https-redirect middleware to redirect HTTP to HTTPS
        # It can be re-used by other stacks in other Docker Compose files
        - traefik.http.middlewares.https-redirect.redirectscheme.scheme=https
        - traefik.http.middlewares.https-redirect.redirectscheme.permanent=true
        # traefik-http set up only to use the middleware to redirect to https
        # Uses the environment variable DOMAIN
        - traefik.http.routers.traefik-public-http.rule=Host(`${DOMAIN?Variable not set}`)
        - traefik.http.routers.traefik-public-http.entrypoints=http
        - traefik.http.routers.traefik-public-http.middlewares=https-redirect
        # traefik-https the actual router using HTTPS
        # Uses the environment variable DOMAIN
        - traefik.http.routers.traefik-public-https.rule=Host(`${DOMAIN?Variable not set}`)
        - traefik.http.routers.traefik-public-https.entrypoints=https
        - traefik.http.routers.traefik-public-https.tls=true
        # Use the special Traefik service api@internal with the web UI/Dashboard
        - traefik.http.routers.traefik-public-https.service=api@internal
        # Use the "le" (Let's Encrypt) resolver created below
        - traefik.http.routers.traefik-public-https.tls.certresolver=le
        # Enable HTTP Basic auth, using the middleware created above
        - traefik.http.routers.traefik-public-https.middlewares=admin-auth
        # Define the port inside of the Docker service to use
        - traefik.http.services.traefik-public.loadbalancer.server.port=8090
    volumes:
      # Add Docker as a mounted volume, so that Traefik can read the labels of other services
      - /var/run/docker.sock:/var/run/docker.sock:ro
      # Mount the volume to store the certificates
      - traefik-public-certificates:/certificates
    command:
      # Enable Docker in Traefik, so that it reads labels from Docker services
      - --providers.docker
      # Add a constraint to only use services with the label "traefik.constraint-label=traefik-public"
      - --providers.docker.constraints=Label(`traefik.constraint-label`, `traefik-public`)
      # Do not expose all Docker services, only the ones explicitly exposed
      - --providers.docker.exposedbydefault=false
      # Enable Docker Swarm mode
      - --providers.docker.swarmmode
      # Create an entrypoint "http" listening on port 80
      - --entrypoints.http.address=:80
      # Create an entrypoint "https" listening on port 443
      - --entrypoints.https.address=:443
      # Create the certificate resolver "le" for Let's Encrypt, uses the environment variable EMAIL
      - --certificatesresolvers.le.acme.email=${EMAIL?Variable not set}
      # Store the Let's Encrypt certificates in the mounted volume
      - --certificatesresolvers.le.acme.storage=/certificates/acme.json
      # Use the TLS Challenge for Let's Encrypt
      - --certificatesresolvers.le.acme.tlschallenge=true
      # Enable the access log, with HTTP requests
      - --accesslog
      # Enable the Traefik log, for configurations and errors
      - --log
      # Enable the Dashboard and API
      - --api
    networks:
      # Use the public network created to be shared between Traefik and
      # any other service that needs to be publicly available with HTTPS
      - traefik-public

volumes:
  # Create a volume to store the certificates, there is a constraint to make sure
  # Traefik is always deployed to the same Docker node with the same volume containing
  # the HTTPS certificates
  traefik-public-certificates:

networks:
  # Use the previously created public network "traefik-public", shared with other
  # services that need to be publicly available via this Traefik
  traefik-public:
    external: true

This is the applcation.yml for the shinyproxy:

proxy:
  title: My title
  port: 8090 
  container-wait-time: 30000
  heartbeat-rate: 10000
  heartbeat-timeout: 60000

  landing-page: /app/myapp

  authentication: keycloak

  admin-groups: admins

  keycloak:
    realm: master                                                     
    auth-server-url: https://keycloak.app.example.com/auth
    resource: shinyproxy                                                  
    credentials-secret: **************  # the Keycloak secret for your "client application"

  # Set the container backend: The container-backend can be one of docker (default), docker-swarm or kubernetes
  container-backend: docker-swarm

  docker:
      internal-networking: true

  # Below is a list of Shiny apps and their config
  specs:
  - id: myapp
    display-name: myapp
    description: my app
    container-image: myappimg
    container-network: sp-net
    access-groups: user

server:
  forward-headers-strategy: "native"

logging:
  file:
    shinyproxy.log
ziyunxiao commented 3 years ago

"Unfortunately, the automatic redirect from http to https"

That's pure Traefik features, has nothing to do with Shinyproxy.

here is the example for traefik labels.

The key is you need to define two routers, shiny-http and shiny-https. on router shiny-http set middlewareto https-redirect.

        - traefik.http.routers.shiny-http.middlewares=https-redirect
        - traefik.http.routers.shiny-http.rule=Host(`shiny.swarm.youdomain.com`) 
        - traefik.http.routers.shiny-https.rule=Host(`shiny.swarm.youdomain.com`)
        - traefik.http.routers.shiny-https.tls=true
        - traefik.http.services.shiny-https.loadbalancer.server.port=8080
        - traefik.http.services.shiny-https.loadbalancer.sticky.cookie=true
LEDfan commented 1 year ago

Hi, as I understand, @ziyunxiao provides the answer to the question (thanks btw!) therefore I'm going to close this issue. If you encounter any other issue with ShinyProxy, feel free to open a new issue.