4 Joal & VPN & qbittorrent & Reverse proxy (with docker)
Anthony Raymond edited this page 2021-10-02 01:42:06 +02:00

What is it

Using docker to deploy JOAL behind a VPN and accessing the services from a reverse proxy

This file is not a guide on how to use or configure JOAL you must be confident what you are doing and understand what's going on

Requirements

  • Docker 18.06.0+
  • docker-compose 1.22.0+
  • an internet router that supports local loopback
  • a DNS name (The DNS provider MUST resolves subdomains as well, see below). If you don't have a DNS you can use DuckDns, it works great and it's free.

Your DNS must resolve subdomain, by this sentence i mean that if your domains is bar.example.com, the address foo.bar.example.com should also resolve to your IP.

Let's talk dirty

This is the folder structure i'm using along that guide.

/home/you/data/
├─ joal/
│  ├─ clients/
│  ├─ torrents/
│  ├─ config.json
├─ openvpn/
│  ├─ vpn.ovpn
├─ traefik/
│  ├─ acme/
│  ├─ dynamics/
│  │  ├─ global.yaml
│  ├─ traefik.yaml
├─ .env
├─ docker-compose.yml

.env

This file is used to store variable automatically resolved by the by the docker-compose.

TZ=Europe/Paris
PUID=1000
PGID=1000
ROOT=/home/you/data

# Traefik config for letsencrypt dns challenge
DUCKDNS_TOKEN=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx

DOMAIN_NAME=xxxxxxxxxx.duckdns.org

#JOAL
JOAL_PATH_PREFIX=XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
JOAL_SECRET_TOKEN=XXXXXXXXXXXXXXXXX

Adapt to your wishes:

  • ROOT: The base path for all the configuration (see tree above).
  • DUCKDNS_TOKEN: Your duckdns token (if using duckdns)
  • DOMAIN_NAME: Your domain name
  • JOAL_PATH_PREFIX: joal path prefix
  • JOAL_SECRET_TOKEN: joal secret token

openvpn/vpn.ovpn

Your openvpn file, you can most likely have one from your VPN provider, if not... well find a proper VPN provider.

traefik/acme/

An empty folder but please create it yourself

traefik/dynamics/global.yaml

http:
  middlewares:
    gzip-compress:
      compress:
        excludedContentTypes:
          - text/event-stream
    basic-auth:
      basicAuth:
        users:
          - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
    redirect-to-https:
      redirectScheme:
        scheme: https
        permanent: true

tls:
  options:
    default:
      minVersion: VersionTLS12

Replace to your wishes:

  • http.middleware.basic-auth.basicAuth.users: This basic auth will be use to protect traefik webui. You must provide a basicauth a basic name:encoded-password pair if you don't know how to generate this couple use thi htpasswd-generator website

traefik/traefik.yaml

#log:
#  level: DEBUG

global:
  sendAnonymousUsage: false
  checknewversion: true

api:
  dashboard: true

providers:
  docker:
    exposedByDefault: false
  file:
    directory: /etc/traefik/dynamics
    watch: false

entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
  tcp-qbittorrent:
    address: ":6881"
  udp-qbittorrent:
    address: ":6881/udp"

certificatesResolvers:
  letsencrypt:
    acme:
      email: "do-not-email@xxxxxxxxxxxxx.duckdns.org"
      storage: /etc/traefik/acme/acme.json
      caServer: "https://acme-v02.api.letsencrypt.org/directory"
      # caServer: "https://acme-staging-v02.api.letsencrypt.org/directory"
      dnsChallenge:
        provider: duckdns

Adapt to your wishes:

  • certificatesResolvers.letsencrypt.email

docker-compose.yml

version: '3.7'

