Jump to content

Tips to setting up Traefik using docker with SSL and IP Whitelist!


Recommended Posts

Posted

I have been using nginx-proxy-manager to manage my reverse proxy needs and it was going fine: it has a super nice GUI and it's super beginner friendly. I've since learnt, however, that it is managed by just one dev and there's a TON of unattended CVEs and unpatched security holes https://youtu.be/uaixCKTaqY0 First of all, while most tutorials were unhelpful on their own, this one was kind of ok as a reference (but not enough), but it might help if you are confused: https://www.youtube.com/watch?v=wLrmmh1eI94

I'll explain things along the way, but I'll try to cover these topics:

1) Set up traefik on docker
2) Reverse proxy your docker services/apps
3) Add SSL and redirect http to https
4) Whitelist your LAN IPs so as to block external IPs from accesing the service via the hostname
5) Reverse proxy your non docker services/apps
So, this will work for Emby but will also work for every other service/app you got going there.  I'M ASSUMING YOU HAVE SOME MINIMUM KNOWLEDGE ABOUT DOCKER AND DOCKER-COMPOSE

Also, I will not go deep into how this works but rather how to basically set it up and you can go down into the rabbit hole of services, providers, routers and middleware yourself if you need to know more.

So, let's start by setting up traefik on docker.  One thing you need to know that will save you a ton of headaches is that there's multiple ways of configuring traefik but they are all mutually exclusive.  What I mean by this is, you can configure it using your docker-compose file or your docker commands OR you could use a configuration file for example, but you can't use both. One will work but it will ignore the other one.  Also, there's dynamic and static configuration.  Static configuration is everything that relates to Traefik itself and it's stored in the traefik.yml file. Dynamic is everything that is related to things you can change on the fly for different services.  This sounds like a lot but let's get started and you'll figure it out.  The following is a docker-compose file with just traefik set on it, we will add services to it as we get things going.

version: '3.6'

services:
    traefik:
        container_name: traefik
        image: traefik:latest
        ports:
          - "80:80" 
          - "443:443" 
          # Left side is host side, right side container side.  You HAVE to have 80 and 443 port forwarded to this ip for this to work.  You can also modify the left side to whatever you want as long as you port forward 80 and 443 to that IP and whatever ports you choose to put there
          - "8080:8080" #GUI and Debug only, you can comment it once you have everything working
        volumes:
          # So that Traefik can listen to the Docker events
          - /var/run/docker.sock:/var/run/docker.sock
          - ${DOCKERCONFIG}/traefik:/etc/traefik
        labels:
          - "traefik.enable=true"
        networks:
          - traefik_network

networks:
    traefik_network:
        driver: bridge

This is all you need to have traefik running in docker-compose, replace ${DOCKERCONFIG} with whatever directory you want for the traefik files. Also notice that I created a network, this is IMPORTANT, you need to add every container to this network so traefik is able to see them.

Now, let's create a traefik.yml that we will store in ${DOCKERCONFIG}/traefik/

global:
    checkNewVersion: true
    sendAnonymousUsage: false  
#above options are self explanatory and you can delete them if you want to
# log:
    # level: DEBUG
    # filePath: /etc/traefik/logs/traefik.log
#you can uncomment the DEBUG logs if needed, same with the accessLogs
accessLog:
    filePath: /etc/traefik/logs/traefik-access.log 
    fields:
        names:
            StartUTC: drop
api:
    dashboard: true
    insecure: true
#above options activate the dashboard which will be accesible at localhost:8080 or IP:8080, useful for debugging purposes at first, strongly recommend disabling it once everything is working

entryPoints:
    web:
        address: ":80"
        http:
            redirections:
                entryPoint:
                    to: websecure
                    scheme: https
    websecure:
        address: ":443"
    
providers:
    docker:
        exposedByDefault: false
    file:
        # watch for dynamic configuration changes
        directory: /etc/traefik
        watch: true

certificatesResolvers:
    letsencrypt:
        acme:
            email: [YOUR EMAIL]
            storage: /etc/traefik/letsencrypt/acme.json
            tlschallenge: true
      
      

Ok, so there's a lot to unpack here.  First few things are basic setup for optional things you can safely ignore but overall are pretty much self explanatory. After that it starts getting weird, so let's dig in.

entrypoints are basically what we want to listen to as an entry to our homelab/server/etc.  In this case, we are saying everything that comes to port 80 or 443 and giving each one of them a name, web and websecure respectively. for web(port 80) we are also configuring a redirection to port 443, so that everything that comes on http will be redirected to https (where we will have our signed certificates). This is optional and you can comment the following if you don't want redirection, but I would strongly recommend you keep it.

http:
   redirections:
       entryPoint:
           to: websecure
           scheme: https

