Skip to content

Instantly share code, notes, and snippets.

@garywill
Last active March 4, 2025 06:00
Show Gist options
  • Select an option

  • Save garywill/4ac25bd6ef840dfdf1bf576da5070d7f to your computer and use it in GitHub Desktop.

Select an option

Save garywill/4ac25bd6ef840dfdf1bf576da5070d7f to your computer and use it in GitHub Desktop.
bubblewrap (bwrap) root overlay filesystem container on opensuse. Can install packages from openeuse repos, to test and try software in sandbox
#!/bin/bash
# 显示本bash脚本收到的完整参数
echo "此脚本文件接收到的来自终端的所有参数: $@"
script=$(readlink -f "$0")
scriptpath=$(dirname "$script")
scriptname="$(basename $script)"
scriptdirname="$(basename $scriptpath)"
DEVELOPER=1
default_app="bash"
sandbox_name="overlaytest" # 注释掉这个,留空就会自动根据路径和脚本名生成
if [[ ! -n "$sandbox_name" ]]; then
sandbox_name="${scriptdirname}_${scriptname}"
fi
# 是否挂载此脚本所在路径
# BIND_SCRIPT_PATH=1
# TODO 禁止沙箱内改写此脚本
# MASK_XDG_OPENS=1 # 屏幕 dolphin firefox xdg-open 等,变成问复制
HOME='/root'
TMPDIR="$(mktemp -d /tmp/sandbox_${sandbox_name}_$(date +"%m%d_%H%M%S_%3N")_XXXXXX)"
echo "此沙箱实例的临时目录: $TMPDIR"
# 是否挂载另一个HOME,以及如何挂载
FAKE_HOME=1
# FAKE_HOME_PATH="$scriptpath/fakehome"
FAKE_HOME_PATH="$TMPDIR/fakehome" ; mkdir -p $FAKE_HOME_PATH
# 是否需要GUI显示在Xorg
# NEED_X=1 # 如果启用,记录再去启用dbus那几行
# NOTE 要在容器里尝试从仓库安装新软件包,此脚本应该在宿主机中sudo运行
# NOTE 在容器里的sudo必须要用一个空bash替代,才能opi
extra_args=(
# --uid 0 --gid 0
--overlay-src /var --tmp-overlay /var
# --overlay-src /run --tmp-overlay /run
--overlay-src /usr --tmp-overlay /usr # 有这个可能找不到bash
--overlay-src /lib64 --tmp-overlay /lib64 # 有这个可能找不到bash
--overlay-src /bin --tmp-overlay /bin
--overlay-src /sbin --tmp-overlay /sbin
--overlay-src /lib --tmp-overlay /lib
--overlay-src /etc --tmp-overlay /etc
)
EXTRA_ARGS_CMD="${extra_args[@]}"
function sandbox_run()
{
echo "bwrap命令将运行: $@"
args=(
--die-with-parent
# --new-session # 用了就会产生一个警告 bash: 无法设定终端进程组(1): 对设备不适当的 ioctl 操作
--info-fd $sandbox_info
--json-status-fd $json_status
--unsetenv SYSTEMD_EXEC_PID
--unsetenv SSH_AGENT_PID
--unsetenv MANAGERPID
# --clearenv
# --setenv PATH "$PATH"
# --setenv PS1 "$PS1" # 为什么无效?
# --setenv QT_IM_MODULE $QT_IM_MODULE
# --setenv QT_IM_SWITCHER $QT_IM_SWITCHER
# --setenv XMODIFIERS $XMODIFIERS
# --setenv GTK_IM_MODULE $GTK_IM_MODULE
--unshare-ipc
--unshare-uts
--unshare-cgroup
--unshare-user
--unshare-pid
--proc /proc
--dev /dev
# --dev-bind /dev/dri /dev/dri # 在 NEED_X 里处理了
# --ro-bind /etc /etc
# --tmpfs /etc
# --ro-bind /etc/passwd /etc/passwd
# --ro-bind /etc/nsswitch.conf /etc/nsswitch.conf
# --ro-bind /etc/hosts /etc/hosts
# --ro-bind /etc/resolv.conf /etc/resolv.conf # 不自定义DNS则需要这个
# # SSL
# --ro-bind /etc/crypto-policies /etc/crypto-policies
# --ro-bind /etc/ca-certificates /etc/ca-certificates
# --ro-bind /etc/ssl /etc/ssl
# --ro-bind /etc/pki /etc/pki
#
# --ro-bind /etc/alternatives /etc/alternatives
# --ro-bind /etc/fonts /etc/fonts
--tmpfs /tmp
# --ro-bind /bin /bin
# # --ro-bind /boot /boot
# --ro-bind /sbin /sbin
# --ro-bind /lib /lib
# --ro-bind /lib64 /lib64
#
# --ro-bind /usr /usr
$MASK_XDG_CMD
# --ro-bind /opt /opt
# --ro-bind /srv /srv
# --ro-bind /sys /sys
--ro-bind /selinux /selinux
# --bind /var /var
# --tmpfs /var
# --ro-bind /var/lib /var/lib
# # --ro-bind /var/lib/ca-certificates /var/lib/ca-certificates
# # /var/cache 就不要共享了,不管需不需要隐藏都不共享
# --ro-bind /var/cache/fontconfig /var/cache/fontconfig
--tmpfs /run
# --symlink /run /var/run
--tmpfs /run/user/$(id -u)
--ro-bind /run/netconfig/resolv.conf /run/netconfig/resolv.conf
--ro-bind /run/nscd/ /run/nscd # 如果自定义了DNS就不要这个
# # 如果用了X,可能还需要dbus
# # --bind /run/dbus/ /run/dbus
# # --bind /run/user/$(id -u)/dbus-1/ /run/user/$(id -u)/dbus-1
# # --bind /run/user/$(id -u)/bus /run/user/$(id -u)/bus
$CUSTOMDNS_CMD # 在etc和var之后
$BIND_HOME_CMD
$BIND_SCRIPTPATH_CMD
$NEED_X_CMD # 有字体缓存相关,在var和home之后
$EXTRA_ARGS_CMD
# --chdir $HOME
--
"$@"
)
echo "${args[@]}" > "$TMPDIR/bwrap_all_args"
bwrap "${args[@]}"
}
if [[ $MASK_XDG_OPENS -eq 1 ]]; then
mask_xdg_args=()
if which firefox >/dev/null 2>&1 ; then
mask_xdg_args+=(--ro-bind $HOME/bin/askbrowser.sh $(which firefox))
fi
if which firefox-esr >/dev/null 2>&1 ; then
mask_xdg_args+=(--ro-bind $HOME/bin/askbrowser.sh $(which firefox-esr))
fi
if which xdg-open >/dev/null 2>&1 ; then
mask_xdg_args+=(--ro-bind $HOME/bin/askbrowser.sh $(which xdg-open))
fi
if which dolphin >/dev/null 2>&1 ; then
mask_xdg_args+=(--ro-bind $HOME/bin/askbrowser.sh $(which dolphin))
fi
MASK_XDG_CMD="${mask_xdg_args[@]}"
fi
get_fd() {
local x
local tmpfile
local _fd_var="$1"
local name="$1"
mkdir -p "$TMPDIR/get_fd"
for x in $(seq 10 $(ulimit -n)); do
if [[ ! -a "/proc/$BASHPID/fd/$x" ]]; then
tmpfile="$TMPDIR/get_fd/$name"
touch "$tmpfile"
exec {x}<>$tmpfile
printf -v "$_fd_var" %s "$x"
# rm $tmpfile # 这里删掉就会在结束后自动销毁,不删就保留在主机文件系统里
return
fi
done
echo 0
}
get_fd sandbox_info
get_fd json_status
if [[ $FAKE_HOME -eq 1 ]] ; then
echo "将挂载另一个HOME: $FAKE_HOME_PATH"
touch "$FAKE_HOME_PATH/.bashrc"
cat << EOF > "$FAKE_HOME_PATH/.bashrc" #用cat不要用echo
LS_OPTIONS='--color=auto'
alias ls='ls \$LS_OPTIONS'
alias ll='ls \$LS_OPTIONS -l'
alias l='ls \$LS_OPTIONS -lA'
alias la='ls \$LS_OPTIONS -lAa'
PS1="sandbox | \\w > "
PS1="\[\e[1;93m\]\$PS1\[\e[0m\]"
echo 00000000000000000000000000000000 > /etc/machine-id
EOF
fake_home_args=(
--unsetenv XDG_CACHE_HOME
--bind $FAKE_HOME_PATH $HOME
# ~/.cache 就不要共享了,不管需不需要隐藏都不共享
# --ro-bind $HOME/.cache/fontconfig $HOME/.cache/fontconfig
# --ro-bind $HOME/.fonts $HOME/.fonts
# --ro-bind $HOME/.fonts.conf $HOME/.fonts.conf
)
BIND_HOME_CMD="${fake_home_args[@]}"
else
echo "将使用真实的HOME"
BIND_HOME_CMD="--bind $HOME $HOME"
fi
if [[ $BIND_SCRIPT_PATH -eq 1 ]] ; then
echo "将挂载脚本所在目录: $scriptpath"
BIND_SCRIPTPATH_CMD="--bind $scriptpath $scriptpath"
fi
if [[ $NEED_X -eq 1 ]]; then
echo "需要Xorg和GUI"
xorg_args=(
--dev-bind /dev/dri /dev/dri
--ro-bind $XAUTHORITY $XAUTHORITY # 一般是/tmp/xauth_xxxxxxxx
--bind /tmp/.X11-unix /tmp/.X11-unix # TODO 这几个要不要由bind改为ro-bind ?
--bind /tmp/.XIM-unix /tmp/.XIM-unix
--bind /tmp/.ICE-unix /tmp/.ICE-unix
--bind /tmp/.font-unix /tmp/.font-unix
--setenv DISPLAY $DISPLAY
--setenv XAUTHORITY $XAUTHORITY
# --tmpfs /etc/fonts # 放上面了
# --ro-bind /etc/fonts /etc/fonts #放上面了
# --ro-bind /var/cache/fontconfig /var/cache/fontconfig #放上面了
# --ro-bind $HOME/.cache/fontconfig $HOME/.cache/fontconfig #放上面了
# --ro-bind $HOME/.fonts $HOME/.fonts
# --ro-bind $HOME/.fonts.conf $HOME/.fonts.conf
)
if [[ -n $ICEAUTHORITY ]]; then
xorg_args+=("--ro-bind $ICEAUTHORITY $ICEAUTHORITY") # 一般是 /run/user/1000/iceauth_xxxxxxxx
xorg_args+=("--setenv ICEAUTHORITY $ICEAUTHORITY")
fi
else
echo "去掉Xorg环境变量"
xorg_args=(
--unsetenv DISPLAY
--unsetenv XAUTHORITY
--unsetenv ICEAUTHORITY
--unsetenv XSESSION_IS_UP
--unsetenv XDG_CURRENT_DESKTOP
--unsetenv XDG_SESSION_TYPE
--unsetenv XDG_SESSION_ID
--unsetenv XDG_SEAT
--unsetenv XDG_CONFIG_DIRS
--unsetenv KDE_SESSION_VERSION
--unsetenv KDE_FULL_SESSION
--unsetenv KDE_SESSION_UID
)
# 用KDE时 XDG_CONFIG_DIRS 里含有kde相关路径所以去掉
fi
NEED_X_CMD="${xorg_args[@]}"
(sleep 0.5 ; cat "$TMPDIR/get_fd/json_status" ) &
if [[ $DEVELOPER -eq 1 && -n "$1" ]] ; then # 用户指定了要运行什么
echo "指定了要运行在容器中的的命令: $@"
sandbox_run bash -c "$@"
else # 用户没指定,运行默认想要运行的app
echo "未指定要运行在容器中的的命令,将运行默认app"
sandbox_run $default_app "$@"
fi
echo "好像需要运行的命令结束了,理论上这时该退出了"
FILE_COUNT=$(find "$TMPDIR" | wc -l)
echo "临时目录里产生了 $FILE_COUNT 个文件,想删除可以手动去删除 $TMPDIR"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment