@@ -8,14 +8,12 @@ detect_platform() {
88 local testfile=" /tmp/.dev_mode_test_$$ "
99 if ! touch " $testfile " 2> /dev/null || ! chattr +i " $testfile " 2> /dev/null; then
1010 DEV_MODE=true
11+ rm -f " $testfile "
1112 else
1213 chattr -i " $testfile " 2> /dev/null || true
1314 rm -f " $testfile "
1415 DEV_MODE=false
1516 fi
16- if [ " $DEV_MODE " = false ] && [ ! -d /sys/class/net/tun0 ] && [ -f /.dockerenv ]; then
17- DEV_MODE=true
18- fi
1917 readonly DEV_MODE
2018 echo " [entrypoint] platform: DEV_MODE=$DEV_MODE "
2119}
@@ -77,11 +75,10 @@ wait_for_x_display() {
7775 return 1
7876}
7977
80- # ===== v3.0 stages — D-09 / PITFALLS M4 串行快速失败 =====
8178
82- prepare_v3_dirs () {
83- echo " [entrypoint] v3: chown /home/claude / workspace-hot /workspace-cold /var/lib/claude-persist "
84- chown -R 1000:1000 \
79+ prepare_workspace_dirs () {
80+ echo " [entrypoint] chown workspace dirs "
81+ chown -R " ${RUN_USER} : ${RUN_USER} " \
8582 /home/claude \
8683 /workspace-hot \
8784 /workspace-cold \
@@ -99,16 +96,16 @@ prepare_persistent_state() {
9996 cp -an /home/claude/.cache/claude/. " $root /.cache/claude/" 2> /dev/null || true
10097 fi
10198
102- chown -R 1000:1000 " $root "
99+ chown -R " ${RUN_USER} : ${RUN_USER} " " $root " 2> /dev/null || true
103100
104101 rm -rf /home/claude/.claude /home/claude/.cache/claude
105102 ln -sfn " $root /.claude" /home/claude/.claude
106103 mkdir -p /home/claude/.cache
107104 ln -sfn " $root /.cache/claude" /home/claude/.cache/claude
108105
109- chown -h 1000:1000 /home/claude/.claude /home/claude/.cache/claude
106+ chown -h " ${RUN_USER} : ${RUN_USER} " /home/claude/.claude /home/claude/.cache/claude 2> /dev/null || true
110107
111- echo " [entrypoint] v3: persistent state ready (volume=/var/lib/claude-persist)"
108+ echo " [entrypoint] persistent state ready (volume=/var/lib/claude-persist)"
112109}
113110
114111prepare_container_disguise () {
@@ -143,30 +140,29 @@ ENVTELEM
143140
144141prepare_mergerfs_check () {
145142 if ! command -v mergerfs > /dev/null 2>&1 ; then
146- echo " [entrypoint] v3: FATAL mergerfs binary missing" >&2
143+ echo " [entrypoint] FATAL mergerfs binary missing" >&2
147144 exit 1
148145 fi
149146 local ver
150147 ver=" $( mergerfs --version 2>&1 | head -n1 || true) "
151- echo " [entrypoint] v3: mergerfs available ($ver ) — mount deferred to cloud-claude (Phase 31) "
152- # SC1 / C1 / C2 — documented params for Phase 31 (static traceability):
148+ echo " [entrypoint] mergerfs available ($ver ) — mount deferred to cloud-claude"
149+ # mergerfs params (static traceability):
153150 # func.readdir=cor:4,cache.attr=30,cache.entry=30,cache.readdir=true,cache.files=off
154151 # category.create=ff, inodecalc=path-hash
155152 # 2-way: /workspace-hot=RW:/workspace-cold=NC,RO
156- # Q10: CLOUD_CLAUDE_MERGERFS_BRANCHES reserved for Phase 31
157- echo " [entrypoint] v3: mergerfs params (Phase 31): func.readdir=cor:4 category.create=ff inodecalc=path-hash"
153+ echo " [entrypoint] mergerfs params: func.readdir=cor:4 category.create=ff inodecalc=path-hash"
158154}
159155
160156assert_tmux_version () {
161157 local tmux_ver
162158 tmux_ver=" $( tmux -V 2> /dev/null | awk ' {print $2}' || true) "
163159 case " $tmux_ver " in
164160 3.4* |3.5* |3.6* |3.7* |3.8* |3.9* |[4-9].* )
165- echo " [entrypoint] v3: tmux ${tmux_ver} >= 3.4 ok"
161+ echo " [entrypoint] tmux ${tmux_ver} >= 3.4 ok"
166162 echo " $tmux_ver " > /etc/cloud-claude/tmux.version
167163 ;;
168164 * )
169- echo " [entrypoint] v3: FATAL tmux ${tmux_ver} < 3.4" >&2
165+ echo " [entrypoint] FATAL tmux ${tmux_ver} < 3.4" >&2
170166 exit 1
171167 ;;
172168 esac
@@ -303,7 +299,7 @@ apply_nft_or_die() {
303299 if [ -n " $SING_BOX_PID " ]; then kill " $SING_BOX_PID " 2> /dev/null || true ; fi
304300 exit 1
305301 fi
306- # 清理 v3.x 残留的 ip cloudproxy 表,避免双表 shadow 导致规则不生效
302+ # TODO(v5): 清理 v3.x 残留的 ip cloudproxy 表。所有 v3.x 部署迁移完毕后移除此行。
307303 nft delete table ip cloudproxy 2> /dev/null || true
308304 if ! nft -f " $NFT_RULESET " ; then
309305 echo " [entrypoint] FATAL: nft 应用失败" >&2
382378if [ " $CONTAINER_USER " != " workspace" ] && id workspace > /dev/null 2>&1 ; then
383379 usermod -l " $CONTAINER_USER " workspace
384380 groupmod -n " $CONTAINER_USER " workspace 2> /dev/null || true
385- # v4.0 (D-53-4): 不再 sed sudoers — v4.0 移除了用户 sudo NOPASSWD 路径。
381+ # v4.0: 用户无 sudo 权限(安全基线)
386382fi
387383
388384RUN_USER=" ${CONTAINER_USER:- workspace} "
389385
390- # v4.0 (D-53-4): 删除 v3.x 的 sudoers NOPASSWD 写入。
391- # 用户拿到 shell 后不再有任何 sudo / root 提权路径。
392- # 兜底清理(防御历史镜像残留 / volume 挂载的 sudoers.d):
386+ # v4.0: 用户无 sudo 权限。兜底清理历史镜像残留的 sudoers.d:
387+ # TODO(v5): 移除此 v3.x sudoers 兜底清理。
393388rm -f /etc/sudoers.d/workspace 2> /dev/null || true
394389if [ " ${RUN_USER} " != " workspace" ]; then
395390 rm -f " /etc/sudoers.d/${RUN_USER} " 2> /dev/null || true
408403
409404echo " ${RUN_USER} :${CONTAINER_PASSWORD} " | chpasswd
410405
411- # Phase 29.1: 验证密码已被成功设置,避免"密码退化为 workspace"的静默失败复现。
406+ # 验证密码已被成功设置,避免"密码退化为 workspace"的静默失败复现。
412407# passwd -S 第 2 列语义:P / PS = 已设置密码;L / LK = 已锁定;NP = 无密码;UNSET = 读取失败。
413408status=" $( passwd -S " ${RUN_USER} " 2> /dev/null | awk ' {print $2}' || echo UNSET) "
414409case " $status " in
@@ -575,19 +570,13 @@ su "${RUN_USER}" -c 'DISPLAY=:99 HOME=/workspace pcmanfm --desktop --profile def
575570# 预热一遍 Chromium 检测,方便排查图标点击失败。
576571su " ${RUN_USER} " -c ' HOME=/workspace /usr/local/bin/launch-chromium.sh --version' >> " ${CHROMIUM_LOG} " 2>&1 || true
577572
578- # ===== v3.0 stages (serialized fail-fast, D-09 order) =====
579- prepare_v3_dirs
573+ prepare_workspace_dirs
580574prepare_persistent_state
581575prepare_container_disguise
582576prepare_mergerfs_check
583577assert_tmux_version
584578
585579fi # end MODE != "local"
586580
587- # v4.0 (Phase 53): 旧的 MODE=local sing-box 分支已删除。
588- # 出网现在由顶部 start_singbox_or_die / apply_nft_or_die 序列统一处理,
589- # 所有 MODE 都强制走 sing-box tun + nft default-deny,没有 proxy fallback。
590- # 用户的 ALL_PROXY / HTTP_PROXY 环境变量也不再注入(应用应直接走 tun)。
591-
592581# Foreground: sshd
593582exec /usr/sbin/sshd -D -e
0 commit comments