From 49aee6760a26b14c06180e4f25088cb18e2037a7 Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Mon, 11 Dec 2023 10:46:58 +0800 Subject: HTTP: added TSTR validation flag to the rewrite option. This is to improve error messages for rewrite configuration. Take the configuration as an example: { "rewrite": "`${a + " } Previously, when applying it the user would see this error message: failed to apply previous configuration After this change, the user will see this improved error message: the previous configuration is invalid: "SyntaxError: Unexpected end of input in default:1" in the "rewrite" value. --- src/nxt_conf_validation.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/nxt_conf_validation.c') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index f00b28b8..32ed4ffd 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -690,6 +690,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_action_common_members[] = { { .name = nxt_string("rewrite"), .type = NXT_CONF_VLDT_STRING, + .flags = NXT_CONF_VLDT_TSTR, }, { .name = nxt_string("response_headers"), -- cgit From 4c91bebb50d06b28e369d68b23022caa072cf62d Mon Sep 17 00:00:00 2001 From: Zhidao HONG Date: Tue, 23 Jan 2024 18:57:30 +0800 Subject: HTTP: enhanced access log with conditional filtering. This feature allows users to specify conditions to control if access log should be recorded. The "if" option supports a string and JavaScript code. If its value is empty, 0, false, null, or undefined, the logs will not be recorded. And the '!' as a prefix inverses the condition. Example 1: Only log requests that sent a session cookie. { "access_log": { "if": "$cookie_session", "path": "..." } } Example 2: Do not log health check requests. { "access_log": { "if": "`${uri == '/health' ? false : true}`", "path": "..." } } Example 3: Only log requests when the time is before 22:00. { "access_log": { "if": "`${new Date().getHours() < 22}`", "path": "..." } } or { "access_log": { "if": "!`${new Date().getHours() >= 22}`", "path": "..." } } Closes: https://github.com/nginx/unit/issues/594 --- src/nxt_conf_validation.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) (limited to 'src/nxt_conf_validation.c') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 32ed4ffd..32124ee9 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -77,6 +77,8 @@ static nxt_int_t nxt_conf_vldt_error(nxt_conf_validation_t *vldt, const char *fmt, ...); static nxt_int_t nxt_conf_vldt_var(nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_str_t *value); +static nxt_int_t nxt_conf_vldt_if(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); nxt_inline nxt_int_t nxt_conf_vldt_unsupported(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data) NXT_MAYBE_UNUSED; @@ -1369,6 +1371,10 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_access_log_members[] = { }, { .name = nxt_string("format"), .type = NXT_CONF_VLDT_STRING, + }, { + .name = nxt_string("if"), + .type = NXT_CONF_VLDT_STRING, + .validator = nxt_conf_vldt_if, }, NXT_CONF_VLDT_END @@ -1538,6 +1544,37 @@ nxt_conf_vldt_var(nxt_conf_validation_t *vldt, nxt_str_t *name, } +static nxt_int_t +nxt_conf_vldt_if(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, + void *data) +{ + nxt_str_t str; + + static nxt_str_t if_str = nxt_string("if"); + + if (nxt_conf_type(value) != NXT_CONF_STRING) { + return nxt_conf_vldt_error(vldt, "The \"if\" must be a string"); + } + + nxt_conf_get_string(value, &str); + + if (str.length == 0) { + return NXT_OK; + } + + if (str.start[0] == '!') { + str.start++; + str.length--; + } + + if (nxt_is_tstr(&str)) { + return nxt_conf_vldt_var(vldt, &if_str, &str); + } + + return NXT_OK; +} + + typedef struct { nxt_mp_t *pool; nxt_str_t *type; -- cgit From eba7378d4f8816799032a0c086ab54d3c15157b3 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Wed, 24 Jan 2024 22:03:12 +0000 Subject: Configuration: Use the NXT_CONF_VLDT_REQUIRED flag for procmap Use the NXT_CONF_VLDT_REQUIRED flag on the app_procmap members. These three settings are required. These are for the uidmap & gidmap settings in the config. Suggested-by: Zhidao HONG Reviewed-by: Zhidao Hong Signed-off-by: Andrew Clayton --- src/nxt_conf_validation.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/nxt_conf_validation.c') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 32124ee9..eb7ef530 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -1327,12 +1327,15 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_app_procmap_members[] = { { .name = nxt_string("container"), .type = NXT_CONF_VLDT_INTEGER, + .flags = NXT_CONF_VLDT_REQUIRED, }, { .name = nxt_string("host"), .type = NXT_CONF_VLDT_INTEGER, + .flags = NXT_CONF_VLDT_REQUIRED, }, { .name = nxt_string("size"), .type = NXT_CONF_VLDT_INTEGER, + .flags = NXT_CONF_VLDT_REQUIRED, }, NXT_CONF_VLDT_END -- cgit From 990fbe7010526bb97f2d414db866050ef5e8f244 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Wed, 24 Jan 2024 22:17:02 +0000 Subject: Configuration: Remove procmap validation code With the previous commit which introduced the use of the NXT_CONF_VLDT_REQUIRED flag, we no longer need to do this separate validation, it's only purpose was to check if the three uidmap/gidmap settings had been provided. Reviewed-by: Zhidao Hong Signed-off-by: Andrew Clayton --- src/nxt_conf_validation.c | 73 ++--------------------------------------------- 1 file changed, 2 insertions(+), 71 deletions(-) (limited to 'src/nxt_conf_validation.c') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index eb7ef530..c843b265 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -218,8 +218,6 @@ static nxt_int_t nxt_conf_vldt_clone_namespaces(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); #if (NXT_HAVE_CLONE_NEWUSER) -static nxt_int_t nxt_conf_vldt_clone_procmap(nxt_conf_validation_t *vldt, - const char* mapfile, nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_clone_uidmap(nxt_conf_validation_t *vldt, nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_clone_gidmap(nxt_conf_validation_t *vldt, @@ -3091,73 +3089,6 @@ nxt_conf_vldt_isolation(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, #if (NXT_HAVE_CLONE_NEWUSER) -typedef struct { - nxt_int_t container; - nxt_int_t host; - nxt_int_t size; -} nxt_conf_vldt_clone_procmap_conf_t; - - -static nxt_conf_map_t nxt_conf_vldt_clone_procmap_conf_map[] = { - { - nxt_string("container"), - NXT_CONF_MAP_INT32, - offsetof(nxt_conf_vldt_clone_procmap_conf_t, container), - }, - - { - nxt_string("host"), - NXT_CONF_MAP_INT32, - offsetof(nxt_conf_vldt_clone_procmap_conf_t, host), - }, - - { - nxt_string("size"), - NXT_CONF_MAP_INT32, - offsetof(nxt_conf_vldt_clone_procmap_conf_t, size), - }, - -}; - - -static nxt_int_t -nxt_conf_vldt_clone_procmap(nxt_conf_validation_t *vldt, const char *mapfile, - nxt_conf_value_t *value) -{ - nxt_int_t ret; - nxt_conf_vldt_clone_procmap_conf_t procmap; - - procmap.container = -1; - procmap.host = -1; - procmap.size = -1; - - ret = nxt_conf_map_object(vldt->pool, value, - nxt_conf_vldt_clone_procmap_conf_map, - nxt_nitems(nxt_conf_vldt_clone_procmap_conf_map), - &procmap); - if (ret != NXT_OK) { - return ret; - } - - if (procmap.container == -1) { - return nxt_conf_vldt_error(vldt, "The %s requires the " - "\"container\" field set.", mapfile); - } - - if (procmap.host == -1) { - return nxt_conf_vldt_error(vldt, "The %s requires the " - "\"host\" field set.", mapfile); - } - - if (procmap.size == -1) { - return nxt_conf_vldt_error(vldt, "The %s requires the " - "\"size\" field set.", mapfile); - } - - return NXT_OK; -} - - static nxt_int_t nxt_conf_vldt_clone_uidmap(nxt_conf_validation_t *vldt, nxt_conf_value_t *value) { @@ -3174,7 +3105,7 @@ nxt_conf_vldt_clone_uidmap(nxt_conf_validation_t *vldt, nxt_conf_value_t *value) return ret; } - return nxt_conf_vldt_clone_procmap(vldt, "uid_map", value); + return NXT_OK; } @@ -3194,7 +3125,7 @@ nxt_conf_vldt_clone_gidmap(nxt_conf_validation_t *vldt, nxt_conf_value_t *value) return ret; } - return nxt_conf_vldt_clone_procmap(vldt, "gid_map", value); + return NXT_OK; } #endif -- cgit From 46cef09f296d9a3d54b98331d25920fc6322bea8 Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Wed, 31 Jan 2024 15:34:57 +0100 Subject: Configuration: Don't corrupt abstract socket names The commit that added support for Unix sockets accepts abstract sockets using '@' in the config, but we stored it internally using '\0'. We want to support abstract sockets transparently to the user, so that if the user configures unitd with '@', if we receive a query about the current configuration, the user should see the same exact thing that was configured. So, this commit avoids the transformation in the internal state file, storing user input pristine, and we only transform the '@' in temporary strings. This commit fixes another bug, where we try to connect to abstract sockets with a trailing '\0' in their name due to calling twice nxt_sockaddr_parse() on the same string. By calling that function only once with each copy of the string, we have fixed that bug. The following code was responsible for this bug, which the second time it was called, considered these sockets as file-backed (not abstract) Unix socket, and so appended a '\0' to the socket name. $ grepc -tfd nxt_sockaddr_unix_parse . | grep -A10 @ if (path[0] == '@') { path[0] = '\0'; socklen--; #if !(NXT_LINUX) nxt_thread_log_error(NXT_LOG_ERR, "abstract unix domain sockets are not supported"); return NULL; #endif } sa = nxt_sockaddr_alloc(mp, socklen, addr->length); This bug was found thanks to some experiment about using 'const' for some strings. And here's some history: - 9041d276fc6a ("nxt_sockaddr_parse() introducted.") This commit introduced support for abstract Unix sockets, but they only worked as "servers", and not as "listeners". We corrupted the JSON config file, and stored a \u0000. This also caused calling connect(2) with a bogus trailing null byte, which tried to connect to a different abstract socket. - d8e0768a5bae ("Fixed support for abstract Unix sockets.") This commit (partially) fixed support for abstract Unix sockets, so they they worked also as listeners. We still corrupted the JSON config file, and stored a \u0000. This caused calling connect(2) (and now bind(2) too) with a bogus trailing null byte. - e2aec6686a4d ("Storing abstract sockets with @ internally.") This commit fixed the problem by which we were corrupting the config file, but only for "listeners", not for "servers". (It also fixes the issue about the terminating '\0'.) We completely forgot about "servers", and other callers of the same function. To reproduce the problem, I used the following config: ```json { "listeners": { "*:80": { "pass": "routes/u" }, "unix:@abstract": { "pass": "routes/a" } }, "routes": { "u": [{ "action": { "pass": "upstreams/u" } }], "a": [{ "action": { "return": 302, "location": "/i/am/not/at/home/" } }] }, "upstreams": { "u": { "servers": { "unix:@abstract": {} } } } } ``` And then check the state file: $ sudo cat /opt/local/nginx/unit/master/var/lib/unit/conf.json \ | jq . \ | grep unix; "unix:@abstract": { "unix:\u0000abstract": {} After this patch, the state file has a '@' as expected: $ sudo cat /opt/local/nginx/unit/unix/var/lib/unit/conf.json \ | jq . \ | grep unix; "unix:@abstract": { "unix:@abstract": {} Regarding the trailing null byte, here are some tests: $ sudo strace -f -e 'bind,connect' /opt/local/nginx/unit/d8e0/sbin/unitd \ |& grep abstract; [pid 22406] bind(10, {sa_family=AF_UNIX, sun_path=@"abstract\0"}, 12) = 0 [pid 22410] connect(134, {sa_family=AF_UNIX, sun_path=@"abstract\0"}, 12) = 0 ^C $ sudo killall unitd $ sudo strace -f -e 'bind,connect' /opt/local/nginx/unit/master/sbin/unitd \ |& grep abstract; [pid 22449] bind(10, {sa_family=AF_UNIX, sun_path=@"abstract"}, 11) = 0 [pid 22453] connect(134, {sa_family=AF_UNIX, sun_path=@"abstract\0"}, 12) = -1 ECONNREFUSED (Connection refused) ^C $ sudo killall unitd $ sudo strace -f -e 'bind,connect' /opt/local/nginx/unit/unix/sbin/unitd \ |& grep abstract; [pid 22488] bind(10, {sa_family=AF_UNIX, sun_path=@"abstract"}, 11) = 0 [pid 22492] connect(134, {sa_family=AF_UNIX, sun_path=@"abstract"}, 11) = 0 ^C Fixes: 9041d276fc6a ("nxt_sockaddr_parse() introducted.") Fixes: d8e0768a5bae ("Fixed support for abstract Unix sockets.") Fixes: e2aec6686a4d ("Storing abstract sockets with @ internally.") Link: Reviewed-by: Andrew Clayton Cc: Liam Crilly Cc: Zhidao Hong Signed-off-by: Alejandro Colomar --- src/nxt_conf_validation.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'src/nxt_conf_validation.c') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index c843b265..bf18cd1a 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -2,6 +2,7 @@ /* * Copyright (C) Valentin V. Bartenev * Copyright (C) NGINX, Inc. + * Copyright 2024, Alejandro Colomar */ #include @@ -1936,10 +1937,13 @@ static nxt_int_t nxt_conf_vldt_proxy(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data) { - nxt_str_t name; + nxt_str_t name, *ret; nxt_sockaddr_t *sa; - nxt_conf_get_string(value, &name); + ret = nxt_conf_get_string_dup(value, vldt->pool, &name); + if (nxt_slow_path(ret == NULL)) { + return NXT_ERROR; + } if (nxt_str_start(&name, "http://", 7)) { name.length -= 7; @@ -2913,13 +2917,11 @@ nxt_conf_vldt_object_iterator(nxt_conf_validation_t *vldt, for ( ;; ) { member = nxt_conf_next_object_member(value, &name, &index); - if (member == NULL) { return NXT_OK; } ret = validator(vldt, &name, member); - if (ret != NXT_OK) { return ret; } @@ -3268,16 +3270,19 @@ nxt_conf_vldt_server(nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value) { nxt_int_t ret; + nxt_str_t str; nxt_sockaddr_t *sa; ret = nxt_conf_vldt_type(vldt, name, value, NXT_CONF_VLDT_OBJECT); - if (ret != NXT_OK) { return ret; } - sa = nxt_sockaddr_parse(vldt->pool, name); + if (nxt_slow_path(nxt_str_dup(vldt->pool, &str, name) == NULL)) { + return NXT_ERROR; + } + sa = nxt_sockaddr_parse(vldt->pool, &str); if (sa == NULL) { return nxt_conf_vldt_error(vldt, "The \"%V\" is not valid " "server address.", name); -- cgit From 9e986704480de0d6b74dafa5ebcf775eaa88333a Mon Sep 17 00:00:00 2001 From: Alejandro Colomar Date: Thu, 8 Feb 2024 11:15:34 +0100 Subject: Configuration: Fix validation of "processes" It's an integer, not a floating number. Fixes: 68c6b67ffc84 ("Configuration: support for rational numbers.") Closes: https://github.com/nginx/unit/issues/1115 Link: Reviewed-by: Zhidao Hong Reviewed-by: Andrew Clayton Cc: Dan Callahan Cc: Valentin Bartenev Signed-off-by: Alejandro Colomar --- src/nxt_conf_validation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/nxt_conf_validation.c') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index bf18cd1a..caa068d2 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -2830,7 +2830,7 @@ nxt_conf_vldt_processes(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, nxt_int_t ret; nxt_conf_vldt_processes_conf_t proc; - if (nxt_conf_type(value) == NXT_CONF_NUMBER) { + if (nxt_conf_type(value) == NXT_CONF_INTEGER) { int_value = nxt_conf_get_number(value); if (int_value < 1) { -- cgit From 07a0c9a34817d6faedff67505507cd4f54752a22 Mon Sep 17 00:00:00 2001 From: Andrew Clayton Date: Mon, 5 Feb 2024 21:50:52 +0000 Subject: Wasm-wc: Wire up the language module to the config system This exposes the various WebAssembly Component Model language module specific options. The application type is "wasm-wasi-component". There is a "component" option that is required, this specifies the full path to the WebAssembly component to be run. This component should be in binary format, i.e a .wasm file. There is also currently one optional option "access" Due to the sandboxed nature of WebAssembly, by default Wasm modules/components don't have any access to the underlying filesystem. There is however a capabilities based mechanism[0] for allowing such access. This adds a config option to the 'wasm-wasi-component' application type (same as for 'wasm'); 'access.filesystem' which takes an array of directory paths that are then made available to the wasm module/component. This access works recursively, i.e everything under a specific path is allowed access to. Example config might look like "applications": { "my-wasm-component": { "type": "wasm-wasi-component", "component": "/path/to/component.wasm", "access" { "filesystem": [ "/tmp", "/var/tmp" ] } } } The actual mechanism used allows directories to be mapped differently in the guest. But at the moment we don't support that and just map say /tmp to /tmp. This can be revisited if it's something users clamour for. [0]: Signed-off-by: Andrew Clayton --- src/nxt_conf_validation.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'src/nxt_conf_validation.c') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index caa068d2..2099f887 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -1095,6 +1095,22 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_wasm_members[] = { }; +static nxt_conf_vldt_object_t nxt_conf_vldt_wasm_wc_members[] = { + { + .name = nxt_string("component"), + .type = NXT_CONF_VLDT_STRING, + .flags = NXT_CONF_VLDT_REQUIRED, + }, { + .name = nxt_string("access"), + .type = NXT_CONF_VLDT_OBJECT, + .validator = nxt_conf_vldt_object, + .u.members = nxt_conf_vldt_wasm_access_members, + }, + + NXT_CONF_VLDT_NEXT(nxt_conf_vldt_common_members) +}; + + static nxt_conf_vldt_object_t nxt_conf_vldt_wasm_access_members[] = { { .name = nxt_string("filesystem"), @@ -2660,6 +2676,7 @@ nxt_conf_vldt_app(nxt_conf_validation_t *vldt, nxt_str_t *name, { nxt_conf_vldt_object, nxt_conf_vldt_ruby_members }, { nxt_conf_vldt_object, nxt_conf_vldt_java_members }, { nxt_conf_vldt_object, nxt_conf_vldt_wasm_members }, + { nxt_conf_vldt_object, nxt_conf_vldt_wasm_wc_members }, }; ret = nxt_conf_vldt_type(vldt, name, value, NXT_CONF_VLDT_OBJECT); -- cgit