TeddyCloud on a Public Server/IP with LetsEncrypt
TonieBoxes are a great kind of toy for fathers of young children. It allows them to play music or audio books on their own, just by placing funny little figures on it. It’s cute, cuddly, extremely intuitive, and not that technically complex. However, there is a major disadvantage: the device is tied to the manufacturer, who has of course invested a lot of time and money in the development of the Toniebox, and it is legitimate for the manufacturer to make money from it. However, there are enough examples of closed, cloud-based systems whose manufacturers discontinue the platform at some point because it is no longer sufficiently profitable to operate. Luckily, some people successfully reverse engineered the box and were able to modify it in a way, which allows to operate your own content server for this system.
When I first came into contact with their software, TeddyCloud, the first question that arose was whether I should install TeddyCloud locally or on a public server. The decision to go with a public server was an easy one: I wanted my kids to be able to use their TonieBox with all its content (both original and custom) while on vacation, for example. After that, however, the question arose as to how I could secure access. Typically, this includes a properly set up SSL and user authentication. For self-hosted services, LetsEncrypt is typically the first choice for SSL certificates. However, TeddyCloud also sets up its own CA to secure communication with the TonieBox, which uses Client Certificate Authentication.
In this context, the TeddyCloud documentation contains the following statement:
Please beware that port 443 cannot be remapped and you cannot use a reverse proxy like nginx or traefik without passing through the TLS (complex, not recommended). The client certificate authentication needs to be done by teddyCloud. Also, there is no SNI.
Challenge Accepted!
Actually, writing this article, I realized that I never checked if custom SSL certificates (e.g., issued by LetsEncrypt) are supported for the TeddyCloud web interface. Anyway, I also couldn’t find any documentation considering authentication for TeddyCloud. So I really wanted to have a reverse proxy, dealing with both, SSL encryption for the web interface with a valid (LetsEncrypt) certificate, and authentication (I decided to just use plain old Basic Auth).
Usually, a reverse proxy either just forwards TCP, or it actually does SSL Offloading by decrypting the encrypted HTTPS traffic and only forwarding the HTTP part to the upstream application. However, since TonieBoxes do client certificate authentication and the TeddyCloud wants to do it by itself to identify the TonieBox, SSL Offloading by the reverse proxy was no option. On the other hand, I somehow had to be able to set a switch if a connection (from a TonieBox) should be forwarded to the TeddyCloud port 443, or if it (from a browser) should end up with the web interface, encrypted with a LetsEncrypt certificate.
Luckily, nginx offers a nice option for this: With a stream
block, I can basically do TCP forwarding, but with the ssl_preread
directive I’m able to somehow peek into the connection and to check if, and which server name is indicated for the SSL connection.
Also, there is no SNI.
Perfect! This saved my day! So, if it’s a request with SNI, I do the SSL Offloading stuff with my LetsEncrypt certificate. If there is no SNI, I just forward it to TeddyCloud as is. Works for me 🙂
Setup
In case you want to use it by yourself, here is the relevant config!
To get things running, do the following steps:
- Set up a server with an OS of your choice, install Docker engine and configure your desired domain name to the IP(s) of that server. I also would suggest setting up a firewall and only allowing ports 22 (SSH), 80 (HTTP) and 443 (HTTPS).
- Copy the configs to your server (
docker-compose.yaml
andnginx.conf
should be in the same directory) and adjust all occurences of the domain nameteddycloud.example.com
to your domain. - Comment out the HTTPS server section in
nginx.conf
, as we don’t have the LetsEncrypt certificates yet. - Start the Docker containers, e.g., using
docker compose up -d
. - Request a LetsEncrypt certificate by running
docker compose exec -it certbot certbot certonly --webroot
. When you’re asked for the webroot directory, please provide/var/www/certbot
. - Uncomment the HTTPS server configuration in
nginx.conf
. - Restart nginx, e.g., using
docker compose restart nginx
.