简体   繁体   中英

How to run a utility in leanest possible lxc container

Need is to run a command-line program installed on the host in an environment that has no Internet access and cannot write to random file system locations. Want to use lxc.

  • Share as much as possible of the host's file system
  • Separate network stack and user ids
  • Unprivileged container

The question is because using a generic template like ubuntu installs lots of separate software, where I would rather use lib and such from the host

In some future this could be used for Evince or LibreOffice for documents that may have scripts and trackers embedded or closed-source software that is up to no good.

Here's how for the privileged container case, based on sshd template it will cost 152 KiB of disk space:

sudo lxc-create --template=$PWD/lxc-sshd-ubuntu --name=x
sudo lxc-start --name=x
sudo lxc-attach --name=x

# check disk space: du -hs /var/lib/lxc/x/rootfs

The template file is below, it is derived from /usr/share/lxc/templates/lxc-sshd with some bugs fixed that prevents the distributed version from actually working. lxc-sshd-ubuntu

#!/bin/bash -eu
# © 2017 Harald Rudell <harald.rudell@gmail.com> (http://haraldrudell.com) ISC License.
auth_key= # undefined
options=$(getopt -o hp:n:S: -l help,rootfs:,path:,name:,auth-key: -- "$@")
if [ $? -ne 0 ]; then echo >&2 "parameter FAIL"; exit 1; fi
eval set -- "$options"
while true; do
    case "$1" in
        -h|--help)      usage $0 && exit 0;;
        -p|--path)      path=$2; shift 2;;
        --rootfs)       rootfs=$2; shift 2;;
        -n|--name)      name=$2; shift 2;;
        -S|--auth-key)  auth_key=$2; shift 2;;
        --)             shift 1; break ;;
        *)              break ;;
    esac
done
if [ $0 = "/sbin/init" ]; then
check_for_cmd() {
    cmd_path=`type $1`
    if [ $? -ne 0 ]; then echo "The command '$1' $cmd_path is not accessible on the system"; exit 1; fi
    cmd_path=`echo $cmd_path |cut -d ' ' -f 3`
}
    PATH="$PATH:/bin:/sbin:/usr/sbin"
    check_for_cmd /usr/sbin/init.lxc
    check_for_cmd sshd
    sshd_path=$cmd_path
    if [ -f /run-dhcp ]; then
        check_for_cmd dhclient
        check_for_cmd ifconfig
        touch /etc/fstab
        rm -f /dhclient.conf
        cat > /dhclient.conf << EOF
send host-name = gethostname();
EOF
        ifconfig eth0 up
        dhclient eth0 -cf /dhclient.conf
        echo "Container IP address:"
        ifconfig eth0 |grep inet
    fi
    exec /usr/sbin/init.lxc -- $sshd_path
    exit 1
fi
if [ -z "$path" ]; then echo "'path' parameter is required"; exit 1; fi
config="$path/config"
if [ -z "$rootfs" ]; then
    if grep -q '^lxc.rootfs' $config 2>/dev/null ; then
        rootfs=$(awk -F= '/^lxc.rootfs =/{ print $2 }' $config)
    else
        rootfs=$path/rootfs
    fi
fi
    tree="\
$rootfs/var/empty/sshd \
$rootfs/var/lib/empty/sshd \
$rootfs/etc/init.d \
$rootfs/etc/rc.d \
$rootfs/etc/ssh \
$rootfs/etc/sysconfig/network-scripts \
$rootfs/dev/shm \
$rootfs/run/sshd \
$rootfs/proc \
$rootfs/sys \
$rootfs/bin \
$rootfs/sbin \
$rootfs/usr \
$rootfs/tmp \
$rootfs/home \
$rootfs/root \
$rootfs/lib \
$rootfs/lib64"
    mkdir -p $tree
    ln -s /run $rootfs/var/run
    cat <<EOF > $rootfs/etc/passwd
root:x:0:0:root:/root:/bin/bash
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
EOF
    cat <<EOF > $rootfs/etc/group
root:x:0:root
sshd:x:74:
EOF
ssh-keygen -t rsa -N "" -f $rootfs/etc/ssh/ssh_host_rsa_key
ssh-keygen -t dsa -N "" -f $rootfs/etc/ssh/ssh_host_dsa_key
    cat <<EOF > $rootfs/etc/ssh/sshd_config
