at 23.11-pre 6.7 kB view raw
1{ config, options, lib, pkgs, utils, ... }: 2with lib; 3let 4 cfg = config.services.unifi; 5 stateDir = "/var/lib/unifi"; 6 cmd = '' 7 @${cfg.jrePackage}/bin/java java \ 8 ${optionalString (cfg.initialJavaHeapSize != null) "-Xms${(toString cfg.initialJavaHeapSize)}m"} \ 9 ${optionalString (cfg.maximumJavaHeapSize != null) "-Xmx${(toString cfg.maximumJavaHeapSize)}m"} \ 10 -jar ${stateDir}/lib/ace.jar 11 ''; 12in 13{ 14 15 options = { 16 17 services.unifi.enable = mkOption { 18 type = types.bool; 19 default = false; 20 description = lib.mdDoc '' 21 Whether or not to enable the unifi controller service. 22 ''; 23 }; 24 25 services.unifi.jrePackage = mkOption { 26 type = types.package; 27 default = if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.3") then pkgs.jdk11 else pkgs.jre8; 28 defaultText = literalExpression ''if (lib.versionAtLeast (lib.getVersion cfg.unifiPackage) "7.3" then pkgs.jdk11 else pkgs.jre8''; 29 description = lib.mdDoc '' 30 The JRE package to use. Check the release notes to ensure it is supported. 31 ''; 32 }; 33 34 services.unifi.unifiPackage = mkOption { 35 type = types.package; 36 default = pkgs.unifi5; 37 defaultText = literalExpression "pkgs.unifi5"; 38 description = lib.mdDoc '' 39 The unifi package to use. 40 ''; 41 }; 42 43 services.unifi.mongodbPackage = mkOption { 44 type = types.package; 45 default = pkgs.mongodb-4_2; 46 defaultText = literalExpression "pkgs.mongodb"; 47 description = lib.mdDoc '' 48 The mongodb package to use. Please note: unifi7 officially only supports mongodb up until 3.6 but works with 4.2. 49 ''; 50 }; 51 52 services.unifi.openFirewall = mkOption { 53 type = types.bool; 54 default = false; 55 description = lib.mdDoc '' 56 Whether or not to open the minimum required ports on the firewall. 57 58 This is necessary to allow firmware upgrades and device discovery to 59 work. For remote login, you should additionally open (or forward) port 60 8443. 61 ''; 62 }; 63 64 services.unifi.initialJavaHeapSize = mkOption { 65 type = types.nullOr types.int; 66 default = null; 67 example = 1024; 68 description = lib.mdDoc '' 69 Set the initial heap size for the JVM in MB. If this option isn't set, the 70 JVM will decide this value at runtime. 71 ''; 72 }; 73 74 services.unifi.maximumJavaHeapSize = mkOption { 75 type = types.nullOr types.int; 76 default = null; 77 example = 4096; 78 description = lib.mdDoc '' 79 Set the maximum heap size for the JVM in MB. If this option isn't set, the 80 JVM will decide this value at runtime. 81 ''; 82 }; 83 84 }; 85 86 config = mkIf cfg.enable { 87 88 users.users.unifi = { 89 isSystemUser = true; 90 group = "unifi"; 91 description = "UniFi controller daemon user"; 92 home = "${stateDir}"; 93 }; 94 users.groups.unifi = {}; 95 96 networking.firewall = mkIf cfg.openFirewall { 97 # https://help.ubnt.com/hc/en-us/articles/218506997 98 allowedTCPPorts = [ 99 8080 # Port for UAP to inform controller. 100 8880 # Port for HTTP portal redirect, if guest portal is enabled. 101 8843 # Port for HTTPS portal redirect, ditto. 102 6789 # Port for UniFi mobile speed test. 103 ]; 104 allowedUDPPorts = [ 105 3478 # UDP port used for STUN. 106 10001 # UDP port used for device discovery. 107 ]; 108 }; 109 110 systemd.services.unifi = { 111 description = "UniFi controller daemon"; 112 wantedBy = [ "multi-user.target" ]; 113 after = [ "network.target" ]; 114 115 # This a HACK to fix missing dependencies of dynamic libs extracted from jars 116 environment.LD_LIBRARY_PATH = with pkgs.stdenv; "${cc.cc.lib}/lib"; 117 # Make sure package upgrades trigger a service restart 118 restartTriggers = [ cfg.unifiPackage cfg.mongodbPackage ]; 119 120 serviceConfig = { 121 Type = "simple"; 122 ExecStart = "${(removeSuffix "\n" cmd)} start"; 123 ExecStop = "${(removeSuffix "\n" cmd)} stop"; 124 Restart = "on-failure"; 125 TimeoutSec = "5min"; 126 User = "unifi"; 127 UMask = "0077"; 128 WorkingDirectory = "${stateDir}"; 129 # the stop command exits while the main process is still running, and unifi 130 # wants to manage its own child processes. this means we have to set KillSignal 131 # to something the main process ignores, otherwise every stop will have unifi.service 132 # fail with SIGTERM status. 133 KillSignal = "SIGCONT"; 134 135 # Hardening 136 AmbientCapabilities = ""; 137 CapabilityBoundingSet = ""; 138 # ProtectClock= adds DeviceAllow=char-rtc r 139 DeviceAllow = ""; 140 DevicePolicy = "closed"; 141 LockPersonality = true; 142 NoNewPrivileges = true; 143 PrivateDevices = true; 144 PrivateMounts = true; 145 PrivateTmp = true; 146 PrivateUsers = true; 147 ProtectClock = true; 148 ProtectControlGroups = true; 149 ProtectHome = true; 150 ProtectHostname = true; 151 ProtectKernelLogs = true; 152 ProtectKernelModules = true; 153 ProtectKernelTunables = true; 154 ProtectSystem = "strict"; 155 RemoveIPC = true; 156 RestrictNamespaces = true; 157 RestrictRealtime = true; 158 RestrictSUIDSGID = true; 159 SystemCallErrorNumber = "EPERM"; 160 SystemCallFilter = [ "@system-service" ]; 161 162 StateDirectory = "unifi"; 163 RuntimeDirectory = "unifi"; 164 LogsDirectory = "unifi"; 165 CacheDirectory= "unifi"; 166 167 TemporaryFileSystem = [ 168 # required as we want to create bind mounts below 169 "${stateDir}/webapps:rw" 170 ]; 171 172 # We must create the binary directories as bind mounts instead of symlinks 173 # This is because the controller resolves all symlinks to absolute paths 174 # to be used as the working directory. 175 BindPaths = [ 176 "/var/log/unifi:${stateDir}/logs" 177 "/run/unifi:${stateDir}/run" 178 "${cfg.unifiPackage}/dl:${stateDir}/dl" 179 "${cfg.unifiPackage}/lib:${stateDir}/lib" 180 "${cfg.mongodbPackage}/bin:${stateDir}/bin" 181 "${cfg.unifiPackage}/webapps/ROOT:${stateDir}/webapps/ROOT" 182 ]; 183 184 # Needs network access 185 PrivateNetwork = false; 186 # Cannot be true due to OpenJDK 187 MemoryDenyWriteExecute = false; 188 }; 189 }; 190 191 }; 192 imports = [ 193 (mkRemovedOptionModule [ "services" "unifi" "dataDir" ] "You should move contents of dataDir to /var/lib/unifi/data" ) 194 (mkRenamedOptionModule [ "services" "unifi" "openPorts" ] [ "services" "unifi" "openFirewall" ]) 195 ]; 196 197 meta.maintainers = with lib.maintainers; [ pennae ]; 198}