Skip to content

Commit 62dd5ed

Browse files
committed
fix(snap): pass config preflight and detect the mTLS gateway reliably
An explicit [openshell.gateway.mtls_auth] table fails config preflight, which validates mTLS auth before the local TLS bundle supplies the client CA. Write a default that pins the Docker driver instead; with the wrapper's TLS bundle the gateway requires client certificates and enables mTLS user auth automatically, as the native packages do. The mTLS gateway rejects TLS handshakes without a client certificate, and it still answers plaintext loopback HTTP for sandbox service routing, so the installer could misdetect it as a legacy plaintext gateway. Probe HTTPS with the root-owned client bundle, and treat a gateway as legacy only when a plaintext gRPC Health call succeeds. Signed-off-by: Drew Newberry <anewberry@nvidia.com>
1 parent dfff5b6 commit 62dd5ed

5 files changed

Lines changed: 66 additions & 15 deletions

File tree

‎install.sh‎

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1331,24 +1331,30 @@ register_snap_gateway() {
13311331
}
13321332

13331333
# Wait for the snap gateway and record its scheme in SNAP_GATEWAY_SCHEME.
1334-
# Current revisions serve mTLS; earlier revisions serve plaintext HTTP. Probe
1335-
# HTTPS first because a TLS gateway also answers plaintext loopback requests
1336-
# for sandbox service routing.
1334+
# Current revisions require a client certificate during the TLS handshake, so
1335+
# probe HTTPS with the root-owned client bundle. Earlier revisions serve
1336+
# plaintext gRPC; a TLS gateway also answers plaintext loopback HTTP for
1337+
# sandbox service routing, so only a successful plaintext gRPC Health call
1338+
# identifies a legacy gateway.
13371339
wait_for_snap_gateway_listener() {
13381340
_timeout="${OPENSHELL_INSTALL_GATEWAY_TIMEOUT:-30}"
13391341
_elapsed=0
13401342
_last_output=""
1343+
_tls_dir="${OPENSHELL_SNAP_TLS_DIR:-/var/snap/openshell/common/tls}"
13411344
_probe_url="https://127.0.0.1:${LOCAL_GATEWAY_PORT}/"
13421345

13431346
info "waiting for local gateway listener to become reachable..."
13441347
while [ "$_elapsed" -lt "$_timeout" ]; do
1345-
# The probe only checks reachability; the CLI verifies the gateway CA.
1346-
if _last_output="$(curl -sS -k --max-time 2 -o /dev/null "$_probe_url" 2>&1)"; then
1348+
if _last_output="$(as_root curl -sS --max-time 2 \
1349+
--cacert "${_tls_dir}/ca.crt" \
1350+
--cert "${_tls_dir}/client/tls.crt" \
1351+
--key "${_tls_dir}/client/tls.key" \
1352+
-o /dev/null "$_probe_url" 2>&1)"; then
13471353
SNAP_GATEWAY_SCHEME=https
13481354
info "local gateway listener is reachable"
13491355
return 0
13501356
fi
1351-
if curl -sS --max-time 2 -o /dev/null "http://127.0.0.1:${LOCAL_GATEWAY_PORT}/" >/dev/null 2>&1; then
1357+
if [ "$(snap_legacy_grpc_health_status)" = "200" ]; then
13521358
SNAP_GATEWAY_SCHEME=http
13531359
info "local gateway listener is reachable"
13541360
return 0
@@ -1362,6 +1368,16 @@ wait_for_snap_gateway_listener() {
13621368
error "local gateway listener did not become reachable at ${_probe_url} within ${_timeout}s"
13631369
}
13641370

1371+
# Print the HTTP status of an empty plaintext gRPC Health call.
1372+
snap_legacy_grpc_health_status() {
1373+
printf '\000\000\000\000\000' |
1374+
curl -s --max-time 2 --http2-prior-knowledge -o /dev/null -w '%{http_code}' \
1375+
-X POST -H 'content-type: application/grpc' -H 'te: trailers' \
1376+
--data-binary @- \
1377+
"http://127.0.0.1:${LOCAL_GATEWAY_PORT}/openshell.v1.OpenShell/Health" 2>/dev/null ||
1378+
true
1379+
}
1380+
13651381
install_linux_snap() {
13661382
require_cmd snap
13671383
set_linux_target_runtime_dir

‎snap/hooks/install‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@
44

55
# Bootstrap the snap gateway config. Operator-owned configs are never touched,
66
# except that the exact config written by earlier snap revisions, which allowed
7-
# unauthenticated local access, is replaced with the mTLS default. The
8-
# post-refresh hook runs this script too so existing installs are migrated.
7+
# unauthenticated local access, is replaced with the default. The post-refresh
8+
# hook runs this script too so existing installs are migrated.
9+
#
10+
# The default pins the Docker driver and leaves user auth unset: with the
11+
# wrapper's local TLS bundle, the gateway requires client certificates and
12+
# enables mTLS user auth automatically. Setting mtls_auth explicitly would fail
13+
# config preflight, which runs before the local TLS defaults are applied.
914

1015
set -eu
1116

@@ -41,9 +46,7 @@ cat >"$temporary_file" <<'CONFIG'
4146
version = 2
4247
4348
[openshell.gateway]
44-
45-
[openshell.gateway.mtls_auth]
46-
enabled = true
49+
compute_driver = "docker"
4750
CONFIG
4851

4952
if [ "$replace" = true ]; then

‎tasks/scripts/test-install-sh.sh‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,40 @@ if ! grep -Fq "without client authentication" "$err"; then
490490
exit 1
491491
fi
492492

493+
assert_snap_listener_scheme() {
494+
local name=$1
495+
local https_ok=$2
496+
local grpc_status=$3
497+
local expected=$4
498+
local actual
499+
500+
actual="$(
501+
as_root() { "$@"; }
502+
curl() {
503+
case " $* " in
504+
*" --cert "*) [ "$https_ok" = "1" ] ;;
505+
*) return 1 ;;
506+
esac
507+
}
508+
snap_legacy_grpc_health_status() { printf '%s' "$grpc_status"; }
509+
sleep() { :; }
510+
info() { :; }
511+
dump_local_gateway_diagnostics() { :; }
512+
error() { printf 'timeout\n'; exit 0; }
513+
OPENSHELL_INSTALL_GATEWAY_TIMEOUT=2
514+
wait_for_snap_gateway_listener 2>/dev/null
515+
printf '%s\n' "${SNAP_GATEWAY_SCHEME:-none}"
516+
)"
517+
if [ "$actual" != "$expected" ]; then
518+
echo "FAIL: ${name}: expected ${expected}, got ${actual}" >&2
519+
exit 1
520+
fi
521+
}
522+
523+
assert_snap_listener_scheme "mTLS gateway accepts the client bundle" 1 404 https
524+
assert_snap_listener_scheme "legacy gateway answers plaintext gRPC" 0 200 http
525+
assert_snap_listener_scheme "plaintext service routing is not a legacy gateway" 0 404 timeout
526+
493527
snap_tls_src="${tmpdir}/snap-tls"
494528
mkdir -p "${snap_tls_src}/client"
495529
printf 'ca\n' >"${snap_tls_src}/ca.crt"

‎tasks/scripts/test-packaging-assets.sh‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ if [[ ! -x "$snap_install_hook" ]]; then
123123
echo "FAIL: Snap install hook must be executable" >&2
124124
exit 1
125125
fi
126-
assert_contains "$snap_install_hook" '[openshell.gateway.mtls_auth]'
126+
assert_contains "$snap_install_hook" 'compute_driver = "docker"'
127127
if [[ ! -x "$(dirname "$snap_install_hook")/post-refresh" ]]; then
128128
echo "FAIL: Snap post-refresh hook must be executable" >&2
129129
exit 1

‎tasks/scripts/test-snap-install-hook.sh‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ cat >"$expected" <<'EOF'
1616
version = 2
1717
1818
[openshell.gateway]
19-
20-
[openshell.gateway.mtls_auth]
21-
enabled = true
19+
compute_driver = "docker"
2220
EOF
2321

2422
legacy="${work}/legacy.toml"

0 commit comments

Comments
 (0)