Linux 如何在最精简的lxc容器中运行实用程序

Linux 如何在最精简的lxc容器中运行实用程序,linux,ubuntu,lxc,Linux,Ubuntu,Lxc,需要在没有Internet访问且无法写入随机文件系统位置的环境中运行安装在主机上的命令行程序。想要使用lxc 尽可能多地共享主机的文件系统 独立的网络堆栈和用户ID 非特权集装箱 问题是因为使用像ubuntu这样的通用模板会安装很多独立的软件,我更愿意使用主机上的lib之类的软件 在未来的某个时候,这可能会被用于Evince或LibreOffice,用于那些可能嵌入了脚本和跟踪器的文档,或者使用了不太好的封闭源代码软件。以下是特权容器的情况,基于sshd模板,它将花费152 KiB的磁盘空间

需要在没有Internet访问且无法写入随机文件系统位置的环境中运行安装在主机上的命令行程序。想要使用lxc

  • 尽可能多地共享主机的文件系统
  • 独立的网络堆栈和用户ID
  • 非特权集装箱
问题是因为使用像ubuntu这样的通用模板会安装很多独立的软件,我更愿意使用主机上的lib之类的软件


在未来的某个时候,这可能会被用于Evince或LibreOffice,用于那些可能嵌入了脚本和跟踪器的文档,或者使用了不太好的封闭源代码软件。

以下是特权容器的情况,基于sshd模板,它将花费152 KiB的磁盘空间:

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
模板文件如下所示,它源于
/usr/share/lxc/templates/lxc sshd
,修复了一些阻止分布式版本实际工作的错误lxcshdubuntu

#!/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
#/宾/巴什-欧盟
#©2017哈拉尔德·鲁德尔(http://haraldrudell.com)ISC许可证。
验证密钥=#未定义
options=$(getopt-ohp:n:S:-l帮助,rootfs:,路径:,名称:,身份验证键:--“$@”)
如果[$?-ne 0];然后echo>&2“参数失败”;出口1;fi
评估集--“$options”
虽然真实;做
案件“$1”
-h |--help)用法$0&&exit 0;;
-p |--path)path=$2;第二班;;
--rootfs)rootfs=$2;第二班;;
-n |--name)name=$2;第二班;;
-S |--auth key)auth_key=$2;第二班;;
--)第1班;打破
*)打破;;
以撒
完成
如果[$0=“/sbin/init”];然后
检查\u是否有\u cmd(){
cmd_path=`type$1`
如果[$?-ne 0];则回显“命令'$1'$cmd_路径在系统上不可访问”;退出1;fi
cmd_path=`echo$cmd_path | cut-d'-f3`
}
PATH=“$PATH:/bin:/sbin:/usr/sbin”
检查是否有cmd/usr/sbin/init.lxc
检查\u是否有\u cmd sshd
sshd_路径=$cmd_路径
如果[-f/运行dhcp];然后
检查\u是否有\u cmd dhclient
检查\u中的\u cmd ifconfig
触摸/etc/fstab
rm-f/dhclient.conf
cat>/dhclient.conf/dev/null;然后
rootfs=$(awk-F='/^lxc.rootfs=/{print$2}'$config)
其他的
rootfs=$path/rootfs
fi
fi
树=”\
$rootfs/var/empty/sshd\
$rootfs/var/lib/empty/sshd\
$rootfs/etc/init.d\
$rootfs/etc/rc.d\
$rootfs/etc/ssh\
$rootfs/etc/sysconfig/networkscripts\
$rootfs/dev/shm\
$rootfs/run/sshd\
$rootfs/proc\
$rootfs/sys\
$rootfs/bin\
$rootfs/sbin\
$rootfs/usr\
$rootfs/tmp\
$rootfs/家\
$rootfs/root\
$rootfs/lib\
$rootfs/lib64“
mkdir-p$树
ln-s/run$rootfs/var/run
cat$path/config
cat$path/config
lxc.utsname=$name
lxc.pts=1024
lxc.cap.drop=系统模块mac\U管理mac\U覆盖系统时间
lxc.aa_剖面=无侧限
lxc.mount.entry=/lib-none-ro,绑定0
lxc.mount.entry=/bin无ro,绑定0
lxc.mount.entry=/usr usr none ro,绑定0
lxc.mount.entry=/sbin sbin none ro,绑定0
lxc.mount.entry=tmpfs运行/sshd-tmpfs模式=0644 0
lxc.mount.entry=/usr/share/lxc/templates/lxc sshd$init_path none ro,绑定0
lxc.mount.entry=/etc/init.d etc/init.d none ro,绑定0
lxc.mount.auto=cgroup:mixed proc:mixed sys:mixed
EOF
if[-d/etc/sysconfig/networkscripts];然后
cat$path/config
lxc.mount.entry=/etc/sysconfig/network scripts etc/sysconfig/network scripts none ro,bind 0
EOF
fi
如果[-d/etc/rc.d];然后
cat$path/config
lxc.mount.entry=/etc/rc.d etc/rc.d none ro,绑定0
EOF
fi
grep-q“^lxc.network.ipv4”$path/config | | touch$rootfs/run dhcp
如果[“$(uname-m)”=“x86_64”];然后
cat$path/config
lxc.mount.entry=/lib64 lib64 none ro,绑定0
EOF
fi

