fix(reboot): support windows99 verifier via host
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 1m1s
CD Pipeline / build-and-deploy (push) Successful in 4m28s
CD Pipeline / post-deploy-checks (push) Successful in 1m45s
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 1m1s
CD Pipeline / build-and-deploy (push) Successful in 4m28s
CD Pipeline / post-deploy-checks (push) Successful in 1m45s
This commit is contained in:
@@ -4,6 +4,7 @@ set -u
|
||||
MODE="check"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
TARGET_HOST="${WINDOWS99_HOST:-192.168.0.99}"
|
||||
VIA_HOST="${WINDOWS99_VIA_HOST:-}"
|
||||
CONNECT_TIMEOUT="${WINDOWS99_CONNECT_TIMEOUT:-3}"
|
||||
SSH_TIMEOUT="${WINDOWS99_SSH_TIMEOUT:-3}"
|
||||
REMOTE_VERIFY_TIMEOUT="${WINDOWS99_REMOTE_VERIFY_TIMEOUT:-45}"
|
||||
@@ -43,7 +44,7 @@ if ! is_positive_int "${MAX_AUTH_USERS}"; then
|
||||
fi
|
||||
|
||||
usage() {
|
||||
printf '%s\n' "usage: $0 [--check|--collect] [--host HOST] [--users 'u1 u2'] [--timeout SECONDS] [--max-auth-users N]"
|
||||
printf '%s\n' "usage: $0 [--check|--collect] [--host HOST] [--via-host USER@HOST] [--users 'u1 u2'] [--timeout SECONDS] [--max-auth-users N]"
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
@@ -58,6 +59,10 @@ while [[ $# -gt 0 ]]; do
|
||||
shift
|
||||
TARGET_HOST="${1:-}"
|
||||
;;
|
||||
--via-host)
|
||||
shift
|
||||
VIA_HOST="${1:-}"
|
||||
;;
|
||||
--users)
|
||||
shift
|
||||
# shellcheck disable=SC2206
|
||||
@@ -173,7 +178,7 @@ elif command -v gtimeout >/dev/null 2>&1; then
|
||||
SSH_TIMEOUT_WRAPPER="gtimeout"
|
||||
fi
|
||||
|
||||
SSH_OPTS=(
|
||||
SSH_COMMON_OPTS=(
|
||||
-o BatchMode=yes
|
||||
-o PreferredAuthentications=publickey
|
||||
-o PubkeyAuthentication=yes
|
||||
@@ -186,9 +191,29 @@ SSH_OPTS=(
|
||||
-o LogLevel=ERROR
|
||||
-o StrictHostKeyChecking=no
|
||||
-o UserKnownHostsFile="${KNOWN_HOSTS_FILE}"
|
||||
)
|
||||
SSH_TARGET_OPTS=(
|
||||
"${SSH_COMMON_OPTS[@]}"
|
||||
-p "${SSH_PORT}"
|
||||
)
|
||||
|
||||
quote_words() {
|
||||
printf '%q ' "$@"
|
||||
}
|
||||
|
||||
run_with_timeout_wrapper() {
|
||||
local timeout_seconds="$1"
|
||||
local timeout_wrapper_seconds="$2"
|
||||
shift 2
|
||||
if [[ "${SSH_TIMEOUT_WRAPPER}" == "timeout" ]]; then
|
||||
timeout "${timeout_wrapper_seconds}s" "$@"
|
||||
elif [[ "${SSH_TIMEOUT_WRAPPER}" == "gtimeout" ]]; then
|
||||
gtimeout "${timeout_wrapper_seconds}s" "$@"
|
||||
else
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
|
||||
run_ssh_timed() {
|
||||
local user="$1"
|
||||
local timeout_seconds="$2"
|
||||
@@ -198,12 +223,20 @@ run_ssh_timed() {
|
||||
timeout_seconds="${SSH_TIMEOUT}"
|
||||
timeout_wrapper_seconds="$((SSH_TIMEOUT + 1))"
|
||||
fi
|
||||
if [[ "${SSH_TIMEOUT_WRAPPER}" == "timeout" ]]; then
|
||||
timeout "${timeout_wrapper_seconds}s" ssh "${SSH_OPTS[@]}" -o ConnectTimeout="${timeout_seconds}" "${user}@${TARGET_HOST}" "$@"
|
||||
elif [[ "${SSH_TIMEOUT_WRAPPER}" == "gtimeout" ]]; then
|
||||
gtimeout "${timeout_wrapper_seconds}s" ssh "${SSH_OPTS[@]}" -o ConnectTimeout="${timeout_seconds}" "${user}@${TARGET_HOST}" "$@"
|
||||
local target_ssh=(ssh "${SSH_TARGET_OPTS[@]}" -o ConnectTimeout="${timeout_seconds}" "${user}@${TARGET_HOST}" "$@")
|
||||
if [[ -n "${VIA_HOST}" ]]; then
|
||||
local inner_command
|
||||
inner_command="$(quote_words "${target_ssh[@]}")"
|
||||
run_with_timeout_wrapper \
|
||||
"${timeout_seconds}" \
|
||||
"${timeout_wrapper_seconds}" \
|
||||
ssh "${SSH_COMMON_OPTS[@]}" -o ConnectTimeout="${timeout_seconds}" \
|
||||
"${VIA_HOST}" "bash -lc $(printf '%q' "${inner_command}")"
|
||||
else
|
||||
ssh "${SSH_OPTS[@]}" -o ConnectTimeout="${timeout_seconds}" "${user}@${TARGET_HOST}" "$@"
|
||||
run_with_timeout_wrapper \
|
||||
"${timeout_seconds}" \
|
||||
"${timeout_wrapper_seconds}" \
|
||||
"${target_ssh[@]}"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -292,6 +325,7 @@ printf '%s\n' "schema_version=windows99_vmware_verify_collector_v1"
|
||||
printf '%s\n' "dry_run=${DRY_RUN}"
|
||||
printf '%s\n' "target_host=${TARGET_HOST}"
|
||||
printf '%s\n' "target_host_alias=99"
|
||||
printf '%s\n' "via_host=${VIA_HOST:-none}"
|
||||
printf '%s\n' "connect_timeout_seconds=${CONNECT_TIMEOUT}"
|
||||
printf '%s\n' "ssh_timeout_seconds=${SSH_TIMEOUT}"
|
||||
printf '%s\n' "remote_verify_timeout_seconds=${REMOTE_VERIFY_TIMEOUT}"
|
||||
|
||||
@@ -18,8 +18,8 @@ VALID_RED_VERIFY = """\
|
||||
AWOOOI_WINDOWS99_VMWARE_AUTOSTART=1
|
||||
MODE=Verify
|
||||
VMRUN_PRESENT=1
|
||||
HOST111_VMWARE_TARGET=explicit_or_required
|
||||
VMX alias=111 path= present=0
|
||||
HOST111_VMWARE_TARGET=not_required_by_default_missing_no_vmx_found_on_99
|
||||
VMX alias=110 path=D:\\Documents\\Virtual Machines\\110\\110.vmx present=1
|
||||
VMX alias=188 path=D:\\Documents\\Virtual Machines\\188\\188.vmx present=1
|
||||
VMX alias=120 path=D:\\Documents\\Virtual Machines\\120\\120.vmx present=1
|
||||
VMX alias=121 path=D:\\Documents\\Virtual Machines\\121\\121.vmx present=1
|
||||
@@ -28,7 +28,7 @@ VMWARE_SERVICE name=VMAuthdService present=1 status=Running startup_type=Auto ok
|
||||
VMWARE_SERVICE name=VMnetDHCP present=1 status=Running startup_type=Auto ok=1
|
||||
VMWARE_AUTOSTART_TASK name=AWOOOI-Start-VMware-VMs present=1 enabled=1 state=Ready trigger_count=1 action_count=1 ok=1
|
||||
WINDOWS_UPDATE_POLICY name=NoAutoRebootWithLoggedOnUsers value=1 expected=1 ok=1
|
||||
VM_POWER alias=111 vmx_present=0 running=0
|
||||
VM_POWER alias=110 vmx_present=1 running=0
|
||||
VM_POWER alias=188 vmx_present=1 running=0
|
||||
VM_POWER alias=120 vmx_present=1 running=0
|
||||
VM_POWER alias=121 vmx_present=1 running=0
|
||||
@@ -37,7 +37,6 @@ VMWARE_AUTOSTART_CONFIG_READY=0
|
||||
VMWARE_AUTOSTART_POWER_READY=0
|
||||
WINDOWS_UPDATE_NO_AUTO_REBOOT_READY=1
|
||||
VMWARE_AUTOSTART_VERIFY_READY=0
|
||||
MISSING_VMX_ALIASES=111
|
||||
"""
|
||||
|
||||
|
||||
@@ -54,6 +53,23 @@ def _run(input_path: Path, output_path: Path | None = None) -> subprocess.Comple
|
||||
)
|
||||
|
||||
|
||||
def _run_with_aliases(input_path: Path, aliases: str) -> subprocess.CompletedProcess[str]:
|
||||
return subprocess.run(
|
||||
[
|
||||
"python3.11",
|
||||
str(SCRIPT),
|
||||
"--input",
|
||||
str(input_path),
|
||||
"--required-aliases",
|
||||
aliases,
|
||||
],
|
||||
cwd=ROOT,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
|
||||
def _kv(stdout: str) -> dict[str, str]:
|
||||
values: dict[str, str] = {}
|
||||
for line in stdout.splitlines():
|
||||
@@ -77,8 +93,8 @@ def test_valid_console_verify_red_artifact_is_accepted_and_normalized(
|
||||
assert values["schema_version"] == "windows99_console_verify_artifact_validator_v1"
|
||||
assert values["status"] == "valid_console_verify_artifact_vmware_not_ready"
|
||||
assert values["missing_required_field_count"] == "0"
|
||||
assert values["missing_vmx_aliases"] == "111"
|
||||
assert values["powered_off_aliases"] == "111,188,120,121,112"
|
||||
assert values["missing_vmx_aliases"] == ""
|
||||
assert values["powered_off_aliases"] == "110,188,120,121,112"
|
||||
assert values["secret_value_read"] == "false"
|
||||
assert normalized.read_text(encoding="utf-8").startswith(
|
||||
"AWOOOI_WINDOWS99_VMWARE_AUTOSTART=1\n"
|
||||
@@ -108,6 +124,28 @@ def test_collector_envelope_is_extracted(tmp_path: Path) -> None:
|
||||
assert "remote_verify_output_begin" not in normalized.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_explicit_host111_artifact_can_be_validated_with_required_alias_override(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
raw = tmp_path / "console-111.txt"
|
||||
raw.write_text(
|
||||
VALID_RED_VERIFY.replace(
|
||||
"HOST111_VMWARE_TARGET=not_required_by_default_missing_no_vmx_found_on_99",
|
||||
"HOST111_VMWARE_TARGET=explicit_or_required",
|
||||
)
|
||||
.replace("VMX alias=110 path=D:\\Documents\\Virtual Machines\\110\\110.vmx present=1", "VMX alias=111 path= present=0")
|
||||
.replace("VM_POWER alias=110 vmx_present=1 running=0", "VM_POWER alias=111 vmx_present=0 running=0"),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
result = _run_with_aliases(raw, "111,188,120,121,112")
|
||||
|
||||
assert result.returncode == 0
|
||||
values = _kv(result.stdout)
|
||||
assert values["missing_vmx_aliases"] == "111"
|
||||
assert values["powered_off_aliases"] == "111,188,120,121,112"
|
||||
|
||||
|
||||
def test_secret_like_console_output_is_rejected(tmp_path: Path) -> None:
|
||||
raw = tmp_path / "console.txt"
|
||||
raw.write_text(VALID_RED_VERIFY + "password=not-allowed\n", encoding="utf-8")
|
||||
|
||||
@@ -50,6 +50,8 @@ def test_collector_contract_forbids_secret_and_runtime_actions() -> None:
|
||||
assert "NumberOfPasswordPrompts=0" in text
|
||||
assert "ConnectionAttempts=1" in text
|
||||
assert "GSSAPIAuthentication=no" in text
|
||||
assert "--via-host" in text
|
||||
assert "via_host=" in text
|
||||
assert "in_memory_stdin_scriptblock" in text
|
||||
assert "[Console]::In.ReadToEnd()" in text
|
||||
assert ".\\\\windows99-vmware-autostart.ps1 -Mode Verify" not in text
|
||||
|
||||
@@ -10,7 +10,7 @@ from pathlib import Path
|
||||
|
||||
|
||||
SCHEMA_VERSION = "windows99_console_verify_artifact_validator_v1"
|
||||
DEFAULT_REQUIRED_ALIASES = ("111", "188", "120", "121", "112")
|
||||
DEFAULT_REQUIRED_ALIASES = ("110", "188", "120", "121", "112")
|
||||
SENSITIVE_RE = re.compile(
|
||||
r"(?i)(password|passwd|secret|token|authorization|cookie|private[_ -]?key)\s*[=:]"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user