Skip to content

Commit cfb19cf

Browse files
committed
run as kernel user in headless and headful
but retain option to run as root
1 parent 9d8cbf7 commit cfb19cf

10 files changed

Lines changed: 103 additions & 110 deletions

File tree

images/chromium-headful/Dockerfile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,6 @@ COPY images/chromium-headful/supervisor/services/ /etc/supervisor/conf.d/service
177177

178178
# copy the kernel-images API binary built in the builder stage
179179
COPY --from=server-builder /out/kernel-images-api /usr/local/bin/kernel-images-api
180-
ENV WITH_KERNEL_IMAGES_API=false
181180

182181
RUN useradd -m -s /bin/bash kernel
183182

images/chromium-headful/run-docker.sh

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,15 @@ RUN_ARGS=(
3434
--tmpfs /dev/shm:size=2g
3535
-v "$HOST_RECORDINGS_DIR:/recordings"
3636
--memory 8192m
37-
-p 9222:9222 \
38-
-e DISPLAY_NUM=1 \
39-
-e HEIGHT=768 \
40-
-e WIDTH=1024 \
41-
-e RUN_AS_ROOT="$RUN_AS_ROOT" \
37+
-p 9222:9222
38+
-p 444:10001
39+
-e DISPLAY_NUM=1
40+
-e HEIGHT=768
41+
-e WIDTH=1024
42+
-e RUN_AS_ROOT="$RUN_AS_ROOT"
4243
--mount type=bind,src="$FLAGS_FILE",dst=/chromium/flags,ro
4344
)
4445

45-
if [[ "${WITH_KERNEL_IMAGES_API:-}" == "true" ]]; then
46-
RUN_ARGS+=( -p 444:10001 )
47-
RUN_ARGS+=( -e WITH_KERNEL_IMAGES_API=true )
48-
fi
49-
5046
# noVNC vs WebRTC port mapping
5147
if [[ "${ENABLE_WEBRTC:-}" == "true" ]]; then
5248
echo "Running container with WebRTC"

images/chromium-headful/run-unikernel.sh

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,22 +41,17 @@ trap 'rm -rf "$FLAGS_DIR"' EXIT
4141

4242

4343
deploy_args=(
44-
-M 8192
44+
-M 4096
4545
-p 9222:9222/tls
46+
-p 444:10001/tls
4647
-e DISPLAY_NUM=1
4748
-e HEIGHT=768
4849
-e WIDTH=1024
49-
-e HOME=/
5050
-e RUN_AS_ROOT="$RUN_AS_ROOT" \
5151
-v "$volume_name":/chromium
5252
-n "$NAME"
5353
)
5454

55-
if [[ "${WITH_KERNEL_IMAGES_API:-}" == "true" ]]; then
56-
deploy_args+=( -p 444:10001/tls )
57-
deploy_args+=( -e WITH_KERNEL_IMAGES_API=true )
58-
fi
59-
6055
if [[ "${ENABLE_WEBRTC:-}" == "true" ]]; then
6156
echo "Deploying with WebRTC enabled"
6257
kraft cloud inst create --start \

images/chromium-headful/start-chromium.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@ export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/dbus/system_bus_socket"
2424
RUN_AS_ROOT="${RUN_AS_ROOT:-false}"
2525

2626
if [[ "$RUN_AS_ROOT" == "true" ]]; then
27+
echo "Running chromium as root"
2728
exec chromium \
2829
--remote-debugging-port="$INTERNAL_PORT" \
2930
--user-data-dir=/home/kernel/user-data \
3031
--password-store=basic \
3132
--no-first-run \
3233
${CHROMIUM_FLAGS:-}
3334
else
35+
echo "Running chromium as kernel user"
3436
exec runuser -u kernel -- env \
3537
DISPLAY=":1" \
3638
DBUS_SESSION_BUS_ADDRESS="unix:path=/run/dbus/system_bus_socket" \

