Skip to content

Instantly share code, notes, and snippets.

@thediveo
Last active December 23, 2019 08:30
Show Gist options
  • Save thediveo/62b9d172c96ea08007c0ffc000d9de08 to your computer and use it in GitHub Desktop.
Save thediveo/62b9d172c96ea08007c0ffc000d9de08 to your computer and use it in GitHub Desktop.
Can child(!) PID namespaces be owned by parent(!) user namespaces?
#!/bin/bash
# Test the hypothesis that PID namespace hierarchy cannot "run on reverse" to
# the user namespace hierarchy. In particular, a PID child namespace cannot be
# owned by a parent user namespace. However, a PID namespace can be a child of a
# PID namespace owned by a parent user namespace.
echo "trying to create a child PID namespace which is owned by a parent user namespace..."
NSTEXTREPR='([[:alpha:]]+):\[([[:digit:]]+)\]'
SECTION="--------------------"
# Given a Linux kernel namespace in textual form "type:[id]", returns (prints)
# a PID suitable for further reference.
function nsreadlink_to_pid {
if [[ $1 =~ $NSTEXTREPR ]]; then
# Extract the type of namespace and its unique id separately...
NSTYPE=${BASH_REMATCH[1]}
NSID=${BASH_REMATCH[2]}
# ...then try to find a namespace id match in what lsns discovers for
# the specific type of namespace, so we get a suitable PID
# corresponding with the namespace in question.
PID=$(lsns -o NS,PID -n -t "${NSTYPE}" | awk "\$1 == ${NSID} { print \$2 }")
echo ${PID}
else
echo "invalid namespace textual representation: \"$1\""
exit 1
fi
}
# Creates new user namespace #1, then inside it, it creates another new user
# namespace #2 as well as a new PID namespace #2, owned by user namespace #2.
# Just for completeness: user namespace #0 normally will be the root user
# namespace or whichever user namespace is active at the time this script is
# run.
# user#0 -- already existing when script runs
# └── user#1
# └── user#2 ⟜─ pid#2
echo ${SECTION}
echo "creating user#1-->user#2+pid#2..."
exec 2> /dev/null 3< <(unshare -U -r /bin/bash -c "sleep infinity 42s & readlink /proc/self/ns/user && unshare -U -p -f -r /bin/bash -c \"readlink /proc/self/ns/user /proc/self/ns/pid && sleep infinity 42s\"")
read <&3 USERNS1; USERNS1PID=$(nsreadlink_to_pid ${USERNS1})
echo "user#1 ${USERNS1} -> PID ${USERNS1PID}"
read <&3 USERNS2; USERNS2PID=$(nsreadlink_to_pid ${USERNS2})
echo "user#2 ${USERNS2} -> PID ${USERNS2PID}"
read <&3 PIDNS2; PIDNS2PID=$(nsreadlink_to_pid ${PIDNS2})
echo "pid#2 ${PIDNS2} -> PID ${PIDNS2PID}"
# For simple starters, let's enter user namespace #1, and create a new PID
# namespace there. This new PID namespace #X is owned by user namespace #1. This
# should succeed without any issues, and is just to show that later joining user
# namespace #1 allows us to create new pid namespaces. Additionally, this shows
# that we can have a child PID namespace #X which is owned by user namespace #1,
# yet is a child of PID namespace #0 -- well, otherwise containers wouldn't work
# ;)
# user#0 ⟜───────── pid#0
# ↑ ↑ ↑
# └── user#1 ⟜─ pid#X │
# ↑ │
# └── user#2 ⟜─ pid#2
echo ${SECTION}
echo "entering user#1, creating pid#X"
# Note, we keep PID namespace #X open in case someone wants to dump the
# namespace configuration in this script...
sudo nsenter --user=/proc/${USERNS1PID}/ns/user \
/bin/bash -c "echo \"entered user#1...\" && unshare -r -p echo -n \"pid#X: \" && readlink /proc/self/ns/pid && echo \"succeeded creating pid#X in #user1\" && sleep infinity 42s || echo '!!! creating pid#X in user#1 failed !!!'" &
# Now enter user namespace #1 and PID namespace #2. Please note that PID
# namespace #2 is owned by user namespace #2, not: user namespace #1. Trying
# to then create a new PID namespace (#1) would mean that PID namespace #1
# should be a child of PID namespace #2, yet the the owner of PID namespace #1
# would be user namespace #1, which would cause kind of a hierarchy confusion.
# user#0
# └── user#1 ⟜────── pid#1
# ↑ ↓ ???not allowed?
# └── user#2 ⟜─ pid#2
echo ${SECTION}
echo "entering user#1 (sic!) and pid#2, then trying to create pid#1..."
sudo nsenter --user=/proc/${USERNS1PID}/ns/user --pid=/proc/${PIDNS2PID}/ns/pid \
/bin/bash -c "echo \"entered user#1+pid#2...\" && unshare -f -r -p /bin/true && echo \"succeeded creating pid#1 in #user1\" || echo '!!! creating pid#1 as child of pid#2 while in user#1 failed !!!'"
# Clean up the newly created namespaces by killing the inner sleep, which then
# will cause the tower of processes to collapse, which in turn will cause the
# namespaces to cease to exists.
echo ${SECTION}
echo "cleaning up processes and namespaces..."
PIDS=$(ps -o pid,command a | grep '[[:digit:]]\+ sleep infinity 42s' | awk '{ print $1 }')
while IFS= read -r PID; do
echo -n "terminating PID ${PID}... "
kill -SIGKILL $PID
while kill -0 $PID 2>/dev/null; do
sleep 1
done
echo "done."
done <<< "$PIDS"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment