Files
mward4 a6f8e67976 VShell 5.0.1-3985 container image
Build the image from the vendor vshell-https .deb on ubuntu:24.04 (required:
GLIBC_2.38 / GLIBCXX_3.4.32). Contains the Dockerfile, the entrypoint that
starts and supervises the three VShell daemons without systemd, the
no-container-runtime build/validate/push tooling used to publish the image, and
the vendored .deb (sha256 pinned).

Published as gitea.milesward.dev/mward4/vshell:5.0.1-3985 and :latest.
Manifest digest sha256:8b0ff00be981fa05f1e4a4e4ff5307b6c7e5e61b487d4fbe7e02fefb2a04659f.
2026-10-05 12:15:20 -04:00

102 lines
3.6 KiB
Bash
Executable File

#!/usr/bin/env bash
# Validate a VShell container rootfs without a container runtime.
#
# Runs the vendor binaries, checks the configuration, then starts the image's
# real entrypoint (docker/entrypoint.sh) inside a user-namespace chroot on
# high ports and probes every listener from the outside.
#
# Usage: tools/validate.sh [/tmp/vshell-rootfs]
#
# Exit status 0 means every check passed.
set -uo pipefail
ROOTFS=${1:-/tmp/vshell-rootfs}
CFG=/tmp/validate_config
CERT=/tmp/validate.p12
SSH_PORT=2222 FTPS_PORT=2121 FTPSI_PORT=2990 HTTP_PORT=8080 HTTPS_PORT=8443
FAILED=0
say() { printf '%s\n' "$*"; }
fail() { printf 'FAIL: %s\n' "$*"; FAILED=1; }
[ -x "$ROOTFS/usr/local/sbin/vshelld" ] || { say "no VShell install under $ROOTFS"; exit 1; }
rm -f "$ROOTFS$CFG" "$ROOTFS$CERT"
# The chroot needs /dev entries for the daemons and openssl to behave normally.
in_ns() {
unshare -rm sh -c "
for d in null zero random urandom tty; do
[ -e $ROOTFS/dev/\$d ] || touch $ROOTFS/dev/\$d
mount --bind /dev/\$d $ROOTFS/dev/\$d
done
$1
"
}
say '== binaries, configuration and license'
in_ns "chroot $ROOTFS /bin/bash -c '
set -e
/usr/local/sbin/vshelld -version
/usr/local/sbin/vshell-httpsd -version
/usr/local/sbin/vshell-ftpsd -version
/usr/local/sbin/vshelld -test_config -f /usr/local/etc/vshelld_config
/usr/local/sbin/vshelld -license
'" || fail 'version/config/license checks'
say '== starting services through the image entrypoint'
in_ns "chroot $ROOTFS /bin/bash -c '
exec env VSHELL_CONFIG=$CFG VSHELL_TLS_P12=$CERT \
VSHELL_SSH_ADDR=0.0.0.0:$SSH_PORT VSHELL_HTTPS_ADDR=0.0.0.0:$HTTPS_PORT \
VSHELL_HTTP_ADDR=0.0.0.0:$HTTP_PORT VSHELL_FTPS_EXPLICIT_ADDR=0.0.0.0:$FTPS_PORT \
VSHELL_FTPS_IMPLICIT_ADDR=0.0.0.0:$FTPSI_PORT \
/usr/local/bin/vshell-entrypoint
'" >/tmp/vshell-validate.log 2>&1 &
sleep 8
sed 's/^/ entrypoint: /' /tmp/vshell-validate.log
banner() { # port, label
local port=$1 label=$2 line
if ! exec 3<>"/dev/tcp/127.0.0.1/$port" 2>/dev/null; then fail "$label ($port): connection refused"; return; fi
if read -t 3 -r line <&3; then say "ok $label ($port): $line"; else fail "$label ($port): no banner"; fi
exec 3<&- 3>&-
}
banner "$SSH_PORT" 'SSH '
banner "$FTPS_PORT" 'FTPS '
http_probe() {
local port=$1 line
if ! exec 3<>"/dev/tcp/127.0.0.1/$port" 2>/dev/null; then fail "HTTP ($port): connection refused"; return; fi
printf 'GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n' >&3
if read -t 4 -r line <&3; then say "ok HTTP ($port): $line"; else fail "HTTP ($port): no response"; fi
exec 3<&- 3>&-
}
http_probe "$HTTP_PORT"
if command -v node >/dev/null; then
node -e '
const tls = require("tls");
const port = Number(process.argv[1]);
const s = tls.connect({ port, host: "127.0.0.1", rejectUnauthorized: false }, () => {
const c = s.getPeerCertificate();
console.log(`ok TLS (${port}): ${s.getProtocol()} CN=${c.subject && c.subject.CN}`);
s.destroy();
});
s.on("error", (e) => { console.log(`FAIL: TLS (${port}): ${e.message}`); process.exitCode = 1; });
s.setTimeout(6000, () => { console.log(`FAIL: TLS (${port}): timeout`); s.destroy(); process.exitCode = 1; });
' "$HTTPS_PORT" || FAILED=1
else
say 'skip TLS probe (node not available)'
fi
say '== stopping'
for p in $(ls /proc | grep -E '^[0-9]+$'); do
[ -r "/proc/$p/cmdline" ] || continue
c=$(tr '\0' ' ' <"/proc/$p/cmdline" 2>/dev/null) || continue
case "$c" in /usr/local/sbin/*) kill "$p" 2>/dev/null && say " killed pid $p";; esac
done
wait 2>/dev/null
if [ "$FAILED" -eq 0 ]; then say '== ALL CHECKS PASSED'; else say '== VALIDATION FAILED'; fi
exit "$FAILED"