diff --git a/.env.sample b/.env.sample index 8530f5b..f7fa027 100644 --- a/.env.sample +++ b/.env.sample @@ -1,18 +1,21 @@ +# General Traefik (reverse proxy) settings TRAEFIK_DOMAIN=mydomain.com ACME_MAIL=my-email@my-provider.com + +# HTTP Auth HTTP_USER=myuser -HTTP_PASSWORD=mypassword_encoded -PORTAINER_ADMIN_PASSWORD=h4ckMePleAse +HTTP_PASSWORD='mypassword_encoded' # Keep these simple quotes! + +# Containers permissions mapping PGID=1000 PUID=1000 -# now these cloufdlare variables are useless -CLOUDFLARE_EMAIL=your@email.com -CLOUDFLARE_API_KEY=your_cloudflare_api_key - # Nextcloud NEXTCLOUD_ADMIN_USER=admin NEXTCLOUD_ADMIN_PASSWORD=nextcloud_admin_password NEXTCLOUD_DB_NAME=nextcloud_db_name NEXTCLOUD_DB_USER=nextcloud -NEXTCLOUD_DB_PASSWORD=nextcloud_db_password \ No newline at end of file +NEXTCLOUD_DB_PASSWORD=nextcloud_db_password + +# Portainer +PORTAINER_ADMIN_PASSWORD=h4ckMePleAse \ No newline at end of file diff --git a/.gitignore b/.gitignore index afa2706..72bae19 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ /config tunnel-options.sh .env +traefik/http_auth backup/ diff --git a/README.md b/README.md index 71f3b2c..d89e463 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ seedbox and personal media server. | Netdata | netdata.yourdomain.com | [netdata/netdata](https://hub.docker.com/r/netdata/netdata) | *latest* | Server monitoring | | Duplicati | duplicati.yourdomain.com | [linuxserver/duplicati](https://hub.docker.com/r/linuxserver/duplicati)| *latest* | Backups | -The front-end reverse proxy (Traefik - *version1 **v1.7-alpine***) routes based on the lowest level subdomain +The front-end reverse proxy (Traefik - **check the next section if you have already the seedbox with Traefik v1**) routes based on the lowest level subdomain (e.g. `deluge.example.com` would route to deluge). Since this is how the router works, it is recommended for you to get a top level domain. If you do not have one, you can edit your domains locally by changing your hosts file or use a @@ -32,6 +32,33 @@ Traefik takes care of valid Let's Encrypt certificates and auto-renewal. Note: Plex is also available directly through the `32400` port without going through the reverse proxy. +## September 2020 - Upgrade to Traefik v2 instructions + +Before upgrading Traefik to version 2, please check the following: + +- In this repo, Traefik v2 upgrade is as seamless as possible (same environment variables than before, out-of-the-box config file...). +- **First, ``git pull`` to grab the latest code.** +- The ``HTTP_PASSWORD`` variable now must be simple-quoted in the .env file. See the updated ``.env.sample`` file (which has also been reorganized) +- Run ``init.sh`` in order to create required Docker objects (network name has changed). +- You can update your acme.json to a Traefik v2-compliant one by doing the following (before launching Traefik v2): + +```sh +mkdir -p /tmp/migration +cd /tmp/migration +sudo cp /opt/traefik/acme.json . +sudo chmod 775 /tmp/migration/acme.json +# Do *NOT* forget the --resolver at the end! (le = Let's Encrypt resolver, see traefik/traefik.yml) +docker run --rm -v ${PWD}:/data -w /data containous/traefik-migration-tool acme -i acme.json -o acme2.json --resolver le +mkdir -p /data/config/traefik +sudo cp acme2.json /data/config/traefik/acme.json +sudo chmod 600 /data/config/traefik/acme.json +# When you already have a backup! +sudo rm -rf /opt/traefik /tmp/migration +``` + +- As from Traefik v2, as Http Authentication is now possible on the Traefik console, the latter is enabled at ``traefik.yourdomain.com``. +- After all this, you can simply do: ``./update-all.sh``! VoilĂ ! + ## Dependencies - [Docker](https://github.com/docker/docker) >= 1.13.0 diff --git a/docker-compose.yml b/docker-compose.yml index b962570..1e74ae4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,30 +2,36 @@ version: '3' services: traefik: - image: traefik:v1.7-alpine + image: traefik container_name: traefik restart: always - networks: - - webgateway - command: --acme.email=${ACME_MAIL} --docker.domain=${TRAEFIK_DOMAIN} #--acme.dnschallenge=true --acme.dnschallenge.provider="cloudflare" --acme.dnschallenge.delaybeforecheck=300 + command: --certificatesresolvers.le.acme.email=${ACME_MAIL} ports: - "80:80" - "443:443" - #- "8080:8080" - # environment: - # - CF_API_EMAIL=${CLOUDFLARE_EMAIL} - # - CF_API_KEY=${CLOUDFLARE_API_KEY} volumes: - /var/run/docker.sock:/var/run/docker.sock - - ./traefik.toml:/traefik.toml - - /opt/traefik/acme.json:/acme.json + - ./traefik:/etc/traefik + - configtraefik:/config + labels: + - "traefik.enable=true" + # HTTP to HTTPS redirection + - "traefik.http.routers.http_catchall.rule=HostRegexp(`{any:.+}`)" + - "traefik.http.routers.http_catchall.entrypoints=insecure" + - "traefik.http.routers.http_catchall.middlewares=https_redirect" + - "traefik.http.middlewares.https_redirect.redirectscheme.scheme=https" + - "traefik.http.middlewares.https_redirect.redirectscheme.permanent=true" + # Docker labels for enabling Traefik dashboard + - "traefik.http.routers.traefik.rule=Host(`traefik.${TRAEFIK_DOMAIN}`)" + - "traefik.http.routers.traefik.entrypoints=secure" + - "traefik.http.routers.traefik.service=api@internal" + - "traefik.http.routers.traefik.tls.certresolver=le" + - "traefik.http.routers.traefik.middlewares=common-auth@file" deluge: image: linuxserver/deluge container_name: deluge restart: always - networks: - - web volumes: - torrents:/torrents - configdeluge:/config @@ -35,18 +41,16 @@ services: - PUID=${PUID} - TZ=Europe/Paris labels: - - 'traefik.backend=deluge' - - 'traefik.port=8112' - - 'traefik.frontend.rule=Host:deluge.${TRAEFIK_DOMAIN}' - - 'traefik.frontend.auth.basic.users=${HTTP_USER}:${HTTP_PASSWORD}' - - 'traefik.enable=true' + - "traefik.enable=true" + - "traefik.http.routers.deluge.rule=Host(`deluge.${TRAEFIK_DOMAIN}`)" + - "traefik.http.routers.deluge.entrypoints=secure" + - "traefik.http.routers.deluge.tls.certresolver=le" + - "traefik.http.routers.deluge.middlewares=common-auth@file" plex: image: linuxserver/plex container_name: plex restart: always - networks: - - web ports: - "32400:32400" - "32400:32400/udp" @@ -62,20 +66,17 @@ services: - PUID=${PUID} - TZ=Europe/Paris - VERSION=latest - #- VERSION=1.14.0.5470-9d51fdfaa labels: - - 'traefik.backend=plex' - - 'traefik.port=32400' - - 'traefik.frontend.rule=Host:plex.${TRAEFIK_DOMAIN}' - #- 'traefik.frontend.auth.basic.users=${HTTP_USER}:${HTTP_PASSWORD}' - - 'traefik.enable=true' + - "traefik.enable=true" + - "traefik.http.services.plex-seedbox.loadbalancer.server.port=32400" + - "traefik.http.routers.plex.rule=Host(`plex.${TRAEFIK_DOMAIN}`)" + - "traefik.http.routers.plex.entrypoints=secure" + - "traefik.http.routers.plex.tls.certresolver=le" jackett: image: linuxserver/jackett container_name: jackett restart: always - networks: - - web volumes: - config:/config - torrents:/downloads @@ -85,18 +86,16 @@ services: - PUID=${PUID} - TZ=Europe/Paris labels: - - 'traefik.backend=jackett' - - 'traefik.port=9117' - - 'traefik.frontend.rule=Host:jackett.${TRAEFIK_DOMAIN}' - - 'traefik.frontend.auth.basic.users=${HTTP_USER}:${HTTP_PASSWORD}' - - 'traefik.enable=true' + - "traefik.enable=true" + - "traefik.http.routers.jackett.rule=Host(`jackett.${TRAEFIK_DOMAIN}`)" + - "traefik.http.routers.jackett.entrypoints=secure" + - "traefik.http.routers.jackett.tls.certresolver=le" + - "traefik.http.routers.jackett.middlewares=common-auth@file" sonarr: image: linuxserver/sonarr:preview container_name: sonarr restart: always - networks: - - web volumes: - configsonarr:/config - torrents:/torrents @@ -106,18 +105,16 @@ services: - PUID=${PUID} - TZ=Europe/Paris labels: - - 'traefik.backend=sonarr' - - 'traefik.port=8989' - - 'traefik.frontend.rule=Host:sonarr.${TRAEFIK_DOMAIN}' - - 'traefik.frontend.auth.basic.users=${HTTP_USER}:${HTTP_PASSWORD}' - - 'traefik.enable=true' + - "traefik.enable=true" + - "traefik.http.routers.sonarr.rule=Host(`sonarr.${TRAEFIK_DOMAIN}`)" + - "traefik.http.routers.sonarr.entrypoints=secure" + - "traefik.http.routers.sonarr.tls.certresolver=le" + - "traefik.http.routers.sonarr.middlewares=common-auth@file" radarr: image: linuxserver/radarr container_name: radarr restart: always - networks: - - web volumes: - configradarr:/config - torrents:/torrents @@ -127,18 +124,16 @@ services: - PUID=${PUID} - TZ=Europe/Paris labels: - - 'traefik.backend=radarr' - - 'traefik.port=7878' - - 'traefik.frontend.rule=Host:radarr.${TRAEFIK_DOMAIN}' - - 'traefik.frontend.auth.basic.users=${HTTP_USER}:${HTTP_PASSWORD}' - - 'traefik.enable=true' + - "traefik.enable=true" + - "traefik.http.routers.radarr.rule=Host(`radarr.${TRAEFIK_DOMAIN}`)" + - "traefik.http.routers.radarr.entrypoints=secure" + - "traefik.http.routers.radarr.tls.certresolver=le" + - "traefik.http.routers.radarr.middlewares=common-auth@file" bazarr: image: linuxserver/bazarr container_name: bazarr restart: always - networks: - - web volumes: - torrents:/torrents - configbazarr:/config @@ -147,18 +142,16 @@ services: - PUID=${PUID} - TZ=Europe/Paris labels: - - 'traefik.backend=bazarr' - - 'traefik.port=6767' - - 'traefik.frontend.rule=Host:bazarr.${TRAEFIK_DOMAIN}' - - 'traefik.frontend.auth.basic.users=${HTTP_USER}:${HTTP_PASSWORD}' - - 'traefik.enable=true' + - "traefik.enable=true" + - "traefik.http.routers.bazarr.rule=Host(`bazarr.${TRAEFIK_DOMAIN}`)" + - "traefik.http.routers.bazarr.entrypoints=secure" + - "traefik.http.routers.bazarr.tls.certresolver=le" + - "traefik.http.routers.bazarr.middlewares=common-auth@file" lidarr: image: linuxserver/lidarr:preview container_name: lidarr restart: always - networks: - - web volumes: - configlidarr:/config - torrents:/torrents @@ -167,18 +160,16 @@ services: - PUID=${PUID} - TZ=Europe/Paris labels: - - 'traefik.backend=lidarr' - - 'traefik.port=8686' - - 'traefik.frontend.rule=Host:lidarr.${TRAEFIK_DOMAIN}' - - 'traefik.frontend.auth.basic.users=${HTTP_USER}:${HTTP_PASSWORD}' - - 'traefik.enable=true' + - "traefik.enable=true" + - "traefik.http.routers.lidarr.rule=Host(`lidarr.${TRAEFIK_DOMAIN}`)" + - "traefik.http.routers.lidarr.entrypoints=secure" + - "traefik.http.routers.lidarr.tls.certresolver=le" + - "traefik.http.routers.lidarr.middlewares=common-auth@file" tautulli: image: linuxserver/tautulli container_name: tautulli restart: always - networks: - - web volumes: - configtautulli:/config - config:/logs:ro # Inside of tautulli, bind to logs via "/logs/Plex Media Server/Logs" @@ -187,16 +178,15 @@ services: - PUID=${PUID} - TZ=Europe/Paris labels: - - 'traefik.tautulli.backend=tautulli' - - 'traefik.tautulli.port=8181' - - 'traefik.tautulli.frontend.rule=Host:tautulli.${TRAEFIK_DOMAIN}' - - 'traefik.enable=true' + - "traefik.enable=true" + - "traefik.http.routers.tautulli.rule=Host(`tautulli.${TRAEFIK_DOMAIN}`)" + - "traefik.http.routers.tautulli.entrypoints=secure" + - "traefik.http.routers.tautulli.tls.certresolver=le" jdownloader: image: jlesage/jdownloader-2 container_name: jdownloader - networks: - - web + restart: unless-stopped volumes: - configjdownloader:/config - downloads:/output @@ -205,18 +195,17 @@ services: - GROUP_ID=${PGID} - TZ=Europe/Paris labels: - - 'traefik.backend=jdownloader' - - 'traefik.port=5800' - - 'traefik.frontend.rule=Host:jdownloader.${TRAEFIK_DOMAIN}' - - 'traefik.frontend.auth.basic.users=${HTTP_USER}:${HTTP_PASSWORD}' - - 'traefik.enable=true' + - "traefik.enable=true" + - "traefik.http.services.jdownloader-seedbox.loadbalancer.server.port=5800" + - "traefik.http.routers.jdownloader.rule=Host(`jdownloader.${TRAEFIK_DOMAIN}`)" + - "traefik.http.routers.jdownloader.entrypoints=secure" + - "traefik.http.routers.jdownloader.tls.certresolver=le" + - "traefik.http.routers.jdownloader.middlewares=common-auth@file" nextcloud: image: wonderfall/nextcloud container_name: nextcloud restart: always - networks: - - web volumes: - confignextcloud:/config - nextclouddata:/data @@ -234,10 +223,10 @@ services: - DB_USER=${NEXTCLOUD_DB_USER} - DB_PASSWORD=${NEXTCLOUD_DB_PASSWORD} labels: - - 'traefik.backend=nextcloud' - - 'traefik.port=8888' - - 'traefik.frontend.rule=Host:nextcloud.${TRAEFIK_DOMAIN}' - - 'traefik.enable=true' + - "traefik.enable=true" + - "traefik.http.routers.nextcloud.rule=Host(`nextcloud.${TRAEFIK_DOMAIN}`)" + - "traefik.http.routers.nextcloud.entrypoints=secure" + - "traefik.http.routers.nextcloud.tls.certresolver=le" portainer: image: portainer/portainer @@ -245,22 +234,18 @@ services: restart: always volumes: - /var/run/docker.sock:/var/run/docker.sock - networks: - - web command: --admin-password ${PORTAINER_ADMIN_PASSWORD} --host=unix:///var/run/docker.sock labels: - - 'traefik.backend=portainer' - - 'traefik.port=9000' - - 'traefik.frontend.rule=Host:portainer.${TRAEFIK_DOMAIN}' - - 'traefik.enable=true' + - "traefik.enable=true" + - "traefik.http.routers.portainer.rule=Host(`portainer.${TRAEFIK_DOMAIN}`)" + - "traefik.http.routers.portainer.entrypoints=secure" + - "traefik.http.routers.portainer.tls.certresolver=le" netdata: image: netdata/netdata restart: always container_name: netdata hostname: netdata.${TRAEFIK_DOMAIN} - networks: - - web environment: PGID: 999 cap_add: @@ -272,18 +257,16 @@ services: - /sys:/host/sys:ro - /var/run/docker.sock:/var/run/docker.sock:rw labels: - - 'traefik.backend=netdata' - - 'traefik.port=19999' - - 'traefik.frontend.rule=Host:netdata.${TRAEFIK_DOMAIN}' - - 'traefik.frontend.auth.basic.users=${HTTP_USER}:${HTTP_PASSWORD}' - - 'traefik.enable=true' + - "traefik.enable=true" + - "traefik.http.routers.netdata.rule=Host(`netdata.${TRAEFIK_DOMAIN}`)" + - "traefik.http.routers.netdata.entrypoints=secure" + - "traefik.http.routers.netdata.tls.certresolver=le" + - "traefik.http.routers.netdata.middlewares=common-auth@file" duplicati: image: linuxserver/duplicati container_name: duplicati restart: unless-stopped - networks: - - web environment: - PUID=${PUID} - PGID=${PGID} @@ -293,18 +276,17 @@ services: - backups:/backups - alldata:/source labels: - - 'traefik.backend=duplicati' - - 'traefik.port=8200' - - 'traefik.frontend.rule=Host:duplicati.${TRAEFIK_DOMAIN}' - - 'traefik.frontend.auth.basic.users=${HTTP_USER}:${HTTP_PASSWORD}' - - 'traefik.enable=true' + - "traefik.enable=true" + - "traefik.http.routers.duplicati.rule=Host(`duplicati.${TRAEFIK_DOMAIN}`)" + - "traefik.http.routers.duplicati.entrypoints=secure" + - "traefik.http.routers.duplicati.tls.certresolver=le" + - "traefik.http.routers.duplicati.middlewares=common-auth@file" + +networks: + default: + external: + name: "traefik-network" -networks: - webgateway: - driver: bridge - web: - external: - name: seedbox_webgateway volumes: alldata: driver: local-persist @@ -314,6 +296,10 @@ volumes: driver: local-persist driver_opts: mountpoint: /data/config + configtraefik: + driver: local-persist + driver_opts: + mountpoint: /data/config/traefik configplex: driver: local-persist driver_opts: diff --git a/init.sh b/init.sh index 5b3e299..160adec 100755 --- a/init.sh +++ b/init.sh @@ -1,8 +1,10 @@ #!/bin/bash echo "[$0] Initializing..." -sudo mkdir /opt/traefik -sudo touch /opt/traefik/acme.json && sudo chmod 600 /opt/traefik/acme.json -cp .env.sample .env -echo "[$0] Please edit .env file" +docker network create traefik-network 2&>1 || true +if [[ ! -f .env ]]; then + cp .env.sample .env + echo "[$0] Please edit .env file" +fi +echo "[$0] Done." exit 0 \ No newline at end of file diff --git a/traefik.toml b/traefik.toml deleted file mode 100644 index 078fb95..0000000 --- a/traefik.toml +++ /dev/null @@ -1,40 +0,0 @@ -#https://docs.traefik.io/toml/ -#https://docs.traefik.io/user-guide/examples/ -################################################################ -# Global configuration -################################################################ -logLevel = "WARNING" -defaultEntryPoints = ["http", "https"] -InsecureSkipVerify = true - -[entryPoints] - [entryPoints.http] - address = ":80" - [entryPoints.http.redirect] - entryPoint = "https" - [entryPoints.https] - address = ":443" - [entryPoints.https.tls] - -[retry] - -[acme] -email = "overriden@in-traefik.yml" -storage = "acme.json" -entryPoint = "https" -onHostRule = true -acmeLogging = true -[acme.httpChallenge] - entryPoint = "http" - -################################################################ -# Docker configuration backend -################################################################ -[docker] -endpoint = "unix:///var/run/docker.sock" -domain = "mydomain.com" -watch = true -exposedByDefault = false - -[file] -watch = true diff --git a/traefik/file-provider.yml b/traefik/file-provider.yml new file mode 100644 index 0000000..7978444 --- /dev/null +++ b/traefik/file-provider.yml @@ -0,0 +1,5 @@ +http: + middlewares: + common-auth: + basicAuth: + usersFile: "/etc/traefik/http_auth" \ No newline at end of file diff --git a/traefik/traefik.yml b/traefik/traefik.yml new file mode 100644 index 0000000..cc39c65 --- /dev/null +++ b/traefik/traefik.yml @@ -0,0 +1,24 @@ +api: + dashboard: true + +providers: + docker: + endpoint: "unix:///var/run/docker.sock" + network: "traefik-network" + exposedByDefault: false # Only expose explicitly enabled containers + file: + filename: /etc/traefik/file-provider.yml + +entryPoints: + insecure: + address: ":80" + secure: + address: ":443" + +certificatesResolvers: + le: + acme: + storage: /config/acme.json + httpChallenge: + # used during the challenge + entryPoint: insecure diff --git a/update-all.sh b/update-all.sh index a00d6ad..17d6098 100755 --- a/update-all.sh +++ b/update-all.sh @@ -1,5 +1,9 @@ #!/bin/bash +# Create/update http_auth file according to values in .env file +source .env +echo "${HTTP_USER}:${HTTP_PASSWORD}" > traefik/http_auth + echo "[$0] ***** Pulling all images... *****" docker-compose pull echo "[$0] ***** Recreating containers if required... *****"