diff --git a/e2e/rust/e2e-podman.sh b/e2e/rust/e2e-podman.sh index c82891338..b352a8e18 100755 --- a/e2e/rust/e2e-podman.sh +++ b/e2e/rust/e2e-podman.sh @@ -4,7 +4,7 @@ # Run the Rust e2e suite against a standalone gateway running the bundled Podman # compute driver. Set OPENSHELL_GATEWAY_ENDPOINT=http://host:port to reuse an -# existing plaintext gateway instead of starting an ephemeral one. +# existing gateway instead of starting an ephemeral one. set -euo pipefail diff --git a/e2e/support/gateway-common.sh b/e2e/support/gateway-common.sh index d8acbd191..2f8a2c141 100644 --- a/e2e/support/gateway-common.sh +++ b/e2e/support/gateway-common.sh @@ -34,6 +34,22 @@ e2e_pick_port() { python3 -c 'import socket; s=socket.socket(); s.bind(("",0)); print(s.getsockname()[1]); s.close()' } +e2e_generate_pki() { + local gateway_bin=$1 + local pki_dir=$2 + shift 2 + # Remaining args are extra --server-san values (e.g. host.containers.internal). + # host.docker.internal and localhost are already in the default SAN list. + + local san_args=() + san_args+=(--server-san host.openshell.internal) + for san in "$@"; do + san_args+=(--server-san "${san}") + done + + "${gateway_bin}" generate-certs --output-dir "${pki_dir}" "${san_args[@]}" +} + e2e_register_plaintext_gateway() { local config_home=$1 local name=$2 @@ -63,9 +79,9 @@ e2e_register_mtls_gateway() { local gateway_config_dir="${config_home}/openshell/gateways/${name}" mkdir -p "${gateway_config_dir}/mtls" - cp "${pki_dir}/ca.crt" "${gateway_config_dir}/mtls/ca.crt" - cp "${pki_dir}/client.crt" "${gateway_config_dir}/mtls/tls.crt" - cp "${pki_dir}/client.key" "${gateway_config_dir}/mtls/tls.key" + cp "${pki_dir}/ca.crt" "${gateway_config_dir}/mtls/ca.crt" + cp "${pki_dir}/client/tls.crt" "${gateway_config_dir}/mtls/tls.crt" + cp "${pki_dir}/client/tls.key" "${gateway_config_dir}/mtls/tls.key" cat >"${gateway_config_dir}/metadata.json" </dev/null 2>&1; then echo "ERROR: docker daemon is not reachable (docker info failed)" >&2 exit 2 fi -if ! command -v openssl >/dev/null 2>&1; then - echo "ERROR: openssl is required to generate ephemeral PKI" >&2 - exit 2 -fi if [ "${GPU_MODE}" = "1" ]; then DOCKER_CDI_SPEC_DIRS="$(docker info --format '{{json .CDISpecDirs}}' 2>/dev/null || true)" if [ -z "${DOCKER_CDI_SPEC_DIRS}" ] \ @@ -390,41 +386,7 @@ if ! docker image inspect "${SANDBOX_IMAGE}" >/dev/null 2>&1; then fi PKI_DIR="${WORKDIR}/pki" -mkdir -p "${PKI_DIR}" -cd "${PKI_DIR}" - -cat > openssl.cnf <<'EOF' -[req] -distinguished_name = dn -prompt = no -[dn] -CN = openshell-server -[san_server] -subjectAltName = @alt_server -[alt_server] -DNS.1 = localhost -DNS.2 = host.openshell.internal -DNS.3 = host.docker.internal -IP.1 = 127.0.0.1 -IP.2 = ::1 -[san_client] -subjectAltName = DNS:openshell-client -EOF - -openssl req -x509 -newkey rsa:2048 -nodes -days 30 \ - -keyout ca.key -out ca.crt -subj "/CN=openshell-e2e-ca" >/dev/null 2>&1 - -openssl req -newkey rsa:2048 -nodes -keyout server.key -out server.csr \ - -config openssl.cnf >/dev/null 2>&1 -openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \ - -out server.crt -days 30 -extfile openssl.cnf -extensions san_server >/dev/null 2>&1 - -openssl req -newkey rsa:2048 -nodes -keyout client.key -out client.csr \ - -subj "/CN=openshell-client" >/dev/null 2>&1 -openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \ - -out client.crt -days 30 -extfile openssl.cnf -extensions san_client >/dev/null 2>&1 - -cd "${ROOT}" +e2e_generate_pki "${GATEWAY_BIN}" "${PKI_DIR}" HOST_PORT=$(e2e_pick_port) STATE_DIR="${WORKDIR}/state" @@ -473,8 +435,8 @@ GATEWAY_CONFIG="${STATE_DIR}/gateway.toml" printf 'default_image = %s\n' "$(toml_string "${SANDBOX_IMAGE}")" printf 'image_pull_policy = "IfNotPresent"\n' printf 'guest_tls_ca = %s\n' "$(toml_string "${PKI_DIR}/ca.crt")" - printf 'guest_tls_cert = %s\n' "$(toml_string "${PKI_DIR}/client.crt")" - printf 'guest_tls_key = %s\n' "$(toml_string "${PKI_DIR}/client.key")" + printf 'guest_tls_cert = %s\n' "$(toml_string "${PKI_DIR}/client/tls.crt")" + printf 'guest_tls_key = %s\n' "$(toml_string "${PKI_DIR}/client/tls.key")" # DOCKER_SUPERVISOR_ARGS holds either ("--docker-supervisor-bin" "") # or ("--docker-supervisor-image" ""); both map to TOML keys on # the docker driver config. @@ -498,8 +460,8 @@ GATEWAY_ARGS=( --bind-address 0.0.0.0 --port "${HOST_PORT}" --drivers docker - --tls-cert "${PKI_DIR}/server.crt" - --tls-key "${PKI_DIR}/server.key" + --tls-cert "${PKI_DIR}/server/tls.crt" + --tls-key "${PKI_DIR}/server/tls.key" --tls-client-ca "${PKI_DIR}/ca.crt" --db-url "sqlite:${STATE_DIR}/gateway.db?mode=rwc" ) diff --git a/e2e/with-podman-gateway.sh b/e2e/with-podman-gateway.sh index 727737d25..d3b93e51a 100755 --- a/e2e/with-podman-gateway.sh +++ b/e2e/with-podman-gateway.sh @@ -11,8 +11,8 @@ # - OPENSHELL_GATEWAY_ENDPOINT=http://host:port: # Use the existing plaintext gateway endpoint and run the command. # -# Podman e2e currently uses plaintext gateway traffic. The Podman driver does -# not yet inject gateway mTLS client materials into sandbox containers. +# HTTPS endpoint-only mode is intentionally unsupported here. Use a named +# gateway config when mTLS materials are needed. set -euo pipefail @@ -277,12 +277,12 @@ if [ -n "${OPENSHELL_GATEWAY_ENDPOINT:-}" ]; then case "${OPENSHELL_GATEWAY_ENDPOINT}" in http://*) ;; https://*) - echo "ERROR: OPENSHELL_GATEWAY_ENDPOINT endpoint mode is HTTP-only for Podman e2e." >&2 - echo " Podman e2e does not yet support sandbox mTLS client material injection." >&2 + echo "ERROR: OPENSHELL_GATEWAY_ENDPOINT endpoint mode is HTTP-only for e2e." >&2 + echo " Register a named gateway with mTLS config instead of using a raw HTTPS endpoint." >&2 exit 2 ;; *) - echo "ERROR: OPENSHELL_GATEWAY_ENDPOINT must start with http:// for Podman e2e endpoint mode." >&2 + echo "ERROR: OPENSHELL_GATEWAY_ENDPOINT must start with http:// for e2e endpoint mode." >&2 exit 2 ;; esac @@ -328,6 +328,9 @@ if ! podman_cmd image exists "${SANDBOX_IMAGE}" 2>/dev/null; then podman_cmd pull "${SANDBOX_IMAGE}" fi +PKI_DIR="${WORKDIR}/pki" +e2e_generate_pki "${GATEWAY_BIN}" "${PKI_DIR}" "host.containers.internal" + HOST_PORT=$(e2e_pick_port) HEALTH_PORT=$(e2e_pick_port) STATE_DIR="${WORKDIR}/state" @@ -366,6 +369,9 @@ GATEWAY_CONFIG="${STATE_DIR}/gateway.toml" printf 'default_image = %s\n' "$(toml_string "${SANDBOX_IMAGE}")" printf 'image_pull_policy = "missing"\n' printf 'supervisor_image = %s\n' "$(toml_string "${SUPERVISOR_IMAGE}")" + printf 'guest_tls_ca = %s\n' "$(toml_string "${PKI_DIR}/ca.crt")" + printf 'guest_tls_cert = %s\n' "$(toml_string "${PKI_DIR}/client/tls.crt")" + printf 'guest_tls_key = %s\n' "$(toml_string "${PKI_DIR}/client/tls.key")" # The in-process Podman driver reads `socket_path` from TOML only — the # OPENSHELL_PODMAN_SOCKET env var is honoured by the standalone driver # binary, not the in-process driver used here. Pin the socket to the one @@ -382,7 +388,9 @@ GATEWAY_ARGS=( --port "${HOST_PORT}" --health-port "${HEALTH_PORT}" --drivers podman - --disable-tls + --tls-cert "${PKI_DIR}/server/tls.crt" + --tls-key "${PKI_DIR}/server/tls.key" + --tls-client-ca "${PKI_DIR}/ca.crt" --db-url "sqlite:${STATE_DIR}/gateway.db?mode=rwc" --log-level info ) @@ -401,12 +409,13 @@ GATEWAY_PID=$! printf '%s\n' "${GATEWAY_PID}" >"${GATEWAY_PID_FILE}" GATEWAY_NAME="openshell-e2e-podman-${HOST_PORT}" -CLI_GATEWAY_ENDPOINT="http://127.0.0.1:${HOST_PORT}" -e2e_register_plaintext_gateway \ +CLI_GATEWAY_ENDPOINT="https://127.0.0.1:${HOST_PORT}" +e2e_register_mtls_gateway \ "${XDG_CONFIG_HOME}" \ "${GATEWAY_NAME}" \ "${CLI_GATEWAY_ENDPOINT}" \ - "${HOST_PORT}" + "${HOST_PORT}" \ + "${PKI_DIR}" export OPENSHELL_GATEWAY="${GATEWAY_NAME}" export OPENSHELL_PROVISION_TIMEOUT="${OPENSHELL_PROVISION_TIMEOUT:-300}"