246 lines
8.7 KiB
Bash
246 lines
8.7 KiB
Bash
#!/bin/bash
|
|||
|
|
|
||
|
|
trap "" SIGPIPE
|
||
|
|
# 安装 napcat
|
||
|
|
if [ ! -f "napcat/napcat.mjs" ] || [ ! -f "napcat/config/napcat.json" ]; then
|
||
|
|
unzip -qo NapCat.Shell.zip -d ./NapCat.Shell || exit 1
|
||
|
|
mkdir -p napcat/config || exit 1
|
||
|
|
if [ ! -f "napcat/napcat.mjs" ]; then
|
||
|
|
for item in NapCat.Shell/*; do
|
||
|
|
if [ "$item" != "NapCat.Shell/config" ]; then
|
||
|
|
cp -rf "$item" napcat/ || exit 1
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
fi
|
||
|
|
if [ ! -f "napcat/config/napcat.json" ]; then
|
||
|
|
mkdir -p napcat/config
|
||
|
|
cp -rn NapCat.Shell/config/. napcat/config/ || exit 1
|
||
|
|
fi
|
||
|
|
rm -rf ./NapCat.Shell
|
||
|
|
fi
|
||
|
|
|
||
|
|
# 配置 WebUI Token
|
||
|
|
CONFIG_PATH=/app/napcat/config/webui.json
|
||
|
|
|
||
|
|
if [ ! -f "${CONFIG_PATH}" ] && [ -n "${WEBUI_TOKEN}" ]; then
|
||
|
|
echo "正在配置 WebUI Token..."
|
||
|
|
jq -n --arg prefix "${WEBUI_PREFIX}" --arg token "${WEBUI_TOKEN}" \
|
||
|
|
'{host: "0.0.0.0", prefix: $prefix, port: 6099, token: $token, loginRate: 3}' > "${CONFIG_PATH}" || exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
# 删除字符串两端的引号
|
||
|
|
remove_quotes() {
|
||
|
|
local str="$1"
|
||
|
|
local first_char="${str:0:1}"
|
||
|
|
local last_char="${str: -1}"
|
||
|
|
|
||
|
|
if [[ ($first_char == '"' && $last_char == '"') || ($first_char == "'" && $last_char == "'") ]]; then
|
||
|
|
# 两端都是双引号
|
||
|
|
if [[ $first_char == '"' ]]; then
|
||
|
|
str="${str:1:-1}"
|
||
|
|
# 两端都是单引号
|
||
|
|
else
|
||
|
|
str="${str:1:-1}"
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
echo "$str"
|
||
|
|
}
|
||
|
|
|
||
|
|
if [ -n "${MODE}" ] && [ ! -f /app/napcat/config/onebot11.json ]; then
|
||
|
|
case "${MODE}" in
|
||
|
|
reverse_ws)
|
||
|
|
jq --arg url "${ONEBOT_URL:?MODE=reverse_ws requires ONEBOT_URL}" \
|
||
|
|
'.network.websocketClients[0].url = $url' \
|
||
|
|
/app/templates/nonebot.json > /app/napcat/config/onebot11.json.tmp || exit 1
|
||
|
|
;;
|
||
|
|
reverse_http)
|
||
|
|
jq --arg url "${ONEBOT_URL:?MODE=reverse_http requires ONEBOT_URL}" \
|
||
|
|
'.network.httpClients = [.network.websocketClients[0] | .name = "reverse_http" | .url = $url | del(.heartInterval, .reconnectInterval)] | .network.websocketClients = []' \
|
||
|
|
/app/templates/nonebot.json > /app/napcat/config/onebot11.json.tmp || exit 1
|
||
|
|
;;
|
||
|
|
*) cp "/app/templates/${MODE}.json" /app/napcat/config/onebot11.json.tmp || exit 1 ;;
|
||
|
|
esac
|
||
|
|
mv /app/napcat/config/onebot11.json.tmp /app/napcat/config/onebot11.json || exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
rm -rf "/tmp/.X1-lock"
|
||
|
|
|
||
|
|
# 删除容器标识
|
||
|
|
rm -f "/.dockerenv"
|
||
|
|
rm -f "/.dockerinit"
|
||
|
|
rm -f "/run/.containerenv"
|
||
|
|
rm -f "/run/systemd/container"
|
||
|
|
rm -f "/dev/.dockerenv"
|
||
|
|
|
||
|
|
# Name规范
|
||
|
|
HOSTNAME_VAL="$(hostname)"
|
||
|
|
if [[ "${HOSTNAME_VAL}" == *docker* || "${HOSTNAME_VAL}" == *container* || "${HOSTNAME_VAL}" == *lxc* ]] \
|
||
|
|
|| [[ "${HOSTNAME_VAL}" =~ ^[a-f0-9]{12,}$ ]]; then
|
||
|
|
hostname "localhost"
|
||
|
|
echo "localhost" > /etc/hostname
|
||
|
|
fi
|
||
|
|
|
||
|
|
# cgroup处理
|
||
|
|
mkdir -p /tmp/fake_cgroup
|
||
|
|
FAKE_CGROUP_DIR="/tmp/fake_cgroup"
|
||
|
|
# /proc/self/cgroup
|
||
|
|
# /proc/1/cgroup
|
||
|
|
for cfile in /proc/self/cgroup /proc/1/cgroup; do
|
||
|
|
if [ -f "$cfile" ]; then
|
||
|
|
FAKE_CG="$FAKE_CGROUP_DIR/cgroup_$(basename $(dirname $cfile))_$(basename $cfile)"
|
||
|
|
cat "$cfile" | sed \
|
||
|
|
-e 's|/docker/|/system.slice/|g' \
|
||
|
|
-e 's|/docker-|/system.slice/|g' \
|
||
|
|
-e 's|/lxc/|/system.slice/|g' \
|
||
|
|
-e 's|/podmaster|/system.slice/|g' \
|
||
|
|
-e 's|/podman/|/system.slice/|g' \
|
||
|
|
-e 's|/kubepods/|/system.slice/|g' \
|
||
|
|
-e 's|/containerd/|/system.slice/|g' \
|
||
|
|
-e 's|/buildkit/|/system.slice/|g' \
|
||
|
|
> "$FAKE_CG"
|
||
|
|
mount --bind "$FAKE_CG" "$cfile" 2>/dev/null || true
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
|
||
|
|
# DMI
|
||
|
|
for dmi_file in /sys/class/dmi/id/product_name /sys/class/dmi/id/product_uuid /sys/class/dmi/id/sys_vendor /sys/class/dmi/id/board_vendor /sys/class/dmi/id/board_name; do
|
||
|
|
if [ -f "$dmi_file" ]; then
|
||
|
|
FAKE_DMI="/tmp/fake_cgroup/$(echo $dmi_file | tr '/' '_')"
|
||
|
|
case "$(basename $dmi_file)" in
|
||
|
|
product_name) echo "Standard PC" > "$FAKE_DMI" ;;
|
||
|
|
product_uuid) echo "00000000-0000-0000-0000-000000000000" > "$FAKE_DMI" ;;
|
||
|
|
sys_vendor|board_vendor) echo "Intel Corporation" > "$FAKE_DMI" ;;
|
||
|
|
board_name) echo "Default string" > "$FAKE_DMI" ;;
|
||
|
|
esac
|
||
|
|
mount --bind "$FAKE_DMI" "$dmi_file" 2>/dev/null || true
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
|
||
|
|
# Docker Socket
|
||
|
|
if [ -S /var/run/docker.sock ]; then
|
||
|
|
mv /var/run/docker.sock /var/run/.docker.sock.hidden 2>/dev/null || true
|
||
|
|
fi
|
||
|
|
|
||
|
|
# Mount Info
|
||
|
|
for mfile in /proc/self/mountinfo /proc/1/mountinfo; do
|
||
|
|
if [ -f "$mfile" ]; then
|
||
|
|
FAKE_MOUNT="$FAKE_CGROUP_DIR/mountinfo_$(basename $(dirname $mfile))_$(basename $mfile)"
|
||
|
|
cat "$mfile" | sed \
|
||
|
|
-e 's|overlay / |ext4 / |g' \
|
||
|
|
-e 's|overlay /|ext4 /|g' \
|
||
|
|
-e '/docker/d' \
|
||
|
|
-e '/containerd/d' \
|
||
|
|
-e '/\.dockerenv/d' \
|
||
|
|
-e '/cgroup2.*docker/d' \
|
||
|
|
> "$FAKE_MOUNT"
|
||
|
|
mount --bind "$FAKE_MOUNT" "$mfile" 2>/dev/null || true
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
|
||
|
|
# /proc/1/cmdline
|
||
|
|
if [ -f /proc/1/cmdline ]; then
|
||
|
|
FAKE_CMDLINE="$FAKE_CGROUP_DIR/cmdline_1"
|
||
|
|
printf '/sbin/init\0' > "$FAKE_CMDLINE"
|
||
|
|
mount --bind "$FAKE_CMDLINE" /proc/1/cmdline 2>/dev/null || true
|
||
|
|
fi
|
||
|
|
|
||
|
|
# /proc/1/sched
|
||
|
|
if [ -f /proc/1/sched ]; then
|
||
|
|
FAKE_SCHED="$FAKE_CGROUP_DIR/sched_1"
|
||
|
|
head -1 /proc/1/sched | sed 's|([^)]*)|(1, #threads=1)|' > "$FAKE_SCHED"
|
||
|
|
tail -n +2 /proc/1/sched >> "$FAKE_SCHED"
|
||
|
|
sed -i 's/^[a-zA-Z0-9_-]*\s/systemd /' "$FAKE_SCHED"
|
||
|
|
mount --bind "$FAKE_SCHED" /proc/1/sched 2>/dev/null || true
|
||
|
|
fi
|
||
|
|
|
||
|
|
# /proc/1/environ
|
||
|
|
if [ -f /proc/1/environ ]; then
|
||
|
|
FAKE_ENVIRON="$FAKE_CGROUP_DIR/environ_1"
|
||
|
|
cat /proc/1/environ | tr '\0' '\n' | \
|
||
|
|
grep -v -i 'container=docker\|container=lxc\|container=podman\|container=containerd' | \
|
||
|
|
tr '\n' '\0' > "$FAKE_ENVIRON"
|
||
|
|
mount --bind "$FAKE_ENVIRON" /proc/1/environ 2>/dev/null || true
|
||
|
|
fi
|
||
|
|
|
||
|
|
# /proc/1/status & /proc/self/status (NSpid)
|
||
|
|
for sfile in /proc/1/status /proc/self/status; do
|
||
|
|
if [ -f "$sfile" ]; then
|
||
|
|
FAKE_STATUS="$FAKE_CGROUP_DIR/status_$(basename $(dirname $sfile))_$(basename $sfile)"
|
||
|
|
cat "$sfile" | sed 's/^NSpid:.*/NSpid:\t1/' > "$FAKE_STATUS"
|
||
|
|
mount --bind "$FAKE_STATUS" "$sfile" 2>/dev/null || true
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
|
||
|
|
# /proc/1/stat
|
||
|
|
if [ -f /proc/1/stat ]; then
|
||
|
|
FAKE_STAT="$FAKE_CGROUP_DIR/stat_1"
|
||
|
|
cat /proc/1/stat | sed 's/^\([0-9]*\) ([-a-zA-Z0-9_]*)/\1 (systemd)/' > "$FAKE_STAT"
|
||
|
|
mount --bind "$FAKE_STAT" /proc/1/stat 2>/dev/null || true
|
||
|
|
fi
|
||
|
|
|
||
|
|
# /etc/hosts
|
||
|
|
if [ -f /etc/hosts ]; then
|
||
|
|
FAKE_HOSTS="$FAKE_CGROUP_DIR/hosts"
|
||
|
|
cat /etc/hosts | grep -v "$(hostname)" > "$FAKE_HOSTS" 2>/dev/null
|
||
|
|
grep -q '^127\.0\.0\.1.*localhost' "$FAKE_HOSTS" || echo "127.0.0.1\tlocalhost" >> "$FAKE_HOSTS"
|
||
|
|
grep -q '^::1.*localhost' "$FAKE_HOSTS" || echo "::1\tlocalhost ip6-localhost ip6-loopback" >> "$FAKE_HOSTS"
|
||
|
|
mount --bind "$FAKE_HOSTS" /etc/hosts 2>/dev/null || true
|
||
|
|
fi
|
||
|
|
|
||
|
|
# /sys/hypervisor/type
|
||
|
|
if [ -f /sys/hypervisor/type ]; then
|
||
|
|
FAKE_HYPER="$FAKE_CGROUP_DIR/hypervisor_type"
|
||
|
|
echo "" > "$FAKE_HYPER"
|
||
|
|
mount --bind "$FAKE_HYPER" /sys/hypervisor/type 2>/dev/null || true
|
||
|
|
fi
|
||
|
|
|
||
|
|
# /proc/self/mounts
|
||
|
|
if [ -f /proc/self/mounts ]; then
|
||
|
|
FAKE_MOUNTS="$FAKE_CGROUP_DIR/mounts_self"
|
||
|
|
cat /proc/self/mounts | sed \
|
||
|
|
-e 's|overlay / |ext4 / |g' \
|
||
|
|
-e 's|overlay /|ext4 /|g' \
|
||
|
|
-e '/docker/d' \
|
||
|
|
-e '/containerd/d' \
|
||
|
|
> "$FAKE_MOUNTS"
|
||
|
|
mount --bind "$FAKE_MOUNTS" /proc/self/mounts 2>/dev/null || true
|
||
|
|
fi
|
||
|
|
|
||
|
|
# /proc/self/cpuset
|
||
|
|
if [ -f /proc/self/cpuset ]; then
|
||
|
|
FAKE_CPUSET="$FAKE_CGROUP_DIR/cpuset_self"
|
||
|
|
cat /proc/self/cpuset | sed \
|
||
|
|
-e 's|/docker/|/|g' \
|
||
|
|
-e 's|/docker-[^/]*/|/|g' \
|
||
|
|
-e 's|/kubepods/|/|g' \
|
||
|
|
-e 's|/containerd/|/|g' \
|
||
|
|
> "$FAKE_CPUSET"
|
||
|
|
mount --bind "$FAKE_CPUSET" /proc/self/cpuset 2>/dev/null || true
|
||
|
|
fi
|
||
|
|
|
||
|
|
: ${NAPCAT_GID:=0}
|
||
|
|
: ${NAPCAT_UID:=0}
|
||
|
|
usermod -o -u ${NAPCAT_UID} napcat
|
||
|
|
groupmod -o -g ${NAPCAT_GID} napcat
|
||
|
|
usermod -g ${NAPCAT_GID} napcat
|
||
|
|
chown -R ${NAPCAT_UID}:${NAPCAT_GID} /app
|
||
|
|
|
||
|
|
# 关闭 core dump。QQ 会预留上 TB 的虚拟地址空间,崩溃时 core 能写出几百 GB,
|
||
|
|
# Docker Desktop (WSL) 下会直接写满宿主机系统盘(NapCatQQ #2060)。
|
||
|
|
# WSL 这类管道形式的 core_pattern 不受 ulimit 限制,所以同时把 coredump_filter 清零,
|
||
|
|
# 崩溃时只留寄存器等基本信息。需要完整 core 调试时设置 NAPCAT_ENABLE_COREDUMP=1。
|
||
|
|
if [ "${NAPCAT_ENABLE_COREDUMP}" != "1" ]; then
|
||
|
|
ulimit -c 0 2>/dev/null || true
|
||
|
|
echo 0 > /proc/self/coredump_filter 2>/dev/null || true
|
||
|
|
fi
|
||
|
|
|
||
|
|
gosu napcat Xvfb :1 -screen 0 1080x760x16 +extension GLX +render > /dev/null 2>&1 &
|
||
|
|
sleep 2
|
||
|
|
|
||
|
|
export DISPLAY=:1
|
||
|
|
cd /app/napcat
|
||
|
|
if [ -n "${ACCOUNT}" ]; then
|
||
|
|
gosu napcat /opt/QQ/qq --no-sandbox -q $ACCOUNT
|
||
|
|
else
|
||
|
|
gosu napcat /opt/QQ/qq --no-sandbox
|
||
|
|
fi
|