services:
  vpn:
    container_name: vpn
    image: dperson/openvpn-client:latest
    restart: always
    devices:
      - /dev/net/tun
    cap_add:
      - NET_ADMIN
    volumes:
      - ${ROOT}/openvpn:/vpn:ro
    dns:
      - "8.8.8.8"
      - "8.8.4.4"
    logging:
      options:
        max-size: "2m"
        max-file: "3"
    environment:
      - FIREWALL
      - TZ=${TZ}

  traefik:
    image: traefik:2.5.3
    container_name: "traefik"
    restart: always
    volumes:
      - ${ROOT}/traefik/traefik.yaml:/etc/traefik/traefik.yaml:ro
      - ${ROOT}/traefik/dynamics:/etc/traefik/dynamics:ro
      - ${ROOT}/traefik/acme:/etc/traefik/acme:rw
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      - DUCKDNS_TOKEN=${DUCKDNS_TOKEN}
    ports:
      - "80:80"
      - "443:443"
      - "6881:6881"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.dashboard-redir.rule=Host(`traefik.${DOMAIN_NAME}`)"
      - "traefik.http.routers.dashboard-redir.entrypoints=web"
      - "traefik.http.routers.dashboard-redir.middlewares=redirect-to-https@file"
      - "traefik.http.routers.dashboard.entrypoints=websecure"
      - "traefik.http.routers.dashboard.rule=Host(`traefik.${DOMAIN_NAME}`)"
      - "traefik.http.routers.dashboard.service=api@internal"
      - "traefik.http.routers.dashboard.tls=true"
      - "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
      - "traefik.http.routers.dashboard.tls.domains[0].main=${DOMAIN_NAME}"
      - "traefik.http.routers.dashboard.tls.domains[0].sans=*.${DOMAIN_NAME}"
      - "traefik.http.routers.dashboard.middlewares=gzip-compress@file"
      - "traefik.http.routers.dashboard.middlewares=basic-auth@file"

  joal:
    depends_on:
      - vpn
      - traefik
    container_name: joal
    image: anthonyraymond/joal:2.1.26
    restart: always
    network_mode: "service:vpn"
    volumes:
      - ${ROOT}/joal:/data
    command: ["--joal-conf=/data", "--spring.main.web-environment=true", "--server.port=80", "--joal.ui.path.prefix=${JOAL_PATH_PREFIX}", "--joal.ui.secret-token=${JOAL_SECRET_TOKEN}"]
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.joal-redir.rule=Host(`joal.${DOMAIN_NAME}`)"  
      - "traefik.http.routers.joal-redir.entrypoints=web"  
      - "traefik.http.routers.joal-redir.middlewares=redirect-to-https@file"
      - "traefik.http.services.joal-service.loadbalancer.server.port=80"
      - "traefik.http.routers.joal-router.entrypoints=websecure"
      - "traefik.http.routers.joal-router.rule=Host(`joal.${DOMAIN_NAME}`)"
      - "traefik.http.routers.joal-router.middlewares=gzip-compress@file"
      - "traefik.http.routers.joal-router.service=joal-service"
      - "traefik.http.routers.joal-router.tls=true"

  qbittorrent:
    depends_on:
      - vpn
      - traefik
    container_name: qbittorrent
    image: linuxserver/qbittorrent:unstable-version-4.4.0202106140855-7320-2bd5aca3aubuntu20.04.1
    restart: always
    network_mode: "service:vpn"
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
      - WEBUI_PORT=8080
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ${ROOT}/qbittorrent/config:/config:rw
      - ${ROOT}/qbittorrent/downloads:/downloads:rw
      - ${ROOT}/joal/torrents:/completed-torrents-files:rw
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.qbittorrent-ui-redir.rule=Host(`qbittorrent.${DOMAIN_NAME}`)"
      - "traefik.http.routers.qbittorrent-ui-redir.entrypoints=web"
      - "traefik.http.routers.qbittorrent-ui-redir.middlewares=redirect-to-https@file"
      - "traefik.http.services.qbittorrent-service.loadbalancer.server.port=8080"
      - "traefik.http.routers.qbittorrent-ui-router.entrypoints=websecure"
      - "traefik.http.routers.qbittorrent-ui-router.rule=Host(`qbittorrent.${DOMAIN_NAME}`)"
      - "traefik.http.routers.qbittorrent-ui-router.middlewares=gzip-compress@file"
      - "traefik.http.routers.qbittorrent-ui-router.service=qbittorrent-service"
      - "traefik.http.routers.qbittorrent-ui-router.tls=true"
      - "traefik.tcp.services.qbittorrent-tcp-service.loadbalancer.server.port=6881"
      - "traefik.tcp.routers.qbittorrent-tcp-router.entrypoints=tcp-qbittorrent"
      - "traefik.tcp.routers.qbittorrent-tcp-router.rule=HostSNI(`*`)"
      - "traefik.tcp.routers.qbittorrent-tcp-router.service=qbittorrent-tcp-service"
      - "traefik.udp.services.qbittorrent-udp-service.loadbalancer.server.port=6881"
      - "traefik.udp.routers.qbittorrent-udp-router.service=qbittorrent-udp-service"
      - "traefik.udp.routers.qbittorrent-udp-router.entrypoints=udp-qbittorrent"

Nothing to change in this file because every variables are extracted to .env