images/chromium-headful/wrapper.sh

Lines changed: 55 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -211,68 +211,66 @@ if [[ "${ENABLE_WEBRTC:-}" == "true" ]]; then
211211
echo "[wrapper] Port 8080 is open"
212212
fi
213213

214-
if [[ "${WITH_KERNEL_IMAGES_API:-}" == "true" ]]; then
215-
echo "[wrapper] ✨ Starting kernel-images API."
216-
217-
API_PORT="${KERNEL_IMAGES_API_PORT:-10001}"
218-
API_FRAME_RATE="${KERNEL_IMAGES_API_FRAME_RATE:-10}"
219-
API_DISPLAY_NUM="${KERNEL_IMAGES_API_DISPLAY_NUM:-${DISPLAY_NUM:-1}}"
220-
API_MAX_SIZE_MB="${KERNEL_IMAGES_API_MAX_SIZE_MB:-500}"
221-
API_OUTPUT_DIR="${KERNEL_IMAGES_API_OUTPUT_DIR:-/recordings}"
222-
223-
# Start via supervisord (env overrides are read by the service's command)
224-
supervisorctl -c /etc/supervisor/supervisord.conf start kernel-images-api
225-
# close the "--no-sandbox unsupported flag" warning when running as root
226-
# in the unikernel runtime we haven't been able to get chromium to launch as non-root without cryptic crashpad errors
227-
# and when running as root you must use the --no-sandbox flag, which generates a warning
228-
if [[ "${RUN_AS_ROOT:-}" == "true" ]]; then
229-
echo "[wrapper] Running as root, attempting to dismiss the --no-sandbox unsupported flag warning"
230-
if read -r WIDTH HEIGHT <<< "$(xdotool getdisplaygeometry 2>/dev/null)"; then
231-
# Work out an x-coordinate slightly inside the right-hand edge of the
232-
OFFSET_X=$(( WIDTH - 30 ))
233-
if (( OFFSET_X < 0 )); then
234-
OFFSET_X=0
235-
fi
214+
echo "[wrapper] ✨ Starting kernel-images API."
215+
216+
API_PORT="${KERNEL_IMAGES_API_PORT:-10001}"
217+
API_FRAME_RATE="${KERNEL_IMAGES_API_FRAME_RATE:-10}"
218+
API_DISPLAY_NUM="${KERNEL_IMAGES_API_DISPLAY_NUM:-${DISPLAY_NUM:-1}}"
219+
API_MAX_SIZE_MB="${KERNEL_IMAGES_API_MAX_SIZE_MB:-500}"
220+
API_OUTPUT_DIR="${KERNEL_IMAGES_API_OUTPUT_DIR:-/recordings}"
221+
222+
# Start via supervisord (env overrides are read by the service's command)
223+
supervisorctl -c /etc/supervisor/supervisord.conf start kernel-images-api
224+
# close the "--no-sandbox unsupported flag" warning when running as root
225+
# in the unikernel runtime we haven't been able to get chromium to launch as non-root without cryptic crashpad errors
226+
# and when running as root you must use the --no-sandbox flag, which generates a warning
227+
if [[ "${RUN_AS_ROOT:-}" == "true" ]]; then
228+
echo "[wrapper] Running as root, attempting to dismiss the --no-sandbox unsupported flag warning"
229+
if read -r WIDTH HEIGHT <<< "$(xdotool getdisplaygeometry 2>/dev/null)"; then
230+
# Work out an x-coordinate slightly inside the right-hand edge of the
231+
OFFSET_X=$(( WIDTH - 30 ))
232+
if (( OFFSET_X < 0 )); then
233+
OFFSET_X=0
234+
fi
236235

