From a3c60d2ddc9f70dca3fa5c5926aefc9a74bd2519 Mon Sep 17 00:00:00 2001 From: Gabriel Fontes Date: Fri, 1 Dec 2023 15:42:46 -0300 Subject: [PATCH] nixos/nginx: make redirect status code configurable Add an option to configure which code globalRedirect and forceSSL use. It previously was always 301 with no easy way to override. --- .../manual/release-notes/rl-2405.section.md | 4 +++ .../services/web-servers/nginx/default.nix | 4 +-- .../web-servers/nginx/vhost-options.nix | 25 ++++++++++++++----- nixos/tests/all-tests.nix | 1 + nixos/tests/nginx-redirectcode.nix | 25 +++++++++++++++++++ 5 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 nixos/tests/nginx-redirectcode.nix diff --git a/nixos/doc/manual/release-notes/rl-2405.section.md b/nixos/doc/manual/release-notes/rl-2405.section.md index 15696e802c12..45e298e682c7 100644 --- a/nixos/doc/manual/release-notes/rl-2405.section.md +++ b/nixos/doc/manual/release-notes/rl-2405.section.md @@ -47,6 +47,10 @@ The pre-existing [services.ankisyncd](#opt-services.ankisyncd.enable) has been m existing process, but will need to start that process from gdb (so it is a child). Or you can set `boot.kernel.sysctl."kernel.yama.ptrace_scope"` to 0. +- [Nginx virtual hosts](#opt-services.nginx.virtualHosts) using `forceSSL` or + `globalRedirect` can now have redirect codes other than 301 through + `redirectCode`. + - Gitea 1.21 upgrade has several breaking changes, including: - Custom themes and other assets that were previously stored in `custom/public/*` now belong in `custom/public/assets/*` - New instances of Gitea using MySQL now ignore the `[database].CHARSET` config option and always use the `utf8mb4` charset, existing instances should migrate via the `gitea doctor convert` CLI command. diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix index cf70dc325945..848d12b17f87 100644 --- a/nixos/modules/services/web-servers/nginx/default.nix +++ b/nixos/modules/services/web-servers/nginx/default.nix @@ -377,7 +377,7 @@ let server_name ${vhost.serverName} ${concatStringsSep " " vhost.serverAliases}; ${acmeLocation} location / { - return 301 https://$host$request_uri; + return ${toString vhost.redirectCode} https://$host$request_uri; } } ''} @@ -396,7 +396,7 @@ let ${optionalString (vhost.root != null) "root ${vhost.root};"} ${optionalString (vhost.globalRedirect != null) '' location / { - return 301 http${optionalString hasSSL "s"}://${vhost.globalRedirect}$request_uri; + return ${toString vhost.redirectCode} http${optionalString hasSSL "s"}://${vhost.globalRedirect}$request_uri; } ''} ${optionalString hasSSL '' diff --git a/nixos/modules/services/web-servers/nginx/vhost-options.nix b/nixos/modules/services/web-servers/nginx/vhost-options.nix index 9db4c8e23025..64a95afab9f4 100644 --- a/nixos/modules/services/web-servers/nginx/vhost-options.nix +++ b/nixos/modules/services/web-servers/nginx/vhost-options.nix @@ -162,10 +162,11 @@ with lib; type = types.bool; default = false; description = lib.mdDoc '' - Whether to add a separate nginx server block that permanently redirects (301) - all plain HTTP traffic to HTTPS. This will set defaults for - `listen` to listen on all interfaces on the respective default - ports (80, 443), where the non-SSL listens are used for the redirect vhosts. + Whether to add a separate nginx server block that redirects (defaults + to 301, configurable with `redirectCode`) all plain HTTP traffic to + HTTPS. This will set defaults for `listen` to listen on all interfaces + on the respective default ports (80, 443), where the non-SSL listens + are used for the redirect vhosts. ''; }; @@ -307,8 +308,20 @@ with lib; default = null; example = "newserver.example.org"; description = lib.mdDoc '' - If set, all requests for this host are redirected permanently to - the given hostname. + If set, all requests for this host are redirected (defaults to 301, + configurable with `redirectCode`) to the given hostname. + ''; + }; + + redirectCode = mkOption { + type = types.ints.between 300 399; + default = 301; + example = 308; + description = lib.mdDoc '' + HTTP status used by `globalRedirect` and `forceSSL`. Possible usecases + include temporary (302, 307) redirects, keeping the request method and + body (307, 308), or explicitly resetting the method to GET (303). + See . ''; }; diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index e0572e3bed9c..9f3bf284da02 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -583,6 +583,7 @@ in { nginx-njs = handleTest ./nginx-njs.nix {}; nginx-proxyprotocol = handleTest ./nginx-proxyprotocol {}; nginx-pubhtml = handleTest ./nginx-pubhtml.nix {}; + nginx-redirectcode = handleTest ./nginx-redirectcode.nix {}; nginx-sso = handleTest ./nginx-sso.nix {}; nginx-status-page = handleTest ./nginx-status-page.nix {}; nginx-tmpdir = handleTest ./nginx-tmpdir.nix {}; diff --git a/nixos/tests/nginx-redirectcode.nix b/nixos/tests/nginx-redirectcode.nix new file mode 100644 index 000000000000..f60434a21a85 --- /dev/null +++ b/nixos/tests/nginx-redirectcode.nix @@ -0,0 +1,25 @@ +import ./make-test-python.nix ({ pkgs, lib, ... }: { + name = "nginx-redirectcode"; + meta.maintainers = with lib.maintainers; [ misterio77 ]; + + nodes = { + webserver = { pkgs, lib, ... }: { + services.nginx = { + enable = true; + virtualHosts.localhost = { + globalRedirect = "example.com/foo"; + # With 308 (and 307), the method and body are to be kept when following it + redirectCode = 308; + }; + }; + }; + }; + + testScript = '' + webserver.wait_for_unit("nginx") + webserver.wait_for_open_port(80) + + # Check the status code + webserver.succeed("curl -si http://localhost | grep '^HTTP/[0-9.]\+ 308 Permanent Redirect'") + ''; +})