Age | Commit message (Collapse) | Author | Files | Lines |
|
Scripted change:
$ grep -ril recevied src/ | xargs sed -i s/recevied/received/
Reported-by: <https://github.com/jeffdafoe>
Closes: <https://github.com/nginx/unit/issues/920>
Cc: <https://github.com/meezaan>
Cc: Timo Stark <t.stark@nginx.com>
Signed-off-by: Alejandro Colomar <alx@nginx.com>
Reviewed-by: Andrew Clayton <a.clayton@nginx.com>
Signed-off-by: Andrew Clayton <a.clayton@nginx.com>
|
|
Due to the sandboxed nature of WebAssembly, by default WASM modules
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' application type;
'access.filesystem' which takes an array of directory paths that are
then made available to the WASM module. This access works recursively,
i.e everything under a specific path is allowed access to.
Example config might look like
"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.
Network sockets are another resource that may be controlled in this
manner, for example there is a wasi_config_preopen_socket() function,
however this requires the runtime to open the network socket then
effectively pass this through to the guest.
This is something that can be revisited in the future if users desire
it.
[0]:
<https://github.com/bytecodealliance/wasmtime/blob/main/docs/WASI-capabilities.md>
Reviewed-by: Alejandro Colomar <alx@nginx.com>
Signed-off-by: Andrew Clayton <a.clayton@nginx.com>
|
|
This exposes various WebAssembly language module specific options.
The application type is "wasm".
There is a "module" option that is required, this specifies the full
path to the WebAssembly module to be run. This module should be in
binary format, i.e a .wasm file.
There are also currently eight function handlers that can be specified.
Three of them are _required_
1) request_handler
The main driving function. This may be called multiple times for a
single HTTP request if the request is larger than the shared memory.
2) malloc_handler
Used to allocate a chunk of memory at language module startup. This
memory is allocated from the WASM modules address space and is what is
sued for communicating between the WASM module (the guest) and Unit (the
host).
3) free_handler
Used to free the memory from above at language module shutdown.
Then there are the following five _optional_ handlers
1) module_init_handler
If set, called at language module startup.
2) module_end_handler
If set, called at language module shutdown.
3) request_init_handler
If set, called at the start of request. Called only once per HTTP
request.
4) request_end_handler
If set, called once all of a request has been sent to the WASM module.
5) response_end_handler
If set, called at the end of a request, once the WASM module has sent
all its headers and data.
Example config
"applications": {
"luw-echo-request": {
"type": "wasm",
"module": "/path/to/unit-wasm/examples/c/luw-echo-request.wasm",
"request_handler": "luw_request_handler",
"malloc_handler": "luw_malloc_handler",
"free_handler": "luw_free_handler",
"module_init_handler": "luw_module_init_handler",
"module_end_handler": "luw_module_end_handler",
}
}
Reviewed-by: Alejandro Colomar <alx@nginx.com>
Signed-off-by: Andrew Clayton <a.clayton@nginx.com>
|
|
|
|
Currently when running in the foreground, unit application processes
will send stdout to the current TTY and stderr to the unit log file.
That behaviour won't change.
When running as a daemon, unit application processes will send stdout to
/dev/null and stderr to the unit log file.
This commit allows to alter the latter case of unit running as a daemon,
by allowing applications to redirect stdout and/or stderr to specific
log files. This is done via two new application options, 'stdout' &
'stderr', e.g
"applications": {
"myapp": {
...
"stdout": "/path/to/log/unit/app/stdout.log",
"stderr": "/path/to/log/unit/app/stderr.log"
}
}
These log files are created by the application processes themselves and
thus the log directories need to be writable by the user (and or group)
of the application processes.
E.g
$ sudo mkdir -p /path/to/log/unit/app
$ sudo chown APP_USER /path/to/log/unit/app
These need to be setup before starting unit with the above config.
Currently these log files do not participate in log-file rotation
(SIGUSR1), that may change in a future commit. In the meantime these
logs can be rotated using the traditional copy/truncate method.
NOTE:
You may or may not see stuff printed to stdout as stdout was
traditionally used by CGI applications to communicate with the
webserver.
Closes: <https://github.com/nginx/unit/issues/197>
Closes: <https://github.com/nginx/unit/issues/846>
Reviewed-by: Alejandro Colomar <alx@nginx.com>
Signed-off-by: Andrew Clayton <a.clayton@nginx.com>
|
|
Currently when using Unix domain sockets for requests, if unit is
reconfigured then it will fail if it tries to bind(2) again to a Unix
domain socket with something like
2023/02/25 19:15:50 [alert] 35274#35274 bind(\"unix:/tmp/unit.sock\") failed (98: Address already in use)
When closing such a socket we really need to unlink(2) it. However that
presents a problem in that when running as root, while the main process
runs as root and creates the socket, it's the router process, that runs
as an unprivileged user, e.g nobody, that closes the socket and would
thus remove it, but couldn't due to not having permission, even if the
socket is mode 0666, you need write permissions on the containing
directory to remove a file.
There are several options to solve this, all with varying degrees of
complexity and utility.
1) Give the user who the router process runs as write permission to
the directory containing the listen sockets. These can then be
unlink(2)'d from the router process.
Simple and would work, but perhaps not the most elegant.
2) Using capabilities(7). The router process could temporarily attain
the CAP_DAC_OVERRIDE capability, unlink(7) the socket, then
relinquish the capability until required again.
These are Linux specific (other systems may have similar mechanisms
which would be extra work to support). There is also a, albeit
small, window where the router process is running with elevated
privileges.
3) Have the main process do the unlink(2), it is after all the process
that created the socket.
This is what this commit implements.
We create a new port IPC message type of NXT_PORT_MSG_SOCKET_UNLINK,
that is used by the router process to notify the main process about a
Unix domain socket to unlink(2).
Upon doing a reconfigure the router process will call
nxt_router_listen_socket_release() which will close the socket, we
extend this function in the case of non-abstract Unix domain sockets, so
that it will send a message to the main process containing a copy of the
nxt_sockaddr_t structure that will contain the filename of the socket.
In the main process the handler that we have defined,
nxt_main_port_socket_unlink_handler(), for this message type will run
and allow us to look for the socket in question in the listen_sockets
array and remove it and unlink(2) the socket.
This then allows the reconfigure to work if it tries to bind(2) again to
a socket that previously existed.
Link: <https://github.com/nginx/unit/issues/669>
Link: <https://github.com/nginx/unit/pull/735>
Reviewed-by: Alejandro Colomar <alx@nginx.com>
Signed-off-by: Andrew Clayton <a.clayton@nginx.com>
|
|
If we don't remove the Unix domain listen socket file then when Unit
restarts it get an error like
2023/02/25 23:10:11 [alert] 36388#36388 bind(\"unix:/tmp/unit.sock\") failed (98: Address already in use)
This patch makes use of the listen_sockets array, that is already
allocated in the main process but never populated, to place the Unix
domain listen sockets into.
At shutdown we can then loop through this array and unlink(2) any Unix
domain sockets found therein.
Closes: <https://github.com/nginx/unit/issues/792>
Reviewed-by: Alejandro Colomar <alx@nginx.com>
Signed-off-by: Andrew Clayton <a.clayton@nginx.com>
|
|
Due to the need to replace our use of clone/__NR_clone on Linux with
fork(2)/unshare(2) for enabling Linux namespaces(7) to keep the
pthreads(7) API working. Let's rename NXT_HAVE_CLONE to
NXT_HAVE_LINUX_NS, i.e name it after the feature, not how it's
implemented, then in future if we change how we do namespaces again we
don't have to rename this.
Reviewed-by: Alejandro Colomar <alx@nginx.com>
Signed-off-by: Andrew Clayton <a.clayton@nginx.com>
|
|
This commit hooks into the cgroup infrastructure added in the previous
commit to create per-application cgroups.
It does this by adding each "prototype process" into its own cgroup,
then each child process inherits its parents cgroup.
If we fail to create a cgroup we simply fail the process. This behaviour
may get enhanced in the future.
This won't actually do anything yet. Subsequent commits will hook this
up to the build and config systems.
Reviewed-by: Alejandro Colomar <alx@nginx.com>
Signed-off-by: Andrew Clayton <a.clayton@nginx.com>
|
|
Signed-off-by: Alejandro Colomar <alx@nginx.com>
|
|
Unix domain sockets are normally backed by files in the
filesystem. This has historically been problematic when closing
and opening again such sockets, since SO_REUSEADDR is ignored for
Unix sockets (POSIX left the behavior of SO_REUSEADDR as
implementation-defined, and most --if not all-- implementations
decided to just ignore this flag).
Many solutions are available for this problem, but all of them
have important caveats:
- unlink(2) the file when it's not needed anymore.
This is not easy, because the process that controls the fd may
not be the same process that created the file, and may not have
file permissions to remove it.
Further solutions can be applied to that caveat:
- unlink(2) the file right after creation.
This will remove the pathname from the filesystem without
closing the socket (it will continue to live until the last fd
is closed). This is not useful for us, since we need the
pathname of the socket as its interface.
- chown(2) or chmod(2) the directory that contains the socket.
For removing a file from the filesystem, a process needs
write permissions in the containing directory. We could
put sockets in dummy directories that can be chown(2)ed to
nobody. This could be dangerous, though, as we don't control
the socket names. It is our users who configure the socket
name in their configuration, and so it's easy that they don't
understand the many implications of not chosing an appropriate
socket pathname. A user could unknowingly put the socket in a
directory that is not supposed to be owned by user nobody, and
if we blindly chown(2) or chmod(2) the directory, we could be
creating a big security hole.
- Ask the main process to remove the socket.
This would require a very complex communication mechanism with
the main process, which is not impossible, but let's avoid it
if there are simpler solutions.
- Give the child process the CAP_DAC_OVERRIDE capability.
That is one of the most powerful capabilities. A process with
that capability can be considered root for most practical
aspects. Even if the capability is disabled for most of the
lifetime of the process, there's a slight chance that a
malicious actor could activate it and then easily do serious
damage to the system.
- unlink(2) the file right before calling bind(2).
This is dangerous because another process (for example, another
running instance of unitd(8)), could be using the socket, and
removing the pathname from the filesystem would be problematic.
To do this correctly, a lot of checks should be added before the
actual unlink(2), which is error-prone, and difficult to do
correctly, and atomically.
- Use abstract-namespace Unix domain sockets.
This is the simplest solution, as it only requires accepting a
slightly different syntax (basically a @ prefix) for the socket
name, to transform it into a string starting with a null byte
('\0') that the kernel can understand. The patch is minimal.
Since abstract sockets live in an abstract namespace, they don't
create files in the filesystem, so there's no need to remove
them later. The kernel removes the name when the last fd to it
has been closed.
One caveat is that only Linux currently supports this kind of
Unix sockets. Of course, a solution to that could be to ask
other kernels to implement such a feature.
Another caveat is that filesystem permissions can't be used to
control access to the socket file (since, of course, there's no
file). Anyone knowing the socket name can access to it. The
only method to control access to it is by using
network_namespaces(7). Since in unitd(8) we're using 0666 file
sockets, abstract sockets should be no more insecure than that
(anyone can already read/write to the listener sockets).
- Ask the kernel to implement a simpler way to unlink(2) socket
files when they are not needed anymore. I've suggested that to
the <linux-fsdevel@vger.kernel.org> mailing list, in:
<lore.kernel.org/linux-fsdevel/0bc5f919-bcfd-8fd0-a16b-9f060088158a@gmail.com/T>
In this commit, I decided to go for the easiest/simplest solution,
which is abstract sockets. In fact, we already had partial
support. This commit only fixes some small bug in the existing
code so that abstract Unix sockets work:
- Don't chmod(2) the socket if it's an abstract one.
This fixes the creation of abstract sockets, but doesn't make them
usable, since we produce them with a trailing '\0' in their name.
That will be fixed in the following commit.
This closes #669 issue on GitHub.
|
|
Some lines (incorrectly) had an indentation of 3 or 5, or 7 or 9,
or 11 or 13, or 15 or 17 spaces instead of 4, 8, 12, or 16. Fix them.
Found with:
$ find src -type f | xargs grep -n '^ [^ ]';
$ find src -type f | xargs grep -n '^ [^ *]';
$ find src -type f | xargs grep -n '^ [^ ]';
$ find src -type f | xargs grep -n '^ [^ *]';
$ find src -type f | xargs grep -n '^ [^ +]';
$ find src -type f | xargs grep -n '^ [^ *+]';
$ find src -type f | xargs grep -n '^ [^ +]';
$ find src -type f | xargs grep -n '^ [^ *+]';
|
|
After the c8790d2a89bb commit, the SIGCHLD handler may return before processing
all awaiting PIDs. To avoid zombie processes and ensure successful main
process termination, waitpid() must be called until an error is returned.
This closes #600 issue on GitHub.
|
|
Application process started with shared port (and queue) already configured.
But still waits for PORT_ACK message from router to start request processing
(so-called "ready state").
Waiting for router confirmation is necessary. Otherwise, the application may
produce response and send it to router before the router have the information
about the application process. This is a subject of further optimizations.
|
|
|
|
This enables the reuse of process creation functions.
|
|
|
|
Introducting application graceful stop. For now only used when application
process reach request limit value.
This closes #585 issue on GitHub.
|
|
Declarations became unused after 6976d36be926.
No functional changes.
|
|
|
|
Found by Clang Static Analyzer.
|
|
This feature allows one to specify blocks of code that are called when certain
lifecycle events occur. A user configures a "hooks" property on the app
configuration that points to a script. This script will be evaluated on boot
and should contain blocks of code that will be called on specific events.
An example of configuration:
{
"type": "ruby",
"processes": 2,
"threads": 2,
"user": "vagrant",
"group": "vagrant",
"script": "config.ru",
"hooks": "hooks.rb",
"working_directory": "/home/vagrant/unit/rbhooks",
"environment": {
"GEM_HOME": "/home/vagrant/.ruby"
}
}
An example of a valid "hooks.rb" file follows:
File.write("./hooks.#{Process.pid}", "hooks evaluated")
on_worker_boot do
File.write("./worker_boot.#{Process.pid}", "worker booted")
end
on_thread_boot do
File.write("./thread_boot.#{Process.pid}.#{Thread.current.object_id}",
"thread booted")
end
on_thread_shutdown do
File.write("./thread_shutdown.#{Process.pid}.#{Thread.current.object_id}",
"thread shutdown")
end
on_worker_shutdown do
File.write("./worker_shutdown.#{Process.pid}", "worker shutdown")
end
This closes issue #535 on GitHub.
|
|
|
|
For listen socket request reply port can be NULL if Router crashes immediately
after issuing the request.
Found by Coverity (CID 366310).
|
|
This patch is required to remove fragmented messages functionality.
|
|
|
|
Introducing manual protocol selection for 'universal' apps and frameworks.
|
|
This closes #486 issue on GitHub.
|
|
This closes #482 issue on GitHub.
|
|
This closes #458 issue on GitHub.
|
|
This closes #459 issue on GitHub.
|
|
|
|
Now it is possible to specify the name of the application callable using
optional parameter 'callable'. Default value is 'application'.
This closes #290 issue on GitHub.
|
|
Now it's possible to disable default bind mounts of
languages by setting:
{
"isolation": {
"automount": {
"language_deps": false
}
}
}
In this case, the user is responsible to provide a "rootfs"
containing the language libraries and required files for
the application.
|
|
|
|
Previously, an error during the prefork phase triggered assert:
src/nxt_port.c:27 assertion failed: port->pair[0] == -1
and resulted in exiting of the main process.
This could be easily reproduced by pushing a configuration with "rootfs",
when daemon is running without required permissions.
|
|
|
|
The process abstraction has changed to:
setup(task, process)
start(task, process_data)
prefork(task, process, mp)
The prefork() occurs in the main process right before fork.
The file src/nxt_main_process.c is completely free of process
specific logic.
The creation of a process now supports a PROCESS_CREATED state. The
The setup() function of each process can set its state to either
created or ready. If created, a MSG_PROCESS_CREATED is sent to main
process, where external setup can be done (required for rootfs under
container).
The core processes (discovery, controller and router) doesn't need
external setup, then they all proceeds to their start() function
straight away.
In the case of applications, the load of the module happens at the
process setup() time and The module's init() function has changed
to be the start() of the process.
The module API has changed to:
setup(task, process, conf)
start(task, data)
As a direct benefit of the PROCESS_CREATED message, the clone(2) of
processes using pid namespaces now doesn't need to create a pipe
to make the child block until parent setup uid/gid mappings nor it
needs to receive the child pid.
|
|
This allows to specify multiple subsequent targets inside PHP applications.
For example:
{
"listeners": {
"*:80": {
"pass": "routes"
}
},
"routes": [
{
"match": {
"uri": "/info"
},
"action": {
"pass": "applications/my_app/phpinfo"
}
},
{
"match": {
"uri": "/hello"
},
"action": {
"pass": "applications/my_app/hello"
}
},
{
"action": {
"pass": "applications/my_app/rest"
}
}
],
"applications": {
"my_app": {
"type": "php",
"targets": {
"phpinfo": {
"script": "phpinfo.php",
"root": "/www/data/admin",
},
"hello": {
"script": "hello.php",
"root": "/www/data/test",
},
"rest": {
"root": "/www/data/example.com",
"index": "index.php"
},
}
}
}
}
|
|
|
|
The setuid/setgid syscalls requires root capabilities but if the kernel
supports unprivileged user namespace then the child process has the full
set of capabilities in the new namespace, then we can allow setting "user"
and "group" in such cases (this is a common security use case).
Tests were added to ensure user gets meaningful error messages for
uid/gid mapping misconfigurations.
|
|
This is required to avoid include cycles, as some nxt_clone_* functions
depend on the credential structures, but nxt_process depends on clone
structures.
|
|
Introduces the functions nxt_process_init_create() and
nxt_process_init_creds_set().
|
|
- Introduced nxt_runtime_process_port_create().
- Moved nxt_process_use() into nxt_process.c from nxt_runtime.c.
- Renamed nxt_runtime_process_remove_pid() as nxt_runtime_process_remove().
- Some public functions transformed to static.
This closes #327 issue on GitHub.
|
|
This avoids memory leak reports from the address sanitizer.
|
|
This patch closes #328 in github.
|
|
|
|
When Unit starts, the main process waits for module discovery message for a
while. If a QUIT signal arrives at this time, the router and controller
processes created by main and Unit stay running. Also, the main process
doesn't stop them after the second QUIT signal is received in this case.
|
|
The <sched.h> is already included by nxt_unix.h.
This closes #314 PR on GitHub.
|
|
Found by Coverity (CID 349485).
|