at 24.11-pre 5.9 kB view raw
1{ config, pkgs, lib, ... }: 2let 3 inherit (lib) mkOption types mkIf mkMerge mkDefault mkEnableOption mkPackageOption maintainers; 4 cfg = config.services.db-rest; 5in 6{ 7 options = { 8 services.db-rest = { 9 enable = mkEnableOption "db-rest service"; 10 11 user = mkOption { 12 type = types.str; 13 default = "db-rest"; 14 description = "User account under which db-rest runs."; 15 }; 16 17 group = mkOption { 18 type = types.str; 19 default = "db-rest"; 20 description = "Group under which db-rest runs."; 21 }; 22 23 host = mkOption { 24 type = types.str; 25 default = "127.0.0.1"; 26 description = "The host address the db-rest server should listen on."; 27 }; 28 29 port = mkOption { 30 type = types.port; 31 default = 3000; 32 description = "The port the db-rest server should listen on."; 33 }; 34 35 redis = { 36 enable = mkOption { 37 type = types.bool; 38 default = false; 39 description = "Enable caching with redis for db-rest."; 40 }; 41 42 createLocally = mkOption { 43 type = types.bool; 44 default = true; 45 description = "Configure a local redis server for db-rest."; 46 }; 47 48 host = mkOption { 49 type = with types; nullOr str; 50 default = null; 51 description = "Redis host."; 52 }; 53 54 port = mkOption { 55 type = with types; nullOr port; 56 default = null; 57 description = "Redis port."; 58 }; 59 60 user = mkOption { 61 type = with types; nullOr str; 62 default = null; 63 description = "Optional username used for authentication with redis."; 64 }; 65 66 passwordFile = mkOption { 67 type = with types; nullOr path; 68 default = null; 69 example = "/run/keys/db-rest/pasword-redis-db"; 70 description = "Path to a file containing the redis password."; 71 }; 72 73 useSSL = mkOption { 74 type = types.bool; 75 default = true; 76 description = "Use SSL if using a redis network connection."; 77 }; 78 }; 79 80 package = mkPackageOption pkgs "db-rest" { }; 81 }; 82 }; 83 84 config = mkIf cfg.enable { 85 assertions = [ 86 { 87 assertion = (cfg.redis.enable && !cfg.redis.createLocally) -> (cfg.redis.host != null && cfg.redis.port != null); 88 message = '' 89 {option}`services.db-rest.redis.createLocally` and redis network connection ({option}`services.db-rest.redis.host` or {option}`services.db-rest.redis.port`) enabled. Disable either of them. 90 ''; 91 } 92 { 93 assertion = (cfg.redis.enable && !cfg.redis.createLocally) -> (cfg.redis.passwordFile != null); 94 message = '' 95 {option}`services.db-rest.redis.createLocally` is disabled, but {option}`services.db-rest.redis.passwordFile` is not set. 96 ''; 97 } 98 ]; 99 100 systemd.services.db-rest = mkMerge [ 101 { 102 description = "db-rest service"; 103 after = [ "network.target" ] 104 ++ lib.optional cfg.redis.createLocally "redis-db-rest.service"; 105 requires = lib.optional cfg.redis.createLocally "redis-db-rest.service"; 106 wantedBy = [ "multi-user.target" ]; 107 serviceConfig = { 108 Type = "simple"; 109 Restart = "always"; 110 RestartSec = 5; 111 WorkingDirectory = cfg.package; 112 User = cfg.user; 113 Group = cfg.group; 114 RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ]; 115 MemoryDenyWriteExecute = false; 116 LoadCredential = lib.optional (cfg.redis.enable && cfg.redis.passwordFile != null) "REDIS_PASSWORD:${cfg.redis.passwordFile}"; 117 ExecStart = mkDefault "${cfg.package}/bin/db-rest"; 118 119 RemoveIPC = true; 120 NoNewPrivileges = true; 121 PrivateDevices = true; 122 ProtectClock = true; 123 ProtectKernelLogs = true; 124 ProtectControlGroups = true; 125 ProtectKernelModules = true; 126 PrivateMounts = true; 127 SystemCallArchitectures = "native"; 128 ProtectHostname = true; 129 LockPersonality = true; 130 ProtectKernelTunables = true; 131 RestrictRealtime = true; 132 RestrictSUIDSGID = true; 133 RestrictNamespaces = true; 134 ProtectSystem = "strict"; 135 ProtectProc = "invisible"; 136 ProcSubset = "pid"; 137 ProtectHome = true; 138 PrivateUsers = true; 139 PrivateTmp = true; 140 CapabilityBoundingSet = ""; 141 }; 142 environment = { 143 NODE_ENV = "production"; 144 NODE_EXTRA_CA_CERTS = "/etc/ssl/certs/ca-certificates.crt"; 145 HOSTNAME = cfg.host; 146 PORT = toString cfg.port; 147 }; 148 } 149 (mkIf cfg.redis.enable (if cfg.redis.createLocally then 150 { environment.REDIS_URL = config.services.redis.servers.db-rest.unixSocket; } 151 else 152 { 153 script = 154 let 155 username = lib.optionalString (cfg.redis.user != null) (cfg.redis.user); 156 host = cfg.redis.host; 157 port = toString cfg.redis.port; 158 protocol = if cfg.redis.useSSL then "rediss" else "redis"; 159 in 160 '' 161 export REDIS_URL="${protocol}://${username}:$(${config.systemd.package}/bin/systemd-creds cat REDIS_PASSWORD)@${host}:${port}" 162 exec ${cfg.package}/bin/db-rest 163 ''; 164 })) 165 ]; 166 167 users.users = lib.mkMerge [ 168 (lib.mkIf (cfg.user == "db-rest") { 169 db-rest = { 170 isSystemUser = true; 171 group = cfg.group; 172 }; 173 }) 174 (lib.mkIf cfg.redis.createLocally { ${cfg.user}.extraGroups = [ "redis-db-rest" ]; }) 175 ]; 176 177 users.groups = lib.mkIf (cfg.group == "db-rest") { db-rest = { }; }; 178 179 services.redis.servers.db-rest.enable = cfg.redis.enable && cfg.redis.createLocally; 180 }; 181 meta.maintainers = with maintainers; [ marie ]; 182}