Port 22
Protocol 2
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_dsa_key
UsePrivilegeSeparation yes
SyslogFacility AUTH
LogLevel INFO
LoginGraceTime 120
PermitRootLogin yes
StrictModes yes
PubkeyAuthentication yes
IgnoreRhosts yes
HostbasedAuthentication no
PermitEmptyPasswords yes
ChallengeResponseAuthentication no
EOF
    if [ -n "$auth_key" -a -f "$auth_key" ]; then
        u_path="/root/.ssh"
        root_u_path="$rootfs/$u_path"
        mkdir -p $root_u_path
        cp $auth_key "$root_u_path/authorized_keys"
        chown -R 0:0 "$rootfs/$u_path"
        chmod 700 "$rootfs/$u_path"
        echo "Inserted SSH public key from $auth_key into $rootfs/$u_path"
    fi
    init_path=$(realpath --relative-to=/ $(readlink -f /sbin/init))
    grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> $path/config
cat <<EOF >> $path/config
lxc.utsname = $name
lxc.pts = 1024
lxc.cap.drop = sys_module mac_admin mac_override sys_time
lxc.aa_profile = unconfined
lxc.mount.entry = /lib lib none ro,bind 0 0
lxc.mount.entry = /bin bin none ro,bind 0 0
lxc.mount.entry = /usr usr none ro,bind 0 0
lxc.mount.entry = /sbin sbin none ro,bind 0 0
lxc.mount.entry = tmpfs run/sshd tmpfs mode=0644 0 0
lxc.mount.entry = /usr/share/lxc/templates/lxc-sshd $init_path none ro,bind 0 0
lxc.mount.entry = /etc/init.d etc/init.d none ro,bind 0 0
lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed
EOF
if [ -d /etc/sysconfig/network-scripts ]; then
    cat <<EOF >> $path/config
lxc.mount.entry = /etc/sysconfig/network-scripts etc/sysconfig/network-scripts none ro,bind 0 0
EOF
    fi

    if [ -d /etc/rc.d ]; then
        cat <<EOF >> $path/config
lxc.mount.entry = /etc/rc.d etc/rc.d none ro,bind 0 0
EOF
    fi
    grep -q "^lxc.network.ipv4" $path/config || touch $rootfs/run-dhcp
    if [ "$(uname -m)" = "x86_64" ]; then
        cat <<EOF >> $path/config
lxc.mount.entry = /lib64 lib64 none ro,bind 0 0
EOF
    fi

A minimal unprivileged 80 KiB lxc container is created with the below script like (Ubuntu):

$ lxc-unpriv
Creating unprivileged container c89-170905-202915 with container root user ids: 100000:100000…
Container configuration file: /home/foxyboy/.local/share/lxc/c89-170905-202915/config

Must change ownership of the root file system to container’s root user: 100000:100000
Elevated privileges are required to execute: sudo chown --recursive 100000:100000 /home/foxyboy/.local/share/lxc/c89-170905-202915/rootfs
Unprivileged container was created successfully.

Useful commands:
Start the container: lxc-start --name=c89-170905-202915
Stop the container: lxc-stop --name=c89-170905-202915 --timeout=1
Get a command prompt in running container: lxc-attach --name=c89-170905-202915
Follow the container’s log: tail -F ~/.local/share/lxc/c89-170905-202915/c89-170905-202915.log
request ip address for urnning container: lxc-attach --name=c89-170905-201943 -- dhclient eth0
Get container ip addresses: lxc-info --name=c89-170905-202915 --ips
Run a command inside the container (container should be stopped): lxc-execute --name=c89-170905-202915 -- command args…

Once created, executing a command can be tested like:

lxc-execute --name=c89-170905-202915 -- hostname
c89-170905-202915

Here is the bash script:

#!/bin/bash -eu
# © 2017 Harald Rudell <harald.rudell@gmail.com> (http://haraldrudell.com) ISC License.
while [ $# -ge 1 ]; do case "$1" in
  --help) echo "lxc-unpriv [--name=name]"; exit;;
  --name=*) NAME="${1#*=}";;
  *) echo >&2 "unknown options: '$*' usage: lxc-unpriv [--name=name]"; exit 2;;
