@dbx-tools/appkit-lakebox 0.0.0-stage → 0.9.131
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +104 -2
- package/index.ts +32 -0
- package/lib/index.d.ts +28 -0
- package/lib/index.js +26 -0
- package/lib/src/_package.d.ts +2 -0
- package/lib/src/_package.js +11 -0
- package/lib/src/_resource-registry.d.ts +41 -0
- package/lib/src/_resource-registry.js +128 -0
- package/lib/src/_service-snapshots.d.ts +55 -0
- package/lib/src/_service-snapshots.js +65 -0
- package/lib/src/config.d.ts +40 -0
- package/lib/src/config.js +86 -0
- package/lib/src/desktop.d.ts +6 -0
- package/lib/src/desktop.js +88 -0
- package/lib/src/metrics.d.ts +59 -0
- package/lib/src/metrics.js +151 -0
- package/lib/src/plugin.d.ts +259 -0
- package/lib/src/plugin.js +329 -0
- package/lib/src/routes.d.ts +75 -0
- package/lib/src/routes.js +441 -0
- package/lib/src/runtime.d.ts +154 -0
- package/lib/src/runtime.js +760 -0
- package/lib/src/services.d.ts +101 -0
- package/lib/src/services.js +188 -0
- package/lib/src/skills.d.ts +5 -0
- package/lib/src/skills.js +21 -0
- package/lib/src/user.d.ts +46 -0
- package/lib/src/user.js +76 -0
- package/lib/src/workspace.d.ts +43 -0
- package/lib/src/workspace.js +138 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +167 -4
- package/scripts/desktop-prerequisites.sh +66 -0
- package/scripts/desktop-session.sh +307 -0
- package/scripts/lakebox-admin.sh +234 -0
- package/scripts/lakebox-service.py +449 -0
- package/skills/html-page/SKILL.md +40 -0
- package/skills/jupyter-lab/SKILL.md +32 -0
- package/skills/lakebox-showcase/SKILL.md +66 -0
- package/skills/network-speed-test/SKILL.md +29 -0
- package/skills/realtime-whiteboard/SKILL.md +28 -0
- package/src/_package.ts +11 -0
- package/src/_resource-registry.ts +160 -0
- package/src/_service-snapshots.ts +84 -0
- package/src/config.ts +128 -0
- package/src/desktop.ts +97 -0
- package/src/metrics.ts +178 -0
- package/src/plugin.ts +446 -0
- package/src/routes.ts +524 -0
- package/src/runtime.ts +952 -0
- package/src/services.ts +239 -0
- package/src/skills.ts +23 -0
- package/src/user.ts +88 -0
- package/src/workspace.ts +185 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Desktop service entrypoint. Runs as the Lakebox user inside a
|
|
3
|
+
# host-environment crun container with a private /tmp, so the X socket is not
|
|
4
|
+
# visible to other Lakebox users. crun owns the process lifecycle.
|
|
5
|
+
set -eu
|
|
6
|
+
: "${DBX_TOOLS_DESKTOP_WALLPAPER_BASE64:?DBX_TOOLS_DESKTOP_WALLPAPER_BASE64 is required}"
|
|
7
|
+
: "${DBX_TOOLS_DESKTOP_BRAND_ICON_BASE64:?DBX_TOOLS_DESKTOP_BRAND_ICON_BASE64 is required}"
|
|
8
|
+
: "${DBX_TOOLS_DESKTOP_TERMINAL_ICON_BASE64:?DBX_TOOLS_DESKTOP_TERMINAL_ICON_BASE64 is required}"
|
|
9
|
+
: "${DBX_TOOLS_DESKTOP_HOMEPAGE_BASE64:?DBX_TOOLS_DESKTOP_HOMEPAGE_BASE64 is required}"
|
|
10
|
+
: "${DBX_TOOLS_DESKTOP_PRIMARY:?DBX_TOOLS_DESKTOP_PRIMARY is required}"
|
|
11
|
+
: "${DBX_TOOLS_DESKTOP_PRIMARY_HOVER:?DBX_TOOLS_DESKTOP_PRIMARY_HOVER is required}"
|
|
12
|
+
: "${DBX_TOOLS_DESKTOP_SURFACE:?DBX_TOOLS_DESKTOP_SURFACE is required}"
|
|
13
|
+
: "${DBX_TOOLS_DESKTOP_FONT:?DBX_TOOLS_DESKTOP_FONT is required}"
|
|
14
|
+
: "${SERVICE_PORT:?SERVICE_PORT is required}"
|
|
15
|
+
display=$((SERVICE_PORT - 20000 + 100))
|
|
16
|
+
|
|
17
|
+
runtime="$HOME/.dbx-tools/desktop"
|
|
18
|
+
session_script="$runtime/start-xfce.sh"
|
|
19
|
+
browser_script="$runtime/open-browser"
|
|
20
|
+
browser_homepage="$runtime/browser-homepage"
|
|
21
|
+
desktop_environment="$runtime/environment"
|
|
22
|
+
desktop_runner="$runtime/run"
|
|
23
|
+
log_file="$runtime/desktop.log"
|
|
24
|
+
wallpaper_svg="$runtime/dbx-tools-wallpaper.svg"
|
|
25
|
+
wallpaper_png="$runtime/dbx-tools-wallpaper.png"
|
|
26
|
+
brand_icon_svg="$runtime/dbx-tools-icon.svg"
|
|
27
|
+
brand_icon="$runtime/dbx-tools-icon.png"
|
|
28
|
+
terminal_icon_svg="$runtime/dbx-tools-terminal.svg"
|
|
29
|
+
terminal_icon="$runtime/dbx-tools-terminal.png"
|
|
30
|
+
|
|
31
|
+
mkdir -p "$runtime" "$HOME/.vnc" "$HOME/.config/gtk-3.0" \
|
|
32
|
+
"$HOME/.config/xfce4/xfconf/xfce-perchannel-xml" "$HOME/.config/xfce4" \
|
|
33
|
+
"$HOME/.local/share/applications" "$HOME/.local/share/xfce4/helpers" "$HOME/Desktop"
|
|
34
|
+
|
|
35
|
+
test -x "$(command -v kasmvncserver)" || {
|
|
36
|
+
printf 'kasmvncserver is unavailable after package setup.\n' >&2
|
|
37
|
+
exit 1
|
|
38
|
+
}
|
|
39
|
+
test -x "$(command -v kasmvncpasswd)" || {
|
|
40
|
+
printf 'kasmvncpasswd is unavailable after package setup.\n' >&2
|
|
41
|
+
exit 1
|
|
42
|
+
}
|
|
43
|
+
test -r /usr/share/kasmvnc/www/index.html || {
|
|
44
|
+
printf 'KasmVNC web client is unavailable after package setup.\n' >&2
|
|
45
|
+
exit 1
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
write_base64_asset() {
|
|
49
|
+
encoded="$1"
|
|
50
|
+
destination="$2"
|
|
51
|
+
temporary="$destination.tmp.$$"
|
|
52
|
+
printf '%s' "$encoded" | base64 -d >"$temporary"
|
|
53
|
+
if [ -f "$destination" ] && cmp -s "$temporary" "$destination"; then
|
|
54
|
+
rm -f "$temporary"
|
|
55
|
+
return 1
|
|
56
|
+
fi
|
|
57
|
+
mv "$temporary" "$destination"
|
|
58
|
+
return 0
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if write_base64_asset "$DBX_TOOLS_DESKTOP_WALLPAPER_BASE64" "$wallpaper_svg" || \
|
|
62
|
+
[ ! -s "$wallpaper_png" ]; then
|
|
63
|
+
rsvg-convert --width 1600 --height 1000 "$wallpaper_svg" >"$wallpaper_png"
|
|
64
|
+
fi
|
|
65
|
+
if write_base64_asset "$DBX_TOOLS_DESKTOP_BRAND_ICON_BASE64" "$brand_icon_svg" || \
|
|
66
|
+
[ ! -s "$brand_icon" ]; then
|
|
67
|
+
rsvg-convert --width 64 --height 64 "$brand_icon_svg" >"$brand_icon"
|
|
68
|
+
fi
|
|
69
|
+
if write_base64_asset "$DBX_TOOLS_DESKTOP_TERMINAL_ICON_BASE64" "$terminal_icon_svg" || \
|
|
70
|
+
[ ! -s "$terminal_icon" ]; then
|
|
71
|
+
rsvg-convert --width 64 --height 64 "$terminal_icon_svg" >"$terminal_icon"
|
|
72
|
+
fi
|
|
73
|
+
write_base64_asset "$DBX_TOOLS_DESKTOP_HOMEPAGE_BASE64" "$browser_homepage" || true
|
|
74
|
+
|
|
75
|
+
printf 'export DISPLAY=:%s\n' "$display" >"$desktop_environment"
|
|
76
|
+
chmod 600 "$desktop_environment"
|
|
77
|
+
cat >"$desktop_runner" <<'EOF'
|
|
78
|
+
#!/bin/sh
|
|
79
|
+
set -eu
|
|
80
|
+
. "$HOME/.dbx-tools/desktop/environment"
|
|
81
|
+
exec "$@"
|
|
82
|
+
EOF
|
|
83
|
+
chmod 700 "$desktop_runner"
|
|
84
|
+
|
|
85
|
+
cat >"$browser_script" <<'EOF'
|
|
86
|
+
#!/bin/sh
|
|
87
|
+
export GTK_A11Y=none
|
|
88
|
+
export WEBKIT_DISABLE_SANDBOX_THIS_IS_DANGEROUS=1
|
|
89
|
+
if [ "$#" -eq 0 ]; then
|
|
90
|
+
set -- "$(cat "$HOME/.dbx-tools/desktop/browser-homepage")"
|
|
91
|
+
fi
|
|
92
|
+
exec epiphany "$@"
|
|
93
|
+
EOF
|
|
94
|
+
chmod 700 "$browser_script"
|
|
95
|
+
|
|
96
|
+
cat >"$HOME/.config/gtk-3.0/gtk.css" <<EOF
|
|
97
|
+
#XfcePanelWindow {
|
|
98
|
+
background-color: $DBX_TOOLS_DESKTOP_PRIMARY;
|
|
99
|
+
color: $DBX_TOOLS_DESKTOP_SURFACE;
|
|
100
|
+
}
|
|
101
|
+
#XfcePanelWindow button {
|
|
102
|
+
color: $DBX_TOOLS_DESKTOP_SURFACE;
|
|
103
|
+
border-color: transparent;
|
|
104
|
+
background-color: transparent;
|
|
105
|
+
border-radius: 8px;
|
|
106
|
+
margin: 3px;
|
|
107
|
+
}
|
|
108
|
+
#XfcePanelWindow button:hover,
|
|
109
|
+
#XfcePanelWindow button:checked {
|
|
110
|
+
background-color: $DBX_TOOLS_DESKTOP_PRIMARY_HOVER;
|
|
111
|
+
}
|
|
112
|
+
EOF
|
|
113
|
+
|
|
114
|
+
if [ -r /etc/xdg/xfce4/panel/default.xml ]; then
|
|
115
|
+
cp /etc/xdg/xfce4/panel/default.xml \
|
|
116
|
+
"$HOME/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml"
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
cat >"$HOME/.config/mimeapps.list" <<'EOF'
|
|
120
|
+
[Default Applications]
|
|
121
|
+
text/html=dbx-tools-browser.desktop
|
|
122
|
+
x-scheme-handler/http=dbx-tools-browser.desktop
|
|
123
|
+
x-scheme-handler/https=dbx-tools-browser.desktop
|
|
124
|
+
EOF
|
|
125
|
+
|
|
126
|
+
cat >"$HOME/.local/share/applications/dbx-tools-browser.desktop" <<EOF
|
|
127
|
+
[Desktop Entry]
|
|
128
|
+
Type=Application
|
|
129
|
+
Name=dbx tools Browser
|
|
130
|
+
Comment=Browse the web from the Lakebox desktop
|
|
131
|
+
Exec=$browser_script %U
|
|
132
|
+
Icon=$brand_icon
|
|
133
|
+
Terminal=false
|
|
134
|
+
Categories=Network;WebBrowser;
|
|
135
|
+
MimeType=text/html;x-scheme-handler/http;x-scheme-handler/https;
|
|
136
|
+
EOF
|
|
137
|
+
|
|
138
|
+
cat >"$HOME/.local/share/xfce4/helpers/dbx-tools-browser.desktop" <<EOF
|
|
139
|
+
[Desktop Entry]
|
|
140
|
+
Version=1.0
|
|
141
|
+
Icon=$brand_icon
|
|
142
|
+
Type=X-XFCE-Helper
|
|
143
|
+
Name=dbx tools Browser
|
|
144
|
+
StartupNotify=true
|
|
145
|
+
X-XFCE-Binaries=$browser_script;
|
|
146
|
+
X-XFCE-Category=WebBrowser
|
|
147
|
+
X-XFCE-Commands=$browser_script;
|
|
148
|
+
X-XFCE-CommandsWithParameter=$browser_script "%s";
|
|
149
|
+
EOF
|
|
150
|
+
|
|
151
|
+
cat >"$HOME/.config/xfce4/helpers.rc" <<'EOF'
|
|
152
|
+
WebBrowser=dbx-tools-browser
|
|
153
|
+
EOF
|
|
154
|
+
rm -f "$HOME/Desktop/Web Browser.desktop"
|
|
155
|
+
|
|
156
|
+
cat >"$HOME/.local/share/applications/dbx-tools-terminal.desktop" <<EOF
|
|
157
|
+
[Desktop Entry]
|
|
158
|
+
Type=Application
|
|
159
|
+
Name=Terminal
|
|
160
|
+
Comment=Open the Lakebox desktop terminal
|
|
161
|
+
Exec=xfce4-terminal
|
|
162
|
+
Icon=$terminal_icon
|
|
163
|
+
Terminal=false
|
|
164
|
+
Categories=System;TerminalEmulator;
|
|
165
|
+
EOF
|
|
166
|
+
chmod 700 "$HOME/.local/share/applications/dbx-tools-terminal.desktop"
|
|
167
|
+
rm -f "$HOME/Desktop/Terminal.desktop"
|
|
168
|
+
|
|
169
|
+
cat >"$HOME/.vnc/kasmvnc.yaml" <<EOF
|
|
170
|
+
desktop:
|
|
171
|
+
resolution:
|
|
172
|
+
width: 1600
|
|
173
|
+
height: 1000
|
|
174
|
+
allow_resize: true
|
|
175
|
+
pixel_depth: 24
|
|
176
|
+
network:
|
|
177
|
+
protocol: http
|
|
178
|
+
interface: 127.0.0.1
|
|
179
|
+
websocket_port: $SERVICE_PORT
|
|
180
|
+
use_ipv4: true
|
|
181
|
+
use_ipv6: false
|
|
182
|
+
ssl:
|
|
183
|
+
pem_certificate:
|
|
184
|
+
pem_key:
|
|
185
|
+
require_ssl: false
|
|
186
|
+
encoding:
|
|
187
|
+
max_frame_rate: 30
|
|
188
|
+
rect_encoding_mode:
|
|
189
|
+
min_quality: 7
|
|
190
|
+
max_quality: 9
|
|
191
|
+
consider_lossless_quality: 10
|
|
192
|
+
rectangle_compress_threads: auto
|
|
193
|
+
video_encoding_mode:
|
|
194
|
+
jpeg_quality: -1
|
|
195
|
+
webp_quality: -1
|
|
196
|
+
max_resolution:
|
|
197
|
+
width: 1920
|
|
198
|
+
height: 1080
|
|
199
|
+
enter_video_encoding_mode:
|
|
200
|
+
time_threshold: 1
|
|
201
|
+
area_threshold: 45%
|
|
202
|
+
exit_video_encoding_mode:
|
|
203
|
+
time_threshold: 2
|
|
204
|
+
scaling_algorithm: progressive_bilinear
|
|
205
|
+
webp_encoding_time: 30
|
|
206
|
+
video_streaming_mode:
|
|
207
|
+
codec: h264
|
|
208
|
+
server:
|
|
209
|
+
http:
|
|
210
|
+
headers:
|
|
211
|
+
- Cross-Origin-Embedder-Policy=require-corp
|
|
212
|
+
- Cross-Origin-Opener-Policy=same-origin
|
|
213
|
+
httpd_directory: /usr/share/kasmvnc/www
|
|
214
|
+
auto_shutdown:
|
|
215
|
+
no_user_session_timeout: never
|
|
216
|
+
active_user_session_timeout: never
|
|
217
|
+
inactive_user_session_timeout: never
|
|
218
|
+
command_line:
|
|
219
|
+
prompt: false
|
|
220
|
+
EOF
|
|
221
|
+
|
|
222
|
+
if [ ! -s "$HOME/.kasmpasswd" ]; then
|
|
223
|
+
kasm_password="$(python3 -c 'import secrets; print(secrets.token_urlsafe(32))')"
|
|
224
|
+
printf '%s\n%s\n' "$kasm_password" "$kasm_password" | \
|
|
225
|
+
kasmvncpasswd -u dbx-tools -r -w "$HOME/.kasmpasswd" >/dev/null
|
|
226
|
+
fi
|
|
227
|
+
|
|
228
|
+
cat >"$session_script" <<EOF
|
|
229
|
+
#!/bin/sh
|
|
230
|
+
set -eu
|
|
231
|
+
runtime="\$HOME/.dbx-tools/desktop"
|
|
232
|
+
ready="\$runtime/session.ready"
|
|
233
|
+
log_file="\$runtime/desktop.log"
|
|
234
|
+
wallpaper="\$runtime/dbx-tools-wallpaper.png"
|
|
235
|
+
brand_icon="\$runtime/dbx-tools-icon.svg"
|
|
236
|
+
rm -f "\$ready"
|
|
237
|
+
if [ -z "\${DBUS_SESSION_BUS_ADDRESS:-}" ]; then
|
|
238
|
+
exec dbus-run-session -- "\$0"
|
|
239
|
+
fi
|
|
240
|
+
startxfce4 >>"\$log_file" 2>&1 &
|
|
241
|
+
xfce_pid=\$!
|
|
242
|
+
for _ in \$(seq 1 160); do
|
|
243
|
+
if pgrep -x xfce4-panel >/dev/null 2>&1 && \
|
|
244
|
+
pgrep -x xfdesktop >/dev/null 2>&1 && pgrep -x xfwm4 >/dev/null 2>&1; then
|
|
245
|
+
set_xfconf() {
|
|
246
|
+
channel="\$1"
|
|
247
|
+
property="\$2"
|
|
248
|
+
type="\$3"
|
|
249
|
+
value="\$4"
|
|
250
|
+
if xfconf-query -c "\$channel" -p "\$property" >/dev/null 2>&1; then
|
|
251
|
+
xfconf-query -c "\$channel" -p "\$property" -s "\$value" >/dev/null 2>&1 || true
|
|
252
|
+
else
|
|
253
|
+
xfconf-query -c "\$channel" -p "\$property" --create -t "\$type" \
|
|
254
|
+
-s "\$value" >/dev/null 2>&1 || true
|
|
255
|
+
fi
|
|
256
|
+
}
|
|
257
|
+
xdg-mime default dbx-tools-browser.desktop text/html >/dev/null 2>&1 || true
|
|
258
|
+
xdg-mime default dbx-tools-browser.desktop x-scheme-handler/http >/dev/null 2>&1 || true
|
|
259
|
+
xdg-mime default dbx-tools-browser.desktop x-scheme-handler/https >/dev/null 2>&1 || true
|
|
260
|
+
xdg-settings set default-web-browser dbx-tools-browser.desktop >/dev/null 2>&1 || true
|
|
261
|
+
gsettings set org.gnome.Epiphany homepage-url \
|
|
262
|
+
"\$(cat "\$runtime/browser-homepage")" >/dev/null 2>&1 || true
|
|
263
|
+
gsettings set org.gnome.Epiphany ask-for-default false >/dev/null 2>&1 || true
|
|
264
|
+
set_xfconf xsettings /Net/ThemeName string Arc-Dark
|
|
265
|
+
set_xfconf xsettings /Net/IconThemeName string hicolor
|
|
266
|
+
set_xfconf xsettings /Gtk/FontName string '$DBX_TOOLS_DESKTOP_FONT 10'
|
|
267
|
+
set_xfconf xfwm4 /general/theme string Arc-Dark
|
|
268
|
+
set_xfconf xfce4-panel /panels/panel-1/size uint 36
|
|
269
|
+
set_xfconf xfce4-panel /plugins/plugin-1 string whiskermenu
|
|
270
|
+
set_xfconf xfce4-panel /plugins/plugin-1/button-icon string "\$brand_icon"
|
|
271
|
+
set_xfconf xfce4-panel /plugins/plugin-1/button-title string 'dbx tools'
|
|
272
|
+
set_xfconf xfce4-panel /plugins/plugin-1/show-button-title bool true
|
|
273
|
+
properties=\$(xfconf-query -c xfce4-desktop -l 2>/dev/null | \
|
|
274
|
+
grep -E '/(last-image|image-path)\$' || true)
|
|
275
|
+
if [ -z "\$properties" ]; then
|
|
276
|
+
properties='/backdrop/screen0/monitor0/workspace0/last-image
|
|
277
|
+
/backdrop/screen0/monitorVNC-0/workspace0/last-image
|
|
278
|
+
/backdrop/screen0/monitorDefault-0/workspace0/last-image'
|
|
279
|
+
fi
|
|
280
|
+
printf '%s\n' "\$properties" | while IFS= read -r property; do
|
|
281
|
+
[ -n "\$property" ] || continue
|
|
282
|
+
xfconf-query -c xfce4-desktop -p "\$property" --create -t string \
|
|
283
|
+
-s "\$wallpaper" >/dev/null 2>&1 || true
|
|
284
|
+
style=\$(printf '%s' "\$property" | sed -E 's/(last-image|image-path)\$/image-style/')
|
|
285
|
+
xfconf-query -c xfce4-desktop -p "\$style" --create -t int -s 5 >/dev/null 2>&1 || true
|
|
286
|
+
done
|
|
287
|
+
xfdesktop --reload >/dev/null 2>&1 || true
|
|
288
|
+
xfce4-panel --restart >/dev/null 2>&1 || true
|
|
289
|
+
touch "\$ready"
|
|
290
|
+
break
|
|
291
|
+
fi
|
|
292
|
+
kill -0 "\$xfce_pid" 2>/dev/null || exit 1
|
|
293
|
+
sleep 0.25
|
|
294
|
+
done
|
|
295
|
+
test -f "\$ready" || {
|
|
296
|
+
printf 'Xfce desktop shell did not become ready.\n' >&2
|
|
297
|
+
exit 1
|
|
298
|
+
}
|
|
299
|
+
wait "\$xfce_pid"
|
|
300
|
+
EOF
|
|
301
|
+
chmod 700 "$session_script"
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
# Each user's display number follows from the derived service port.
|
|
305
|
+
exec kasmvncserver ":$display" -fg -select-de xfce -xstartup "$session_script" \
|
|
306
|
+
-geometry 1600x1000 -depth 24 -interface 127.0.0.1 -websocketPort "$SERVICE_PORT" \
|
|
307
|
+
-disableBasicAuth -SecurityTypes None
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Package-owned privileged Lakebox administration. Runs as root through the
|
|
3
|
+
# fixed bootstrap capability only; never receives REST, agent, or service input
|
|
4
|
+
# other than the validated hash, UID, digest, and port arguments below.
|
|
5
|
+
set -eu
|
|
6
|
+
umask 022
|
|
7
|
+
|
|
8
|
+
agent_home=/home/sandbox-agent
|
|
9
|
+
state=$agent_home/.dbx-tools
|
|
10
|
+
users=$state/users
|
|
11
|
+
identities=$state/identities
|
|
12
|
+
runtime=$state/runtime
|
|
13
|
+
bundle=$runtime/container-v1
|
|
14
|
+
run_root=/run/lakebox
|
|
15
|
+
port_min=20000
|
|
16
|
+
port_max=31999
|
|
17
|
+
agent_uid=$(id -u sandbox-agent)
|
|
18
|
+
|
|
19
|
+
valid_hash() {
|
|
20
|
+
case "$1" in
|
|
21
|
+
''|*[!0-9a-v]*) return 1 ;;
|
|
22
|
+
esac
|
|
23
|
+
[ "${#1}" -eq 7 ]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
valid_number() {
|
|
27
|
+
case "$1" in
|
|
28
|
+
''|*[!0-9]*) return 1 ;;
|
|
29
|
+
esac
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
with_lock() {
|
|
33
|
+
install -d -m 0755 "$run_root"
|
|
34
|
+
exec 9>"$run_root/admin.lock"
|
|
35
|
+
flock 9
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
ensure_layout() {
|
|
39
|
+
chmod 0711 "$agent_home"
|
|
40
|
+
install -d -m 0711 -o root -g root "$state" "$users" "$runtime"
|
|
41
|
+
install -d -m 0700 -o root -g root "$identities"
|
|
42
|
+
install -d -m 0755 -o root -g root "$run_root" "$runtime/bin"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
ensure_bundle() {
|
|
46
|
+
if [ -x "$bundle/root/usr/bin/buildah" ] && [ -x "$bundle/root/usr/bin/crun" ] &&
|
|
47
|
+
[ -f "$bundle/complete" ]; then
|
|
48
|
+
return 0
|
|
49
|
+
fi
|
|
50
|
+
echo "installing container runtime bundle"
|
|
51
|
+
debs=$(mktemp -d)
|
|
52
|
+
rm -rf "$bundle.tmp"
|
|
53
|
+
install -d -m 0755 "$bundle.tmp/root"
|
|
54
|
+
apt-get update -qq
|
|
55
|
+
DEBIAN_FRONTEND=noninteractive apt-get -qq -o Dir::Cache::archives="$debs" \
|
|
56
|
+
-o APT::Keep-Downloaded-Packages=true --download-only install -y \
|
|
57
|
+
--no-install-recommends buildah crun uidmap >/dev/null
|
|
58
|
+
for deb in "$debs"/*.deb; do dpkg-deb -x "$deb" "$bundle.tmp/root"; done
|
|
59
|
+
rm -rf "$debs"
|
|
60
|
+
chmod -R a+rX "$bundle.tmp"
|
|
61
|
+
touch "$bundle.tmp/complete"
|
|
62
|
+
rm -rf "$bundle"
|
|
63
|
+
mv "$bundle.tmp" "$bundle"
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
ensure_ttyd() {
|
|
67
|
+
version=1.7.7
|
|
68
|
+
case "$(uname -m)" in
|
|
69
|
+
aarch64) checksum=b38acadd89d1d396a0f5649aa52c539edbad07f4bc7348b27b4f4b7219dd4165 ;;
|
|
70
|
+
x86_64) checksum=8a217c968aba172e0dbf3f34447218dc015bc4d5e59bf51db2f2cd12b7be4f55 ;;
|
|
71
|
+
*) echo "unsupported ttyd architecture $(uname -m)" >&2; return 1 ;;
|
|
72
|
+
esac
|
|
73
|
+
target=$runtime/bin/ttyd
|
|
74
|
+
if [ -x "$target" ] && printf '%s %s\n' "$checksum" "$target" | sha256sum --check --status; then
|
|
75
|
+
return 0
|
|
76
|
+
fi
|
|
77
|
+
echo "installing ttyd $version"
|
|
78
|
+
curl -fsSL -o "$target.tmp" \
|
|
79
|
+
"https://github.com/tsl0922/ttyd/releases/download/$version/ttyd.$(uname -m)"
|
|
80
|
+
printf '%s %s\n' "$checksum" "$target.tmp" | sha256sum --check --status
|
|
81
|
+
chmod 0755 "$target.tmp"
|
|
82
|
+
mv -f "$target.tmp" "$target"
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# Installs one package-owned helper delivered as base64 when its content changed.
|
|
86
|
+
install_helper() {
|
|
87
|
+
name=$1
|
|
88
|
+
case "$name" in
|
|
89
|
+
lakebox-service|desktop-session|desktop-prerequisites) ;;
|
|
90
|
+
*) echo "unknown helper $name" >&2; return 2 ;;
|
|
91
|
+
esac
|
|
92
|
+
target=$runtime/bin/$name
|
|
93
|
+
temporary=$target.tmp.$$
|
|
94
|
+
printf %s "$2" | base64 -d >"$temporary"
|
|
95
|
+
if [ -f "$target" ] && cmp -s "$temporary" "$target"; then
|
|
96
|
+
rm -f "$temporary"
|
|
97
|
+
return 0
|
|
98
|
+
fi
|
|
99
|
+
chmod 0755 "$temporary"
|
|
100
|
+
mv -f "$temporary" "$target"
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
ensure_firewall() {
|
|
104
|
+
if nft list table inet lakebox >/dev/null 2>&1; then return 0; fi
|
|
105
|
+
nft -f - <<NFT
|
|
106
|
+
table inet lakebox {
|
|
107
|
+
set allowed {
|
|
108
|
+
typeof meta skuid . tcp dport
|
|
109
|
+
}
|
|
110
|
+
chain output {
|
|
111
|
+
type filter hook output priority 0; policy accept;
|
|
112
|
+
oifname "lo" tcp dport $port_min-$port_max meta skuid { 0, $agent_uid } accept
|
|
113
|
+
oifname "lo" tcp dport $port_min-$port_max meta skuid . tcp dport @allowed accept
|
|
114
|
+
oifname "lo" tcp dport $port_min-$port_max drop
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
NFT
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
# Recreates one account whose persistent home already exists. The UID is read
|
|
121
|
+
# from the persistent directory so file ownership survives every restart.
|
|
122
|
+
restore_account() {
|
|
123
|
+
hash=$1
|
|
124
|
+
persist=$users/$hash
|
|
125
|
+
name=lakebox-$hash
|
|
126
|
+
uid=$(stat -c %u "$persist")
|
|
127
|
+
existing=$(getent passwd "$uid" | cut -d: -f1 || true)
|
|
128
|
+
if [ -n "$existing" ] && [ "$existing" != "$name" ]; then
|
|
129
|
+
echo "UID $uid for $name is already used by $existing" >&2
|
|
130
|
+
return 1
|
|
131
|
+
fi
|
|
132
|
+
if ! getent group "$name" >/dev/null; then groupadd --gid "$uid" "$name"; fi
|
|
133
|
+
if ! getent passwd "$name" >/dev/null; then
|
|
134
|
+
useradd --uid "$uid" --gid "$uid" --no-create-home --home-dir "/home/$name" \
|
|
135
|
+
--shell /bin/bash "$name" 2>/dev/null
|
|
136
|
+
fi
|
|
137
|
+
passwd --lock "$name" >/dev/null 2>&1 || true
|
|
138
|
+
ln -sfn "$persist" "/home/$name"
|
|
139
|
+
install -d -m 0700 -o "$uid" -g "$uid" "$run_root/$hash"
|
|
140
|
+
for definition in "$persist"/.dbx-tools/services/*/service.json; do
|
|
141
|
+
[ -f "$definition" ] || continue
|
|
142
|
+
port=$(python3 -c 'import json, sys; print(int(json.load(open(sys.argv[1]))["port"]))' "$definition") || continue
|
|
143
|
+
allow_port "$uid" "$port"
|
|
144
|
+
done
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
allow_port() {
|
|
148
|
+
valid_number "$1" && valid_number "$2" || { echo "invalid port arguments" >&2; return 2; }
|
|
149
|
+
[ "$2" -ge "$port_min" ] && [ "$2" -le "$port_max" ] || {
|
|
150
|
+
echo "port $2 is outside the Lakebox service range" >&2
|
|
151
|
+
return 2
|
|
152
|
+
}
|
|
153
|
+
ensure_firewall
|
|
154
|
+
nft add element inet lakebox allowed "{ $1 . $2 }"
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
command=${1:-}
|
|
158
|
+
case "$command" in
|
|
159
|
+
bootstrap)
|
|
160
|
+
shift
|
|
161
|
+
with_lock
|
|
162
|
+
ensure_layout
|
|
163
|
+
for helper in "$@"; do
|
|
164
|
+
install_helper "${helper%%=*}" "${helper#*=}"
|
|
165
|
+
done
|
|
166
|
+
ensure_bundle
|
|
167
|
+
ensure_ttyd
|
|
168
|
+
ensure_firewall
|
|
169
|
+
for persist in "$users"/*; do
|
|
170
|
+
[ -d "$persist" ] || continue
|
|
171
|
+
hash=$(basename "$persist")
|
|
172
|
+
valid_hash "$hash" || continue
|
|
173
|
+
if restore_account "$hash"; then
|
|
174
|
+
echo "__LAKEBOX_USER__=$hash"
|
|
175
|
+
else
|
|
176
|
+
echo "skipped $hash" >&2
|
|
177
|
+
fi
|
|
178
|
+
done
|
|
179
|
+
echo "__LAKEBOX_BOOTSTRAP__=ready"
|
|
180
|
+
;;
|
|
181
|
+
ensure-user)
|
|
182
|
+
hash=${2:-}
|
|
183
|
+
uid=${3:-}
|
|
184
|
+
digest=${4:-}
|
|
185
|
+
valid_hash "$hash" && valid_number "$uid" || { echo "invalid user arguments" >&2; exit 2; }
|
|
186
|
+
case "$digest" in ''|*[!0-9a-f]*) echo "invalid identity digest" >&2; exit 2 ;; esac
|
|
187
|
+
with_lock
|
|
188
|
+
ensure_layout
|
|
189
|
+
marker=$identities/$hash
|
|
190
|
+
if [ -f "$marker" ] && [ "$(cat "$marker")" != "$digest" ]; then
|
|
191
|
+
echo "Lakebox user hash $hash collides with another identity" >&2
|
|
192
|
+
exit 3
|
|
193
|
+
fi
|
|
194
|
+
persist=$users/$hash
|
|
195
|
+
if [ -d "$persist" ]; then
|
|
196
|
+
owner=$(stat -c %u "$persist")
|
|
197
|
+
if [ "$owner" != "$uid" ]; then
|
|
198
|
+
echo "Lakebox user $hash home is owned by UID $owner, expected $uid" >&2
|
|
199
|
+
exit 3
|
|
200
|
+
fi
|
|
201
|
+
else
|
|
202
|
+
existing=$(getent passwd "$uid" | cut -d: -f1 || true)
|
|
203
|
+
if [ -n "$existing" ] && [ "$existing" != "lakebox-$hash" ]; then
|
|
204
|
+
echo "Derived UID $uid for lakebox-$hash is already used by $existing" >&2
|
|
205
|
+
exit 3
|
|
206
|
+
fi
|
|
207
|
+
install -d -m 0700 -o "$uid" -g "$uid" "$persist"
|
|
208
|
+
fi
|
|
209
|
+
printf '%s\n' "$digest" >"$marker"
|
|
210
|
+
chmod 0600 "$marker"
|
|
211
|
+
restore_account "$hash"
|
|
212
|
+
;;
|
|
213
|
+
allow-port)
|
|
214
|
+
allow_port "${2:-}" "${3:-}"
|
|
215
|
+
;;
|
|
216
|
+
desktop-prerequisites)
|
|
217
|
+
with_lock
|
|
218
|
+
sh "$runtime/bin/desktop-prerequisites"
|
|
219
|
+
;;
|
|
220
|
+
metrics)
|
|
221
|
+
# Machine-level counters only; never reads process tables.
|
|
222
|
+
printf 'cores %s\n' "$(nproc)"
|
|
223
|
+
head -n 1 /proc/stat
|
|
224
|
+
awk '{ print "load", $1 }' /proc/loadavg
|
|
225
|
+
awk '/^MemTotal:|^MemAvailable:/ { print $1, $2 }' /proc/meminfo
|
|
226
|
+
df -B1 --output=size,used "$agent_home" | awk 'NR == 2 { print "storage", $1, $2 }'
|
|
227
|
+
awk -F'[: ]+' 'NR > 2 && $2 != "lo" { rx += $3; tx += $11 }
|
|
228
|
+
END { print "network", rx + 0, tx + 0 }' /proc/net/dev
|
|
229
|
+
;;
|
|
230
|
+
*)
|
|
231
|
+
echo "usage: lakebox-admin bootstrap [name=base64...]|ensure-user|allow-port|desktop-prerequisites|metrics" >&2
|
|
232
|
+
exit 2
|
|
233
|
+
;;
|
|
234
|
+
esac
|