In this article we will describe how you can secure an internal site with an automatically renewed Let’s Encrypt certificate. We will use Jellyfin Media Server as an example, but with a little modification this approach can be used for any internally running web application.
When you install Jellyfin, by default it will use HTTP. This means that data in transit is not encrypted. This might not really be an issue if you only use Jellyfin inside your network.
If you want to make Jellyfin accessible via the internet you can put Jellyfin behind a reverse proxy and let that arrange a SSL certificate for you. For this to work you normally have to open port 80 and 443 on your firewall, so that the reverse proxy can request the certificate.
But what if you do not want to expose Jellyfin to the internet and you do not want to open port 80 and 443, but you still want to secure Jellyfin with a SSL certificate?
We will show you how you can secure your Jellyfin Media Server with a SSL certificate. We will request this certificate through Let’s Encrypt, with certbot and we will use the DNS challenge method.
For this particular guide we make the following assumptions:
- You have already installed Jellyfin and are using HTTP
- You are running Jellyfin on Ubuntu Server
- You are using Cloudflare to manage your domains DNS
Step 1: Install the tools
The first thing we have to do is to install the necessary tools on our Linux server to be able to request a Let’s Encrypt certificate. To do this, login to your Jellyfin server via SSH and install certbot. On Ubuntu you do this with the command below:
sudo apt install certbot python3-certbot-dns-cloudflare
Step 2: Get a Cloudflare API token
We are going to use a DNS challenge to get a certificate from Let’s Encrypt and we are going to use certbot to do this.
The way this works is when you request a certificate with certbot we will use the Cloudflare DNS plugin. This plugin will use the Cloudflare API to add a txt record, with a particular code, to your Cloudflare DNS.
Next Let’s Encrypt will query that txt record and validate the value. This is the DNS challenge. This will guarantee Let’s Encrypt that you have control over your domain and will allow them to issue you a certificate for the chosen domain.
For this to work we must create an API token and create a configuration file on our server first.
Step 2a: Create an API token
Go to cloudflare.com, login to your dashboard and choose your domain. On the bottom right you will find the Get your API token link.
In the next screen you can create your API token.
Click on the Create Token button and choose the Edit zone DNS template. Give it a proper name and select the zone you want to give this token access to under Zone Resources.
Click on Continue to summary and in the next screen click on Create Token. You will now be given a token which you should copy so that we can use it later.
Step 2b: Create a configuration file
Now back on your Jellyfin server we have to create a configuration file for certbot, so that it knows how to access the Cloudflare API.
Paste in the following content:
# Cloudflare API token used by Certbot dns_cloudflare_api_token = IP8u9LEzu4EeFgP1O9Z6j9E4-5iKivaiNXkP96io
Then change the permissions of the file
chmod 600 cloudflare.ini
Step 3: Request the certificate
Now that we have our configuration file, which contains our API key, we will use certbot to request the certificate. Execute the command below to do this.
sudo certbot certonly \ --dns-cloudflare \ --dns-cloudflare-credentials ~/cloudflare.ini \ -d jellyfin.internal.example.com
What we do here, is we instruct certbot to request a certificate for the domain jellyfin.internal.example.com and use Cloudflare DNS challenge, while also telling it which credentials to use (the API key).
Once this command completes you will find the certificates that were issued in the following directory:
The files you will find here are:
While this is great, actually none of these are directly usable for Jellyfin, as Jellyfin requires a PKCS#12 certificate. We will have to create that.
Step 4: Create the hook script
Whenever you renew a certificate, you can run some extra scripts before or after. We are going to use this to run a script after renewal. This script will:
- Generate PKCS#12 file for Jellyfin
- Change PKCS#12 file permissions
- Restart Jellyfin
This script must be placed in:
To do this, do the following. Create the script jellyfin.sh:
cd /etc/letsencrypt/renewal-hooks/post sudo nano jellyfin.sh
Paste in the following code:
#!/bin/bash # Generate PKCS12 file for Jellyfin openssl pkcs12 \ -export \ -inkey /etc/letsencrypt/live/jellyfin.internal.example.com/privkey.pem \ -in /etc/letsencrypt/live/jellyfin.internal.example.com/cert.pem -name jellyfin \ -out /etc/letsencrypt/live/jellyfin.internal.example.com/jellyfin.pfx \ -password pass: # Change PKCS12 file permissions chmod 755 /etc/letsencrypt/live/jellyfin.internal.example.com/jellyfin.pfx # Restart Jellyfin systemctl restart jellyfin.service
The first command uses openssl to generate the PKCS#12 certificate. It basically combines the signed certificate and the private key in one file. The output file is jellyfin.pfx and there is no password set on this certificate.
Next it changes the jellyfin.pfx file permissions and restarts Jellyfin.
Finally set the permissions of this script:
sudo chmod 700 jellyfin.sh
The jellyfin.sh script will now be executed every time you renew the certificate.
Step 5: Renew the certificate
Now let’s test if this works. Let’s renew the certificate and see what happens. Issue the following command to renew the certificate:
sudo certbot renew --force-renew
This will now request a renewal of the certificate through Let’s Encrypt and run the post renewal hook automatically.
Afterwards you should see in the /etc/letsencrypt/live/jellyfin.internal.example.com/ directory a file named jellyfin.pfx being generated.
This is the certificate we can use in Jellyfin to enable HTTPS.
Step 6: Configure the certificate in Jellyfin
Login to your Jellyfin instance and go to the Network section in your Dashboard.
Here you should enable HTTPS and in the HTTPS Setting section point Jellyfin to the certificate to use.
Now restart Jellyfin (or the Ubuntu server) and if you connect to https://jellyfin.internal.example.com:8920 you will see that you have properly secured Jellyfin with a Let’s Encrypt certificate.
Step 7: Certificate renewal
You have to renew your certificate before it expires. These Let’s Encrypt certificates expire relatively fast, namely after 90 days.
Luckily certbot does this automatically for you. When you requested the certificate certbot also created a systemd timer to monitor your certificate’s validity. This timer will renew the certificate when needed.