URI:
       ns - scripts - various script and utils
  HTML git clone git://z3bra.org/scripts
   DIR Log
   DIR Files
   DIR Refs
       ---
       ns (7136B)
       ---
            1 #!/bin/sh
            2 
            3 set -e
            4 
            5 verbose=0
            6 create=0
            7 detach=0
            8 nsdir=${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/ns
            9 nstmpl="ns-XXXX"
           10 nsuser="wgs"
           11 nsnet="172.16.0.0/12"
           12 
           13 usage() {
           14         echo "usage: $(basename $0) [-vclk] [-n name] [cmd [arg…]]" >&2
           15 }
           16 
           17 logset() {
           18         [ "${verbose:-0}" -gt 0 ] && printf '[ ] %s ' "$*" >&2 || true
           19 }
           20 
           21 logchk() {
           22         [ "${verbose:-0}" -gt 0 ] && printf '\r[x]\n' >&2 || true
           23 }
           24 
           25 logdie() {
           26         [ "${verbose:-0}" -gt 0 ] && printf '\r[!]\n' >&2
           27         ns_cleanup $nsname
           28         exit 1
           29 }
           30 
           31 nslist() {
           32         [ -d /run/netns ] && ls -1 /run/netns
           33 }
           34 
           35 nspid() { # 2nd PID is unshare(1)'s first child, aka PID 1
           36         ip netns pids $nsname|head -n2|tail -n1
           37 }
           38 
           39 nscwd() {
           40         readlink -f /proc/$(nspid)/cwd
           41 }
           42 
           43 nsip() {
           44         ns_exec ip -4 -o addr show "if"|awk '{print $4}'
           45 }
           46 
           47 ns_cleanup() {
           48         nsname="$1"
           49         if=$(ip -o link show|awk "/netns $nsname/{print \$2}"|cut -d@ -f1)
           50         if [ -n "$if" ]; then
           51                 net=$(ip -o -4 addr show dev "$if"|awk '{print $4}')
           52                 doas iptables -t nat -D POSTROUTING -s "$net" -j MASQUERADE
           53         fi
           54         ip netns pids $nsname | xargs -r kill -9
           55         doas ip netns del $nsname
           56         doas umount -Rq $nsdir/$nsname
           57         rmdir "$nsdir/$nsname"
           58 }
           59 
           60 ns_mkfs() {
           61         dir=$1
           62         logset "Create rootfs structure"
           63 
           64         mkdir -p $dir
           65         doas mount -t tmpfs tmpfs "$dir"
           66         for d in bin etc lib home/$nsuser tmp mnt dev dev/pts dev/shm sys proc; do
           67                 mkdir -p $dir/$d
           68         done
           69         for d in null zero full random urandom tty; do
           70             touch "$dir/dev/$d"
           71             doas mount --bind "/dev/$d" "$dir/dev/$d"
           72         done
           73         logchk
           74 
           75         logset "Generate system files"
           76         cp -r /usr/share/terminfo $dir/etc/
           77         install -D /etc/ssl/certs/ca-certificates.crt $dir/etc/ssl/certs/ca-certificates.crt
           78         echo "$nsname" > $dir/etc/hostname
           79         echo "hosts: files dns" > $dir/etc/nsswitch.conf
           80         echo "127.0.0.1 localhost" > $dir/etc/hosts
           81         printf 'nameserver %s\n' $(resolvectl dns|cut -d: -f2)|sort|uniq > $dir/etc/resolv.conf
           82         printf "export PS1='(%s) $ '" $nsname > $dir/etc/profile
           83         printf "%s:x:0:0::/home/%s:/bin/sh" $nsuser $nsuser > $dir/etc/passwd
           84         printf "%s:*::0:::::" $nsuser > $dir/etc/shadow
           85         printf "world:x:0:%s\n" $nsuser > $dir/etc/group
           86         ln -s ../proc/mounts "$dir/etc/mtab"
           87         ln -sf pts/ptmx "$dir/dev/ptmx"
           88         ln -sf / "$dir/usr"
           89         logchk
           90 
           91         cat <<-EOF > "$dir/etc/rc"
           92         #!/bin/sh
           93         /bin/mount -t sysfs sys /sys
           94         /bin/mount -t tmpfs tmpfs /dev/shm
           95         /bin/mount -t devpts devpts /dev/pts -o newinstance,ptmxmode=0666
           96         /bin/hostname $nsname
           97         EOF
           98         chmod +x $dir/etc/rc
           99 
          100         logset "Compile /bin/init"
          101         cc -x c - -o $dir/bin/init <<-EOF
          102         #include <signal.h>
          103         #include <unistd.h>
          104         #include <sys/wait.h>
          105         int main() {
          106           sigset_t set; int signum;
          107           if (getpid()!=1) return 1;
          108           sigfillset(&set); sigprocmask(SIG_BLOCK, &set, 0); 
          109           if (fork()>0) while(!sigwait(&set, &signum)) wait(NULL);
          110           sigprocmask(SIG_UNBLOCK, &set, 0); setsid(); setpgid(0, 0);
          111           return execve("/etc/rc", (char *[]){"rc", 0}, (char *[]){0});
          112         }
          113         EOF
          114         logchk
          115 }
          116 
          117 ns_install() {
          118         dir=$1
          119         logset "Boostrap utilities"
          120         # https://pubs.opengroup.org/onlinepubs/9799919799/idx/utilities.html
          121         exe="admin alias ar asa at awk basename batch bc bg c17 cal"
          122         exe="$exe cat cd cflow chgrp chmod chown cksum cmp comm command"
          123         exe="$exe compress cp crontab csplit ctags cut cxref date dd"
          124         exe="$exe delta df diff dirname du echo ed env ex expand expr"
          125         exe="$exe false fc fg file find fold fuser gencat get getconf"
          126         exe="$exe getopts gettext grep hash head iconv id ipcrm ipcs"
          127         exe="$exe jobs join kill lex link ln locale localedef logger"
          128         exe="$exe logname lp ls m4 mailx make man mesg mkdir mkfifo more"
          129         exe="$exe msgfmt mv newgrp ngettext nice nl nm nohup od paste"
          130         exe="$exe patch pathchk pax pr printf prs ps pwd read readlink"
          131         exe="$exe realpath renice rm rmdel rmdir sact sccs sed sh sleep"
          132         exe="$exe sort split strings strip stty tabs tail talk tee test"
          133         exe="$exe time timeout touch tput tr true tsort tty type ulimit"
          134         exe="$exe umask unalias uname uncompress unexpand unget uniq"
          135         exe="$exe unlink uucp uudecode uuencode uustat uux val vi wait"
          136         exe="$exe wc what who write xargs xgettext yacc zcat"
          137 
          138         # Missing from the above list
          139         exe="$exe mksh bash busybox mount umount mountpoint"
          140         exe="$exe tar gzip bzip2 xz zstd less"
          141 
          142         # Network utils
          143         exe="$exe hostname ip ping host dig curl ssh tcpdump"
          144 
          145         which $exe|xargs cp -t $dir/bin
          146         ldd $(readlink -f `which $exe`) 2>/dev/null \
          147                 |awk '/=>/{print $3}/^\s+\//{print $1}' \
          148                 |sed 's,^/usr,,'|sort|uniq|cpio --quiet -updL $dir
          149         logchk
          150 }
          151 
          152 ns_boot() {
          153         ns=$1
          154         logset "Start init process"
          155         unshare -rf -Uuimpn --mount-proc --kill-child -R $ns -- /bin/init &
          156         doas ip netns attach $nsname $!
          157 
          158         [ -z "$(nspid)" ] && logdie "Init process failed to start"
          159         logchk
          160 }
          161 
          162 ns_network() {
          163         ns=$1
          164         pid=$(nspid)
          165         nsif="$nsname"
          166         logset "Setup network"
          167 
          168         # Grab a random prefix from the given netblock to handle
          169         # communication between host and namespace (through NAT)
          170         pfx=20
          171         net=$(ipcalc -nb "$nsnet" $pfx|awk '/Network/{print $2}'|shuf|head -n1)
          172         min=$(ipcalc -nb $net|awk '/HostMin/{print $2}')
          173         max=$(ipcalc -nb $net|awk '/HostMax/{print $2}')
          174         doas ip link add $nsif type veth peer ${nsif}-peer
          175         doas ip link set $nsif up
          176         doas ip addr add "$min/$pfx" dev $nsif
          177         doas sysctl -qw net.ipv4.ip_forward=1
          178         doas iptables -t nat -A POSTROUTING -s $net -j MASQUERADE
          179 
          180         # Move 2nd pair of veth inside the namespace
          181         doas ip link set ${nsif}-peer netns $nsname
          182         nsenter -t "$(nspid)" -r --wd=$ns --preserve-credentials -U -u -p -i -n -m -- env -i /bin/sh <<-EOF
          183         /bin/ip link set lo up
          184         /bin/ip link set ${nsif}-peer name if
          185         /bin/ip link set if up
          186         /bin/ip address add ${max}/${pfx} dev if
          187         /bin/ip route add default via ${min}
          188         EOF
          189         logchk
          190 }
          191 
          192 ns_pop() {
          193         # Generate random nsname if not provided
          194         : ${nsname:=ns-$(tr -cd a-z </dev/urandom|dd status=none bs=1 count=5)}
          195 
          196         mkdir -p   "$nsdir/$nsname"
          197         trap "ns_cleanup $nsname" EXIT INT TERM QUIT
          198 
          199         ns_mkfs    "$nsdir/$nsname"
          200         ns_install "$nsdir/$nsname"
          201         ns_boot    "$nsdir/$nsname"
          202         ns_network "$nsdir/$nsname"
          203         trap - EXIT
          204 }
          205 
          206 ns_exec() {
          207         pid=$(nspid)
          208         cwd=$(nscwd)
          209         nshome=$(head -n1 $cwd/etc/passwd|cut -d: -f 6)
          210         nsenter -t $pid -r --wd=$cwd --preserve-credentials -U -u -p -i -n -m -- \
          211                 env -i -C $nshome \
          212                         PATH=/bin \
          213                         USER=$nsuser \
          214                         HOME=$nshome \
          215                         TERM="${TERM:-vt100}" \
          216                         LESS="${LESS:-R}" \
          217                         PAGER="${PAGER:-less}" \
          218                         EDITOR="${EDITOR:-vi}" \
          219                         VISUAL="${VISUAL:-vi}" \
          220                         "$@"
          221 }
          222 
          223 ns_info() {
          224         nsname="$1"
          225         [ -z "$(nspid)" ] && exit 1
          226         cat <<-EOF
          227         name: $nsname
          228         user: $(cut -d: -f1 < $(nscwd)/etc/passwd)
          229         root: $(nscwd)
          230         init: $(nspid)
          231         ipv4: $(nsip)
          232         EOF
          233 }
          234 
          235 while getopts "hvcdli:k:n:u:" OPT; do
          236         case $OPT in
          237         d) detach=1;;
          238         n) nsname="$OPTARG";;
          239         u) nsuser="$OPTARG";;
          240         v) verbose=1;;
          241         c) create=1;; 
          242         i) ns_info "$OPTARG"; exit 0;;
          243         k) ns_cleanup "$OPTARG"; exit 0;;
          244         l) nslist; exit 0;;
          245         h) usage; exit 0;;
          246         *) usage; exit 1;;
          247         esac
          248 done
          249 shift $((OPTIND-1))
          250 
          251 # Calling ns(1) without argument will attach to the first available
          252 # namespace. If no namespace is available, a new one will be spun up.
          253 if [ "$create" -eq 1 -o -z "$(nslist)" ]; then
          254         ns_pop
          255         [ "$detach" -eq 1 ] && { echo $nsname; exit 0; }
          256 fi
          257 
          258 [ -z "$nsname" ] && nsname=$(nslist|head -n1)
          259 
          260 # Pop a shell or whatever args provided
          261 ns_exec ${@:-mksh -l}