at 24.11-pre 4.6 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8let 9 inherit (lib) 10 mkEnableOption 11 mkPackageOption 12 mkOption 13 maintainers 14 ; 15 inherit (lib.types) 16 bool 17 port 18 str 19 submodule 20 ; 21 cfg = config.services.navidrome; 22 settingsFormat = pkgs.formats.json { }; 23in 24{ 25 options = { 26 services.navidrome = { 27 28 enable = mkEnableOption "Navidrome music server"; 29 30 package = mkPackageOption pkgs "navidrome" { }; 31 32 settings = mkOption { 33 type = submodule { 34 freeformType = settingsFormat.type; 35 36 options = { 37 Address = mkOption { 38 default = "127.0.0.1"; 39 description = "Address to run Navidrome on."; 40 type = str; 41 }; 42 43 Port = mkOption { 44 default = 4533; 45 description = "Port to run Navidrome on."; 46 type = port; 47 }; 48 }; 49 }; 50 default = { }; 51 example = { 52 MusicFolder = "/mnt/music"; 53 }; 54 description = "Configuration for Navidrome, see <https://www.navidrome.org/docs/usage/configuration-options/> for supported values."; 55 }; 56 57 user = mkOption { 58 type = str; 59 default = "navidrome"; 60 description = "User under which Navidrome runs."; 61 }; 62 63 group = mkOption { 64 type = str; 65 default = "navidrome"; 66 description = "Group under which Navidrome runs."; 67 }; 68 69 openFirewall = mkOption { 70 type = bool; 71 default = false; 72 description = "Whether to open the TCP port in the firewall"; 73 }; 74 }; 75 }; 76 77 config = 78 let 79 inherit (lib) mkIf optional getExe; 80 WorkingDirectory = "/var/lib/navidrome"; 81 in 82 mkIf cfg.enable { 83 systemd = { 84 tmpfiles.settings.navidromeDirs = { 85 "${cfg.settings.DataFolder or WorkingDirectory}"."d" = { 86 mode = "700"; 87 inherit (cfg) user group; 88 }; 89 "${cfg.settings.CacheFolder or (WorkingDirectory + "/cache")}"."d" = { 90 mode = "700"; 91 inherit (cfg) user group; 92 }; 93 }; 94 services.navidrome = { 95 description = "Navidrome Media Server"; 96 after = [ "network.target" ]; 97 wantedBy = [ "multi-user.target" ]; 98 serviceConfig = { 99 ExecStart = '' 100 ${getExe cfg.package} --configfile ${settingsFormat.generate "navidrome.json" cfg.settings} 101 ''; 102 User = cfg.user; 103 Group = cfg.group; 104 StateDirectory = "navidrome"; 105 inherit WorkingDirectory; 106 RuntimeDirectory = "navidrome"; 107 RootDirectory = "/run/navidrome"; 108 ReadWritePaths = ""; 109 BindPaths = 110 optional (cfg.settings ? DataFolder) cfg.settings.DataFolder 111 ++ optional (cfg.settings ? CacheFolder) cfg.settings.CacheFolder; 112 BindReadOnlyPaths = [ 113 # navidrome uses online services to download additional album metadata / covers 114 "${ 115 config.environment.etc."ssl/certs/ca-certificates.crt".source 116 }:/etc/ssl/certs/ca-certificates.crt" 117 builtins.storeDir 118 "/etc" 119 ] ++ optional (cfg.settings ? MusicFolder) cfg.settings.MusicFolder; 120 CapabilityBoundingSet = ""; 121 RestrictAddressFamilies = [ 122 "AF_UNIX" 123 "AF_INET" 124 "AF_INET6" 125 ]; 126 RestrictNamespaces = true; 127 PrivateDevices = true; 128 PrivateUsers = true; 129 ProtectClock = true; 130 ProtectControlGroups = true; 131 ProtectHome = true; 132 ProtectKernelLogs = true; 133 ProtectKernelModules = true; 134 ProtectKernelTunables = true; 135 SystemCallArchitectures = "native"; 136 SystemCallFilter = [ 137 "@system-service" 138 "~@privileged" 139 ]; 140 RestrictRealtime = true; 141 LockPersonality = true; 142 MemoryDenyWriteExecute = true; 143 UMask = "0066"; 144 ProtectHostname = true; 145 }; 146 }; 147 }; 148 149 users.users = mkIf (cfg.user == "navidrome") { 150 navidrome = { 151 inherit (cfg) group; 152 isSystemUser = true; 153 }; 154 }; 155 156 users.groups = mkIf (cfg.group == "navidrome") { navidrome = { }; }; 157 158 networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.settings.Port ]; 159 }; 160 meta.maintainers = with maintainers; [ nu-nu-ko ]; 161}