Unprivileged containers as root, an oxymoron?

It’s not, it can actually be accomplished by using subuid (subordinate user id’s) & subgid (subordinate group id’s) built into the linux kernel.

So why do we need this?

Well, it lets us isolate namespaces inside of containers.

Basically switching the root users id (UID:0) inside of the container to UID:****** on the host, thereby making sure that if a user (hacker) escapes the container they only gain unprivileged access to the host.

Ipso facto securing your hosts integrity. (presuming that you’ve configured it according to all security guidelines)

I wrote a script to make calculations of new subuid & subgid’s easier.

#!/bin/bash

# one less than 2 to the 16th power, which is the highest number that can be represented by an unsigned 16-bit binary number.
INCREMENT_BY=65535 

# FILE exists and has a size greater than zero
if [[ -s /etc/subuid ]]
then
	U_FROM=$(tail -1 /etc/subuid | cut -f2 -d:)
	U_TO=$(tail -1 /etc/subuid | cut -f3 -d:)
	U_NEW_FROM=$(($U_FROM+$U_TO))
	U_NEW_TO=$(($U_NEW_FROM+$INCREMENT_BY))
else
	U_NEW_FROM=100000
	U_NEW_TO=165535
fi
if [[ -s /etc/subgid ]]
then
	G_FROM=$(tail -1 /etc/subgid | cut -f2 -d:)
	G_TO=$(tail -1 /etc/subgid | cut -f3 -d:)
	G_NEW_FROM=$(($G_FROM+$G_TO))
	G_NEW_TO=$(($G_NEW_FROM+$INCREMENT_BY))
else
	G_NEW_FROM=100000
	G_NEW_TO=165535
fi

cat <<EOF

# Run to add SubUID & SubGID for new client (/etc/sub?id):

	usermod --add-sub-uids ${U_NEW_FROM}-${U_NEW_TO} --add-sub-gids ${G_NEW_FROM}-${G_NEW_TO} root

# Save to: /etc/lxc/client-default.conf

	lxc.include = /etc/lxc/default.conf
	lxc.id_map = u 0 ${U_NEW_FROM} 65536
	lxc.id_map = g 0 ${G_NEW_FROM} 65536

# Create new container:

	lxc-create -n new_container_name -t download --config /etc/lxc/client-default.conf -- --no-validate

EOF

The script doesn’t execute anything, it only outputs suggestions for you to use. It’s kind of tricky to get these numbers right by hand.

# Run to add SubUID & SubGID for new client

    usermod --add-sub-uids 1935008-2000543 --add-sub-gids 1935008-2000543 root

# Save: /etc/lxc/client-default.conf

    lxc.include = /etc/lxc/default.conf
    lxc.id_map = u 0 1935008 65536
    lxc.id_map = g 0 1935008 65536

# Create new container

    lxc-create -n new_container_name -t download --config /etc/lxc/client-default.conf -- --no-validate

So what about migration?

Switching SubUID and SubGID on a container filesystem requires running chown while shifting to the appropriate values.

#!/bin/bash

[[ $# < 3 ]] && echo "$0 <container_name> <new_root_uid> <new_root_gid>" && exit 1

export C="$1"
export SHIFT_TO_UID="$2"
export SHIFT_TO_GID="$3"

export SHIFT_FROM_UID=$(grep id_map /var/lib/lxc/${C}/config | grep 'u 0' | awk '{print $5}')
export SHIFT_FROM_GID=$(grep id_map /var/lib/lxc/${C}/config | grep 'g 0' | awk '{print $5}')

[[ -z "$SHIFT_FROM_UID" ]] && export SHIFT_FROM_UID=0
[[ -z "$SHIFT_FROM_GID" ]] && export SHIFT_FROM_GID=0

echo "# Shifting user and group from id ${SHIFT_FROM_UID}:${SHIFT_FROM_GID} to ${SHIFT_TO_UID}:${SHIFT_TO_GID}"

function splitit() {
	FILE_UID=$(stat -c '%u' $@)
	FILE_GID=$(stat -c '%g' $@)
	echo "chmod $(( $FILE_UID - $SHIFT_FROM_UID + $SHIFT_TO_UID )):$(( $FILE_GID - $SHIFT_FROM_GID + $SHIFT_TO_GID)) $@"
}

export -f splitit

find /var/lib/lxc/$C/rootfs -exec bash -c 'splitit "$0"' {} \;