Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions docs/phase4-platform-acceptance.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,21 @@ site-equivalent path without touching the public website. It creates an
unrelated WSL control distribution and proves uninstall retains it. It never
claims Windows artifact creation or signing.

### Retained host provisioning

`ops/platform-acceptance/windows-host/` tracks the bootstrap media for this host:
the answer file, the first-boot driver/guest-agent/OpenSSH setup, and a
reproducible ISO build. Its README records two host constraints that are easy to
get wrong. Proxmox always creates the TPM state volume as raw, and a raw volume
on directory storage blocks snapshots for the entire VM even when every other
disk is qcow2 — so the acceptance host cannot honour "restore the accepted clean
snapshot before routing" unless that small volume sits on snapshot-capable
storage. Verify with `qm snapshot <vmid> probe` before provisioning. Separately,
Windows 11 25H2 (build 26200) does not auto-apply an answer file from secondary
media and ignores `setup.exe /unattend:`; the README documents the offline
`dism /apply-image` path that still runs the specialize, oobeSystem and
auditUser passes.

## Recovery and teardown

First disable the applicable repository variable so new jobs cannot route.
Expand Down
2 changes: 2 additions & 0 deletions ops/platform-acceptance/windows-host/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# The per-site operator public key is never committed to this public repository.
authorized_key.pub

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

git check-ignore -v --no-index \
  ops/platform-acceptance/windows-host/1helm-windows-unattend.iso

Repository: gitcommit90/1Helm

Length of output: 155


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- relevant files ---'
git ls-files 'ops/platform-acceptance/windows-host/*' | sed -n '1,120p'

printf '%s\n' '--- ignore rules ---'
cat -n ops/platform-acceptance/windows-host/.gitignore

printf '%s\n' '--- ISO/key references ---'
rg -n -C 4 --glob '!.git' \
  '1helm-windows-unattend\.iso|authorized_key\.pub|build-unattend-iso' \
  ops/platform-acceptance/windows-host

Repository: gitcommit90/1Helm

Length of output: 5986


Information Disclosure (CWE-200): Exposure of Sensitive Information to an Unauthorized Actor

Reachability: Internal

Ignore the generated bootstrap ISO.

The default 1helm-windows-unattend.iso contains authorized_key.pub for Administrator SSH access but is not ignored. Add the ISO to .gitignore or write it outside the worktree.

Proposed fix
 authorized_key.pub
+1helm-windows-unattend.iso
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
authorized_key.pub
authorized_key.pub
1helm-windows-unattend.iso
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ops/platform-acceptance/windows-host/.gitignore` at line 2, Update the
Windows host .gitignore entry containing authorized_key.pub to also ignore the
generated 1helm-windows-unattend.iso file, ensuring the bootstrap ISO is not
tracked in the worktree.

50 changes: 50 additions & 0 deletions ops/platform-acceptance/windows-host/Autounattend.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
<settings pass="windowsPE">
<component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<SetupUILanguage><UILanguage>en-US</UILanguage></SetupUILanguage>
<InputLocale>en-US</InputLocale><SystemLocale>en-US</SystemLocale><UILanguage>en-US</UILanguage><UserLocale>en-US</UserLocale>
</component>
<component name="Microsoft-Windows-PnpCustomizationsWinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<DriverPaths>
<PathAndCredentials wcm:action="add" wcm:keyValue="1"><Path>E:\vioscsi\w11\amd64</Path></PathAndCredentials>
<PathAndCredentials wcm:action="add" wcm:keyValue="2"><Path>E:\NetKVM\w11\amd64</Path></PathAndCredentials>
Comment on lines +10 to +11

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Define the required VirtIO media and drive order.

Lines 10-11 require the VirtIO driver tree on E:. The bootstrap ISO contains only the answer file, setup.ps1, and the key. The build script instructs operators to attach it as the second CD-ROM beside Windows media. In that layout, E: is the bootstrap ISO and WinPE cannot load the VirtIO storage and network drivers.

Require and document a third medium with Windows on D:, VirtIO on E:, and the bootstrap ISO on F:. Alternatively, remove the fixed drive-letter dependency.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ops/platform-acceptance/windows-host/Autounattend.xml` around lines 10 - 11,
Update the Autounattend.xml VirtIO driver paths in the PathAndCredentials
entries to match the documented three-medium layout: Windows on D:, VirtIO media
on E:, and the bootstrap ISO on F:. Also update the associated build or operator
instructions to require and document that drive order, or remove the fixed
drive-letter dependency while preserving driver discovery.