237-
# Wait for kernel-images API port to be ready.
238-
echo "[wrapper] Waiting for kernel-images API port 127.0.0.1:${API_PORT}..."
239-
while ! nc -z 127.0.0.1 "${API_PORT}" 2>/dev/null; do
240-
sleep 0.5
241-
done
242-
echo "[wrapper] Port ${API_PORT} is open"
243-
244-
# Wait for Chromium window to open before dismissing the --no-sandbox warning.
245-
target='New Tab - Chromium'
246-
echo "[wrapper] Waiting for Chromium window \"${target}\" to appear and become active..."
247-
while :; do
248-
win_id=$(xwininfo -root -tree 2>/dev/null | awk -v t="$target" '$0 ~ t {print $1; exit}')
249-
if [[ -n $win_id ]]; then
250-
win_id=${win_id%:}
251-
if xdotool windowactivate --sync "$win_id"; then
252-
echo "[wrapper] Focused window $win_id ($target) on $DISPLAY"
253-
break
254-
fi
236+
# Wait for kernel-images API port to be ready.
237+
echo "[wrapper] Waiting for kernel-images API port 127.0.0.1:${API_PORT}..."
238+
while ! nc -z 127.0.0.1 "${API_PORT}" 2>/dev/null; do
239+
sleep 0.5
240+
done
241+
echo "[wrapper] Port ${API_PORT} is open"
242+
243+
# Wait for Chromium window to open before dismissing the --no-sandbox warning.
244+
target='New Tab - Chromium'
245+
echo "[wrapper] Waiting for Chromium window \"${target}\" to appear and become active..."
246+
while :; do
247+
win_id=$(xwininfo -root -tree 2>/dev/null | awk -v t="$target" '$0 ~ t {print $1; exit}')
248+
if [[ -n $win_id ]]; then
249+
win_id=${win_id%:}
250+
if xdotool windowactivate --sync "$win_id"; then
251+
echo "[wrapper] Focused window $win_id ($target) on $DISPLAY"
252+
break
255253
fi
256-
sleep 0.5
257-
done
258-
259-
# wait... not sure but this just increases the likelihood of success
260-
# without the sleep you often open the live view and see the mouse hovering over the "X" to dismiss the warning, suggesting that it clicked before the warning or chromium appeared
261-
sleep 5
262-
263-
# Attempt to click the warning's close button
264-
echo "[wrapper] Clicking the warning's close button at x=$OFFSET_X y=115"
265-
if curl -s -o /dev/null -X POST \
266-
http://localhost:${API_PORT}/computer/click_mouse \
267-
-H "Content-Type: application/json" \
268-
-d "{\"x\":${OFFSET_X},\"y\":115}"; then
269-
echo "[wrapper] Successfully clicked the warning's close button"
270-
else
271-
echo "[wrapper] Failed to click the warning's close button" >&2
272254
fi
255+
sleep 0.5
256+
done
257+
258+
# wait... not sure but this just increases the likelihood of success
259+
# without the sleep you often open the live view and see the mouse hovering over the "X" to dismiss the warning, suggesting that it clicked before the warning or chromium appeared
260+
sleep 5
261+
262+
# Attempt to click the warning's close button
263+
echo "[wrapper] Clicking the warning's close button at x=$OFFSET_X y=115"
264+
if curl -s -o /dev/null -X POST \
265+
http://localhost:${API_PORT}/computer/click_mouse \
266+
-H "Content-Type: application/json" \
267+
-d "{\"x\":${OFFSET_X},\"y\":115}"; then
268+
echo "[wrapper] Successfully clicked the warning's close button"
273269
else
274-
echo "[wrapper] xdotool failed to obtain display geometry; skipping sandbox warning dismissal." >&2
270+
echo "[wrapper] Failed to click the warning's close button" >&2
275271
fi
272+
else
273+
echo "[wrapper] xdotool failed to obtain display geometry; skipping sandbox warning dismissal." >&2
276274
fi
277275
fi
278276

