Amateur docker-compose SSL configuration (self signed, manual let's encrypt)

I’ve recently struggled to get SSL working on my TT-RSS docker-compose install and wanted to give back to the community by writing about my experiences for anyone else who wants to follow in my footsteps. Please note that I’m an amateur and make no claims about the soundness of my advice. I’m also writing this after the fact and trying to recreate some of the steps from memory so I may have missed something.

SELF SIGNED

The ttrss-docker/docker-compose.yml file defines a web container (no SSL) and a web-ssl container (SSL). Unfortunately for anyone setting up TT-RSS on a private network the web-ssl container is configured to use Let’s Encrypt with a public address. If your TT-RSS server is not on the open internet the web-ssl container is a dead end.

So, I decided to do all my configuration in the existing web container.

The first thing you need to do is generate your SSL certificate. You can find a thousand guides for this on the internet but you should make sure your certificate’s common name is the IP of the TT-RSS server and that your certificate contains the subjectAltName extension set to the same IP. For example (using openssl to generate the certificate):

openssl req -new -newkey rsa:4096 -x509 -sha256 -days 365 -nodes -addext “subjectAltName = IP:x.x.x.x” -out fullchain.pem -keyout privkey.pem

Where x.x.x.x is the IP of your TT-RSS server. Next you need give the web container access to your certificate. Put your certificate files (fullchain.pem and privkey.pem) somewhere, e.g. /home/user/ssl, then edit ttrss-docker/docker-compose.yml and add a volume under the web container like so:

    volumes:
      - app:/var/www/html:ro
      - /home/user/ssl:/ssl:ro

This makes the /home/usr/ssl directory accessible to the web container (located at /ssl inside the container, “ro” means read only).

Next you need to tell the web container where the certificate is. Edit ttrss-docker/web/Caddyfile and add this immediately after 0.0.0.0 (so it should be the second line):

tls /ssl/fullchain.pem /ssl/privkey.pem

The Caddyfile is copied into the web container whenever the container is built and contains the configuration for Caddy, the webserver TT-RSS uses. However, Caddy won’t serve this certificate for every request, only for requests matching the certificate’s SNI (Server Name Indication). My web browser (Firefox) doesn’t send an SNI when accessing TT-RSS via IP so it fails. This can be solved. Edit ttrss-docker/web/Dockerfile and add this to the end:

CMD ["--conf", "/etc/Caddyfile", "--log", "stdout", "--agree=$ACME_AGREE", "--default-sni", "x.x.x.x"]

Where, again, x.x.x.x is the IP of your TT-RSS server. This tells the container to start Caddy with the default-sni parameter so that if a request is received without an SNI it checks the certificate associated with the IP you specify here. The other parameters are straight out of the parent container, abiosoft/caddy.

This should be everything. The Caddyfile and Dockerfile are only used when building the container so you’ll have to do a rebuild with docker-compose up --build -d but then it should work.

Note that as of a few days ago the TT-RSS Android app stopped accepting self signed certificates of any kind but you can get around this by installing the certificate into your Android phone manually. Just go to Settings > Security > Advanced > Encryption & credentials > Install from storage and give it your fullchain.pem file.

MANUAL LET’S ENCRYPT

If you don’t want your TT-RSS server to be web accessible but you own a domain name you can still make use of Let’s Encrypt certificates to get rid of all those pesky security warnings and exceptions.

First, get your Let’s Encrypt certificate with certbot. Use an unused subdomain off the domain name you own. Since your server is not web accessible you can’t use an HTTP challenge so you’ll have to use a DNS challenge instead. I used certbot in manual mode for this. There are tons of guides to do so and it’s even possible to automate it with a certbot plugin. I’ll leave that up to you.

Edit ttrss-docker/docker-compose.yml and add a few more volumes:

    volumes:
      - app:/var/www/html:ro
      - /etc/letsencrypt/live/your.domain.name:/letsencrypt/live/your.domain.name:ro
      - /etc/letsencrypt/archive/your.domain.name:/letsencrypt/archive/your.domain.name:ro

You have to add both of these because the certificates under the live directory link to the certificates under the archive directory.

Edit ttrss-docker/web/Caddyfile and change the tls line:

tls /letsencrypt/live/your.domain.name/fullchain.pem /letsencrypt/live/your.domain.name/privkey.pem

Edit ttrss-docker/web/Dockerfile and change the CMD line:

CMD ["--conf", "/etc/Caddyfile", "--log", "stdout", "--agree=$ACME_AGREE", "--default-sni", "your.domain.name"]

Note: You may not need the custom Dockerfile anymore. I think it may only come into play when accessing TT-RSS via IP. I’m too exhausted to investigate any further.

Finally you need to make your computer think your.domain.name is your TT-RSS server. You can do so with your hosts file. I’m using Pi Hole so I just edited my Pi Hole’s /etc/hosts file to add:

x.x.x.x    your.domain.here

That should be it!

I just want to end by saying thank you to the TT-RSS developers. I’ve been using it for about a month now and it’s awesome. Thanks!

Another option that I use is to simply use a plain HTTP set-up for TT-RSS and have nginx as a HTTPS proxy in front of TT-RSS and any other container you are using. I know using traefik in front of other containers is also popular.