at 23.05-pre 4.3 kB view raw
1{ config, pkgs, lib, ... }: 2 3with lib; 4let 5 cfg = config.services.tandoor-recipes; 6 pkg = cfg.package; 7 8 # SECRET_KEY through an env file 9 env = { 10 GUNICORN_CMD_ARGS = "--bind=${cfg.address}:${toString cfg.port}"; 11 DEBUG = "0"; 12 MEDIA_ROOT = "/var/lib/tandoor-recipes"; 13 } // optionalAttrs (config.time.timeZone != null) { 14 TIMEZONE = config.time.timeZone; 15 } // ( 16 lib.mapAttrs (_: toString) cfg.extraConfig 17 ); 18 19 manage = 20 let 21 setupEnv = lib.concatStringsSep "\n" (mapAttrsToList (name: val: "export ${name}=\"${val}\"") env); 22 in 23 pkgs.writeShellScript "manage" '' 24 ${setupEnv} 25 exec ${pkg}/bin/tandoor-recipes "$@" 26 ''; 27in 28{ 29 meta.maintainers = with maintainers; [ ambroisie ]; 30 31 options.services.tandoor-recipes = { 32 enable = mkOption { 33 type = lib.types.bool; 34 default = false; 35 description = lib.mdDoc '' 36 Enable Tandoor Recipes. 37 38 When started, the Tandoor Recipes database is automatically created if 39 it doesn't exist and updated if the package has changed. Both tasks are 40 achieved by running a Django migration. 41 42 A script to manage the instance (by wrapping Django's manage.py) is linked to 43 `/var/lib/tandoor-recipes/tandoor-recipes-manage`. 44 ''; 45 }; 46 47 address = mkOption { 48 type = types.str; 49 default = "localhost"; 50 description = lib.mdDoc "Web interface address."; 51 }; 52 53 port = mkOption { 54 type = types.port; 55 default = 8080; 56 description = lib.mdDoc "Web interface port."; 57 }; 58 59 extraConfig = mkOption { 60 type = types.attrs; 61 default = { }; 62 description = lib.mdDoc '' 63 Extra tandoor recipes config options. 64 65 See [the example dot-env file](https://raw.githubusercontent.com/vabene1111/recipes/master/.env.template) 66 for available options. 67 ''; 68 example = { 69 ENABLE_SIGNUP = "1"; 70 }; 71 }; 72 73 package = mkOption { 74 type = types.package; 75 default = pkgs.tandoor-recipes; 76 defaultText = literalExpression "pkgs.tandoor-recipes"; 77 description = lib.mdDoc "The Tandoor Recipes package to use."; 78 }; 79 }; 80 81 config = mkIf cfg.enable { 82 systemd.services.tandoor-recipes = { 83 description = "Tandoor Recipes server"; 84 85 serviceConfig = { 86 ExecStart = '' 87 ${pkg.python.pkgs.gunicorn}/bin/gunicorn recipes.wsgi 88 ''; 89 Restart = "on-failure"; 90 91 User = "tandoor_recipes"; 92 DynamicUser = true; 93 StateDirectory = "tandoor-recipes"; 94 WorkingDirectory = "/var/lib/tandoor-recipes"; 95 RuntimeDirectory = "tandoor-recipes"; 96 97 BindReadOnlyPaths = [ 98 "${config.environment.etc."ssl/certs/ca-certificates.crt".source}:/etc/ssl/certs/ca-certificates.crt" 99 builtins.storeDir 100 "-/etc/resolv.conf" 101 "-/etc/nsswitch.conf" 102 "-/etc/hosts" 103 "-/etc/localtime" 104 "-/run/postgresql" 105 ]; 106 CapabilityBoundingSet = ""; 107 LockPersonality = true; 108 MemoryDenyWriteExecute = true; 109 PrivateDevices = true; 110 PrivateUsers = true; 111 ProtectClock = true; 112 ProtectControlGroups = true; 113 ProtectHome = true; 114 ProtectHostname = true; 115 ProtectKernelLogs = true; 116 ProtectKernelModules = true; 117 ProtectKernelTunables = true; 118 RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; 119 RestrictNamespaces = true; 120 RestrictRealtime = true; 121 SystemCallArchitectures = "native"; 122 # gunicorn needs setuid 123 SystemCallFilter = [ "@system-service" "~@privileged" "@resources" "@setuid" "@keyring" ]; 124 UMask = "0066"; 125 } // lib.optionalAttrs (cfg.port < 1024) { 126 AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ]; 127 CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ]; 128 }; 129 130 wantedBy = [ "multi-user.target" ]; 131 132 preStart = '' 133 ln -sf ${manage} tandoor-recipes-manage 134 135 # Let django migrate the DB as needed 136 ${pkg}/bin/tandoor-recipes migrate 137 ''; 138 139 environment = env // { 140 PYTHONPATH = "${pkg.python.pkgs.makePythonPath pkg.propagatedBuildInputs}:${pkg}/lib/tandoor-recipes"; 141 }; 142 }; 143 }; 144}