runROOTLESS: rootless OCI container runtime with ptrace hacks
Quick start (No root privileges nor SUID binaries are required!)
Install
Requires: Go, runc
user$ go get github.com/rootless-containers/runrootless
user$ $GOPATH/src/github.com/rootless-containers/runrootless/install-proot.sh
Future version should install a pre-built PRoot binary automatically on the first run.
Usage
Create an example Ubuntu bundle:
user$ cd ./examples/ubuntu
user$ ./prepare.sh
user$ ls -1F
config.json
prepare.sh
rootfs/
Make sure the bundle cannot be executed with the regular runc
:
user$ runc run ubuntu
rootless containers require user namespaces
Note that even with runc spec --rootless
, you cannot execute apt
:
user$ rm config.json
user$ runc spec --rootless
user$ sed -i 's/"readonly": true/"readonly": false/' config.json
user$ runc run ubuntu
# apt update
E: setgroups 65534 failed - setgroups (1: Operation not permitted)
E: setegid 65534 failed - setegid (22: Invalid argument)
E: seteuid 100 failed - seteuid (22: Invalid argument)
E: setgroups 0 failed - setgroups (1: Operation not permitted)
Reading package lists... Done
W: chown to _apt:root of directory /var/lib/apt/lists/partial failed - SetupAPTPartialDirectory (22: Invalid argument)
E: setgroups 65534 failed - setgroups (1: Operation not permitted)
E: setegid 65534 failed - setegid (22: Invalid argument)
E: seteuid 100 failed - seteuid (22: Invalid argument)
E: setgroups 0 failed - setgroups (1: Operation not permitted)
E: Method gave invalid 400 URI Failure message: Failed to setgroups - setgroups (1: Operation not permitted)
E: Method http has died unexpectedly!
E: Sub-process http returned an error code (112)_
With runrootless
, you can execute apt
successfully:
user$ ./prepare.sh
user$ runrootless run ubuntu
# apt update
# apt install -y cowsay
# /usr/games/cowsay hello rootless world
______________________
< hello rootless world >
----------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
Other examples
CentOS:
user$ cd ./examples/centos
user$ ./prepare.sh
user$ runrootless run centos
sh-4.2# yum install -y epel-release
sh-4.2# yum install -y cowsay
sh-4.2# cowsay hello rootless world
Alpine Linux:
user$ cd ./examples/alpine
user$ ./prepare.sh
user$ runrootless run alpine
/ # apk update
/ # apk add fortune
/ # fortune
Arbitrary Docker image:
user$ cd ./examples/docker-image
user$ ./prepare.sh opensuse
user$ runrootless run opensuse
sh-4.3# zypper install cowsay
sh-4.3# cowsay hello rootless world
Arbitrary container image, using skopeo and umoci.
umoci and runROOTLESS share emulated chown(2)
information via user.rootlesscontainers
xattr.
user$ cd ./examples/skopeo-umoci
user$ ./prepare.sh docker://ubuntu
user$ cd umoci-bundle
user$ runrootless run ubuntu
runROOTLESS can be also executed inside Docker container, but --privileged
is still required ( opencontainers/runc#1456 )
host$ docker run -it --rm --privileged akihirosuda/runrootless
~ $ id
uid=1000(user) gid=1000(user)
~ $ cd ~/examples/ubuntu/
~/examples/ubuntu $ ./prepare.sh
~/examples/ubuntu $ runrootless run ubuntu
#
Environment variables
RUNROOTLESS_SECCOMP=1
: enable seccomp acceleration (unstable)
How it works
- Transform a regular
config.json
to rootless one, and create a new OCI runtime bundle with it. - Bind-mount a static PRoot binary so as to allow
apt
/yum
commands. - Inject the PRoot binary to
process.args
. - Invoke plain runC.
Known issues
apt
/dpkg
may crash when seccomp acceleration is enabled: #4
Future work
OCI Runtime Hook mode
runROOTLESS could be reimplemented as a OCI Runtime Hook (prestart) that works with an arbitrary OCI Runtime.
This work would need adding support for PTRACE_ATTACH
to PRoot.
Also, it would require YAMA to be disabled.
Reimplement PRoot in Go
This is hard than I initially thought...