charlielin0121 commited on
Commit
ac2af53
·
verified ·
1 Parent(s): 6db7aac

Upload 10 files

Browse files
.dockerignore ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ .git/
2
+ .env
3
+ *.log
4
+ __pycache__/
5
+ .browser.htpasswd
6
+ chromium-profile/
7
+ browser-data/
Dockerfile CHANGED
@@ -1,34 +1,49 @@
1
- FROM debian:bullseye-slim
2
-
3
- # 1. 安裝核心組件 (增加 dbus-x11 減少錯誤)
4
- RUN apt-get update && apt-get install -y \
5
- chromium \
6
- xvfb \
7
- x11vnc \
8
- novnc \
9
- openbox \
10
- python3-websockify \
11
- dbus-x11 \
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  && rm -rf /var/lib/apt/lists/*
13
 
14
- # 2. 【關鍵:強行修正 X11 權限】
15
- # 在切換使用者前,先建立好 X11 需要的資料夾並開放權限
16
- RUN mkdir -p /tmp/.X11-unix && chmod 1777 /tmp/.X11-unix
 
 
 
 
 
 
 
 
17
 
18
- # 3. 設定全域變數 (這樣 Chromium 絕對不會找不到 DISPLAY)
19
- ENV DISPLAY=:1
20
- ENV HOME=/home/user
21
- RUN useradd -m -u 1000 user
22
  USER user
23
- WORKDIR /home/user
24
 
25
  EXPOSE 7860
26
 
27
- # 4. 啟動指令:增加 -nolisten tcp 減少權限報錯,並確保執行順序
28
- CMD Xvfb :1 -screen 0 1280x800x16 -nolisten tcp & \
29
- sleep 3 && \
30
- openbox & \
31
- x11vnc -display :1 -nopw -forever -shared -rfbport 5900 & \
32
- /usr/share/novnc/utils/launch.sh --vnc localhost:5900 --listen 7860 & \
33
- sleep 3 && \
34
- chromium --no-sandbox --disable-gpu --disable-dev-shm-usage --start-maximized --no-first-run
 
1
+ # syntax=docker/dockerfile:1.7
2
+
3
+ FROM debian:bookworm-slim
4
+
5
+ ARG DEBIAN_FRONTEND=noninteractive
6
+
7
+ ENV DISPLAY=:99 \
8
+ HOME=/home/user \
9
+ BROWSER_WIDTH=1920 \
10
+ BROWSER_HEIGHT=1080 \
11
+ START_URL=https://arena.ai/text/direct
12
+
13
+ RUN apt-get update && apt-get install -y --no-install-recommends \
14
+ apache2-utils \
15
+ ca-certificates \
16
+ chromium \
17
+ dbus-x11 \
18
+ fonts-liberation \
19
+ fonts-noto-cjk \
20
+ fonts-noto-color-emoji \
21
+ nginx \
22
+ novnc \
23
+ openbox \
24
+ supervisor \
25
+ tini \
26
+ websockify \
27
+ x11-utils \
28
+ x11vnc \
29
+ xvfb \
30
  && rm -rf /var/lib/apt/lists/*
31
 
32
+ RUN useradd --create-home --uid 1000 --shell /bin/bash user \
33
+ && mkdir -p /home/user/app /home/user/nginx /home/user/browser-data \
34
+ && chown -R user:user /home/user/app /home/user/nginx /home/user/browser-data
35
+
36
+ WORKDIR /home/user/app
37
+
38
+ COPY --chown=user:user nginx.conf supervisord.conf start.sh ./
39
+ COPY --chown=user:user scripts ./scripts
40
+ COPY --chown=user:user README.md ./README.md
41
+
42
+ RUN chmod 0755 /home/user/app/start.sh /home/user/app/scripts/*.sh
43
 
 
 
 
 
44
  USER user
 
45
 
46
  EXPOSE 7860
47
 
48
+ ENTRYPOINT ["/usr/bin/tini", "--"]
49
+ CMD ["/home/user/app/start.sh"]
 
 
 
 
 
 
README.md CHANGED
@@ -1,10 +1,207 @@
1
  ---
2
- title: Broswer
3
- emoji: 🌍
4
- colorFrom: blue
5
- colorTo: green
6
  sdk: docker
 
7
  pinned: false
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Private Manual Browser
3
+ emoji: 🖥️
4
+ colorFrom: gray
5
+ colorTo: blue
6
  sdk: docker
7
+ app_port: 7860
8
  pinned: false
9
  ---
10
 
11
+ # Private Manual Browser Hugging Face Docker Space
12
+
13
+ 這是一個只供帳號本人操作的瀏覽器 Space:
14
+
15
+ ```text
16
+ Chromium → Xvfb → x11vnc → websockify/noVNC → Nginx :7860
17
+ ```
18
+
19
+ 用途是讓使用者透過瀏覽器開啟 Hugging Face Space,手動操作遠端 Chromium。它不會自動解 CAPTCHA、不會自動接受條款,也不包含 OpenAI API Bridge。
20
+
21
+ ## 必要 Secrets
22
+
23
+ 在 **Space → Settings → Variables and secrets → Secrets** 建立:
24
+
25
+ | Secret | 說明 |
26
+ |---|---|
27
+ | `BROWSER_USERNAME` | noVNC 網頁的 HTTP Basic Auth 使用者名稱。 |
28
+ | `BROWSER_PASSWORD` | 強隨機密碼,建議至少 24 個字元。 |
29
+
30
+ 產生密碼:
31
+
32
+ ```bash
33
+ python -c "import secrets; print(secrets.token_urlsafe(32))"
34
+ ```
35
+
36
+ 不要把 Google、Arena 或其他網站密碼設定為 `BROWSER_PASSWORD`。
37
+
38
+ ## 建議 Variables
39
+
40
+ | Variable | 預設值 | 說明 |
41
+ |---|---:|---|
42
+ | `BROWSER_WIDTH` | `1920` | 虛擬桌面寬度,640–3840。 |
43
+ | `BROWSER_HEIGHT` | `1080` | 虛擬桌面高度,640–2160。 |
44
+ | `START_URL` | `https://arena.ai/text/direct` | Chromium 啟動頁面。 |
45
+
46
+ 資源較少時可使用:
47
+
48
+ ```text
49
+ BROWSER_WIDTH=1366
50
+ BROWSER_HEIGHT=768
51
+ ```
52
+
53
+ ## 部署
54
+
55
+ Space repository 根目錄:
56
+
57
+ ```text
58
+ Dockerfile
59
+ README.md
60
+ nginx.conf
61
+ supervisord.conf
62
+ start.sh
63
+ scripts/
64
+ launch-browser.sh
65
+ launch-nginx.sh
66
+ launch-openbox.sh
67
+ launch-x11vnc.sh
68
+ ```
69
+
70
+ 建立 Docker Space、上傳檔案、設定兩個必要 Secrets,然後 Factory Rebuild。
71
+
72
+ ## 開啟瀏覽器
73
+
74
+ 造訪 Space 的直接網址:
75
+
76
+ ```text
77
+ https://YOUR-SPACE.hf.space/
78
+ ```
79
+
80
+ 瀏覽器會要求 HTTP Basic Auth:
81
+
82
+ ```text
83
+ Username: BROWSER_USERNAME
84
+ Password: BROWSER_PASSWORD
85
+ ```
86
+
87
+ 成功後會進入 noVNC,並自動連接遠端桌面。可使用 noVNC 左側控制列調整縮放、全螢幕及剪貼簿。
88
+
89
+ ## 持久化 Chrome Profile
90
+
91
+ 如果 Space 已附加 Hugging Face Persistent Storage,程式會使用:
92
+
93
+ ```text
94
+ /data/manual-browser/chromium-profile
95
+ ```
96
+
97
+ 其中包含:
98
+
99
+ - Cookies
100
+ - Local Storage
101
+ - 登入 Session
102
+ - 瀏覽器設定
103
+ - 網站權限
104
+ - 歷史紀錄
105
+
106
+ 如果 `/data` 不存在,會改用:
107
+
108
+ ```text
109
+ /home/user/browser-data/chromium-profile
110
+ ```
111
+
112
+ 並在 Logs 顯示 profile 是暫存的;容器重啟後可能遺失。
113
+
114
+ ## 安全要求
115
+
116
+ 1. **強烈建議使用 Private Space。** Basic Auth 是第二層保護,不應取代 Hugging Face 存取控制。
117
+ 2. 使用獨立、強隨機的 `BROWSER_PASSWORD`。
118
+ 3. 不要分享 Space URL 或密碼。
119
+ 4. `/data` 內的 Chromium profile 等同登入憑證,不能公開或下載分享。
120
+ 5. 不要在多人共用 Space 中登入私人 Google 帳號。
121
+ 6. 不使用時應 Pause Space;需要撤銷所有 Session 時,刪除 `/data/manual-browser/chromium-profile`。
122
+ 7. `x11vnc` 與 `websockify` 只監聽容器內的 `127.0.0.1`,外部只能經過 Nginx Basic Auth。
123
+ 8. Chromium 在容器中使用 `--no-sandbox`,因此只能在受控的 Private Space 中操作可信網站。
124
+
125
+ ## Google 登入
126
+
127
+ 這是由使用者本人操作的可見 Chromium,不是自動輸入帳密的腳本。但 Google 仍可能因資料中心 IP、新裝置、Chromium 環境或帳號政策要求:
128
+
129
+ - MFA/2FA
130
+ - 裝置確認
131
+ - CAPTCHA
132
+ - 拒絕登入
133
+
134
+ 請由帳號本人處理;本專案不會繞過 Google 安全驗證。如果 Google 拒絕此環境,請改用自己電腦的一般 Chrome 建立 Arena Storage State。
135
+
136
+ ## Health Check
137
+
138
+ 不需認證的程序存活端點:
139
+
140
+ ```text
141
+ https://YOUR-SPACE.hf.space/healthz
142
+ ```
143
+
144
+ 回傳:
145
+
146
+ ```json
147
+ {"status":"alive"}
148
+ ```
149
+
150
+ 它只表示 Nginx 存活,不代表 Chromium 已登入任何網站。
151
+
152
+ ## Cronitor
153
+
154
+ 可監控:
155
+
156
+ ```text
157
+ GET https://YOUR-SPACE.hf.space/healthz
158
+ ```
159
+
160
+ Assertions:
161
+
162
+ ```text
163
+ response.code = 200
164
+ response.body contains "alive"
165
+ ```
166
+
167
+ 監控請求可能喚醒 Space,但不能保證 Hugging Face 免費 Space 永不休眠。
168
+
169
+ ## 常見問題
170
+
171
+ ### 401 Unauthorized
172
+
173
+ 確認 `BROWSER_USERNAME`、`BROWSER_PASSWORD` 是 Secrets,修改後 Restart/Factory Rebuild。
174
+
175
+ ### 黑畫面
176
+
177
+ 等待 10–20 秒後重新整理;檢查 Logs 中 Xvfb、Openbox、Chromium、x11vnc 是否都進入 RUNNING。
178
+
179
+ ### noVNC 顯示 WebSocket disconnected
180
+
181
+ 確認 Nginx、websockify、x11vnc 都在運行,並使用根網址自動產生的 `path=websockify`。
182
+
183
+ ### 重啟後登出
184
+
185
+ 確認 Space 已附加 Persistent Storage,Logs 應顯示:
186
+
187
+ ```text
188
+ Browser profile: persistent /data/manual-browser/chromium-profile
189
+ ```
190
+
191
+ ### 清除所有登入狀態
192
+
193
+ 在 Space 停止後刪除:
194
+
195
+ ```text
196
+ /data/manual-browser/chromium-profile
197
+ ```
198
+
199
+ 再重新啟動。這會登出所有網站並清除瀏覽器資料。
200
+
201
+ ## 限��
202
+
203
+ - 這是互動式遠端瀏覽器,不是高效能桌面。
204
+ - 音訊、視訊、WebGL 與硬體加速可能受限。
205
+ - 免費 Space 可能休眠。
206
+ - Persistent Storage 需要 Hugging Face 支援的儲存方案。
207
+ - 本專案不會自動化 CAPTCHA、MFA、OAuth 或法律條款同意。
nginx.conf ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ worker_processes 1;
2
+ pid /tmp/nginx.pid;
3
+ error_log /dev/stderr info;
4
+
5
+ events {
6
+ worker_connections 512;
7
+ }
8
+
9
+ http {
10
+ include /etc/nginx/mime.types;
11
+ default_type application/octet-stream;
12
+ access_log /dev/stdout;
13
+ sendfile on;
14
+ keepalive_timeout 65;
15
+
16
+ client_body_temp_path /tmp/nginx-client;
17
+ proxy_temp_path /tmp/nginx-proxy;
18
+
19
+ map $http_upgrade $connection_upgrade {
20
+ default upgrade;
21
+ '' close;
22
+ }
23
+
24
+ server {
25
+ listen 7860;
26
+ server_name _;
27
+ server_tokens off;
28
+
29
+ auth_basic "Private browser";
30
+ auth_basic_user_file /home/user/.browser.htpasswd;
31
+
32
+ add_header X-Content-Type-Options nosniff always;
33
+ add_header Referrer-Policy no-referrer always;
34
+ add_header Cache-Control "no-store" always;
35
+
36
+ location = /healthz {
37
+ auth_basic off;
38
+ default_type application/json;
39
+ return 200 '{"status":"alive"}';
40
+ }
41
+
42
+ location = / {
43
+ return 302 /vnc.html?autoconnect=true&resize=scale&path=websockify;
44
+ }
45
+
46
+ location = /websockify {
47
+ proxy_pass http://127.0.0.1:6080;
48
+ proxy_http_version 1.1;
49
+ proxy_set_header Upgrade $http_upgrade;
50
+ proxy_set_header Connection $connection_upgrade;
51
+ proxy_set_header Host $host;
52
+ proxy_read_timeout 86400s;
53
+ proxy_send_timeout 86400s;
54
+ proxy_buffering off;
55
+ }
56
+
57
+ location / {
58
+ root /usr/share/novnc;
59
+ try_files $uri $uri/ =404;
60
+ }
61
+ }
62
+ }
scripts/launch-browser.sh ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/sh
2
+ set -eu
3
+ until xdpyinfo -display "${DISPLAY:-:99}" >/dev/null 2>&1; do
4
+ sleep 0.25
5
+ done
6
+
7
+ exec /usr/bin/chromium \
8
+ --no-sandbox \
9
+ --disable-dev-shm-usage \
10
+ --no-first-run \
11
+ --no-default-browser-check \
12
+ --password-store=basic \
13
+ --user-data-dir="${BROWSER_PROFILE}" \
14
+ --window-size="${BROWSER_WIDTH},${BROWSER_HEIGHT}" \
15
+ --start-maximized \
16
+ "${START_URL}"
scripts/launch-nginx.sh ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ #!/bin/sh
2
+ set -eu
3
+ mkdir -p /tmp/nginx-client /tmp/nginx-proxy
4
+ exec /usr/sbin/nginx \
5
+ -p /home/user/nginx \
6
+ -c /home/user/app/nginx.conf \
7
+ -g 'daemon off;'
scripts/launch-openbox.sh ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ #!/bin/sh
2
+ set -eu
3
+ until xdpyinfo -display "${DISPLAY:-:99}" >/dev/null 2>&1; do
4
+ sleep 0.25
5
+ done
6
+ exec openbox-session
scripts/launch-x11vnc.sh ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/sh
2
+ set -eu
3
+ until xdpyinfo -display "${DISPLAY:-:99}" >/dev/null 2>&1; do
4
+ sleep 0.25
5
+ done
6
+ exec /usr/bin/x11vnc \
7
+ -display "${DISPLAY:-:99}" \
8
+ -localhost \
9
+ -nopw \
10
+ -forever \
11
+ -shared \
12
+ -repeat \
13
+ -noxdamage \
14
+ -rfbport 5900
start.sh ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/sh
2
+ set -eu
3
+
4
+ : "${BROWSER_USERNAME:?Set BROWSER_USERNAME as a Hugging Face Space Secret}"
5
+ : "${BROWSER_PASSWORD:?Set BROWSER_PASSWORD as a Hugging Face Space Secret}"
6
+
7
+ case "${BROWSER_WIDTH:-1920}" in
8
+ ''|*[!0-9]*) echo "BROWSER_WIDTH must be numeric" >&2; exit 64 ;;
9
+ esac
10
+ case "${BROWSER_HEIGHT:-1080}" in
11
+ ''|*[!0-9]*) echo "BROWSER_HEIGHT must be numeric" >&2; exit 64 ;;
12
+ esac
13
+
14
+ if [ "${BROWSER_WIDTH}" -lt 640 ] || [ "${BROWSER_WIDTH}" -gt 3840 ]; then
15
+ echo "BROWSER_WIDTH must be between 640 and 3840" >&2
16
+ exit 64
17
+ fi
18
+ if [ "${BROWSER_HEIGHT}" -lt 640 ] || [ "${BROWSER_HEIGHT}" -gt 2160 ]; then
19
+ echo "BROWSER_HEIGHT must be between 640 and 2160" >&2
20
+ exit 64
21
+ fi
22
+
23
+ # Use paid Hugging Face persistent storage when /data is writable; otherwise
24
+ # fall back to ephemeral container storage and warn clearly.
25
+ if [ -d /data ] && [ -w /data ]; then
26
+ BROWSER_PROFILE=/data/manual-browser/chromium-profile
27
+ mkdir -p /data/manual-browser "$BROWSER_PROFILE"
28
+ chmod 700 /data/manual-browser "$BROWSER_PROFILE" 2>/dev/null || true
29
+ echo "Browser profile: persistent /data/manual-browser/chromium-profile"
30
+ else
31
+ BROWSER_PROFILE=/home/user/browser-data/chromium-profile
32
+ mkdir -p "$BROWSER_PROFILE"
33
+ chmod 700 "$BROWSER_PROFILE"
34
+ echo "WARNING: /data is unavailable; browser profile is ephemeral."
35
+ fi
36
+
37
+ export BROWSER_PROFILE
38
+ export START_URL="${START_URL:-https://arena.ai/text/direct}"
39
+ export XDG_RUNTIME_DIR=/tmp/runtime-user
40
+ mkdir -p "$XDG_RUNTIME_DIR"
41
+ chmod 700 "$XDG_RUNTIME_DIR"
42
+
43
+ # Read the password through stdin so it is not exposed in the htpasswd process argv.
44
+ printf '%s\n' "$BROWSER_PASSWORD" | htpasswd -iBc /home/user/.browser.htpasswd "$BROWSER_USERNAME" >/dev/null
45
+ chmod 600 /home/user/.browser.htpasswd
46
+
47
+ # Do not pass credentials to Chromium, noVNC, or other child processes.
48
+ unset BROWSER_PASSWORD BROWSER_USERNAME
49
+
50
+ exec /usr/bin/supervisord -n -c /home/user/app/supervisord.conf
supervisord.conf ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [supervisord]
2
+ nodaemon=true
3
+ logfile=/dev/null
4
+ pidfile=/tmp/supervisord.pid
5
+ childlogdir=/tmp
6
+
7
+ [program:xvfb]
8
+ command=/usr/bin/Xvfb :99 -screen 0 %(ENV_BROWSER_WIDTH)sx%(ENV_BROWSER_HEIGHT)sx24 -ac -nolisten tcp
9
+ autostart=true
10
+ autorestart=true
11
+ priority=10
12
+ startsecs=1
13
+ stopasgroup=true
14
+ killasgroup=true
15
+ stdout_logfile=/dev/fd/1
16
+ stdout_logfile_maxbytes=0
17
+ stderr_logfile=/dev/fd/2
18
+ stderr_logfile_maxbytes=0
19
+
20
+ [program:openbox]
21
+ command=/home/user/app/scripts/launch-openbox.sh
22
+ autostart=true
23
+ autorestart=true
24
+ priority=20
25
+ startsecs=1
26
+ stopasgroup=true
27
+ killasgroup=true
28
+ stdout_logfile=/dev/fd/1
29
+ stdout_logfile_maxbytes=0
30
+ stderr_logfile=/dev/fd/2
31
+ stderr_logfile_maxbytes=0
32
+
33
+ [program:chromium]
34
+ command=/home/user/app/scripts/launch-browser.sh
35
+ autostart=true
36
+ autorestart=true
37
+ priority=30
38
+ startsecs=3
39
+ stopasgroup=true
40
+ killasgroup=true
41
+ stdout_logfile=/dev/fd/1
42
+ stdout_logfile_maxbytes=0
43
+ stderr_logfile=/dev/fd/2
44
+ stderr_logfile_maxbytes=0
45
+
46
+ [program:x11vnc]
47
+ command=/home/user/app/scripts/launch-x11vnc.sh
48
+ autostart=true
49
+ autorestart=true
50
+ priority=40
51
+ startsecs=1
52
+ stopasgroup=true
53
+ killasgroup=true
54
+ stdout_logfile=/dev/fd/1
55
+ stdout_logfile_maxbytes=0
56
+ stderr_logfile=/dev/fd/2
57
+ stderr_logfile_maxbytes=0
58
+
59
+ [program:websockify]
60
+ command=/usr/bin/websockify 127.0.0.1:6080 127.0.0.1:5900
61
+ autostart=true
62
+ autorestart=true
63
+ priority=50
64
+ startsecs=1
65
+ stopasgroup=true
66
+ killasgroup=true
67
+ stdout_logfile=/dev/fd/1
68
+ stdout_logfile_maxbytes=0
69
+ stderr_logfile=/dev/fd/2
70
+ stderr_logfile_maxbytes=0
71
+
72
+ [program:nginx]
73
+ command=/home/user/app/scripts/launch-nginx.sh
74
+ autostart=true
75
+ autorestart=true
76
+ priority=60
77
+ startsecs=1
78
+ stopasgroup=true
79
+ killasgroup=true
80
+ stdout_logfile=/dev/fd/1
81
+ stdout_logfile_maxbytes=0
82
+ stderr_logfile=/dev/fd/2
83
+ stderr_logfile_maxbytes=0