Strato DNS Challenge Plugin

Hi@all,

first of all a "hello" to the round, I am new here :slight_smile:

A little about the configuration so far, please excuse the long preface. The real question you will find below :slight_smile:

++ Background ++

I have a domain at Strato e.g. 'example.de'. For each host in my LAN to which I need HTTPS access I have created a corresponding subdomain at Strato e.g. cloud.lan.example.de'. At Strato I have enabled dynamic DNS for the subdomain.

On my pfSense I let update the current WAN IP of my pfSense automatically at Strato. Furthermore, I have set up the ACME plugin on the pfSense which takes care of the automatic renewal of certificates for all subdomains.

Also running on the pfSense is the HA proxy which receives incoming HTTPS requests, equips them with a trusted certificate and forwards the request to the appropriate internal server depending on the subdomain.

So only the pfSense has the Let's Encrypt certificate. The actual server in the LAN still has a self-signed certificate. However, the requesting client (external) does not notice anything because, as mentioned above, the pfSense accepts the connection. The pfSense opens for the certificate renewal time-controlled the port 80, so it is a HTTP-01 challenge.

So far this all sounds fine and works. But the fact that the servers in the LAN itself have a self-signed certificate becomes a problem when I or an application access the server via HTTPS.

I would like to illustrate this with an example. At Strato I have set up the subdomain 'media.lan.example.de' and included it in my configuration as described above. On the internal server 'media.lan.beispiel.de' runs my Jellyfin media server.

On the internal LAN I have Kodi & Jellyfin plugin running on my mini computer on my TV. Now when I configure 'media.lan.example.de' in Kodi it logically gets presented with a self-signed certificate and Kodi doesn't like that at all! It simply does not work.

As a stopgap solution, I set up a virtual network device at the pfSense, set up an additional subdomain and let the HA proxy listen to it. This routes the connection to the media server. Sounds good, but brings further disadvantages. The entire data stream runs through the pfSense which is of course anything but optimal with multiple stream clients :frowning:

++ Question ++
I want the above configuration now so that each server itself has a trusted LE certificate. However, no port 80 into the LAN should be opened. As far as I have read this works with the DNS-01 challenge. As I could find out further, this must be supported by the provider.

At Strato itself I did not find any information if it is supported. In the web I found information that there is a way to realize it. It is probably not official.

Can someone tell me if it works and especially how?

with best
pixel24

1 Like

Could you provide this info?

3 Likes

yes with pleasure. See below. I hope I have also understood the information correctly

Ah, a guide for Nginx Proxy Manager.. Shivers down my spine :scream: :nauseated_face:

Assuming the scripts still work, you could just use the info provided on GitHub - Buxdehuda/strato-certbot: Wildcard certificates for strato.de as your guide instead of the NPM guide you've linked.

Except for when you actually are using NPM (I hope not).

Note that it's not very "economic" to get single certificates for all your hosts inside your LAN. If you're using the DNS challenge anyway, you might want to consider getting a single wildcard certificate on your pfSense system and somehow securely distribute it to your internal hosts.

5 Likes

Can you use a different name [or subdomain] for the internal side?
Like:

  • www.ext.example.com, wwx.example.com
  • www.int.example.com, wwi.example.com

If so, then also, can pfSense allow those internal names to be proxied to their respective internal systems?

3 Likes

@rg305 I'm not sure I follow your train of thought. How would that help OP get certs for the internal hostnames?

@pixel24 Can pfSense do split horizon DNS perhaps?

1 Like

They are to help define the parameters required for the solution.

3 Likes

Hmkay.

3 Likes

No I do not use this

since I only need this for one machine running Jellyfin I'll try this way. However, without wildcard certificate that is apparently not so easy with Strato.

The internal host on which Jellyfin runs is: media01.lan.example.de

I have activated a script on the pfSense that when this renews the certificate the files:

media01.lan.example.en.crt
media01.lan.example.en.fullchain
media01.lan.example.en.key

to the media01 in the directory /etc/ssl/.

After that I tried to follow Jellyfin's instructions for NGINX setup with LE.

Since on the pfSense the files:

  • /etc/ssl/options-ssl-nginx.conf
  • /etc/ssl/ssl-dhparams.pem

do not exist on pfSense, but the sample configuration in the Jellyfin docs use them, so I loaded them from the internals.

Then I created the file /etc/nginx/sites-available/jellyfin.conf as follows:

server {
    listen 80;
    listen [::]:80;
    server_name media01.lan.example.de;

    # Uncomment to redirect HTTP to HTTPS
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name media01.lan.example.de;

    ## The default `client_max_body_size` is 1M, this might not be enough for some posters, etc.
    client_max_body_size 20M;

    # use a variable to store the upstream proxy
    # in this example we are using a hostname which is resolved via DNS
    # (if you aren't using DNS remove the resolver line and change the variable to point to an IP address e.g `set $jellyfin 127.0.0.1`)
    set $jellyfin media01;
    resolver 127.0.0.1 valid=30;

    ssl_certificate /etc/ssl/media01.lan.example.de.fullchain;
    ssl_certificate_key /etc/ssl/media01.lan.example.de.key;
    include /etc/ssl/options-ssl-nginx.conf;
    ssl_dhparam /etc/ssl/ssl-dhparams.pem;
    add_header Strict-Transport-Security "max-age=31536000" always;
    ssl_trusted_certificate /etc/ssl/media01.lan.example.de.fullchain;
    ssl_stapling on;
    ssl_stapling_verify on;

    # Security / XSS Mitigation Headers
    # NOTE: X-Frame-Options may cause issues with the webOS app
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    # Content Security Policy
    # See: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
    # Enforces https content and restricts JS/CSS to origin
    # External Javascript (such as cast_sender.js for Chromecast) must be whitelisted.
    # NOTE: The default CSP headers may cause issues with the webOS app
    #add_header Content-Security-Policy "default-src https: data: blob: http://image.tmdb.org; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' https://www.gstatic.com/cv/js/sender/v1/cast_sender.js https://www.gstatic.com/eureka/clank/95/cast_sender.js https://www.gstatic.com/eureka/clank/96/cast_sender.js https://www.gstatic.com/eureka/clank/97/cast_sender.js https://www.youtube.com blob:; worker-src 'self' blob:; connect-src 'self'; object-src 'none'; frame-ancestors 'self'";

    location = / {
        return 302 http://$host/web/;
        return 302 https://$host/web/;
    }

    location / {
        # Proxy main Jellyfin traffic
        proxy_pass http://$jellyfin:8096;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Protocol $scheme;
        proxy_set_header X-Forwarded-Host $http_host;

        # Disable buffering when the nginx proxy gets very resource heavy upon streaming
        proxy_buffering off;
    }

    # location block for /web - This is purely for aesthetics so /web/#!/ works instead of having to go to /web/index.html/#!/
    location = /web/ {
        # Proxy main Jellyfin traffic
        proxy_pass http://$jellyfin:8096/web/index.html;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Protocol $scheme;
        proxy_set_header X-Forwarded-Host $http_host;
    }

    location /socket {
        # Proxy Jellyfin Websockets traffic
        proxy_pass http://$jellyfin:8096;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Protocol $scheme;
        proxy_set_header X-Forwarded-Host $http_host;
    }
}

If I now call the address from the LAN:

https://media01.lan.example.de

but I still get the certificate warning

What does this mean?

2 Likes

From here:

an here:

I searched further on the web and found this configuration:

# NGiNX reverse proxy configuration for Jellyfin
#
# Use this configuration on NGiNX running on the same server as your Jellyfin instance. Replace the values as needed.
# LetsEncrypt is enabled and configured using `certbot`: install it via apt on Debian (`sudo apt install certbot`) or
# your package manager of choice. To start, make only the first section (port 80) active, then once that configuration
# is active, run Certbot for the first time to request your cert. Then, add the second section (port 443) and reload.
# With this config left in place, Certbot's automatic renewal will work and automatically replace the cert and
# reload NGiNX as needed, without interrupting your Jellyfin instance.
#

server {
    listen 443 ssl;
    listen [::]:443 ssl;
        
    # Update to your desired hostname, or leave as default to capture all virtual hosts
    server_name _;

    # SSL configuration
    # dhparams.pem should be generated with `openssl dhparam -out /etc/ssl/dhparams.pem 2048`; don't use your OS default!
    ssl_dhparam /etc/ssl/dhparams.pem;
    # Paths to the Certbot folder; replace <your_domain_name> with the LetsEncrypt cert name
    ssl_certificate /etc/letsencrypt/live/<your_domain_name>/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/<your_domain_name>/privkey.pem;
    # SSL tuning options; good defaults for modern browsers
    ssl_session_cache shared:le_nginx_SSL:1m;
    ssl_session_timeout 1440m;
    ssl_prefer_server_ciphers on;
    ssl_ciphers ALL;
    # Remove TLSv1.3 for nginx < 1.13; enable TLSv1.0 if your browser complains
    ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
    # Optional HSTS compliance with a 1-year age; force browsers to remember HTTPS status
    add_header Strict-Transport-Security "max-age=31536000" always;
        
    # Pass through everything to Jellyfin
    location / {
        proxy_pass http://jellyfin;
    }
}
upstream jellyfin {
    server localhost:8096;
}

Here I adjusted the paths of the certificate files and created the dhparams.pem as described in the config.

Of course I "enabled" the page but the result is the same -> certificate warning :frowning:

And you copied the certificate files over from pfSense to your Jellyfin server?

2 Likes

Yes, as I have written here:

Your sample nginx has a path of /etc/letsencrypt/live/

But, you say you copied the certs to /etc/ssl/

Did you just show us faulty config files or did you copy them to wrong place?

This would be much easier if you just always used the actual config and did not try to hide things.

3 Likes

Maybe English isn't your primary language (as it also isn't mine), but I didn't understand that part. For example, it was missing a verb, so I have no clue what you did to those three poor files.

3 Likes

Thank you.

What does this show?

echo | openssl s_client -connect media01.lan.example.club:443 | head   
3 Likes

That looks like a good cert and chain. When you say https requests show a "certificate warning", can you show or explain that warning in more detail?

4 Likes

I can connect perfectly to Jellyfin from here, no security warning, perfectly fine certificate installed.

2 Likes

Can't you access https://media01.lan.example.club from your lan? It worked just fine with the openssl test I asked you to do earlier.

That works because nginx is a reverse proxy back to your jellyfin (at least from what you showed recently). nginx uses HTTP back to jellyfin (not HTTPS). That's normal because you don't necessarily need HTTPS between two machines on your own lan.

But, this latest example shows you connecting directly to the jellyfin port 8920 using HTTPS. I don't understand why you need to do that.

And, if you do, what have you done to configure jellyfin to support direct HTTPS connections? Usually servers need config to support that like you did with nginx.

4 Likes

Jellyfin itself cannot bind port 443 because it runs as user "Jellyfin" and they are not allowed to bind ports below 1024.

Yes, HTTPS is configured in the Jallyfin configuration. But as I said, this does not work on 443, which is why you need the NGINX "in front of" it.