esac; shift; done
if [ ! "${NAME-}" ]; then NAME=`hostname --short`-`date --utc +%y%m%d-%H%M%S`; fi
root_uid=100000
root_gid=100000
echo "Creating unprivileged container $NAME with container root user ids: $root_uid:$root_gid…"
lxc-create --template=none --name="$NAME"
config=~/".local/share/lxc/$NAME/config"
echo "Container configuration file: $config"
if [ ! -s "$config" ]; then echo >2 "Container configuration file missing: '$config'"; exit 1; fi
rootfs=~/".local/share/lxc/$NAME/rootfs"
mkdir --parents \
"$rootfs/bin" \
"$rootfs/etc/init.d" \
"$rootfs/etc/rc.d" \
"$rootfs/etc/sysconfig/network-scripts" \
"$rootfs/home" \
"$rootfs/lib" \
"$rootfs/lib64" \
"$rootfs/proc" \
"$rootfs/root" \
"$rootfs/sbin" \
"$rootfs/sys" \
"$rootfs/tmp" \
"$rootfs/usr" \
"$rootfs/var"
touch "$rootfs/etc/fstab"
grep --quiet "^lxc.network.ipv4" "$config" || echo "send host-name = gethostname();" >"$rootfs/dhclient.conf"
ln --symbolic "$rootfs/run" "$rootfs/var/run"
cat <<EOF >"$rootfs/etc/passwd"
root:x:0:0:root:/root:/bin/bash
EOF
cat <<EOF >"$rootfs/etc/group"
root:x:0:root
EOF
cat <<EOF >>"$config"
lxc.include = /usr/share/lxc/config/ubuntu.common.conf
lxc.include = /usr/share/lxc/config/ubuntu.userns.conf
lxc.rootfs = $rootfs
lxc.utsname = $NAME
lxc.pts = 1024
lxc.cap.drop = sys_module mac_admin mac_override sys_time
lxc.aa_profile = unconfined
lxc.mount.entry = /bin bin none ro,bind 0 0
lxc.mount.entry = /etc/init.d etc/init.d none ro,bind 0 0
lxc.mount.entry = /lib lib none ro,bind 0 0
lxc.mount.entry = /sbin sbin none ro,bind 0 0
lxc.mount.entry = /usr usr none ro,bind 0 0
lxc.mount.auto = cgroup:mixed proc:mixed sys:mixed
EOF
if [ -d /etc/sysconfig/network-scripts ]; then cat <<EOF >>"$config"
lxc.mount.entry = /etc/sysconfig/network-scripts etc/sysconfig/network-scripts none ro,bind 0 0
EOF
fi
if [ -d /etc/rc.d ]; then cat <<EOF >>"$config"
lxc.mount.entry = /etc/rc.d etc/rc.d none ro,bind 0 0
EOF
fi
if [ "$(uname --machine)" = x86_64 ]; then cat <<EOF >>"$config"
lxc.mount.entry = /lib64 lib64 none ro,bind 0 0
EOF
echo "lxc.arch = x86_64" >>"$config"
fi
CMD=(sudo chown --recursive $root_uid:$root_gid "$rootfs")
echo -e "\nMust change ownership of the root file system to container’s root user: $root_uid:$root_gid"
echo "Elevated privileges are required to execute: ${CMD[*]}"
"${CMD[@]}"
echo -e "Unprivileged container was created successfully.\n\nUseful commands:"
echo "Start the container: lxc-start --name=$NAME"
echo "Stop the container: lxc-stop --name=$NAME --timeout=1"
echo "Get a command prompt in running container: lxc-attach --name=$NAME"
echo "Follow the container’s log: tail -F ~/.local/share/lxc/$NAME/$NAME.log"
echo "request ip address for urnning container: lxc-attach --name=c89-170905-201943 -- dhclient eth0"
echo "Get container ip addresses: lxc-info --name=$NAME --ips"
echo "Run a command inside the container (container should be stopped): lxc-execute --name=$NAME -- command args…"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM