Skip to content

Instantly share code, notes, and snippets.

@garywill
Last active March 4, 2025 06:00
Show Gist options
  • Save garywill/4ac25bd6ef840dfdf1bf576da5070d7f to your computer and use it in GitHub Desktop.
Save garywill/4ac25bd6ef840dfdf1bf576da5070d7f to your computer and use it in GitHub Desktop.

Revisions

  1. garywill revised this gist Mar 4, 2025. No changes.
  2. garywill created this gist Mar 4, 2025.
    298 changes: 298 additions & 0 deletions root_overlay_sandbox_run.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,298 @@
    #!/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"