</DriverPaths>
</component>
<component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<DiskConfiguration>
<Disk wcm:action="add"><DiskID>0</DiskID><WillWipeDisk>true</WillWipeDisk>
<CreatePartitions>
<CreatePartition wcm:action="add"><Order>1</Order><Type>EFI</Type><Size>260</Size></CreatePartition>
<CreatePartition wcm:action="add"><Order>2</Order><Type>MSR</Type><Size>16</Size></CreatePartition>
<CreatePartition wcm:action="add"><Order>3</Order><Type>Primary</Type><Extend>true</Extend></CreatePartition>
</CreatePartitions>
<ModifyPartitions>
<ModifyPartition wcm:action="add"><Order>1</Order><PartitionID>1</PartitionID><Format>FAT32</Format><Label>System</Label></ModifyPartition>
<ModifyPartition wcm:action="add"><Order>2</Order><PartitionID>3</PartitionID><Format>NTFS</Format><Label>Windows</Label></ModifyPartition>
</ModifyPartitions>
</Disk>
</DiskConfiguration>
<ImageInstall><OSImage><InstallFrom><MetaData wcm:action="add"><Key>/IMAGE/INDEX</Key><Value>6</Value></MetaData></InstallFrom><InstallTo><DiskID>0</DiskID><PartitionID>3</PartitionID></InstallTo></OSImage></ImageInstall>
<UserData><AcceptEula>true</AcceptEula><FullName>1Helm</FullName><Organization>1Helm acceptance</Organization></UserData>
<DynamicUpdate><Enable>false</Enable><WillShowUI>OnError</WillShowUI></DynamicUpdate>
</component>
</settings>
<settings pass="specialize">
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<ComputerName>ONEHELM-WIN</ComputerName><TimeZone>UTC</TimeZone><RegisteredOwner>1Helm</RegisteredOwner><RegisteredOrganization>1Helm acceptance</RegisteredOrganization>
</component>
</settings>
<settings pass="oobeSystem">
<component name="Microsoft-Windows-Deployment" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<Reseal><Mode>Audit</Mode></Reseal>
</component>
</settings>
<settings pass="auditUser">
<component name="Microsoft-Windows-Deployment" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<RunSynchronous>
<RunSynchronousCommand wcm:action="add"><Order>1</Order><Description>Provision retained 1Helm acceptance host</Description><Path>powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "$v=(Get-Volume -FileSystemLabel ONEHELM); &amp; ($v.DriveLetter + ':\setup.ps1')"</Path><WillReboot>Never</WillReboot></RunSynchronousCommand>
</RunSynchronous>
</component>
</settings>
</unattend>
75 changes: 75 additions & 0 deletions ops/platform-acceptance/windows-host/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Retained Windows acceptance-host provisioning

These files build the bootstrap ISO for the dedicated Windows 11 Phase 4
acceptance host described in `docs/phase4-platform-acceptance.md`. They were
previously carried only as a pre-built ISO on the hypervisor, so a rebuild could
not be reviewed or reproduced. They are tracked here for that reason.

Nothing here publishes a release, creates a tag, deploys the website, or touches
production data or services.

## What it does

`Autounattend.xml` partitions the disk (GPT, EFI + MSR + NTFS), installs the Pro
image, sets the computer name and UTC timezone, then reseals into **audit mode**
and runs `setup.ps1` during the `auditUser` pass.

`setup.ps1` installs the VirtIO network and serial drivers plus the QEMU guest
agent, enables OpenSSH Server pinned to the operator's public key (supplied at build
time, see below) with password authentication off, disables sleep and hibernate, and records
`C:\1HelmAcceptance\ready.json`. It is idempotent: an existing `ready.json`
makes it exit 0 immediately.

## Build

```sh
./build-unattend-iso.sh 1helm-windows-unattend.iso /path/to/operator_key.pub
```

The operator public key is **not** committed: this repository is public, and
publishing which key is authorized as Administrator on the acceptance host is
needless disclosure. Pass it at build time (or via
`HELM_ACCEPTANCE_AUTHORIZED_KEY`); the build stages it as `authorized_key.pub`
on the ISO and `setup.ps1` refuses to continue without it. `.gitignore` keeps a
local copy out of git.

Two constraints are load-bearing:

- `-iso-level 4` — at genisoimage's default level the ISO9660 namespace
truncates `Autounattend.xml` to `AUTOUNAT.XML`, which Windows Setup does not
recognize as an answer file.
- Volume label `ONEHELM` — the `auditUser` pass locates `setup.ps1` with
`Get-Volume -FileSystemLabel ONEHELM`.

## Host VM shape

The acceptance workflow restores an accepted clean snapshot before every job, so
the VM must actually be able to snapshot. Proxmox always creates the **TPM state
volume as raw**, and a raw volume on directory storage blocks snapshots for the
whole VM even when every other disk is qcow2. Place the disks as qcow2 and put
the small `tpmstate0` volume on snapshot-capable storage (thin-LVM), which keeps
TPM 2.0 and Secure Boot intact so no Windows 11 requirement bypass is needed.

Verify before installing anything:

```sh
qm snapshot <vmid> probe && qm delsnapshot <vmid> probe
```

## Windows 11 25H2 answer-file caveat

On build 26200 (25H2) the new setup engine (`setuphost.exe`) does **not**
auto-apply `Autounattend.xml` from a secondary disc, and `setup.exe /unattend:`
is ignored. The answer file is readable from WinPE — it simply is not consumed,
so setup falls through to the interactive product-key page.