Done !

⚠️ First launch will take a LONG time because traefik need to chat with letsencrypt to create your certificate. Be patient and dont stop & restart the containers too much before the certificates are generated, otherwise you will hit the letencrypt request limit. Check the traefik logs to follow the progress

After obtaining the certificates you'll be able to reach your services at (assuming you have the domain name example.duckdns.org):

https:traefik.example.duckdns.org (you'll be presented a basic login form, use the basic auth credential generated earlier)
https:joal.example.duckdns.org (connection settings within webui: serverAddress=joal.example.duckdns.org, serverPort=443, path prefix and secret token are the value you haved defined in the .env file)
https:qbitorrent.example.duckdns.org

Going further

If you want to, adding sonarr, radarr, jackett and flaresolver is a matter of second. Simply add those to yout docker-compose:

  sonarr:
    depends_on:
      - traefik
      - qbittorrent
    container_name: sonarr
    image: linuxserver/sonarr:version-3.0.6.1265
    restart: always
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ${ROOT}/sonarr/config:/config:rw
      - /xxxxxxxxxxxxxxxxxxxxxxxxxxx/path/to/your/tv/show/folder:/tv
      - ${ROOT}/qbittorrent/downloads:/downloads
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.sonarr-redir.rule=Host(`sonarr.${DOMAIN_NAME}`)"
      - "traefik.http.routers.sonarr-redir.entrypoints=web"
      - "traefik.http.routers.sonarr-redir.middlewares=redirect-to-https@file"
      - "traefik.http.services.sonarr-service.loadbalancer.server.port=8989"
      - "traefik.http.routers.sonarr-router.entrypoints=websecure"
      - "traefik.http.routers.sonarr-router.rule=Host(`sonarr.${DOMAIN_NAME}`)"
      - "traefik.http.routers.sonarr-router.middlewares=gzip-compress@file"
      - "traefik.http.routers.sonarr-router.service=sonarr-service"
      - "traefik.http.routers.sonarr-router.tls=true"

  radarr:
    depends_on:
      - traefik
      - qbittorrent
    container_name: radarr
    image: linuxserver/radarr:version-3.2.2.5080
    restart: always
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ${ROOT}/radarr/config:/config:rw
      - /xxxxxxxxxxxxxxxxxxxxxxxxxxx/path/to/your/movies/folder:/movies
      - ${ROOT}/qbittorrent/downloads:/downloads
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.radarr-redir.rule=Host(`radarr.${DOMAIN_NAME}`)"
      - "traefik.http.routers.radarr-redir.entrypoints=web"
      - "traefik.http.routers.radarr-redir.middlewares=redirect-to-https@file"
      - "traefik.http.services.radarr-service.loadbalancer.server.port=7878"
      - "traefik.http.routers.radarr-router.entrypoints=websecure"
      - "traefik.http.routers.radarr-router.rule=Host(`radarr.${DOMAIN_NAME}`)"
      - "traefik.http.routers.radarr-router.middlewares=gzip-compress@file"
      - "traefik.http.routers.radarr-router.service=radarr-service"
      - "traefik.http.routers.radarr-router.tls=true"

  flaresolverr:
    container_name: flaresolverr
    image: flaresolverr/flaresolverr:v1.2.9
    restart: always
    network_mode: "service:vpn"
    environment:
      - LOG_LEVEL=info
      - LOG_HTML=false
      - CAPTCHA_SOLVER=none

  jackett:
    depends_on:
      - traefik
      - flaresolverr
    container_name: jackett
    image: linuxserver/jackett:version-v0.18.795
    restart: always
    network_mode: "service:vpn"
    environment:
      - PUID=${PUID}
      - PGID=${PGID}
      - TZ=${TZ}
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - ${ROOT}/jackett/config:/config:rw
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.jackett-redir.rule=Host(`jackett.${DOMAIN_NAME}`)"
      - "traefik.http.routers.jackett-redir.entrypoints=web"
      - "traefik.http.routers.jackett-redir.middlewares=redirect-to-https@file"
      - "traefik.http.services.jackett-service.loadbalancer.server.port=9117"
      - "traefik.http.routers.jackett-router.entrypoints=websecure"
      - "traefik.http.routers.jackett-router.rule=Host(`jackett.${DOMAIN_NAME}`)"
      - "traefik.http.routers.jackett-router.middlewares=gzip-compress@file"
      - "traefik.http.routers.jackett-router.service=jackett-service"
      - "traefik.http.routers.jackett-router.tls=true"