at master 9.6 kB view raw
1{ 2 config, 3 pkgs, 4 lib, 5 ... 6}: 7 8let 9 cfg = config.services.glitchtip; 10 pkg = cfg.package; 11 inherit (pkg.passthru) python; 12 13 environment = lib.mapAttrs ( 14 _: value: 15 if value == true then 16 "True" 17 else if value == false then 18 "False" 19 else 20 toString value 21 ) cfg.settings; 22in 23 24{ 25 meta.maintainers = with lib.maintainers; [ 26 defelo 27 felbinger 28 ]; 29 30 options = { 31 services.glitchtip = { 32 enable = lib.mkEnableOption "GlitchTip"; 33 34 package = lib.mkPackageOption pkgs "glitchtip" { }; 35 36 user = lib.mkOption { 37 type = lib.types.str; 38 description = "The user account under which GlitchTip runs."; 39 default = "glitchtip"; 40 }; 41 42 group = lib.mkOption { 43 type = lib.types.str; 44 description = "The group under which GlitchTip runs."; 45 default = "glitchtip"; 46 }; 47 48 listenAddress = lib.mkOption { 49 type = lib.types.str; 50 description = "The address to listen on."; 51 default = "127.0.0.1"; 52 example = "0.0.0.0"; 53 }; 54 55 port = lib.mkOption { 56 type = lib.types.port; 57 description = "The port to listen on."; 58 default = 8000; 59 }; 60 61 stateDir = lib.mkOption { 62 type = lib.types.path; 63 description = "State directory of glitchtip."; 64 default = "/var/lib/glitchtip"; 65 }; 66 67 settings = lib.mkOption { 68 description = '' 69 Configuration of GlitchTip. See <https://glitchtip.com/documentation/install#configuration> for more information. 70 ''; 71 default = { }; 72 defaultText = lib.literalExpression '' 73 { 74 DEBUG = 0; 75 DEBUG_TOOLBAR = 0; 76 DATABASE_URL = lib.mkIf config.services.glitchtip.database.createLocally "postgresql://@/glitchtip"; 77 REDIS_URL = lib.mkIf config.services.glitchtip.redis.createLocally "unix://''${config.services.redis.servers.glitchtip.unixSocket}"; 78 CELERY_BROKER_URL = lib.mkIf config.services.glitchtip.redis.createLocally "redis+socket://''${config.services.redis.servers.glitchtip.unixSocket}"; 79 } 80 ''; 81 example = { 82 GLITCHTIP_DOMAIN = "https://glitchtip.example.com"; 83 DATABASE_URL = "postgres://postgres:postgres@postgres/postgres"; 84 }; 85 86 type = lib.types.submodule { 87 freeformType = 88 with lib.types; 89 attrsOf (oneOf [ 90 str 91 int 92 bool 93 ]); 94 95 options = { 96 GLITCHTIP_DOMAIN = lib.mkOption { 97 type = lib.types.str; 98 description = "The URL under which GlitchTip is externally reachable."; 99 example = "https://glitchtip.example.com"; 100 }; 101 102 ENABLE_USER_REGISTRATION = lib.mkOption { 103 type = lib.types.bool; 104 description = '' 105 When true, any user will be able to register. When false, user self-signup is disabled after the first user is registered. Subsequent users must be created by a superuser on the backend and organization invitations may only be sent to existing users. 106 ''; 107 default = false; 108 }; 109 110 ENABLE_ORGANIZATION_CREATION = lib.mkOption { 111 type = lib.types.bool; 112 description = '' 113 When false, only superusers will be able to create new organizations after the first. When true, any user can create a new organization. 114 ''; 115 default = false; 116 }; 117 }; 118 }; 119 }; 120 121 environmentFiles = lib.mkOption { 122 type = lib.types.listOf lib.types.path; 123 default = [ ]; 124 example = [ "/run/secrets/glitchtip.env" ]; 125 description = '' 126 Files to load environment variables from in addition to [](#opt-services.glitchtip.settings). 127 This is useful to avoid putting secrets into the nix store. 128 See <https://glitchtip.com/documentation/install#configuration> for more information. 129 ''; 130 }; 131 132 database.createLocally = lib.mkOption { 133 type = lib.types.bool; 134 default = true; 135 description = '' 136 Whether to enable and configure a local PostgreSQL database server. 137 ''; 138 }; 139 140 redis.createLocally = lib.mkOption { 141 type = lib.types.bool; 142 default = true; 143 description = '' 144 Whether to enable and configure a local Redis instance. 145 ''; 146 }; 147 148 gunicorn.extraArgs = lib.mkOption { 149 type = lib.types.listOf lib.types.str; 150 default = [ ]; 151 description = "Extra arguments for gunicorn."; 152 }; 153 154 celery.extraArgs = lib.mkOption { 155 type = lib.types.listOf lib.types.str; 156 default = [ ]; 157 description = "Extra arguments for celery."; 158 }; 159 }; 160 }; 161 162 config = lib.mkIf cfg.enable { 163 services.glitchtip.settings = { 164 DEBUG = lib.mkDefault 0; 165 DEBUG_TOOLBAR = lib.mkDefault 0; 166 PYTHONPATH = "${python.pkgs.makePythonPath pkg.propagatedBuildInputs}:${pkg}/lib/glitchtip"; 167 DATABASE_URL = lib.mkIf cfg.database.createLocally "postgresql://@/glitchtip"; 168 REDIS_URL = lib.mkIf cfg.redis.createLocally "unix://${config.services.redis.servers.glitchtip.unixSocket}"; 169 CELERY_BROKER_URL = lib.mkIf cfg.redis.createLocally "redis+socket://${config.services.redis.servers.glitchtip.unixSocket}"; 170 GLITCHTIP_VERSION = pkg.version; 171 }; 172 173 systemd.services = 174 let 175 commonService = { 176 wantedBy = [ "multi-user.target" ]; 177 178 wants = [ "network-online.target" ]; 179 requires = 180 lib.optional cfg.database.createLocally "postgresql.target" 181 ++ lib.optional cfg.redis.createLocally "redis-glitchtip.service"; 182 after = [ 183 "network-online.target" 184 ] 185 ++ lib.optional cfg.database.createLocally "postgresql.target" 186 ++ lib.optional cfg.redis.createLocally "redis-glitchtip.service"; 187 188 inherit environment; 189 }; 190 191 commonServiceConfig = { 192 User = cfg.user; 193 Group = cfg.group; 194 RuntimeDirectory = "glitchtip"; 195 StateDirectory = "glitchtip"; 196 EnvironmentFile = cfg.environmentFiles; 197 WorkingDirectory = "${pkg}/lib/glitchtip"; 198 BindPaths = [ "${cfg.stateDir}/uploads:${pkg}/lib/glitchtip/uploads" ]; 199 200 # hardening 201 AmbientCapabilities = ""; 202 CapabilityBoundingSet = [ "" ]; 203 DevicePolicy = "closed"; 204 LockPersonality = true; 205 MemoryDenyWriteExecute = true; 206 NoNewPrivileges = true; 207 PrivateDevices = true; 208 PrivateTmp = true; 209 PrivateUsers = true; 210 ProcSubset = "pid"; 211 ProtectClock = true; 212 ProtectControlGroups = true; 213 ProtectHome = true; 214 ProtectHostname = true; 215 ProtectKernelLogs = true; 216 ProtectKernelModules = true; 217 ProtectKernelTunables = true; 218 ProtectProc = "invisible"; 219 ProtectSystem = "strict"; 220 RemoveIPC = true; 221 RestrictAddressFamilies = [ "AF_INET AF_INET6 AF_UNIX" ]; 222 RestrictNamespaces = true; 223 RestrictRealtime = true; 224 RestrictSUIDSGID = true; 225 SystemCallArchitectures = "native"; 226 SystemCallFilter = [ 227 "@system-service" 228 "~@privileged" 229 "~@resources" 230 "@chown" 231 ]; 232 UMask = "0077"; 233 }; 234 in 235 { 236 glitchtip = commonService // { 237 description = "GlitchTip"; 238 239 preStart = '' 240 ${lib.getExe pkg} migrate 241 ''; 242 243 serviceConfig = commonServiceConfig // { 244 ExecStart = '' 245 ${lib.getExe python.pkgs.gunicorn} \ 246 --bind=${cfg.listenAddress}:${toString cfg.port} \ 247 ${lib.concatStringsSep " " cfg.gunicorn.extraArgs} \ 248 glitchtip.wsgi 249 ''; 250 }; 251 }; 252 253 glitchtip-worker = commonService // { 254 description = "GlitchTip Job Runner"; 255 256 serviceConfig = commonServiceConfig // { 257 ExecStart = '' 258 ${lib.getExe python.pkgs.celery} \ 259 -A glitchtip worker \ 260 -B -s /run/glitchtip/celerybeat-schedule \ 261 ${lib.concatStringsSep " " cfg.celery.extraArgs} 262 ''; 263 }; 264 }; 265 }; 266 267 services.postgresql = lib.mkIf cfg.database.createLocally { 268 enable = true; 269 ensureDatabases = [ "glitchtip" ]; 270 ensureUsers = [ 271 { 272 name = "glitchtip"; 273 ensureDBOwnership = true; 274 } 275 ]; 276 }; 277 278 services.redis.servers.glitchtip.enable = cfg.redis.createLocally; 279 280 users.users = lib.mkIf (cfg.user == "glitchtip") { 281 glitchtip = { 282 group = cfg.group; 283 extraGroups = lib.optionals cfg.redis.createLocally [ "redis-glitchtip" ]; 284 isSystemUser = true; 285 }; 286 }; 287 288 users.groups = lib.mkIf (cfg.group == "glitchtip") { glitchtip = { }; }; 289 290 systemd.tmpfiles.settings.glitchtip."${cfg.stateDir}/uploads".d = { inherit (cfg) user group; }; 291 292 environment.systemPackages = 293 let 294 glitchtip-manage = pkgs.writeShellScriptBin "glitchtip-manage" '' 295 set -o allexport 296 ${lib.toShellVars environment} 297 ${lib.concatMapStringsSep "\n" (f: "source ${f}") cfg.environmentFiles} 298 ${config.security.wrapperDir}/sudo -E -u ${cfg.user} ${lib.getExe pkg} "$@" 299 ''; 300 in 301 [ glitchtip-manage ]; 302 }; 303}