After entrypoints, we have providers.  providers are basically the sources of our "routers" (I'll explain it later) and "services".  Basically means, "where do I find my config info".  In this case, I've added docker as a source for services and routers and added a config line that will not add every container by default, so you'll have to enable the ones you want in the docker-compose file. I've also added "file" as a different provider of configurations.  This means I can create files with configs and it will read from them as long as those are in the traefik directory.  This will be useful at the end when we want to add services that are not hosted using docker.

Finally (for now) for our traefik.yml we have certificateresolvers, in this case, let's encrypt.  This is the basic config so you can auto generate certificates for your services that will auto renew.  You only need to do two things.  Complete  with your e-mail AND create the folder letsencrypt inside your traefik config folder.

At this point, you can run traefik and if everything is right you should be able to log into the dashboard at localhost:8080 or IP:8080.  If that happens, traefik is working and we can now start adding our services and routers.

For our first service, I'll use something not emby, for example, let's say radarr.

Add the following to your services now in docker-compose.yml:

    radarr:
#       privileged: true
        container_name: radarr
        environment:
            - PUID=${PUID}
            - PGID=${PGID}
            - TZ=${TZ}
        ports:
            - '7878:7878' #not needed anymore, left there for legacy reasons
        labels:
            - "traefik.enable=true"
            - "traefik.http.routers.radarr.tls=true"
            - "traefik.http.routers.radarr.rule=Host(`radarr.myhostname.net`)"
            - "traefik.http.routers.radarr.entrypoints=web,websecure"
            - "traefik.http.routers.radarr.tls.certresolver=letsencrypt"    
        volumes:
            - '${DOCKERCONFIG}/radarr:/config'
            - '${MEDIA}:/media'
        networks:
            - traefik_network
        restart: unless-stopped
        image: 'linuxserver/radarr:latest'

Ok, so there's two things added to the basic radarr config: the labels and the network. Network I already explained: you need that so it shares a network with Traefik and it can access it.  Regarding the labels.  Remember we set docker as a "provider"? That means traefik will read those labels and autoconfig the reverse proxy for radarr. So, from top to bottom:

1)traefik.enable -> enables the reverse proxy for this container
2)traefik.http.routers.radarr.tls=true -> enables the let's encrypt signed certificates
3)traefik.http.routers.radarr.rule=Host(`radarr.myhostname.net`) -> basically sets the hostname it will use so that when you go to https://radarr.myhostname.net it will go to radarr
4)traefik.http.routers.radarr.entrypoints=web,websecure -> this are the entrypoints we defined, we are basically saying: "Please listen to port 80 and 443"
5)traefik.http.routers.radarr.tls.certresolver=letsencrypt ->use let's encrypt (which we defined earlier as a certificate resolver)

Now, if you do docker-compose up -d radarr you can go to radarr.myhostname.net and it will work! You will notice we never specified neither ip nor port to traefik: this is not needed, traefik will get it from the container itself.  You can even remove the port definition and it will still work!  This works for 90% of the containers, some (like Emby though) need to have their ports explicitly assigned so let's do a really simple barebones emby install:

    emby:
        container_name: emby
        networks:
            - traefik_network
        environment:
            PUID: ${PUID}
            PGID: ${PGID}
            GIDLIST: 0
            TZ: ${TZ}
        volumes:
            - ${DOCKERCONFIG}/emby:/config
            - ${MEDIA}:/media
        ports:
            - '8096:8096' # HTTP port
            - '8920:8920' # HTTPS port
            - '7359:7359'
            - '1900:1900'
        labels:
            - "traefik.enable=true"
            - "traefik.http.routers.emby.tls=true"
            - "traefik.http.routers.emby.rule=Host(`myhostname.net`,`emby.myhostname.net`,`www.myhostname.net`)"
            - "traefik.http.routers.emby.entrypoints=web,websecure"
            - "traefik.http.routers.emby.tls.certresolver=letsencrypt"
            - "traefik.http.services.emby.loadbalancer.server.port=8096"       
        restart: unless-stopped
        image: emby

Ok, so you'll notice two things have changed.  I've added more than one hostname (three, myhostname.net, emby.myhostname.net and www.myhostname.net) and all of this will redirect to emby. Also, the last label manually assigns port 8096.  Don't worry,  Traefik will handle the https.

Now that we have two services running with reverse proxy, let's add a whitelist to radarr, only allow access to local IPs.  To do this we need to create a "middleware": this is something that goes between the external connection and the service.  In this case, it filters request by IP. So we open the traefik.yml file and we add this:

global:
    checkNewVersion: true
    sendAnonymousUsage: false  
#above options are self explanatory and you can delete them if you want to
# log:
    # level: DEBUG
    # filePath: /etc/traefik/logs/traefik.log
#you can uncomment the DEBUG logs if needed, same with the accessLogs
accessLog:
    filePath: /etc/traefik/logs/traefik-access.log 
    fields:
        names:
            StartUTC: drop
