-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: directly signal session action processes using CAP_KILL #196
base: mainline
Are you sure you want to change the base?
feat: directly signal session action processes using CAP_KILL #196
Conversation
Signed-off-by: Josh Usiskin <[email protected]>
Signed-off-by: Josh Usiskin <[email protected]>
Signed-off-by: Josh Usiskin <[email protected]>
Signed-off-by: Josh Usiskin <[email protected]>
Signed-off-by: Josh Usiskin <[email protected]>
f4ba9ba
to
a1eeb1d
Compare
Signed-off-by: Josh Usiskin <[email protected]>
a1eeb1d
to
621232c
Compare
Quality Gate failedFailed conditions |
# https://man7.org/linux/man-pages/man3/cap_set_proc.3.html | ||
libcap.cap_set_proc.restype = ctypes.c_int | ||
libcap.cap_set_proc.argtypes = [ | ||
ctypes.POINTER(Cap), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: You have this defined above as cap_t
break | ||
|
||
# If we did not find any child processes yet, sleep for some time and retry | ||
time.sleep(min(0.05, now - start)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
now - start
? Do you mean timeout_seconds - (now-start)
? i.e. Sleep for the lesser of 0.05s or the time remaining before timeout.
As written, this'll sleep for 0s the first time and then 0.05s every time after that.
if not sys.platform.startswith("linux"): | ||
raise OSError(f"libcap is only available on Linux, but found platform: {sys.platform}") | ||
|
||
libcap = ctypes.CDLL(find_library("cap"), use_errno=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if libcap
is not installed/available? I notice that you had to install the lib into the docker container, for instance. If we get an exception, then that'll probably end up unhandled in the LoggingSubprocess?
Related: We should also update the README to let folk know that having this library available is beneficial.
What was the problem/requirement? (What/Why)
Background:
The
Session.cancel_action()
method needs to send signals to the session action subprocess. On POSIX systems, processes can only send signals to processes owned by the same user unless the process is root, or on Linux if the process has theCAP_KILL
Linux capability (see capabilities(7) man page).To overcome this restriction when the
Session
was provided with a different user than the host process, theSession.cancel_action()
method would launch a subprocess usingsudo
to send the signal to the target process.Problem:
Under low memory conditions, there may not be sufficient memory to launch the subprocesses in order to send the signals and cancel the session action.
What was the solution? (How)
When sending the
SIGTERM
signal, a simplification was made to always send a signal to the subprocess sincesudo
will forward signals that it can intercept, includingSIGTERM
, to its child process.When the
CAP_KILL
Linux capability is present, a process can send OS signals to processes belonging to other users. The code was modified to detect whenCAP_KILL
is in the process' permitted/effective capability set(s) and, if so, send signals directly to the process.The implementation falls back to using the
sudo
andbash
approach for signalling cross-user if the direct signaling is not possible or fails.If
CAP_KILL
is in the permitted capability set, but not the effective capability set, then openjd will uselibcap
viactypes
to temporarily promoteCAP_KILL
from permitted → effective and undo this after signalling.Along the way, a simplification of the process tree was made. Before,
openjd-sessions
would leave an intermediatebash
process. This bash script had code to handle signal forwarding. This implementation turned out to be unnecessary. To simplify the process tree, the bash script was modified to use theexec
bash built-in which replaces the process with the session action command. This means one less process in the process tree and we do not need signal forwarding. The process tree change is illustrated in the diagram below:What is the impact of this change?
CAP_KILL
capability, theSession.cancel_action()
method is more robust under low-memory conditions.openjd-sessions
is simplified and there is no longer an intermediate bash process kept around and forwarding OS signalsHow was this change tested?
See DEVELOPMENT.md for information on running tests.
CAP_KILL
is in the permitted and effective capability setCAP_KILL
is in the permitted capability set onlyCAP_KILL
is temporarily added to the effective capability set and restored after signallingCAP_KILL
is not present and that the original implementation is used as a fallbackWas this change documented?
Yes
Is this a breaking change?
A breaking change is one that modifies a public contract in a way that is not backwards compatible. See the
Public Interfaces section
of the DEVELOPMENT.md for more information on the public contracts.
No, the public APIs and functional behaviours are unchanged.
Does this change impact security?
Yes, the change temporarily elevates privileges in
Session.cancel_action()
for sending cross-user OS signals.openjd-sessions
is now "capability aware". IfCAP_KILL
is in the process thread's permitted capability set but not in its effective capability set, it will:CAP_KILL
to the thread's effective capability setCAP_KILL
from the thread's effective capability setA integration test has been added to assert that
CAP_KILL
is removed from the effective capability set.By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.