Securing Jellyfin

How to secure a Jellyfin media server

1. Use A Container

Run it in a rootless container. Classic case of least privilege. Running Jellyfin in a container not only makes deployment easy it means the application will be running with minimal privileges on the host machine and in the unlikely event the container is compromised the attacker will only have access to the applications configuration and media files and will need to escape the container to read or change anything on the host.

podman run \
 --detach \
 --rm \
 --name jellyfin \
 --net slirp4netns \
 --device /dev/dri/:/dev/dri/ \
 --publish 8096:8096/tcp \
 --user $(id -u):$(id -g) \
 --userns keep-id \
 -v /media/shaun/media/.jellyfin/cache:/cache:Z \
 -v /media/shaun/media/.jellyfin/config:/config:Z \
 -v /media/shaun/media/:/media:z \
 docker.io/jellyfin/jellyfin:latest

2. Use Readonly And Private Bind Mounts

Use read only bind mounts and private (:Z) selinux labels. This ensures data is not shared between containers and will help maintain the integrity of your media library.

--volume jellyfin-cache:/cache:Z \
--volume jellyfin-config:/config:Z \
--mount type=bind,source=/path/to/media,destination=/media,ro=true,relabel=private \

3. Automatic Updates

By labeling the container with "io.containers.autoupdate=registry" and using the System D Podman Auto Update Service this will automatically pull and re run the container on a regular basis to ensure security patches are applied.

4. Use A Reverse Proxy

When exposing the server to the internet front it with a reverse proxy (Caddy is fantastic). This allows you to host the server on port 443 and handle TLS before handing off requests to Jellyfin.

caddy will automatically handle TLS for your domain.

caddy reverse-proxy --from example.com --to 127.0.0.1:8096

5. IP Whitelisting

If you will be accessing the server from known locations configure the firewall to only allow connections from those IP addresses.

sudo firewall-cmd --permanent --zone=external --add-rich-rule=' rule family="ipv4" source address="173.245.48.0/20" port protocol="tcp" port="443" accept'

6. Obscure Domain

TLS uses SNI to determine what certificate and subsequently website the web server should serve to the user, this means an external attacker needs to know the domain name of your Jellyfin server in order to get requests to it. Hosting your server on a obscure subdomain will make it harder to discover and therefore attack.

7. Use A Strong Admin Password

It pretty much goes without saying, use a strong randomly generated password for the admin account, generated with something like KeePassXC

8. Use Separate Admin and User Accounts

Create at least two user accounts, one for administering the server and one for watching media, Ensure your media user has the "Allow this user to manage the server" option unchecked.

9. Disable External Admin Access

Block the admin account the accessing the server from the internet, internal (LAN) access only.

10. Hide Users From the Login Screen

Hide users from the login screen, this means to login you need to know both the username and password.

11. Enable Account Lockout

Enable an account lockout for all users to protect against password bruteforce and stuffing attacks.

12. Block Simultaneous User Sessions

Only allow users to have one session at a time.

13. Rename The Admin User

Rename the admin user as well as hiding it from the login screen.

14. Do Not Allow Media Deletion

If you are using a read only volume mount then any deletion attempt will fail, but otherwise limit the deletion of media to just the admin user.

15. Use A CDN

Fronting the Jellyfin server with a CDN like CloudFlare will not only speed up access due to caching, you can also block all connections that are not coming through the CDN edge servers effectively hiding the origin server from the internet. This will also provide DOS protection and can be used to block access from certain geographic locations, bots and certain device types. It should be noted however that it is against the TOS to run a media server on CloudFlare's free tier so this might be pricey.