images/chromium-headless/image/start-chromium.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ if [[ "$RUN_AS_ROOT" == "true" ]]; then
3030
--no-first-run \
3131
${CHROMIUM_FLAGS:-}
3232
else
33+
echo "Running chromium as kernel user"
3334
exec runuser -u kernel -- env \
3435
DISPLAY=":1" \
3536
DBUS_SESSION_BUS_ADDRESS="unix:path=/run/dbus/system_bus_socket" \

images/chromium-headless/image/wrapper.sh

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -222,15 +222,13 @@ for i in {1..100}; do
222222
sleep 0.2
223223
done
224224

225-
if [[ "${WITH_KERNEL_IMAGES_API:-}" == "true" ]]; then
226-
echo "[wrapper] ✨ Starting kernel-images API via supervisord."
227-
supervisorctl -c /etc/supervisor/supervisord.conf start kernel-images-api
228-
API_PORT="${KERNEL_IMAGES_API_PORT:-10001}"
229-
echo "[wrapper] Waiting for kernel-images API on 127.0.0.1:${API_PORT}..."
230-
while ! (echo >/dev/tcp/127.0.0.1/"${API_PORT}") >/dev/null 2>&1; do
231-
sleep 0.5
232-
done
233-
fi
225+
echo "[wrapper] ✨ Starting kernel-images API via supervisord."
226+
supervisorctl -c /etc/supervisor/supervisord.conf start kernel-images-api
227+
API_PORT="${KERNEL_IMAGES_API_PORT:-10001}"
228+
echo "[wrapper] Waiting for kernel-images API on 127.0.0.1:${API_PORT}..."
229+
while ! (echo >/dev/tcp/127.0.0.1/"${API_PORT}") >/dev/null 2>&1; do
230+
sleep 0.5
231+
done
234232

235233
echo "[wrapper] startup complete!"
236234
# Re-enable scale-to-zero once startup has completed (when not under Docker)

images/chromium-headless/run-docker.sh

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,11 @@ RUN_ARGS=(
1515
--privileged
1616
--tmpfs /dev/shm:size=2g
1717
-p 9222:9222
18+
-p 444:10001
19+
-v "$HOST_RECORDINGS_DIR:/recordings"
1820
-e WITH_DOCKER=true
1921
)
2022