api:
    dashboard: true
    insecure: true
#above options activate the dashboard which will be accesible at localhost:8080 or IP:8080, useful for debugging purposes at first, strongly recommend disabling it once everything is working

entryPoints:
    web:
        address: ":80"
        http:
            redirections:
                entryPoint:
                    to: websecure
                    scheme: https
    websecure:
        address: ":443"
    
providers:
    docker:
        exposedByDefault: false
http:
    middlewares:
        lanwhitelist:
            ipWhitelist:
                sourceRange: 127.0.0.1/32, 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8

certificatesResolvers:
    letsencrypt:
        acme:
            email: [YOUR EMAIL]
            storage: /etc/traefik/letsencrypt/acme.json
            tlschallenge: true
      

We added this small section which will create an ip whitelist called lan whitelist.  I've set a few ranges in there but you can customize these of course:

http:
    middlewares:
        lanwhitelist:
            ipWhitelist:
                sourceRange: 127.0.0.1/32, 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8

Now we need to tell radarr to use that middleware, so we add the following label to the list:

- "traefik.http.routers.bazarr.middlewares=lanwhitelist@file"

This basically says: use the middleware called "lanwhitelist" that is stored in the config files.  If you now run radarr again, you'll see that you can access it via LAN but not WAN, which is cool!

 

Finally, suppose you have ANOTHER server on another IP or something that is running on the same server but not on docker, or something that is running on docker but using network_mode: host.  Basically any other server you know the IP:PORT.  This involves a few more things but it is definitely not hard.  Remember we added "file" as a provider? Let's suppose we have a service (called "myservice")at YOURIP:YOURPORT.   Create inside the config folder a config.yml file and add the following:

http:
    routers:
        myservice:
            entryPoints:
                - web
                - websecure
            rule: Host(`myservice.myhostname.net`)
            tls:
                certResolver: letsencrypt
            middlewares:
                - lanwhitelist@file
            service: myservice             
    services:
        myservice:
            loadbalancer:
                servers:
                    - url: http://YOURIP:YOURPORT

I won't go to deep into what's going on here but you can see the syntax is actually the same as in the labels.  What we've done here (and what we also did, unknowingly, when setting up the docker containers) is create a router and a service both named "myservice". The router basically defines how traefik should handle stuff that is incoming from WAN. So that would mean defining the entrypoints (we are using the same ones we created before, web and websecure for 80 and 443 respectively) what hostname it should listen to, if it should generate and use a certificate, the middleware (in this case, the optional ip whitelist) and to which service should it point (in this case, myservice).  After that, we define the services myservice and inside that we basically just added the url for that particular service.

That's IT! now you have traefik configured, with http redirect to https, with SSL enabled, with valid certificates and with an optional IP whitelist for both docker services AND other stuff you might have on your network.

 

I know this is a LOT but if you need any help just let me know and I'll do what I can.  If you have no idea what docker is or some of the super basic concepts, I'm not going to teach you everything from scratch but if you get the basic gist and you can't get it to work for some reason, I'm not an expert but I'm open to trying to help. 

Hope this is useful!

PS: Luke or any other mod/admin that might read this, it would be great for guides and docker-compose files if there's by any chance a plugin for the forum that adds syntax highlighting for yaml code that is used in docker-compose and several other apps.  I'm guessing it exists but I obviously have no idea if it actually does and how easy it might be to add, but hey, never hurts to ask.

  • Thanks 1
  • 3 months later...
Posted

Hey @Ikariothis is great, however I am having such a hard time getting this to work for me. I have the labels correct etc, treafik reverse proxy works with all other services except Emby. 

What configurations do you do if any within Emby itself in addition to what you built above?

Posted (edited)

I don't think I changed anything on my Emby config when I migrated from NPM to Traefik but I might have done something before using NPM and I can't recall. Give me a few minutes and I'll try to get you some screenshots of what my Emby network config look like.

When you say it doesn't work with Emby, what do you mean? What error do you get, have you checked the logs? is there a chance that you asked for too many certs in the production caServer and now you are timed out for a few days?

 

EDIT: Added screenshot with my network settings

emby network settings.jpg

Edited by Ikario
added settings screenshot
  • Like 1
Posted

@Ikariothanks for the follow up, and thanks for confirming your settings. That is what I am running as well. 

As it turns out, I found the root cause of my issue and the certs generating. https://www.cloudflarestatus.com/

I am using DNS challenge with cloudflare and this happens with an API token to access my domain DNS. Both services toast and have been the basically 2 days I have been banging my head on this.

Screenshot 2023-11-03 at 3.49.41 PM.png

  • Thanks 1
Posted

Yeah, similar thing happened to me so I feel you but I'm glad you know what it is now  and honestly that this guide helped somebody!

  • Thanks 1

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...