nixos/nginx: add locations.<name>.useGrpcErrorPages option

If enabled, it sets up error pages that are valid gRPC messages.
This is useful if you proxy gRPC and want to emit errors from nginx, for
example when adding authentication on top.
This commit is contained in:
Florian Klink
2026-08-05 17:36:53 +03:00
parent c38efa9027
commit 833cd50ea6
5 changed files with 94 additions and 0 deletions

View File

@@ -192,6 +192,8 @@
- `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.
- `services.nginx.virtualHosts.<name>.locations.<name>` gained a new `useGrpcErrorPages` option. If enabled, it sets up error pages that are valid gRPC messages. This is useful if you proxy gRPC and want to emit errors from nginx, for example when adding authentication on top.
- `security.polkit.settings` added for RFC42 style configuration of the polkitd daemon.
- `boot.supportedFilesystems.ntfs` installs `ntfsprogs-plus` instead of `ntfs3g` on kernel version 7.1 and later, unless `boot.supportedFilesystems.ntfs-3g` is explicitly enabled.

View File

@@ -398,6 +398,11 @@ let
hostListen = if vhost.forceSSL then filter (x: x.ssl) defaultListen else defaultListen;
# If there's any location setting `useGrpcErrorPages`, we need to add the location blocks.
locationsWantGrpcErrorPages = builtins.any (location: location.useGrpcErrorPages) (
attrValues vhost.locations
);
listenString =
{
addr,
@@ -515,6 +520,10 @@ let
${mkBasicAuth vhostName vhost}
${optionalString locationsWantGrpcErrorPages ''
include ${./grpc-locations.conf};
''}
${optionalString (vhost.root != null) "root ${vhost.root};"}
${optionalString (vhost.globalRedirect != null) ''
@@ -559,6 +568,9 @@ let
optionalAttrs (config.fastcgiParams != { }) (defaultFastcgiParams // config.fastcgiParams)
)
)}
${optionalString config.useGrpcErrorPages ''
include ${./grpc-error-pages.conf};
''}
${optionalString (config.index != null) "index ${config.index};"}
${optionalString (config.tryFiles != null) "try_files ${config.tryFiles};"}
${optionalString (config.root != null) "root ${config.root};"}

View File

@@ -0,0 +1,22 @@
error_page 400 = @grpc_internal;
error_page 401 = @grpc_unauthenticated;
error_page 403 = @grpc_permission_denied;
error_page 404 = @grpc_unimplemented;
error_page 429 = @grpc_unavailable;
error_page 502 = @grpc_unavailable;
error_page 503 = @grpc_unavailable;
error_page 504 = @grpc_unavailable;
# NGINX-to-gRPC status code mappings
# Ref: https://github.com/grpc/grpc/blob/master/doc/statuscodes.md
#
error_page 405 = @grpc_internal; # Method not allowed
error_page 408 = @grpc_deadline_exceeded; # Request timeout
error_page 413 = @grpc_resource_exhausted; # Payload too large
error_page 414 = @grpc_resource_exhausted; # Request URI too large
error_page 415 = @grpc_internal; # Unsupported media type;
error_page 426 = @grpc_internal; # HTTP request was sent to HTTPS port
error_page 495 = @grpc_unauthenticated; # Client certificate authentication error
error_page 496 = @grpc_unauthenticated; # Client certificate not presented
error_page 497 = @grpc_internal; # HTTP request was sent to mutual TLS port
error_page 500 = @grpc_internal; # Server error
error_page 501 = @grpc_internal; # Not implemented

View File

@@ -0,0 +1,46 @@
# gRPC error responses
# Ref: https://github.com/grpc/grpc-go/blob/master/codes/codes.go
# Ref: https://grpc.io/docs/guides/wire
#
location @grpc_deadline_exceeded {
add_header grpc-status 4;
add_header grpc-message 'deadline exceeded';
default_type application/grpc;
return 200;
}
location @grpc_permission_denied {
add_header grpc-status 7;
add_header grpc-message 'permission denied';
default_type application/grpc;
return 200;
}
location @grpc_resource_exhausted {
add_header grpc-status 8;
add_header grpc-message 'resource exhausted';
default_type application/grpc;
return 200;
}
location @grpc_unimplemented {
add_header grpc-status 12;
add_header grpc-message unimplemented;
default_type application/grpc;
return 200;
}
location @grpc_internal {
add_header grpc-status 13;
add_header grpc-message 'internal error';
default_type application/grpc;
return 200;
}
location @grpc_unavailable {
add_header grpc-status 14;
add_header grpc-message unavailable;
default_type application/grpc;
return 200;
}
location @grpc_unauthenticated {
add_header grpc-status 16;
add_header grpc-message unauthenticated;
default_type application/grpc;
return 200;
}

View File

@@ -158,5 +158,17 @@ with lib;
Enable recommended uwsgi settings.
'';
};
useGrpcErrorPages = mkOption {
type = types.bool;
default = false;
description = ''
Whether to configure error codes to be emitted as gRPC-compatible errors.
Should be set when proxying gRPC, and returning responses from nginx (like when adding authentication).
This defines a few `@grpc-*` locations inside the containing vhost.
'';
};
};
}