Working alternative on affected media: from the WinPE shell (Shift+F10),
partition with `diskpart`, apply the image with
`dism /apply-image /imagefile:<media>:\sources\install.wim /index:6 /applydir:W:\`,
copy `Autounattend.xml` to `W:\Windows\Panther\unattend.xml`, then
`bcdboot W:\Windows /s S: /f UEFI`. First boot still runs `specialize`,
`oobeSystem` and `auditUser`, so the computer name, audit reseal and
`setup.ps1` all still apply.

Note that WinPE on this media has no `curl`, no `taskkill`, and no configured
network, so stage anything you need on the bootstrap ISO itself.
36 changes: 36 additions & 0 deletions ops/platform-acceptance/windows-host/build-unattend-iso.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env bash
# Build the retained Windows acceptance-host bootstrap ISO.
#
# Attach the result as a second CD-ROM alongside the Windows installation media
# on the dedicated Phase 4 acceptance VM. It carries the answer file and the
# first-boot bootstrap; it publishes nothing and contains no credentials beyond
# the operator's own SSH public key in setup.ps1.
set -euo pipefail

here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
out="${1:-1helm-windows-unattend.iso}"
key="${2:-${HELM_ACCEPTANCE_AUTHORIZED_KEY:-}}"

if [ -z "$key" ] || [ ! -f "$key" ]; then
echo "usage: build-unattend-iso.sh [out.iso] <path-to-authorized-key.pub>" >&2
echo " or: HELM_ACCEPTANCE_AUTHORIZED_KEY=/path/to/key.pub build-unattend-iso.sh [out.iso]" >&2
echo "The operator public key is deliberately not committed to this public repository." >&2
exit 2
fi

stage="$(mktemp -d)"
trap 'rm -rf "$stage"' EXIT

cp "$here/Autounattend.xml" "$here/setup.ps1" "$stage/"
printf 'Retained Windows acceptance bootstrap for 1Helm.\n' > "$stage/1helm-acceptance.txt"
install -m 0644 "$key" "$stage/authorized_key.pub"

# -iso-level 4 keeps long file names. At genisoimage's default level the
# ISO9660 namespace truncates Autounattend.xml to AUTOUNAT.XML, which Windows
# Setup does not recognize as an answer file.
#
# The volume label MUST remain ONEHELM: the auditUser pass in Autounattend.xml
# locates setup.ps1 via Get-Volume -FileSystemLabel ONEHELM.
genisoimage -quiet -iso-level 4 -J -r -V ONEHELM -o "$out" "$stage"

echo "wrote $out"
62 changes: 62 additions & 0 deletions ops/platform-acceptance/windows-host/setup.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
$ErrorActionPreference = "Stop"
$root = "C:\1HelmAcceptance"
New-Item -ItemType Directory -Force -Path $root | Out-Null
$log = Join-Path $root "bootstrap.log"
Start-Transcript -Path $log -Append
try {
if (Test-Path (Join-Path $root "ready.json")) { exit 0 }

$virtio = Get-Volume | Where-Object { $_.DriveLetter -and (Test-Path ("{0}:\guest-agent\qemu-ga-x86_64.msi" -f $_.DriveLetter)) } | Select-Object -First 1
if (-not $virtio) { throw "VirtIO 0.1.271 media was not found." }
$drive = "{0}:" -f $virtio.DriveLetter
& pnputil.exe /add-driver "$drive\NetKVM\w11\amd64\*.inf" /subdirs /install
if ($LASTEXITCODE -notin 0, 3010) { throw "NetKVM driver install failed: $LASTEXITCODE" }
# The QEMU guest agent reaches the host over a VirtIO serial port. Without
# this driver the QEMU-GA service still starts and reports Running, while the
# host side ("qm agent <vmid> ping") stays dead and there is no guest-exec
# channel to provision the host with.
& pnputil.exe /add-driver "$drive\vioserial\w11\amd64\*.inf" /subdirs /install
if ($LASTEXITCODE -notin 0, 3010) { throw "VirtIO serial driver install failed: $LASTEXITCODE" }
& msiexec.exe /i "$drive\guest-agent\qemu-ga-x86_64.msi" /qn /norestart
if ($LASTEXITCODE -notin 0, 3010) { throw "QEMU guest agent install failed: $LASTEXITCODE" }
Comment on lines +13 to +21

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

file='ops/platform-acceptance/windows-host/setup.ps1'

rg -n -C 4 '3010|/norestart|ready\.json|Restart-Computer|shutdown\.exe' "$file"

if ! rg -qE 'Restart-Computer|shutdown\.exe.*[/-]r' "$file"; then
  echo 'No explicit restart exists for an accepted 3010 result.' >&2
  exit 1
fi

Repository: gitcommit90/1Helm

Length of output: 2426


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

file='ops/platform-acceptance/windows-host/setup.ps1'

printf '%s\n' '--- setup.ps1 ---'
cat -n "$file" | sed -n '1,75p'

printf '%s\n' '--- references to setup.ps1 and ready.json ---'
rg -n -C 3 'setup\.ps1|ready\.json|bootstrap\.log|QEMU-GA|Start-Service' . \
  -g '!node_modules' -g '!dist' -g '!build'

Repository: gitcommit90/1Helm

Length of output: 15357


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path

setup = Path("ops/platform-acceptance/windows-host/setup.ps1").read_text()
unattend = Path("ops/platform-acceptance/windows-host/Autounattend.xml").read_text()

accepted_3010 = [
    line for line in setup.splitlines()
    if "3010" in line and "-notin" in line
]
restart_calls = [
    line for line in setup.splitlines()
    if "Restart-Computer" in line or "shutdown.exe" in line
]
ready_writes = [
    (i + 1, line) for i, line in enumerate(setup.splitlines())
    if "ready.json" in line and "Set-Content" in line
]

print(f"accepted_3010_checks={len(accepted_3010)}")
print(f"explicit_restart_calls={len(restart_calls)}")
print(f"ready_writes={ready_writes}")
print(f"unattend_will_reboot_never={'<WillReboot>Never</WillReboot>' in unattend}")
print("accepted_3010_checks:")
for line in accepted_3010:
    print(line.strip())
PY

Repository: gitcommit90/1Helm

Length of output: 670


Do not publish readiness after exit code 3010.

When any installation returns 3010, stop provisioning, persist the restart-required state, and reboot the host. Ensure setup.ps1 runs again after reboot and writes ready.json only after the required drivers and services are ready.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ops/platform-acceptance/windows-host/setup.ps1` around lines 13 - 21, Update
the installation checks in setup.ps1, including the NetKVM, VirtIO serial, and
QEMU guest agent commands, so exit code 3010 persists a restart-required state,
stops provisioning, and reboots the host instead of continuing. Ensure setup.ps1
is scheduled to rerun after reboot and writes ready.json only after all required
drivers and services complete successfully without a pending restart.


$capability = Get-WindowsCapability -Online | Where-Object Name -Like "OpenSSH.Server*" | Select-Object -First 1
if (-not $capability) { throw "Windows did not expose the OpenSSH Server capability." }
if ($capability.State -ne "Installed") { Add-WindowsCapability -Online -Name $capability.Name | Out-Null }

New-Item -ItemType Directory -Force -Path "C:\ProgramData\ssh" | Out-Null
# The authorized key is supplied per site on the bootstrap media rather than
# committed to this public repository. build-unattend-iso.sh stages it.
$keySource = Join-Path $PSScriptRoot "authorized_key.pub"
if (-not (Test-Path $keySource)) { throw "authorized_key.pub is missing from the bootstrap media; see ops/platform-acceptance/windows-host/README.md" }
$authorizedKey = (Get-Content -Raw $keySource).Trim()
if ($authorizedKey -notmatch '^(ssh-ed25519|ssh-rsa|ecdsa-sha2-nistp[0-9]+) ') { throw "authorized_key.pub is not an OpenSSH public key" }
Set-Content -Encoding ascii -Path "C:\ProgramData\ssh\administrators_authorized_keys" -Value $authorizedKey
& icacls.exe "C:\ProgramData\ssh\administrators_authorized_keys" /inheritance:r /grant "Administrators:F" /grant "SYSTEM:F" | Out-Null
$config = "C:\ProgramData\ssh\sshd_config"
if (Test-Path $config) {
$text = Get-Content -Raw $config
$text = [regex]::Replace($text, '(?m)^\s*#?\s*PasswordAuthentication\s+.*$', 'PasswordAuthentication no')
$text = [regex]::Replace($text, '(?m)^\s*#?\s*PubkeyAuthentication\s+.*$', 'PubkeyAuthentication yes')
Set-Content -Encoding ascii -Path $config -Value $text
}
Comment on lines +37 to +42

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

file='ops/platform-acceptance/windows-host/setup.ps1'

rg -n -C 3 'PasswordAuthentication|PubkeyAuthentication|sshd_config|sshd -t' "$file"

# Expected after the fix: explicit fallback insertion for absent directives
# and an `sshd -t` validation before Start-Service sshd.
rg -n 'sshd -t' "$file"

Repository: gitcommit90/1Helm

Length of output: 1007


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

file='ops/platform-acceptance/windows-host/setup.ps1'

printf '%s\n' '--- setup.ps1 ---'
nl -ba "$file" | sed -n '1,130p'

printf '%s\n' '--- relevant call sites and configuration setup ---'
rg -n -C 3 'Add-WindowsCapability|OpenSSH|sshd_config|New-NetFirewallRule|Start-Service|Set-Service|ready\.json|3010|reboot' \
  ops/platform-acceptance/windows-host

Repository: gitcommit90/1Helm

Length of output: 213


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

file='ops/platform-acceptance/windows-host/setup.ps1'

printf '%s\n' '--- setup.ps1 ---'
awk '{ printf "%5d  %s\n", NR, $0 }' "$file" | sed -n '1,130p'

printf '%s\n' '--- relevant call sites and configuration setup ---'
rg -n -C 3 'Add-WindowsCapability|OpenSSH|sshd_config|New-NetFirewallRule|Start-Service|Set-Service|ready\.json|3010|reboot' \
  ops/platform-acceptance/windows-host

Repository: gitcommit90/1Helm

Length of output: 12548


🌐 Web query:

Microsoft Windows OpenSSH Server default sshd_config PasswordAuthentication PubkeyAuthentication Windows 11

💡 Result:

When you first install and start the OpenSSH Server on Windows 11, the sshd_config file is automatically generated by the service at %ProgramData%\ssh\sshd_config [1][2][3]. By default, the OpenSSH Server is configured to allow both password-based authentication and public key authentication [4]. Specifically, the generated configuration file defaults to the following settings for these authentication methods: PubkeyAuthentication yes [4] PasswordAuthentication yes [4] To modify these settings, you can edit the %ProgramData%\ssh\sshd_config file with a text editor (run as an administrator) and change the values to no [5][4]. After modifying the file, you must restart the OpenSSH SSH Server service (using the Services app or PowerShell) for the changes to take effect [5][6]. For security, especially if you intend to disable password authentication in favor of key-based authentication, ensure you have correctly configured the authorized_keys files. Note that Windows OpenSSH enforces specific Access Control List (ACL) permissions on these files; they must be owned by the user or the SYSTEM account, and other users must not have write access to them [3][7]. Additionally, if you are using an administrator account, the server looks for authorized keys in a specific location at %ProgramData%\ssh\administrators_authorized_keys by default, rather than the user's home directory [3][8].

Citations:


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

path = Path("ops/platform-acceptance/windows-host/setup.ps1")
text = path.read_text()

assert "if (Test-Path $config)" in text
assert "Start-Service sshd" in text
assert text.index("if (Test-Path $config)") < text.index("Start-Service sshd")
assert "PasswordAuthentication no" in text
assert "PubkeyAuthentication yes" in text

# Model both replacements with a configuration that omits both directives.
config = "Host *\n    AuthorizedKeysFile .ssh/authorized_keys\n"
updated = re.sub(
    r"(?m)^\s*#?\s*PasswordAuthentication\s+.*$",
    "PasswordAuthentication no",
    config,
)
updated = re.sub(
    r"(?m)^\s*#?\s*PubkeyAuthentication\s+.*$",
    "PubkeyAuthentication yes",
    updated,
)

assert "PasswordAuthentication no" not in updated
assert "PubkeyAuthentication yes" not in updated
print("Absent directives remain absent; service start follows the conditional rewrite.")
PY

Repository: gitcommit90/1Helm

Length of output: 235


Broken Authentication (CWE-287): Improper Authentication

Reachability: External · Exploitability: Moderate

Append the SSH authentication directives.

When sshd_config omits either directive, the replacements add nothing. Start-Service sshd can then retain the Windows default PasswordAuthentication yes, while the later firewall rule allows TCP/22 on every profile. Append missing directives and run sshd -t before starting sshd.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ops/platform-acceptance/windows-host/setup.ps1` around lines 37 - 42, Update
the sshd_config handling in setup.ps1 to append PasswordAuthentication no and
PubkeyAuthentication yes when either directive is absent, while preserving
replacement of existing or commented directives. Validate the resulting
configuration with sshd -t before the Start-Service sshd call, and only proceed
when validation succeeds.

Set-Service sshd -StartupType Automatic
Start-Service sshd
# Add-WindowsCapability already creates OpenSSH-Server-In-TCP, but scoped to
# the Private profile only. A freshly bridged VM is categorized Public, so a
# bare existence check short-circuits and leaves port 22 unreachable even
# though sshd reports Running. Ensure the rule exists AND covers every profile.
if (Get-NetFirewallRule -Name OpenSSH-Server-In-TCP -ErrorAction SilentlyContinue) {
Set-NetFirewallRule -Name OpenSSH-Server-In-TCP -Enabled True -Profile Any
} else {
New-NetFirewallRule -Name OpenSSH-Server-In-TCP -DisplayName "OpenSSH Server (sshd)" -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22 -Profile Any | Out-Null
}
Set-Service QEMU-GA -StartupType Automatic
Start-Service QEMU-GA
powercfg.exe /change standby-timeout-ac 0 | Out-Null
powercfg.exe /change hibernate-timeout-ac 0 | Out-Null
$record = [ordered]@{ ready = $true; computer = $env:COMPUTERNAME; build = [Environment]::OSVersion.Version.ToString(); at = (Get-Date).ToUniversalTime().ToString("o") }
Set-Content -Encoding utf8 -Path (Join-Path $root "ready.json") -Value ($record | ConvertTo-Json -Compress)
} finally {
Stop-Transcript
}
Loading