Gate image builds on socktop CLI compatibility; fix agent probe; caps for entrypoint
- CI now builds the image locally on the (arm64) runner and runs scripts/verify-image-socktop-flags.sh before pushing: every --flag the restricted/session shells pass must be documented by the socktop binary actually installed in the image. Catches the 0.3.9 failure class (cached apt layer shipping a pre-flag socktop) at build time. - Manifest adds CHOWN/DAC_OVERRIDE/FOWNER alongside SETUID/SETGID: with ALL dropped, uid 0 has no implicit file privilege and prepare_demo_home crash-looped on the demo-owned 700 home dir. Sessions still run with zero capabilities via setpriv. - Agent liveness probe uses /proc instead of kill -0: without CAP_KILL even root gets EPERM signalling the socktop-user agent, so the old check false-alarmed in the pod logs. - 0.3.11 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -84,6 +84,25 @@ jobs:
|
|||||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||||
|
|
||||||
|
# Build into the runner's docker first (runner is arm64, so the image
|
||||||
|
# runs natively), gate on the CLI-compatibility check, and only then
|
||||||
|
# push. The layer cache makes the second build a no-op.
|
||||||
|
- name: Build Docker image (local, for verification)
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile
|
||||||
|
platforms: linux/arm64
|
||||||
|
push: false
|
||||||
|
load: true
|
||||||
|
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:candidate
|
||||||
|
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache
|
||||||
|
|
||||||
|
- name: Verify installed socktop understands the shells' flags
|
||||||
|
run: |
|
||||||
|
chmod +x scripts/verify-image-socktop-flags.sh
|
||||||
|
scripts/verify-image-socktop-flags.sh ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:candidate
|
||||||
|
|
||||||
- name: Build and push Docker image
|
- name: Build and push Docker image
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
|
|||||||
Generated
+1
-1
@@ -2609,7 +2609,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webterm"
|
name = "webterm"
|
||||||
version = "0.3.10"
|
version = "0.3.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix",
|
"actix",
|
||||||
"actix-files",
|
"actix-files",
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ documentation = "https://docs.rs/webterm"
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
categories = ["web-programming", "web-programming::websocket", "web-programming::http-server", "command-line-utilities"]
|
categories = ["web-programming", "web-programming::websocket", "web-programming::http-server", "command-line-utilities"]
|
||||||
keywords = ["terminal", "xterm", "websocket", "terminus", "console"]
|
keywords = ["terminal", "xterm", "websocket", "terminus", "console"]
|
||||||
version = "0.3.10"
|
version = "0.3.11"
|
||||||
authors = ["fabian.freyer@physik.tu-berlin.de","jasonpwitty+socktop@proton.me"]
|
authors = ["fabian.freyer@physik.tu-berlin.de","jasonpwitty+socktop@proton.me"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "BSD-3-Clause"
|
license = "BSD-3-Clause"
|
||||||
|
|||||||
@@ -96,8 +96,10 @@ start_socktop_agent() {
|
|||||||
# Give it a moment to start
|
# Give it a moment to start
|
||||||
sleep 1
|
sleep 1
|
||||||
|
|
||||||
# Check if it's running
|
# Check if it's running. /proc, not kill -0: the agent runs as another UID
|
||||||
if kill -0 $AGENT_PID 2>/dev/null; then
|
# and the pod's capability set strips CAP_KILL, so even root gets EPERM
|
||||||
|
# from a probe signal and the check would false-alarm.
|
||||||
|
if [ -d "/proc/$AGENT_PID" ]; then
|
||||||
echo " ✓ socktop-agent is running on port 3001"
|
echo " ✓ socktop-agent is running on port 3001"
|
||||||
else
|
else
|
||||||
echo " ⚠ socktop-agent may have failed to start (check /tmp/socktop-agent.log)"
|
echo " ⚠ socktop-agent may have failed to start (check /tmp/socktop-agent.log)"
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ spec:
|
|||||||
|
|
||||||
containers:
|
containers:
|
||||||
- name: webterm
|
- name: webterm
|
||||||
image: gt.wittyoneoff.com/jason/socktop-webterm:0.3.10
|
image: gt.wittyoneoff.com/jason/socktop-webterm:0.3.11
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
|
|
||||||
command: ["/docker-entrypoint.sh"]
|
command: ["/docker-entrypoint.sh"]
|
||||||
@@ -180,6 +180,15 @@ spec:
|
|||||||
add:
|
add:
|
||||||
- SETUID
|
- SETUID
|
||||||
- SETGID
|
- SETGID
|
||||||
|
# prepare_demo_home (entrypoint.sh) writes into and re-owns
|
||||||
|
# /home/demo, which the image ships as demo-owned 700. With
|
||||||
|
# ALL dropped, uid 0 has no implicit file privilege, so the
|
||||||
|
# three file caps must come back or the entrypoint crashes on
|
||||||
|
# mkdir/chown/chmod. Sessions still get zero caps — session-
|
||||||
|
# shell.sh drops them all via setpriv --inh-caps -all.
|
||||||
|
- CHOWN
|
||||||
|
- DAC_OVERRIDE
|
||||||
|
- FOWNER
|
||||||
readOnlyRootFilesystem: false
|
readOnlyRootFilesystem: false
|
||||||
runAsUser: 0
|
runAsUser: 0
|
||||||
runAsGroup: 0
|
runAsGroup: 0
|
||||||
|
|||||||
Executable
+54
@@ -0,0 +1,54 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Verify that the socktop binary baked into a built image understands every
|
||||||
|
# --flag the restricted/session shells pass to it.
|
||||||
|
#
|
||||||
|
# Why this exists: image 0.3.9 shipped with a cached apt layer holding socktop
|
||||||
|
# 1.60.1, while restricted-shell.sh had started passing --no-kill (added in
|
||||||
|
# 1.60.2). 1.60.1 parsed the unknown flag as the positional websocket URL,
|
||||||
|
# prompted to overwrite the 'local' profile with url "--no-kill", and broke the
|
||||||
|
# demo. This check fails the build whenever the shells and the installed
|
||||||
|
# binary disagree about the CLI.
|
||||||
|
#
|
||||||
|
# Usage: verify-image-socktop-flags.sh IMAGE
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
IMAGE="${1:?usage: verify-image-socktop-flags.sh IMAGE}"
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
# Every --flag appearing on a socktop invocation line in the shells.
|
||||||
|
FLAGS=$(grep -hE '/usr/bin/socktop' docker/restricted-shell.sh docker/session-shell.sh 2>/dev/null |
|
||||||
|
grep -oE -- '--[a-z][a-z-]*' | sort -u)
|
||||||
|
if [ -z "$FLAGS" ]; then
|
||||||
|
echo "ERROR: found no socktop flags to verify — did the shells move?" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The image is arm64-only; pin the platform so the check behaves the same on
|
||||||
|
# the arm64 CI runner and on an amd64 box with qemu binfmt.
|
||||||
|
run_socktop() {
|
||||||
|
docker run --rm --platform linux/arm64 --entrypoint /usr/bin/socktop "$IMAGE" "$@" 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
if ! VERSION=$(run_socktop --version); then
|
||||||
|
echo "ERROR: could not run socktop from ${IMAGE}:" >&2
|
||||||
|
echo "$VERSION" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
HELP=$(run_socktop --help || true)
|
||||||
|
echo "image socktop: ${VERSION}"
|
||||||
|
|
||||||
|
rc=0
|
||||||
|
for flag in $FLAGS; do
|
||||||
|
if printf '%s' "$HELP" | grep -q -- "$flag"; then
|
||||||
|
echo " ok: $flag"
|
||||||
|
else
|
||||||
|
echo " MISSING: installed socktop does not document $flag" >&2
|
||||||
|
rc=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$rc" -ne 0 ]; then
|
||||||
|
echo "FAIL: the image's socktop predates flags the shells pass." >&2
|
||||||
|
echo "Bump SOCKTOP_VERSION in the Dockerfile to a release that has them." >&2
|
||||||
|
fi
|
||||||
|
exit $rc
|
||||||
Reference in New Issue
Block a user