at 23.11-pre 6.8 kB view raw
1{ config, lib, options, pkgs, ... }: 2 3with lib; 4let 5 cfg = config.services.galene; 6 opt = options.services.galene; 7 defaultstateDir = "/var/lib/galene"; 8 defaultrecordingsDir = "${cfg.stateDir}/recordings"; 9 defaultgroupsDir = "${cfg.stateDir}/groups"; 10 defaultdataDir = "${cfg.stateDir}/data"; 11in 12{ 13 options = { 14 services.galene = { 15 enable = mkEnableOption (lib.mdDoc "Galene Service"); 16 17 stateDir = mkOption { 18 default = defaultstateDir; 19 type = types.str; 20 description = lib.mdDoc '' 21 The directory where Galene stores its internal state. If left as the default 22 value this directory will automatically be created before the Galene server 23 starts, otherwise the sysadmin is responsible for ensuring the directory 24 exists with appropriate ownership and permissions. 25 ''; 26 }; 27 28 user = mkOption { 29 type = types.str; 30 default = "galene"; 31 description = lib.mdDoc "User account under which galene runs."; 32 }; 33 34 group = mkOption { 35 type = types.str; 36 default = "galene"; 37 description = lib.mdDoc "Group under which galene runs."; 38 }; 39 40 insecure = mkOption { 41 type = types.bool; 42 default = false; 43 description = lib.mdDoc '' 44 Whether Galene should listen in http or in https. If left as the default 45 value (false), Galene needs to be fed a private key and a certificate. 46 ''; 47 }; 48 49 certFile = mkOption { 50 type = types.nullOr types.str; 51 default = null; 52 example = "/path/to/your/cert.pem"; 53 description = lib.mdDoc '' 54 Path to the server's certificate. The file is copied at runtime to 55 Galene's data directory where it needs to reside. 56 ''; 57 }; 58 59 keyFile = mkOption { 60 type = types.nullOr types.str; 61 default = null; 62 example = "/path/to/your/key.pem"; 63 description = lib.mdDoc '' 64 Path to the server's private key. The file is copied at runtime to 65 Galene's data directory where it needs to reside. 66 ''; 67 }; 68 69 httpAddress = mkOption { 70 type = types.str; 71 default = ""; 72 description = lib.mdDoc "HTTP listen address for galene."; 73 }; 74 75 httpPort = mkOption { 76 type = types.port; 77 default = 8443; 78 description = lib.mdDoc "HTTP listen port."; 79 }; 80 81 staticDir = mkOption { 82 type = types.str; 83 default = "${cfg.package.static}/static"; 84 defaultText = literalExpression ''"''${package.static}/static"''; 85 example = "/var/lib/galene/static"; 86 description = lib.mdDoc "Web server directory."; 87 }; 88 89 recordingsDir = mkOption { 90 type = types.str; 91 default = defaultrecordingsDir; 92 defaultText = literalExpression ''"''${config.${opt.stateDir}}/recordings"''; 93 example = "/var/lib/galene/recordings"; 94 description = lib.mdDoc "Recordings directory."; 95 }; 96 97 dataDir = mkOption { 98 type = types.str; 99 default = defaultdataDir; 100 defaultText = literalExpression ''"''${config.${opt.stateDir}}/data"''; 101 example = "/var/lib/galene/data"; 102 description = lib.mdDoc "Data directory."; 103 }; 104 105 groupsDir = mkOption { 106 type = types.str; 107 default = defaultgroupsDir; 108 defaultText = literalExpression ''"''${config.${opt.stateDir}}/groups"''; 109 example = "/var/lib/galene/groups"; 110 description = lib.mdDoc "Web server directory."; 111 }; 112 113 package = mkOption { 114 default = pkgs.galene; 115 defaultText = literalExpression "pkgs.galene"; 116 type = types.package; 117 description = lib.mdDoc '' 118 Package for running Galene. 119 ''; 120 }; 121 }; 122 }; 123 124 config = mkIf cfg.enable { 125 assertions = [ 126 { 127 assertion = cfg.insecure || (cfg.certFile != null && cfg.keyFile != null); 128 message = '' 129 Galene needs both certFile and keyFile defined for encryption, or 130 the insecure flag. 131 ''; 132 } 133 ]; 134 135 systemd.services.galene = { 136 description = "galene"; 137 after = [ "network.target" ]; 138 wantedBy = [ "multi-user.target" ]; 139 140 preStart = '' 141 ${optionalString (cfg.insecure != true) '' 142 install -m 700 -o '${cfg.user}' -g '${cfg.group}' ${cfg.certFile} ${cfg.dataDir}/cert.pem 143 install -m 700 -o '${cfg.user}' -g '${cfg.group}' ${cfg.keyFile} ${cfg.dataDir}/key.pem 144 ''} 145 ''; 146 147 serviceConfig = mkMerge [ 148 { 149 Type = "simple"; 150 User = cfg.user; 151 Group = cfg.group; 152 WorkingDirectory = cfg.stateDir; 153 ExecStart = ''${cfg.package}/bin/galene \ 154 ${optionalString (cfg.insecure) "-insecure"} \ 155 -data ${cfg.dataDir} \ 156 -groups ${cfg.groupsDir} \ 157 -recordings ${cfg.recordingsDir} \ 158 -static ${cfg.staticDir}''; 159 Restart = "always"; 160 # Upstream Requirements 161 LimitNOFILE = 65536; 162 StateDirectory = [ ] ++ 163 optional (cfg.stateDir == defaultstateDir) "galene" ++ 164 optional (cfg.dataDir == defaultdataDir) "galene/data" ++ 165 optional (cfg.groupsDir == defaultgroupsDir) "galene/groups" ++ 166 optional (cfg.recordingsDir == defaultrecordingsDir) "galene/recordings"; 167 168 # Hardening 169 CapabilityBoundingSet = [ "" ]; 170 DeviceAllow = [ "" ]; 171 LockPersonality = true; 172 MemoryDenyWriteExecute = true; 173 NoNewPrivileges = true; 174 PrivateDevices = true; 175 PrivateTmp = true; 176 PrivateUsers = true; 177 ProcSubset = "pid"; 178 ProtectClock = true; 179 ProtectControlGroups = true; 180 ProtectHome = true; 181 ProtectHostname = true; 182 ProtectKernelLogs = true; 183 ProtectKernelModules = true; 184 ProtectKernelTunables = true; 185 ProtectProc = "invisible"; 186 ProtectSystem = "strict"; 187 ReadWritePaths = cfg.recordingsDir; 188 RemoveIPC = true; 189 RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; 190 RestrictNamespaces = true; 191 RestrictRealtime = true; 192 RestrictSUIDSGID = true; 193 SystemCallArchitectures = "native"; 194 SystemCallFilter = [ "@system-service" "~@privileged" ]; 195 UMask = "0077"; 196 } 197 ]; 198 }; 199 200 users.users = mkIf (cfg.user == "galene") 201 { 202 galene = { 203 description = "galene Service"; 204 group = cfg.group; 205 isSystemUser = true; 206 }; 207 }; 208 209 users.groups = mkIf (cfg.group == "galene") { 210 galene = { }; 211 }; 212 }; 213 meta.maintainers = with lib.maintainers; [ rgrunbla ]; 214}