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