*****     ***    ******     ***    **   **  ***  **  **   **  **   **  **   **
**        ** **   **   **   ** **   **   **  **** **   ** **    ** **    ** ** 
 *****   **   **  ******   **   **  *******  ** ****    ***      ***      ***  
     **  *******  **  **   *******  **   **  **  ***    ***     ** **    ** ** 
 *****   **   **  **   **  **   **  **   **  **   **    ***    **   **  **   **

Cloudflare Tunnels

So, I came across a situation where I wanted to expose web services through my cloudflare-managed domain without opening ports or messing with proxy hosts, etc. I came across info for “Cloudflare Argo Tunnels”, or the cloudflared dameon, and decided to look into it.

Turns out, you can get this as a docker container, though documentation is slim. I’ll be providing how I got it working below.

An Example Service:

Let’s take my jellyfin instance below:

version: "3"
services:
  jellyfin:
    image: jellyfin/jellyfin
    container_name: jellyfin
    user: 0:0
    # network_mode: "host"
    restart: "unless-stopped"
    volumes:
      - ${PWD}/data/config:/config
      - ${PWD}/data/cache:/cache
      - /wolf/media/:/media
    ports:
            - "8096:8096"

So, this service exposes the jellyfin instance to my local network on port 8096. How about getting it on the internet without opening ports?

This is where the next service definition in the file comes in:

  cloudflared:
    container_name: jellyfin_cloudflared
    image: cloudflare/cloudflared:2021.12.3-amd64
    volumes:
      - '${PWD}/cloudflared/:/home/nonroot/.cloudflared/'
    links: 
      - jellyfin
    command: 'tunnel --config /home/nonroot/.cloudflared/config.yml run'

I’ve gone ahead and added the links section so that our cloudflared container can reference the jellyfin container by name and regardless of other host networking.

But, it’s not that easy. This does require some setup. There are a few commands that need to be run in the docker container to get the right configs and certificate from cloudflare.

  1. Create the cloudflared folder in the same directory as the docker-compose.yml, and also do chown 65532:65532 cloudlared to give it the right permissions for the container.
  2. docker-compose run cloudflared tunnel login to authorize this container to login to your cloudflare account and manage one or more of your zones. This’ll give you a URL to open in browser to complete the authorization there.
  3. docker-compose run cloudflared tunnel create media to create a configuration for this tunnel
  4. docker-compose run cloudflared tunnel route dns media media.nyxx.me to create/manage a CNAME in your cloudflare zone to point to this tunnel in cloudflare’s system.

These commands talk with cloudflare to configure the tunnel and create a couple files in the cloudflared folder from the docker-compose.yml file:

  1. 5e6231ce-6b54-11ec-90d6-0242ac120003.json - a json file named with a UUID for the cloudflare tunnel (My 5e62 is just an example, yours will be different). This contains the account ID and tunnel authentication
  2. cert.pem for securing the tunnel with cloudflare.

Now, we’ll want to create a config file ourselves in the cloudflared folder called config.yml:

url: http://jellyfin:8096
tunnel: 5e6231ce-6b54-11ec-90d6-0242ac120003
credentials-file: /home/nonroot/.cloudflared/5e6231ce-6b54-11ec-90d6-0242ac120003.json
  1. url: sets up what endpoint to point the tunnel to
  2. tunnel - this is the UUID of the tunnel, which we get from the filename of the generated .json file. from the docker-compose run commands earlier.
  3. credentials-file: - we point this at the file location inside the container that corresponds to that generated json file. Now, if we just do a docker-compose run in the directory of our docker-compose.yml file, this should spin up a cloudflared docker container with our configurations and get our service online.