1{ config, pkgs, lib, ... }:
2
3with lib;
4
5let
6 cfg = config.services.jellyfin;
7in
8{
9 options = {
10 services.jellyfin = {
11 enable = mkEnableOption "Jellyfin Media Server";
12
13 user = mkOption {
14 type = types.str;
15 default = "jellyfin";
16 description = "User account under which Jellyfin runs.";
17 };
18
19 package = mkOption {
20 type = types.package;
21 default = pkgs.jellyfin;
22 defaultText = literalExpression "pkgs.jellyfin";
23 description = ''
24 Jellyfin package to use.
25 '';
26 };
27
28 group = mkOption {
29 type = types.str;
30 default = "jellyfin";
31 description = "Group under which jellyfin runs.";
32 };
33
34 openFirewall = mkOption {
35 type = types.bool;
36 default = false;
37 description = ''
38 Open the default ports in the firewall for the media server. The
39 HTTP/HTTPS ports can be changed in the Web UI, so this option should
40 only be used if they are unchanged.
41 '';
42 };
43 };
44 };
45
46 config = mkIf cfg.enable {
47 systemd.services.jellyfin = {
48 description = "Jellyfin Media Server";
49 after = [ "network.target" ];
50 wantedBy = [ "multi-user.target" ];
51
52 serviceConfig = rec {
53 User = cfg.user;
54 Group = cfg.group;
55 StateDirectory = "jellyfin";
56 CacheDirectory = "jellyfin";
57 ExecStart = "${cfg.package}/bin/jellyfin --datadir '/var/lib/${StateDirectory}' --cachedir '/var/cache/${CacheDirectory}'";
58 Restart = "on-failure";
59
60 # Security options:
61
62 NoNewPrivileges = true;
63
64 AmbientCapabilities = "";
65 CapabilityBoundingSet = "";
66
67 # ProtectClock= adds DeviceAllow=char-rtc r
68 DeviceAllow = "";
69
70 LockPersonality = true;
71
72 PrivateTmp = true;
73 PrivateDevices = true;
74 PrivateUsers = true;
75
76 ProtectClock = true;
77 ProtectControlGroups = true;
78 ProtectHostname = true;
79 ProtectKernelLogs = true;
80 ProtectKernelModules = true;
81 ProtectKernelTunables = true;
82
83 RemoveIPC = true;
84
85 RestrictNamespaces = true;
86 # AF_NETLINK needed because Jellyfin monitors the network connection
87 RestrictAddressFamilies = [ "AF_NETLINK" "AF_INET" "AF_INET6" ];
88 RestrictRealtime = true;
89 RestrictSUIDSGID = true;
90
91 SystemCallArchitectures = "native";
92 SystemCallErrorNumber = "EPERM";
93 SystemCallFilter = [
94 "@system-service"
95 "~@cpu-emulation" "~@debug" "~@keyring" "~@memlock" "~@obsolete" "~@privileged" "~@setuid"
96 ];
97 };
98 };
99
100 users.users = mkIf (cfg.user == "jellyfin") {
101 jellyfin = {
102 group = cfg.group;
103 isSystemUser = true;
104 };
105 };
106
107 users.groups = mkIf (cfg.group == "jellyfin") {
108 jellyfin = {};
109 };
110
111 networking.firewall = mkIf cfg.openFirewall {
112 # from https://jellyfin.org/docs/general/networking/index.html
113 allowedTCPPorts = [ 8096 8920 ];
114 allowedUDPPorts = [ 1900 7359 ];
115 };
116
117 };
118
119 meta.maintainers = with lib.maintainers; [ minijackson ];
120}