5

As you know a OCI container (more commonly known as Docker container) terminates once its entrypoint process (a.k.a. PID1 inside the container's namespace) exits.

However, I'm curious if there's a way hot replacing this PID 1 process given you have shell access and root privileges inside this conatiner?

Imagine the PID1 is a [/bin/sleep, 99999] process, is there a way to replace this without causing the container to terminate, say, with a brand new process like [/bin/sleep, 100] while retaining its pid (as if it was execve'd from the process itself)?

Note that this question is not about replacing the executable file, but the running process itself with another process.

1 Answer 1

4

as if it was execve'd from the process itself

The way is to make the process call execve.

That's the job of a debugger.

You need to have at least the privileges of the user running the process. Depending on the setting of kernel.yama.ptrace_scope, you may need to be root inside the container, which is usually the same thing. I'm not sure what privileges apply from outside the container: I don't know how the ptrace access checking rules interact with Docker.

Here's a script using sh and gdb. You can run it from inside the container (pass 1 as the PID) or outside (pass the PID of the container's init process).

#!/bin/sh
set -e

help_and_exit () {
  cat <<EOF
Usage: $0 PID COMMAND [ARG...]
Use gdb to replace the running process PID by the specified command.
EOF
  exit $1
}
if [ "$1" = "--help" ]; then
  help_and_exit
elif [ $# -lt 2 ]; then
  help_and_exit 120 >&2
fi

pid=$1; shift

# Quote the command path and the arguments as a C string.
args=
add_arg () {
  args="$args\""
  while case "$1" in *[\\\"]*) true;; *) false;; esac; do
    set -- "${1#*[\\\"]}" "${1%"${1#*[\\\"]}"}"
    args="$args${2%?}\\${2#"${2%?}"}"
  done
  args="$args$1\", "
}
add_arg "$1"
args="$args$args"; shift
for x; do
  add_arg "$x"
done
args="${args}(char*)0"

gdb -n -pid "$pid" -batch -ex "call execlp($args)"

If you don't want to install gdb, you can write a small program in Python or C (or whatever language you prefer) that makes the necessary ptrace calls.

Note that if the container's PID 1 is a program that's meant to be used as init, it probably has a way to tell it to re-exec itself, and you should use that. The program may clean up before exec-ing and it may pass its successor executable additional data, for example in a file or in an environment variable.

2
  • 1
    This is mostly to potentially "hack" the container, not to actually do a clean rollout or anything. But this helps, thanks. Commented Nov 9, 2020 at 20:40
  • 1
    This is very handy. A quicker way is to use the gdb command directly. Something like gdb -n -pid 1 -batch -ex "call execlp(\"sleep\", \"86400\", (char*)0)" will keep the container alive for 24h and allows you to run other commands directly and debug it Commented Jul 25, 2023 at 21:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .