commit 5e2bb0736ec2070949c4173dd12c59e47f9042bd
parent c89fdace29ababe01bf19e336bf695041c85411e
Author: Jan Pobrislo <ccx@webprojekty.cz>
Date: Fri, 18 Dec 2020 05:07:00 +0100
Rewrite the scripts for entering namespace/container in pure execline and AWK, list it's parameters and function.
Diffstat:
M | sbin/ns_run | | | 134 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- |
A | sbin/ns_umount_script.awk | | | 58 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
2 files changed, 187 insertions(+), 5 deletions(-)
diff --git a/sbin/ns_run b/sbin/ns_run
@@ -1,14 +1,138 @@
-#!/command/execlineb -S0
+#!/command/execlineb -s1
+
+## usage: ns_run <root_directory> <executable> [<arg1> ...]
+##
+## Creates isolated namespace/container with given root and runs given
+## executable in it.
+## Environment variables used:
+## HOST - hostname to set
+## NS_ROOT - where to bind-mount the root directory
+## NS_EXTRA - extra execline script to run after setting up the namespaces
+## and mounting essential filesystems but before entering it
+## and unmounting host filesystem
+## NS_FSTAB - file with extra mounts to make after running above script
+## NS_TMPFS - place to store binaries in the container that are run
+## before dropping privs, relative path from new root
+## NS_BIN - directory to get said binaries from; currently needs:
+## - `if` from execline
+## - busybox with `umount` and `chpst` functions
+## - anything you wish to call before and for dropping privs,
+## eg. s6-applyuidgid
+## All statically linked of course.
+
+# import variables from environment, with defaults
+multisubstitute {
+ importas -D mnt/ns NS_TMPFS
+ importas -D /mnt/volumes/containers/bin NS_BIN
+ importas -D /mnt/chroot NS_ROOT
+}
+
+# check we are PID1 (in a new PID namespace)
getpid PID
ifelse { importas -i PID PID test 1 -ne $PID } {
fdmove -c 1 2
echo "ns_run: fatal: not PID 1"
- exit 2
+ exit 111
}
unexport PID
+
unshare -m -u -i # new mount, UTS and IPC namespaces
foreground { importas -i HOST HOST hostname $HOST }
-fdmove -c 3 0 # store original stdin as we are calling pipelined script
-pipeline -d { /root/ns_execline.zsh $@ }
-execlineb /dev/stdin
+
+# We will generate final script we exec into before we start mounting anything,
+# so only the mountpoints that exist at this point will get unmounted and
+# everything we will mount below will stay.
+backtick -i NS_FINAL_SCRIPT {
+ # Generate execline script that performs pivot_root
+ # and umounts all the filesystems not used by the container
+ # formerly: /root/ns_execline.zsh $@
+
+ if {
+ printf "%s\n"
+ "if { pivot_root . \$NS_TMPFS/oldroot }"
+ }
+ if {
+ ns_umount_script.awk
+ -vROOT=/$NS_TMPFS/oldroot
+ -vBIN=/$NS_TMPFS/bin
+ /proc/self/mountinfo
+ }
+ if { printf "%s\n" "./$NS_TMPFS/bin/busybox chpst -/ ." }
+ # quote all the arguments we got for interpretation by execline
+ forx -o 0 X { $@ } importas -i X X s6-quote $X
+}
+
+# mount and enter the chroot directory
+if { mount --rbind $1 $NS_ROOT }
+cd $NS_ROOT
+
+## this will be better handled by NS_EXTRA script
+## and perhaps /etc/resolv.conf -> /run/resolv.conf symlink
+# foreground { cp /etc/resolv.conf etc/ }
+
+# /proc
+if { mount -t proc proc proc }
+
+# /dev
+if { s6-mount -nwt tmpfs -o nosuid,dev,mode=0755 dev dev }
+if { mknod -m 666 dev/null c 1 3 }
+if { mknod -m 666 dev/full c 1 7 }
+if { mknod -m 666 dev/ptmx c 5 2 }
+if { mknod -m 644 dev/random c 1 8 }
+if { mknod -m 644 dev/urandom c 1 9 }
+if { mknod -m 666 dev/zero c 1 5 }
+if { mknod -m 666 dev/tty c 5 0 }
+
+# shm, pts and mqueue are provided below
+# also have some convenience dirs in place for optionally bind-mounting them
+if {
+ mkdir
+ dev/shm
+ dev/pts
+ dev/mqueue
+
+ dev/block
+ dev/bus
+ dev/char
+ dev/dri
+ dev/input
+ dev/loop
+ dev/net
+ dev/snd
+ dev/usb
+
+}
+if { mount -t devpts devpts dev/pts }
+if { s6-mount -nwt tmpfs -o nosuid,nodev,mode=1777 shm dev/shm }
+if { s6-mount -nwt mqueue -o nosuid,nodev,noexec,relatime mqueue dev/mqueue }
+# leave /dev read-write for now, so stuff can be added by scripts below
+
+# mountpoint for privileged operations and pivot_root
+if { s6-mount -nwt tmpfs -o nosuid,nodev,mode=700 mnt_ns $NS_TMPFS }
+if { mkdir $NS_TMPFS/oldroot $NS_TMPFS/bin }
+#if { s6-hiercopy $NS_BIN $NS_TMPFS/bin }
+if { s6-mount -n -o bind,ro $NS_BIN ${NS_TMPFS}/bin }
+
+# container-specific setup provided in environment
+if {
+ if -t { s6-test -V NS_EXTRA }
+ importas -i NS_EXTRA NS_EXTRA
+ execlineb -c $NS_EXTRA
+}
+
+if {
+ if -t { s6-test -V NS_FSTAB }
+ importas -i NS_FSTAB NS_FSTAB
+ mount -a --fstab $NS_FSTAB
+}
+
+# now we can make /dev immutable
+if { mount -o remount,ro dev }
+
+# exec into the script we generated above, it:
+# * does pivot_root to change rootdir
+# * umounts all undesired filesystems
+# * execs into $@
+importas -i NS_FINAL_SCRIPT NS_FINAL_SCRIPT
+execlineb -c $NS_FINAL_SCRIPT
diff --git a/sbin/ns_umount_script.awk b/sbin/ns_umount_script.awk
@@ -0,0 +1,58 @@
+#!/usr/bin/awk -f
+function el_quote(s) {
+ RESULT="\"${${${1}//\\/\\\\}//\"/\\\"}\""
+ gsub(/\\/, "\\\\", s); # first double all backslashes
+ gsub(/"/, "\\\"", s); # then escape quote marks
+ return "\"" s "\"" # then surround with quote marks
+}
+
+BEGIN {
+ # check if necessary variables were defined
+ if(BIN == "") { exit 100 }
+ if(ROOT == "") { exit 100 }
+
+ # quote common stings
+ IF = el_quote(BIN "/if")
+ UMOUNT = el_quote(BIN "/busybox") " umount"
+
+ # mount IDs seem to be unsigned, so let's use -1 to signify not found
+ max_id = -1
+ root_id = -1
+}
+
+# read in /proc/self/mountinfo
+$5 == "/" { root_id = $1 }
+{
+ max_id = max_id < $1 ? $1 : max_id
+ parents[$1] = $2
+ mountpoints[$1] = $5
+}
+
+function _if(opts, s) {
+ return IF opts " { " s " } "
+}
+
+function umount(opts, s) {
+ return UMOUNT opts " " el_quote(ROOT s)
+}
+
+function print_umount(mtp){
+ print _if("", \
+ _if(" -n -t", umount("", mtp)) \
+ umount(" -l", mtp) \
+ )
+}
+
+function recursively_umount(mount_id, id) {
+ for(id=max_id; id>=0; id--){
+ if(parents[id] == mount_id){
+ recursively_umount(id)
+ }
+ }
+ print_umount(mountpoints[mount_id])
+}
+
+END{
+ if(root_id == -1) { exit 111 }
+ recursively_umount(root_id)
+}