21-
if [[ "${WITH_KERNEL_IMAGES_API:-}" == "true" ]]; then
22-
RUN_ARGS+=( -p 444:10001 )
23-
RUN_ARGS+=( -e WITH_KERNEL_IMAGES_API=true )
24-
RUN_ARGS+=( -v "$HOST_RECORDINGS_DIR:/recordings" )
25-
fi
26-
2723
# If a positional argument is given, use it as the entrypoint
2824
ENTRYPOINT_ARG=()
2925
if [[ $# -ge 1 && -n "$1" ]]; then

images/chromium-headless/run-unikernel.sh

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,19 @@ source ../../shared/ensure-common-build-run-vars.sh chromium-headless
88

99
kraft cloud inst rm "$NAME" || true
1010

11+
RUN_AS_ROOT="${RUN_AS_ROOT:-false}"
12+
1113
deploy_args=(
1214
--start
13-
-M 1G
15+
--scale-to-zero idle
16+
--scale-to-zero-cooldown 3000ms
17+
--scale-to-zero-stateful
18+
-M 1024
19+
-e RUN_AS_ROOT="$RUN_AS_ROOT"
1420
-p 9222:9222/tls
15-
--vcpus 1
21+
-p 444:10001/tls
22+
--vcpus 2
1623
-n "$NAME"
1724
)
1825

19-
if [[ "${WITH_KERNEL_IMAGES_API:-}" == "true" ]]; then
20-
deploy_args+=( -p 444:10001/tls )
21-
deploy_args+=( -e WITH_KERNEL_IMAGES_API=true )
22-
fi
23-
2426
kraft cloud inst create "${deploy_args[@]}" "$IMAGE"

server/e2e/e2e_chromium_test.go

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,15 @@ func ensurePlaywrightDeps(t *testing.T) {
8989

9090
func TestChromiumHeadfulUserDataSaving(t *testing.T) {
9191
ensurePlaywrightDeps(t)
92-
runChromiumUserDataSavingFlow(t, headfulImage, containerName, true)
92+
runChromiumUserDataSavingFlow(t, headfulImage, containerName)
9393
}
9494

9595
func TestChromiumHeadlessPersistence(t *testing.T) {
9696
ensurePlaywrightDeps(t)
97-
runChromiumUserDataSavingFlow(t, headlessImage, containerName, true)
97+
runChromiumUserDataSavingFlow(t, headlessImage, containerName)
9898
}
9999

100-
func runChromiumUserDataSavingFlow(t *testing.T, image, containerName string, runAsRoot bool) {
100+
func runChromiumUserDataSavingFlow(t *testing.T, image, containerName string) {
101101
t.Helper()
102102
logger := slog.New(slog.NewTextHandler(t.Output(), &slog.HandlerOptions{
103103
Level: slog.LevelInfo,
@@ -111,7 +111,7 @@ func runChromiumUserDataSavingFlow(t *testing.T, image, containerName string, ru
111111
},
112112
}))
113113
baseCtx := logctx.AddToContext(context.Background(), logger)
114-
logger.Info("[e2e]", "action", "starting chromium cookie saving flow", "image", image, "name", containerName, "runAsRoot", runAsRoot)
114+
logger.Info("[e2e]", "action", "starting chromium cookie saving flow", "image", image, "name", containerName)
115115
if _, err := exec.LookPath("docker"); err != nil {
116116
t.Fatalf("[precheck] docker not available: %v", err)
117117
}
@@ -124,14 +124,6 @@ func runChromiumUserDataSavingFlow(t *testing.T, image, containerName string, ru
124124
t.Fatalf("[setup] failed to stop container %s: %v", containerName, err)
125125
}
126126
env := map[string]string{
127-
"WITH_KERNEL_IMAGES_API": "true",
128-
"RUN_AS_ROOT": fmt.Sprintf("%t", runAsRoot),
129-
"USER": func() string {
130-
if runAsRoot {
131-
return "root"
132-
}
133-
return "kernel"
134-
}(),
135127
"WIDTH": "1024",
136128
"HEIGHT": "768",
137129
"ENABLE_WEBRTC": os.Getenv("ENABLE_WEBRTC"),
@@ -255,6 +247,14 @@ func runChromiumUserDataSavingFlow(t *testing.T, image, containerName string, ru
255247
t.Fatalf("[snapshot] upload cleaned zip: %v", err)
256248
}
257249

250+
// Check file state after uploading
251+
logger.Info("[restart]", "action", "checking file state after uploading")
252+
if err := runCookieDebugScript(ctx, t); err != nil {
253+
logger.Warn("[restart]", "action", "post-stop debug script failed", "error", err)
254+
} else {
255+
logger.Info("[restart]", "action", "post-stop debug script completed")
256+
}
257+
258258
// Verify that the cookie exists in the container's cookies database after upload
259259
logger.Info("[snapshot]", "action", "verifying cookie in container database", "cookieName", cookieName)
260260
if err := verifyCookieInContainerDB(ctx, cookieName); err != nil {
@@ -768,7 +768,13 @@ func uploadUserDataZip(ctx context.Context, zipBytes []byte) error {
768768
return err
769769
}
770770
_, err = client.UploadZipWithBodyWithResponse(ctx, w.FormDataContentType(), &body)
771-
return err
771+
if err != nil {
772+
return err
773+
}
774+
if _, err := execCombinedOutput(ctx, "chown", []string{"-R", "kernel:kernel", "/home/kernel/user-data"}); err != nil {
775+
return err
776+
}
777+
return nil
772778
}
773779

774780
func startChromiumViaAPI(ctx context.Context) error {

0 commit comments

Comments
 (0)