at master 11 kB view raw
1{ 2 options, 3 config, 4 lib, 5 pkgs, 6 ... 7}: 8 9with lib; 10 11let 12 runDir = "/run/searx"; 13 14 cfg = config.services.searx; 15 16 settingsFile = pkgs.writeText "settings.yml" (builtins.toJSON cfg.settings); 17 18 faviconsSettingsFile = (pkgs.formats.toml { }).generate "favicons.toml" cfg.faviconsSettings; 19 limiterSettingsFile = (pkgs.formats.toml { }).generate "limiter.toml" cfg.limiterSettings; 20 21 generateConfig = '' 22 cd ${runDir} 23 24 # write NixOS settings as JSON 25 ( 26 umask 077 27 ${pkgs.envsubst}/bin/envsubst < ${settingsFile} > settings.yml 28 ) 29 ''; 30 31 settingType = 32 with types; 33 (oneOf [ 34 bool 35 int 36 float 37 str 38 (listOf settingType) 39 (attrsOf settingType) 40 ]) 41 // { 42 description = "JSON value"; 43 }; 44in 45{ 46 options = { 47 services.searx = { 48 enable = mkOption { 49 type = types.bool; 50 default = false; 51 relatedPackages = [ "searx" ]; 52 description = "Whether to enable Searx, the meta search engine."; 53 }; 54 55 domain = mkOption { 56 type = types.str; 57 description = '' 58 The domain under which searxng will be served. 59 Right now this is only used with the configureNginx option. 60 ''; 61 }; 62 63 environmentFile = mkOption { 64 type = types.nullOr types.path; 65 default = null; 66 description = '' 67 Environment file (see {manpage}`systemd.exec(5)` "EnvironmentFile=" section for the syntax) to define variables for Searx. 68 This option can be used to safely include secret keys into the Searx configuration. 69 ''; 70 }; 71 72 redisCreateLocally = mkOption { 73 type = types.bool; 74 default = false; 75 description = '' 76 Configure a local Redis server for SearXNG. 77 This is required if you want to enable the rate limiter and bot protection of SearXNG. 78 ''; 79 }; 80 81 settings = mkOption { 82 type = types.submodule { 83 freeformType = settingType; 84 imports = [ 85 (mkRenamedOptionModule [ "redis" ] [ "valkey" ]) 86 ]; 87 }; 88 default = { }; 89 example = literalExpression '' 90 { 91 server.port = 8080; 92 server.bind_address = "0.0.0.0"; 93 server.secret_key = "$SEARX_SECRET_KEY"; 94 95 engines = [ { 96 name = "wolframalpha"; 97 shortcut = "wa"; 98 api_key = "$WOLFRAM_API_KEY"; 99 engine = "wolframalpha_api"; 100 } ]; 101 } 102 ''; 103 description = '' 104 Searx settings. 105 These will be merged with (taking precedence over) the default configuration. 106 It's also possible to refer to environment variables (defined in [](#opt-services.searx.environmentFile)) using the syntax `$VARIABLE_NAME`. 107 108 ::: {.note} 109 For available settings, see the Searx [docs](https://docs.searxng.org/admin/settings/index.html). 110 ::: 111 ''; 112 }; 113 114 settingsFile = mkOption { 115 type = types.path; 116 default = "${runDir}/settings.yml"; 117 description = '' 118 The path of the Searx server settings.yml file. 119 If no file is specified, a default file is used (default config file has debug mode enabled). 120 121 ::: {.note} 122 Setting this options overrides [](#opt-services.searx.settings). 123 ::: 124 125 ::: {.warning} 126 This file, along with any secret key it contains, will be copied into the world-readable Nix store. 127 ::: 128 ''; 129 }; 130 131 faviconsSettings = mkOption { 132 type = types.attrsOf settingType; 133 default = { }; 134 example = literalExpression '' 135 { 136 favicons = { 137 cfg_schema = 1; 138 cache = { 139 db_url = "/var/cache/searx/faviconcache.db"; 140 HOLD_TIME = 5184000; 141 LIMIT_TOTAL_BYTES = 2147483648; 142 BLOB_MAX_BYTES = 40960; 143 MAINTENANCE_MODE = "auto"; 144 MAINTENANCE_PERIOD = 600; 145 }; 146 }; 147 } 148 ''; 149 description = '' 150 Favicons settings for SearXNG. 151 152 ::: {.note} 153 For available settings, see the SearXNG 154 [schema file](https://github.com/searxng/searxng/blob/master/searx/favicons/favicons.toml). 155 ::: 156 ''; 157 }; 158 159 limiterSettings = mkOption { 160 type = types.attrsOf settingType; 161 default = { }; 162 example = literalExpression '' 163 { 164 real_ip = { 165 x_for = 1; 166 ipv4_prefix = 32; 167 ipv6_prefix = 56; 168 } 169 botdetection.ip_lists.block_ip = [ 170 # "93.184.216.34" # example.org 171 ]; 172 } 173 ''; 174 description = '' 175 Limiter settings for SearXNG. 176 177 ::: {.note} 178 For available settings, see the SearXNG [schema file](https://github.com/searxng/searxng/blob/master/searx/limiter.toml). 179 ::: 180 ''; 181 }; 182 183 package = mkPackageOption pkgs "searxng" { }; 184 185 configureUwsgi = mkOption { 186 type = types.bool; 187 default = false; 188 description = '' 189 Whether to run searx in uWSGI as a "vassal", instead of using its 190 built-in HTTP server. This is the recommended mode for public or 191 large instances, but is unnecessary for LAN or local-only use. 192 193 ::: {.warning} 194 The built-in HTTP server logs all queries by default. 195 ::: 196 ''; 197 }; 198 199 configureNginx = mkOption { 200 type = types.bool; 201 default = false; 202 description = '' 203 Whether to configure nginx as an frontend to uwsgi. 204 ''; 205 }; 206 207 uwsgiConfig = mkOption { 208 inherit (options.services.uwsgi.instance) type; 209 default = { 210 http = ":8080"; 211 }; 212 example = literalExpression '' 213 { 214 disable-logging = true; 215 http = ":8080"; # serve via HTTP... 216 socket = "/run/searx/searx.sock"; # ...or UNIX socket 217 chmod-socket = "660"; # allow the searx group to read/write to the socket 218 } 219 ''; 220 description = '' 221 Additional configuration of the uWSGI vassal running searx. It 222 should notably specify on which interfaces and ports the vassal 223 should listen. 224 ''; 225 }; 226 }; 227 }; 228 229 imports = [ 230 (mkRenamedOptionModule [ "services" "searx" "configFile" ] [ "services" "searx" "settingsFile" ]) 231 (mkRenamedOptionModule [ "services" "searx" "runInUwsgi" ] [ "services" "searx" "configureUwsgi" ]) 232 ]; 233 234 config = mkIf cfg.enable { 235 environment = { 236 etc = { 237 "searxng/favicons.toml" = lib.mkIf (cfg.faviconsSettings != { }) { 238 source = faviconsSettingsFile; 239 }; 240 "searxng/limiter.toml" = lib.mkIf (cfg.limiterSettings != { }) { 241 source = limiterSettingsFile; 242 }; 243 }; 244 systemPackages = [ cfg.package ]; 245 }; 246 247 services = { 248 nginx = lib.mkIf cfg.configureNginx { 249 enable = true; 250 virtualHosts."${cfg.domain}".locations = { 251 "/" = { 252 recommendedProxySettings = true; 253 recommendedUwsgiSettings = true; 254 uwsgiPass = "unix:${config.services.uwsgi.instance.vassals.searx.socket}"; 255 extraConfig = # nginx 256 '' 257 uwsgi_param HTTP_HOST $host; 258 uwsgi_param HTTP_CONNECTION $http_connection; 259 uwsgi_param HTTP_X_SCHEME $scheme; 260 uwsgi_param HTTP_X_SCRIPT_NAME ""; # NOTE: When we ever make the path configurable, this must be set to anything not "/"! 261 uwsgi_param HTTP_X_REAL_IP $remote_addr; 262 uwsgi_param HTTP_X_FORWARDED_FOR $proxy_add_x_forwarded_for; 263 ''; 264 }; 265 "/static/".alias = lib.mkDefault "${cfg.package}/share/static/"; 266 }; 267 }; 268 269 redis.servers.searx = lib.mkIf cfg.redisCreateLocally { 270 enable = true; 271 user = "searx"; 272 port = 0; 273 }; 274 275 searx = { 276 configureUwsgi = lib.mkIf cfg.configureNginx true; 277 settings = { 278 # merge NixOS settings with defaults settings.yml 279 use_default_settings = mkDefault true; 280 server.base_url = lib.mkIf cfg.configureNginx "http${ 281 lib.optionalString (lib.any lib.id ( 282 with config.services.nginx.virtualHosts."${cfg.domain}"; 283 [ 284 onlySSL 285 addSSL 286 forceSSL 287 ] 288 )) "s" 289 }://${cfg.domain}/"; 290 valkey.url = lib.mkIf cfg.redisCreateLocally "unix://${config.services.redis.servers.searx.unixSocket}"; 291 }; 292 }; 293 294 uwsgi = mkIf cfg.configureUwsgi { 295 enable = true; 296 plugins = [ "python3" ]; 297 instance.type = "emperor"; 298 instance.vassals.searx = { 299 type = "normal"; 300 strict = true; 301 immediate-uid = "searx"; 302 immediate-gid = "searx"; 303 lazy-apps = true; 304 enable-threads = true; 305 module = "searx.webapp"; 306 env = [ 307 "SEARXNG_SETTINGS_PATH=${cfg.settingsFile}" 308 ]; 309 buffer-size = 32768; 310 pythonPackages = _: [ cfg.package ]; 311 } 312 // lib.optionalAttrs cfg.configureNginx { 313 socket = "/run/searx/uwsgi.sock"; 314 chmod-socket = "660"; 315 } 316 // cfg.uwsgiConfig; 317 }; 318 }; 319 320 systemd.services = { 321 nginx = lib.mkIf cfg.configureNginx { 322 serviceConfig.SupplementaryGroups = [ "searx" ]; 323 }; 324 325 searx-init = { 326 description = "Initialise Searx settings"; 327 serviceConfig = { 328 Type = "oneshot"; 329 RemainAfterExit = true; 330 User = "searx"; 331 RuntimeDirectory = "searx"; 332 RuntimeDirectoryMode = "750"; 333 RuntimeDirectoryPreserve = "yes"; 334 } 335 // optionalAttrs (cfg.environmentFile != null) { 336 EnvironmentFile = cfg.environmentFile; 337 }; 338 script = generateConfig; 339 }; 340 341 searx = mkIf (!cfg.configureUwsgi) { 342 description = "Searx server, the meta search engine."; 343 wantedBy = [ "multi-user.target" ]; 344 requires = [ "searx-init.service" ]; 345 after = [ 346 "searx-init.service" 347 "network.target" 348 ]; 349 serviceConfig = { 350 User = "searx"; 351 Group = "searx"; 352 ExecStart = lib.getExe cfg.package; 353 } 354 // optionalAttrs (cfg.environmentFile != null) { 355 EnvironmentFile = cfg.environmentFile; 356 }; 357 environment = { 358 SEARXNG_SETTINGS_PATH = cfg.settingsFile; 359 }; 360 }; 361 362 uwsgi = mkIf cfg.configureUwsgi { 363 requires = [ "searx-init.service" ]; 364 after = [ "searx-init.service" ]; 365 restartTriggers = [ 366 cfg.package 367 cfg.settingsFile 368 ] 369 ++ lib.optional (cfg.environmentFile != null) cfg.environmentFile; 370 }; 371 }; 372 373 users = { 374 groups.searx = { }; 375 users.searx = { 376 description = "Searx daemon user"; 377 group = "searx"; 378 isSystemUser = true; 379 }; 380 }; 381 }; 382 383 meta.maintainers = with maintainers; [ 384 SuperSandro2000 385 _999eagle 386 ]; 387}