diff --git a/nixos/doc/manual/release-notes/rl-2611.section.md b/nixos/doc/manual/release-notes/rl-2611.section.md index 6600a0cb9479..78618850df08 100644 --- a/nixos/doc/manual/release-notes/rl-2611.section.md +++ b/nixos/doc/manual/release-notes/rl-2611.section.md @@ -73,6 +73,8 @@ - `boot.loader.systemd-boot` gained support for [Automatic Boot Assessment](https://systemd.io/AUTOMATIC_BOOT_ASSESSMENT/) via the new [`boot.loader.systemd-boot.bootCounting`](#opt-boot.loader.systemd-boot.bootCounting.enable) options, allowing automatic detection of and recovery from bad NixOS generations. As part of this change, boot loader entries on the ESP/XBOOTLDR partition are now named `nixos-.conf` instead of `nixos-generation-.conf`; existing entries are migrated automatically on the next `nixos-rebuild boot`/`switch`. +- `services.nginx` gained a [`lua`](#opt-services.nginx.lua.enable) option to enable Lua scripting via OpenResty's lua-nginx-module on a stock nginx, configuring `lua_package_path`/`lua_package_cpath` from the packages listed in [`services.nginx.lua.extraPackages`](#opt-services.nginx.lua.extraPackages). Use this to add Lua to a regular nginx; for the full OpenResty platform (libraries that rely on its bundled lualib, such as `lua-resty-openidc`), set `services.nginx.package` to `pkgs.openresty` instead — the option configures the Lua search path for it too. + - `security.polkit.settings` added for RFC42 style configuration of the polkitd daemon. - The `programs.fuse` module, which provides the `fusermount3` executable and the `/etc/fuse.conf` config file, is now opt-in. The obligation to enable it has been shifted to its various consumers (e.g. gvfs, flatpak, appimage, sshfs). This can break fuse consumers at runtime, that don't explicitly declare that dependency with a module, e.g the mounting functionality in various backup tools (borg, restic, rclone, ...). diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix index 73efd441d23d..c213aaf652ea 100644 --- a/nixos/modules/services/web-servers/nginx/default.nix +++ b/nixos/modules/services/web-servers/nginx/default.nix @@ -126,6 +126,28 @@ let '') (filterAttrs (name: conf: conf.enable) cfg.proxyCachePath) ); + # openresty bundles the lua module and resty.core; stock nginx needs them added. + packageBundlesLua = p: lib.getName p == "openresty"; + luaEnv = pkgs.luajit_openresty.withPackages ( + ps: + lib.optional (cfg.lua.enable && !packageBundlesLua cfg.package) ps.lua-resty-core + ++ cfg.lua.extraPackages ps + ); + luaVersion = pkgs.luajit_openresty.luaversion; + # Lua modules install under lib/lua or share/lua depending on the package; ;; keeps nginx's defaults. + luaConfig = '' + lua_package_path '${ + lib.concatMapStringsSep ";" (s: "${luaEnv}/${s}") [ + "lib/lua/${luaVersion}/?.lua" + "lib/lua/${luaVersion}/?/init.lua" + "share/lua/${luaVersion}/?.lua" + "share/lua/${luaVersion}/?/init.lua" + ] + };;'; + lua_package_cpath '${luaEnv}/lib/lua/${luaVersion}/?.so;;'; + lua_ssl_trusted_certificate ${config.security.pki.caBundle}; + ''; + toUpstreamParameter = key: value: if builtins.isBool value then lib.optionalString value key else "${key}=${toString value}"; @@ -295,6 +317,8 @@ let server_tokens ${if cfg.serverTokens then "on" else "off"}; + ${optionalString cfg.lua.enable luaConfig} + ${cfg.commonHttpConfig} ${proxyCachePathConfig} @@ -771,7 +795,11 @@ in apply = p: p.override { - modules = lib.unique (p.modules ++ cfg.additionalModules); + modules = lib.unique ( + p.modules + ++ cfg.additionalModules + ++ lib.optional (cfg.lua.enable && !packageBundlesLua p) pkgs.nginxModules.lua + ); }; description = '' Nginx package to use. This defaults to the stable version. Note @@ -791,6 +819,36 @@ in ''; }; + lua = { + enable = mkEnableOption '' + Lua scripting in nginx via OpenResty's lua-nginx-module, + wiring up `lua_package_path`/`lua_package_cpath` for + {option}`services.nginx.lua.extraPackages`. + + Use this to add Lua to a stock nginx. For the full OpenResty platform — + required by libraries that depend on its bundled lualib (for example + `lua-resty-openidc`, which needs `resty.string` and friends) — set + {option}`services.nginx.package` to `pkgs.openresty` instead; this option + then only sets up the search path and leaves OpenResty's built-in Lua + module in place + ''; + + extraPackages = mkOption { + type = types.functionTo (types.listOf types.package); + default = ps: [ ]; + defaultText = literalExpression "ps: [ ]"; + example = literalExpression '' + ps: with ps; [ lua-resty-openidc ] + ''; + description = '' + Extra Lua packages to put on `lua_package_path` / `lua_package_cpath`, + for both stock nginx and `pkgs.openresty`. Packages are selected from + `pkgs.luajit_openresty.pkgs`. `lua-resty-core`, which the Lua module + requires to start, is added automatically. + ''; + }; + }; + logError = mkOption { default = "stderr"; type = types.str; diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index aa1aa8933a87..90dd935d27ed 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -1128,6 +1128,7 @@ in nginx-etag-compression = runTest ./nginx-etag-compression.nix; nginx-globalredirect = runTest ./nginx-globalredirect.nix; nginx-http3 = import ./nginx-http3.nix { inherit pkgs runTest; }; + nginx-lua = runTest ./nginx-lua.nix; nginx-mime = runTest ./nginx-mime.nix; nginx-modsecurity = runTest ./nginx-modsecurity.nix; nginx-moreheaders = runTest ./nginx-moreheaders.nix; diff --git a/nixos/tests/nginx-lua.nix b/nixos/tests/nginx-lua.nix new file mode 100644 index 000000000000..8159882a7d5c --- /dev/null +++ b/nixos/tests/nginx-lua.nix @@ -0,0 +1,37 @@ +{ lib, ... }: +{ + name = "nginx-lua"; + + meta.maintainers = [ lib.maintainers.kranzes ]; + + nodes.machine = { + services.nginx = { + enable = true; + lua = { + enable = true; + extraPackages = p: [ + p.lua-resty-lrucache + p.lua-cjson + ]; + }; + virtualHosts."localhost".locations."/" = { + extraConfig = '' + default_type text/plain; + content_by_lua_block { + local cache = require("resty.lrucache").new(8) + cache:set("greeting", require("cjson").decode('"Hello world!"')) + ngx.say((cache:get("greeting"))) + } + ''; + }; + }; + }; + + testScript = '' + machine.wait_for_unit("nginx") + machine.wait_for_open_port(80) + + response = machine.wait_until_succeeds("curl -fsS http://127.0.0.1/").strip() + assert response == "Hello world!", f"Expected 'Hello world!', got '{response}'" + ''; +} diff --git a/nixos/tests/openresty-lua.nix b/nixos/tests/openresty-lua.nix index 367154947371..999bd6963470 100644 --- a/nixos/tests/openresty-lua.nix +++ b/nixos/tests/openresty-lua.nix @@ -1,12 +1,4 @@ -{ pkgs, lib, ... }: -let - luaLibs = [ - pkgs.lua.pkgs.markdown - ]; - - getLuaPath = lib: "${lib}/share/lua/${pkgs.lua.luaversion}/?.lua"; - luaPath = lib.concatStringsSep ";" (map getLuaPath luaLibs); -in +{ pkgs, ... }: { name = "openresty-lua"; meta = with pkgs.lib.maintainers; { @@ -15,7 +7,7 @@ in nodes = { webserver = - { pkgs, lib, ... }: + { pkgs, ... }: { networking = { extraHosts = '' @@ -27,9 +19,10 @@ in enable = true; package = pkgs.openresty; - commonHttpConfig = '' - lua_package_path '${luaPath};;'; - ''; + lua = { + enable = true; + extraPackages = p: [ p.markdown ]; + }; virtualHosts."default.test" = { default = true;