以下是特权容器的情况,基于sshd模板,它将花费152 KiB的磁盘空间:

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
模板文件如下所示,它源于
/usr/share/lxc/templates/lxc sshd
,修复了一些阻止分布式版本实际工作的错误lxcshdubuntu

#!/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
#/宾/巴什-欧盟
#©2017哈拉尔德·鲁德尔(http://haraldrudell.com)ISC许可证。
验证密钥=#未定义
options=$(getopt-ohp:n:S:-l帮助,rootfs:,路径:,名称:,身份验证键:--“$@”)
如果[$?-ne 0];然后echo>&2“参数失败”;出口1;fi
评估集--“$options”
虽然真实;做
案件“$1”
-h |--help)用法$0&&exit 0;;
-p |--path)path=$2;第二班;;
--rootfs)rootfs=$2;第二班;;
-n |--name)name=$2;第二班;;
-S |--auth key)auth_key=$2;第二班;;
--)第1班;打破
*)打破;;
以撒
完成
如果[$0=“/sbin/init”];然后
检查\u是否有\u cmd(){
cmd_path=`type$1`
如果[$?-ne 0];则回显“命令'$1'$cmd_路径在系统上不可访问”;退出1;fi
cmd_path=`echo$cmd_path | cut-d'-f3`
}
PATH=“$PATH:/bin:/sbin:/usr/sbin”
检查是否有cmd/usr/sbin/init.lxc
检查\u是否有\u cmd sshd
sshd_路径=$cmd_路径
如果[-f/运行dhcp];然后
检查\u是否有\u cmd dhclient
检查\u中的\u cmd ifconfig
触摸/etc/fstab
rm-f/dhclient.conf
cat>/dhclient.conf/dev/null;然后
rootfs=$(awk-F='/^lxc.rootfs=/{print$2}'$config)
其他的
rootfs=$path/rootfs
fi
fi
树=”\
$rootfs/var/empty/sshd\
$rootfs/var/lib/empty/sshd\
$rootfs/etc/init.d\
$rootfs/etc/rc.d\
$rootfs/etc/ssh\
$rootfs/etc/sysconfig/networkscripts\
$rootfs/dev/shm\
$rootfs/run/sshd\
$rootfs/proc\
$rootfs/sys\
$rootfs/bin\
$rootfs/sbin\
$rootfs/usr\
$rootfs/tmp\
$rootfs/家\
$rootfs/root\
$rootfs/lib\
$rootfs/lib64“
mkdir-p$树
ln-s/run$rootfs/var/run
cat$path/config
cat$path/config
lxc.utsname=$name
lxc.pts=1024
lxc.cap.drop=系统模块mac\U管理mac\U覆盖系统时间
lxc.aa_剖面=无侧限
#!/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…"