darkfire514 commited on
Commit
e599ff5
·
verified ·
1 Parent(s): 15c9720

Upload 6 files

Browse files
Files changed (6) hide show
  1. Dockerfile +65 -0
  2. README.md +118 -11
  3. nginx.conf +68 -0
  4. oauth2-proxy-github.cfg +12 -0
  5. sign_in.html +104 -0
  6. start.sh +91 -0
Dockerfile ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use a lightweight Debian base for a simplified Linux environment
2
+ FROM debian:bookworm-slim
3
+
4
+ # Set environment variables to avoid interactive prompts during installation
5
+ ENV DEBIAN_FRONTEND=noninteractive
6
+ ENV HOME=/home/user
7
+ ENV PATH=$HOME/.local/bin:$PATH
8
+
9
+ # Install essential system packages and build tools
10
+ # - sudo: Required for root privileges
11
+ # - build-essential, cmake: Required for compiling software like OpenClaw
12
+ # - curl, wget, git: Basic tools for downloading and version control
13
+ # - vim, nano: Text editors
14
+ # - nginx, netcat-openbsd: Required for Auth Proxy
15
+ RUN apt-get update && apt-get install -y \
16
+ curl \
17
+ wget \
18
+ git \
19
+ sudo \
20
+ vim \
21
+ nano \
22
+ unzip \
23
+ procps \
24
+ net-tools \
25
+ nginx \
26
+ netcat-openbsd \
27
+ build-essential \
28
+ cmake \
29
+ pkg-config \
30
+ && apt-get clean && rm -rf /var/lib/apt/lists/*
31
+
32
+ # Install ttyd (Web Terminal)
33
+ RUN wget https://github.com/tsl0922/ttyd/releases/download/1.7.7/ttyd.x86_64 -O /usr/bin/ttyd \
34
+ && chmod +x /usr/bin/ttyd
35
+
36
+ # Install oauth2-proxy
37
+ RUN wget https://github.com/oauth2-proxy/oauth2-proxy/releases/download/v7.6.0/oauth2-proxy-v7.6.0.linux-amd64.tar.gz \
38
+ && tar -xzf oauth2-proxy-v7.6.0.linux-amd64.tar.gz \
39
+ && mv oauth2-proxy-v7.6.0.linux-amd64/oauth2-proxy /usr/bin/oauth2-proxy \
40
+ && chmod +x /usr/bin/oauth2-proxy \
41
+ && rm -rf oauth2-proxy-v7.6.0.linux-amd64*
42
+
43
+ # Create a non-root user 'user' (UID 1000) for security and Hugging Face compatibility
44
+ # Grant sudo privileges without password for easy administration
45
+ RUN useradd -m -u 1000 user && \
46
+ echo 'user ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
47
+
48
+ # Set working directory to user's home
49
+ WORKDIR $HOME
50
+
51
+ # Copy configuration files
52
+ COPY --chown=user:user nginx.conf /etc/nginx/nginx.conf
53
+ COPY --chown=user:user oauth2-proxy-github.cfg $HOME/oauth2-proxy-github.cfg
54
+ COPY --chown=user:user sign_in.html /var/www/html/theme/sign_in.html
55
+ COPY --chown=user:user start.sh $HOME/start.sh
56
+ RUN chmod +x $HOME/start.sh
57
+
58
+ # Switch to the non-root user
59
+ USER user
60
+
61
+ # Expose port 7860 (Standard for Hugging Face Spaces)
62
+ EXPOSE 7860
63
+
64
+ # Start via entrypoint script
65
+ CMD ["./start.sh"]
README.md CHANGED
@@ -1,11 +1,118 @@
1
- ---
2
- title: VPS Linux OpenClaw 01
3
- emoji: 🌖
4
- colorFrom: pink
5
- colorTo: green
6
- sdk: docker
7
- pinned: false
8
- license: mit
9
- ---
10
-
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: VPS Linux
3
+ emoji: 🐨
4
+ colorFrom: red
5
+ colorTo: green
6
+ sdk: docker
7
+ pinned: false
8
+ license: mit
9
+ ---
10
+
11
+ # Simplified Linux VPS on Hugging Face Spaces
12
+
13
+ This project provides a simplified Linux VPS on Hugging Face Spaces with:
14
+
15
+ - Debian Bookworm Slim base
16
+ - Browser terminal powered by `ttyd`
17
+ - GitHub OAuth authentication via `nginx + oauth2-proxy`
18
+ - Simple allowlist control
19
+
20
+ Users can access the Linux terminal only after successful GitHub login.
21
+
22
+ ## 🚀 Quick Start
23
+
24
+ ### 1) Create a Hugging Face Space
25
+
26
+ 1. Log in to Hugging Face.
27
+ 2. Click your avatar in the top right corner and choose **New Space**.
28
+ 3. Enter a Space name (for example: `my-linux-vps`).
29
+ 4. Select **SDK: Docker**.
30
+ 5. Select **Docker Template: Blank**.
31
+ 6. Click **Create Space**.
32
+
33
+ ### 2) Upload project files
34
+
35
+ Upload all files from this folder to your Space repository.
36
+
37
+ ### 3) Configure Variables and Secrets
38
+
39
+ In **Space Settings -> Variables and Secrets**, add:
40
+
41
+ **Variables**
42
+
43
+ - `SPACE_PUBLIC_URL=https://<your-space-name>.hf.space`
44
+ - `ALLOWED_USERS=user1@gmail.com,github_username_1` (Supports GitHub email addresses or usernames)
45
+
46
+ **Secrets**
47
+
48
+ - `GITHUB_CLIENT_ID=<github oauth app client id>`
49
+ - `GITHUB_CLIENT_SECRET=<github oauth app client secret>`
50
+ - `OAUTH2_PROXY_COOKIE_SECRET=<32-byte-base64-secret>`
51
+
52
+ Requirements:
53
+
54
+ - Secret values must be plain text (no quotes, no leading/trailing spaces).
55
+ - `SPACE_PUBLIC_URL` must not include a trailing slash.
56
+
57
+ ### 4) Configure GitHub OAuth callback
58
+
59
+ In your GitHub OAuth App, set callback URL to:
60
+
61
+ `https://<your-space-name>.hf.space/oauth2/callback`
62
+
63
+ `SPACE_PUBLIC_URL` and callback domain must match exactly.
64
+ During login consent, the app requests `user:email` scope only.
65
+
66
+ ### 5) Access the VPS
67
+
68
+ 1. Wait until the Space status is **Running**.
69
+ 2. Open the **App** tab.
70
+ 3. Sign in with GitHub.
71
+ 4. After successful authentication, the web terminal will be available.
72
+
73
+ ## 🛠️ Features
74
+
75
+ - **Base system**: Debian Bookworm Slim
76
+ - **Privileges**: default `user` has passwordless sudo
77
+ - **Toolchain**: `git`, `curl`, `wget`, `vim`, `nano`, `build-essential`, `cmake`
78
+ - **Web terminal**: `ttyd`
79
+ - **Authentication**: `nginx + oauth2-proxy` (GitHub only)
80
+ - **Allowlist**: supports GitHub email addresses and GitHub usernames in `ALLOWED_USERS`
81
+
82
+ ## 🎮 How to Build OpenClaw
83
+
84
+ OpenClaw needs extra dependencies. Hugging Face Spaces has no native display, so GUI execution usually fails unless you set up VNC/X11 manually.
85
+
86
+ ### Install dependencies
87
+
88
+ ```bash
89
+ sudo apt-get update
90
+ sudo apt-get install -y \
91
+ libsdl2-dev \
92
+ libsdl2-image-dev \
93
+ libsdl2-mixer-dev \
94
+ libsdl2-ttf-dev \
95
+ libxml2-dev \
96
+ zlib1g-dev
97
+ ```
98
+
99
+ ### Clone and compile
100
+
101
+ ```bash
102
+ git clone https://github.com/pjasicek/OpenClaw.git
103
+ cd OpenClaw
104
+ mkdir build && cd build
105
+ cmake ..
106
+ make -j$(nproc)
107
+ ```
108
+
109
+ ## Notes
110
+
111
+ - Running `./OpenClaw` may fail with `Could not initialize SDL: No available video device`.
112
+ - After Space restart, files outside `/data` may be lost. Save important data in `/data` (if persistent storage is enabled) or sync with Git.
113
+ - **Troubleshooting 500 Errors**: If you encounter a 500 error during login, ensure you have removed any legacy `OAUTH2_PROXY_GITHUB_ORG` variables from Space Settings, revoke the GitHub App authorization, and clear your browser cookies.
114
+
115
+ ## ⚠️ Security Warning
116
+
117
+ - This VPS has elevated privileges. Do not store sensitive keys inside it.
118
+ - Public Spaces are accessible unless protected. This project protects terminal access with GitHub OAuth and allowlist rules.
nginx.conf ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ worker_processes auto;
2
+ pid /tmp/nginx.pid;
3
+
4
+ events {
5
+ worker_connections 768;
6
+ }
7
+
8
+ http {
9
+ sendfile on;
10
+ tcp_nopush on;
11
+ types_hash_max_size 2048;
12
+ include /etc/nginx/mime.types;
13
+ default_type application/octet-stream;
14
+
15
+ access_log /dev/stdout;
16
+ error_log /dev/stderr;
17
+
18
+ client_body_temp_path /tmp/client_body;
19
+ proxy_temp_path /tmp/proxy;
20
+ fastcgi_temp_path /tmp/fastcgi;
21
+ uwsgi_temp_path /tmp/uwsgi;
22
+ scgi_temp_path /tmp/scgi;
23
+
24
+ server {
25
+ listen 7860;
26
+ server_name localhost;
27
+
28
+ # Custom sign-in page
29
+ location = /signin {
30
+ root /var/www/html/theme;
31
+ try_files /sign_in.html =404;
32
+ }
33
+
34
+ # OAuth2 Proxy endpoints
35
+ location /oauth2/ {
36
+ proxy_pass http://127.0.0.1:4180;
37
+ proxy_set_header Host $host;
38
+ proxy_set_header X-Real-IP $remote_addr;
39
+ proxy_set_header X-Scheme $scheme;
40
+ proxy_set_header X-Auth-Request-Redirect $request_uri;
41
+ }
42
+
43
+ location = /oauth2/auth {
44
+ internal;
45
+ proxy_pass http://127.0.0.1:4180;
46
+ proxy_set_header Host $host;
47
+ proxy_set_header X-Real-IP $remote_addr;
48
+ proxy_set_header X-Scheme $scheme;
49
+ proxy_set_header Content-Length "";
50
+ proxy_pass_request_body off;
51
+ }
52
+
53
+ # Main application (ttyd) protected by auth_request
54
+ location / {
55
+ auth_request /oauth2/auth;
56
+ error_page 401 = /signin;
57
+
58
+ proxy_pass http://127.0.0.1:7681;
59
+ proxy_http_version 1.1;
60
+ proxy_set_header Upgrade $http_upgrade;
61
+ proxy_set_header Connection "upgrade";
62
+ proxy_set_header Host $host;
63
+ proxy_set_header X-Real-IP $remote_addr;
64
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
65
+ proxy_set_header X-Forwarded-Proto $scheme;
66
+ }
67
+ }
68
+ }
oauth2-proxy-github.cfg ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ http_address = "127.0.0.1:4180"
2
+ provider = "github"
3
+ email_domains = ["*"]
4
+ cookie_secure = true
5
+ cookie_httponly = true
6
+ cookie_refresh = "1h"
7
+ cookie_expire = "168h"
8
+ reverse_proxy = true
9
+ set_xauthrequest = true
10
+ skip_provider_button = true
11
+ upstreams = ["http://127.0.0.1:7681"]
12
+ request_logging = true
sign_in.html ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Sign In - VPS Linux</title>
7
+ <style>
8
+ :root {
9
+ /* 页面背景色 - 极深黑 */
10
+ --bg-color: #050505;
11
+ /* 卡片背景色 - 稍浅的黑 */
12
+ --card-bg: #0f0f0f;
13
+ /* 标题颜色 - 纯白 */
14
+ --title-color: #ffffff;
15
+ /* 副标题颜色 - 灰色 */
16
+ --subtitle-color: #888888;
17
+ /* 边框颜色 - 极淡灰 */
18
+ --border-color: #222222;
19
+ /* 按钮背景 - 深灰 */
20
+ --button-bg: #1c1c1c;
21
+ /* 按钮文字 - 浅灰 */
22
+ --button-text: #e1e1e1;
23
+ /* 按钮悬停 - 稍亮灰 */
24
+ --button-hover: #2a2a2a;
25
+ }
26
+
27
+ body {
28
+ background-color: var(--bg-color);
29
+ color: var(--title-color);
30
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
31
+ display: flex;
32
+ align-items: center;
33
+ justify-content: center;
34
+ height: 100vh;
35
+ margin: 0;
36
+ }
37
+
38
+ .login-container {
39
+ background-color: var(--card-bg);
40
+ border: 1px solid var(--border-color);
41
+ border-radius: 12px;
42
+ padding: 48px 40px;
43
+ width: 100%;
44
+ max-width: 360px;
45
+ text-align: center;
46
+ /* 柔和阴影 */
47
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4);
48
+ }
49
+
50
+ .logo {
51
+ width: 64px;
52
+ height: 64px;
53
+ margin: 0 auto 24px;
54
+ /* 红色实心机器人 SVG */
55
+ background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><path fill="%23ff595e" d="M32 6C18.745 6 8 16.745 8 30c0 8.837 4.785 16.685 12.049 21.146-.688 2.375-2.049 4.354-2.049 4.354s4.5.5 8.5-2.5c1.616.323 3.298.5 5 .5 13.255 0 24-10.745 24-24S45.255 6 32 6zm-8 22c-2.209 0-4-1.791-4-4s1.791-4 4-4 4 1.791 4 4-1.791 4-4 4zm16 0c-2.209 0-4-1.791-4-4s1.791-4 4-4 4 1.791 4 4-1.791 4-4 4z"/><circle cx="24" cy="28" r="2" fill="%23333"/><circle cx="40" cy="28" r="2" fill="%23333"/></svg>');
56
+ background-repeat: no-repeat;
57
+ background-position: center;
58
+ background-size: contain;
59
+ }
60
+
61
+ h1 {
62
+ font-size: 24px;
63
+ font-weight: 600;
64
+ margin: 0 0 12px;
65
+ color: var(--title-color);
66
+ }
67
+
68
+ p {
69
+ color: var(--subtitle-color);
70
+ margin: 0 0 32px;
71
+ font-size: 14px;
72
+ line-height: 1.5;
73
+ }
74
+
75
+ .btn {
76
+ display: block;
77
+ width: 100%;
78
+ padding: 14px;
79
+ border-radius: 8px;
80
+ border: none;
81
+ background-color: var(--button-bg);
82
+ color: var(--button-text);
83
+ font-size: 14px;
84
+ font-weight: 600;
85
+ text-decoration: none;
86
+ box-sizing: border-box;
87
+ cursor: pointer;
88
+ transition: background-color 0.2s ease;
89
+ }
90
+
91
+ .btn:hover {
92
+ background-color: var(--button-hover);
93
+ }
94
+ </style>
95
+ </head>
96
+ <body>
97
+ <div class="login-container">
98
+ <div class="logo"></div>
99
+ <h1>Welcome Back</h1>
100
+ <p>Sign in to access your VPS Linux environment</p>
101
+ <a class="btn" href="/oauth2/start?rd=/">Sign in with GitHub</a>
102
+ </div>
103
+ </body>
104
+ </html>
start.sh ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ set -e
3
+ set -x
4
+
5
+ # 1. 强制清理遗留的组织校验变量(避免500错误)
6
+ unset OAUTH2_PROXY_GITHUB_ORG OAUTH2_PROXY_GITHUB_ORGS
7
+ unset OAUTH2_PROXY_GITHUB_TEAM OAUTH2_PROXY_GITHUB_TEAMS
8
+ unset OAUTH2_PROXY_GITHUB_REPO OAUTH2_PROXY_GITHUB_REPOS
9
+ unset OAUTH2_PROXY_ALLOWED_GROUPS
10
+
11
+ # 2. 检查必要变量
12
+ if [ -z "$ALLOWED_USERS" ]; then
13
+ echo "Error: ALLOWED_USERS is required."
14
+ exit 1
15
+ fi
16
+
17
+ if [ -z "$GITHUB_CLIENT_ID" ] || [ -z "$GITHUB_CLIENT_SECRET" ] || [ -z "$OAUTH2_PROXY_COOKIE_SECRET" ]; then
18
+ echo "Error: GitHub Secrets (CLIENT_ID, CLIENT_SECRET, COOKIE_SECRET) are required."
19
+ exit 1
20
+ fi
21
+
22
+ if [ -z "$SPACE_PUBLIC_URL" ]; then
23
+ echo "Error: SPACE_PUBLIC_URL is required."
24
+ exit 1
25
+ fi
26
+
27
+ # 3. 变量清洗
28
+ trim_secret() {
29
+ printf "%s" "$1" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | tr -d '\r' | sed 's/^"//;s/"$//'
30
+ }
31
+ GITHUB_CLIENT_ID=$(trim_secret "$GITHUB_CLIENT_ID")
32
+ GITHUB_CLIENT_SECRET=$(trim_secret "$GITHUB_CLIENT_SECRET")
33
+ OAUTH2_PROXY_COOKIE_SECRET=$(trim_secret "$OAUTH2_PROXY_COOKIE_SECRET")
34
+ SPACE_PUBLIC_URL=$(echo "$SPACE_PUBLIC_URL" | sed "s/[[:space:]]//g" | sed "s/\`//g")
35
+ SPACE_PUBLIC_URL="${SPACE_PUBLIC_URL%/}"
36
+
37
+ # 4. 生成白名单文件
38
+ AUTH_FILE="/tmp/authenticated_emails.txt"
39
+ > "$AUTH_FILE"
40
+ declare -a GITHUB_USERS
41
+ IFS=',' read -ra USERS <<< "$ALLOWED_USERS"
42
+ for USER_ITEM in "${USERS[@]}"; do
43
+ USER_ITEM=$(echo "$USER_ITEM" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
44
+ [ -z "$USER_ITEM" ] && continue
45
+
46
+ # 区分邮箱与用户名
47
+ if [[ "$USER_ITEM" == *"@"* ]]; then
48
+ echo "$USER_ITEM" | tr '[:upper:]' '[:lower:]' >> "$AUTH_FILE"
49
+ else
50
+ GITHUB_USERS+=("$(echo "$USER_ITEM" | tr '[:upper:]' '[:lower:]')")
51
+ fi
52
+ done
53
+
54
+ # 5. 启动 ttyd
55
+ echo "Starting ttyd..."
56
+ ttyd -p 7681 -i 127.0.0.1 -W bash &
57
+
58
+ # 6. 启动 oauth2-proxy (GitHub)
59
+ echo "Starting oauth2-proxy..."
60
+ GITHUB_CMD=(oauth2-proxy \
61
+ --config=$HOME/oauth2-proxy-github.cfg \
62
+ --client-id="$GITHUB_CLIENT_ID" \
63
+ --client-secret="$GITHUB_CLIENT_SECRET" \
64
+ --cookie-secret="$OAUTH2_PROXY_COOKIE_SECRET" \
65
+ --cookie-name="_oauth2_proxy_github" \
66
+ --scope="user:email" \
67
+ --show-debug-on-error=true \
68
+ --redirect-url="$SPACE_PUBLIC_URL/oauth2/callback")
69
+
70
+ # 按需挂载白名单
71
+ if [ -s "$AUTH_FILE" ]; then
72
+ GITHUB_CMD+=("--authenticated-emails-file=$AUTH_FILE")
73
+ fi
74
+ for GH_USER in "${GITHUB_USERS[@]}"; do
75
+ GITHUB_CMD+=("--github-user=$GH_USER")
76
+ done
77
+
78
+ "${GITHUB_CMD[@]}" 2>&1 &
79
+
80
+ # 7. 等待服务就绪
81
+ for i in {1..30}; do
82
+ if nc -z 127.0.0.1 4180 && nc -z 127.0.0.1 7681; then
83
+ echo "Services ready."
84
+ break
85
+ fi
86
+ sleep 1
87
+ done
88
+
89
+ # 8. 启动 Nginx
90
+ echo "Starting Nginx..."
91
+ nginx -g "daemon off;"