fix: repair Windows WebVNC credentials
This commit is contained in:
parent
a0af15bd47
commit
4e5ce36538
@ -10,6 +10,7 @@
|
||||
### Fixed
|
||||
|
||||
- Fixed auto-shell command reconstruction so arguments with spaces stay quoted when shell operators such as `&&` are present.
|
||||
- Fixed Windows WebVNC credential handling so generated portal links preserve special characters and managed TightVNC sessions copy service passwords into the logged-in user's registry profile.
|
||||
|
||||
## 0.5.0 - 2026-05-04
|
||||
|
||||
|
||||
@ -32,9 +32,11 @@ The runner VNC service stays bound to loopback.
|
||||
|
||||
`--open` opens the portal page after the bridge starts. If the VNC password is
|
||||
available, the command also places it in the URL fragment for the local browser
|
||||
tab. URL fragments are not sent to the coordinator. If the portal login flow
|
||||
redirects first, the page may still prompt for the VNC password; use the
|
||||
password printed by the command.
|
||||
tab. URL fragments are not sent to the coordinator, and Crabbox preserves
|
||||
special characters such as `!` when building the fragment. If the portal login
|
||||
flow redirects first, the page may still prompt for the VNC password; use the
|
||||
password printed by the command. If an old browser tab is retrying with a stale
|
||||
fragment, close it before opening the new bridge URL.
|
||||
|
||||
Flags:
|
||||
|
||||
|
||||
@ -28,6 +28,8 @@ Bootstrap flow:
|
||||
- Crabbox installs Git for Windows and TightVNC.
|
||||
- Crabbox creates a local `crabbox` administrator.
|
||||
- Windows auto-logon starts a visible console session for that user.
|
||||
- TightVNC runs in that logged-in user session, with its HKCU password values
|
||||
copied from the service configuration during startup.
|
||||
- The generated password is stored at
|
||||
`C:\ProgramData\crabbox\vnc.password`.
|
||||
- VNC remains reachable only through the SSH tunnel.
|
||||
@ -103,6 +105,13 @@ VNC opens an OS credential prompt
|
||||
Check `managed:` in `crabbox vnc` output. If it is `false`, you opened a static
|
||||
host. Use that host's credentials and pass `--host-managed` intentionally.
|
||||
|
||||
WebVNC keeps retrying in the browser
|
||||
|
||||
Close any older retrying tab and start a fresh `crabbox webvnc` bridge. A stale
|
||||
tab can keep reconnecting with an old URL fragment. On managed AWS Windows,
|
||||
Crabbox configures TightVNC in the logged-in user's registry profile; if direct
|
||||
VNC auth also fails, recreate the lease with a current Crabbox build.
|
||||
|
||||
Related docs:
|
||||
|
||||
- [Interactive desktop and VNC](interactive-desktop-vnc.md)
|
||||
|
||||
@ -336,15 +336,23 @@ $password = (Get-Content -Raw -LiteralPath (Join-Path $base "vnc.password")).Tri
|
||||
$serverKey = "HKCU:\Software\TightVNC\Server"
|
||||
$serviceKey = "HKLM:\Software\TightVNC\Server"
|
||||
$serviceConfig = Get-ItemProperty -Path $serviceKey -ErrorAction SilentlyContinue
|
||||
function Set-TightVNCBinaryValue($Name) {
|
||||
$hex = ""
|
||||
if ($serviceConfig -and $serviceConfig.$Name) {
|
||||
$bytes = [byte[]]$serviceConfig.$Name
|
||||
if ($bytes -and $bytes.Length -gt 0) {
|
||||
$hex = -join ($bytes | ForEach-Object { $_.ToString("X2") })
|
||||
}
|
||||
}
|
||||
if ($hex) {
|
||||
& reg.exe add "HKCU\Software\TightVNC\Server" /v $Name /t REG_BINARY /d $hex /f | Out-Null
|
||||
}
|
||||
}
|
||||
New-Item -Force -Path $serverKey | Out-Null
|
||||
New-ItemProperty -Force -Path $serverKey -Name UseVncAuthentication -PropertyType DWord -Value 1 | Out-Null
|
||||
if ($serviceConfig -and $serviceConfig.Password) {
|
||||
New-ItemProperty -Force -Path $serverKey -Name Password -PropertyType Binary -Value $serviceConfig.Password | Out-Null
|
||||
}
|
||||
Set-TightVNCBinaryValue "Password"
|
||||
New-ItemProperty -Force -Path $serverKey -Name UseControlAuthentication -PropertyType DWord -Value 1 | Out-Null
|
||||
if ($serviceConfig -and $serviceConfig.ControlPassword) {
|
||||
New-ItemProperty -Force -Path $serverKey -Name ControlPassword -PropertyType Binary -Value $serviceConfig.ControlPassword | Out-Null
|
||||
}
|
||||
Set-TightVNCBinaryValue "ControlPassword"
|
||||
New-ItemProperty -Force -Path $serverKey -Name AllowLoopback -PropertyType DWord -Value 1 | Out-Null
|
||||
New-ItemProperty -Force -Path $serverKey -Name AcceptHttpConnections -PropertyType DWord -Value 0 | Out-Null
|
||||
$exe = "C:\Program Files\TightVNC\tvnserver.exe"
|
||||
|
||||
@ -134,6 +134,9 @@ func TestAWSUserDataWindowsProfile(t *testing.T) {
|
||||
"CrabboxUserVNC",
|
||||
"crabbox-user-vnc.cmd",
|
||||
"start-user-vnc.ps1",
|
||||
"Set-TightVNCBinaryValue",
|
||||
`reg.exe add "HKCU\Software\TightVNC\Server"`,
|
||||
`$hex = -join ($bytes | ForEach-Object { $_.ToString("X2") })`,
|
||||
"-run",
|
||||
"Set-Service -StartupType Disabled",
|
||||
"New-CrabboxPassword",
|
||||
|
||||
@ -248,6 +248,8 @@ func webVNCPortalURL(base, leaseID, username, password string) string {
|
||||
}
|
||||
u.Path = strings.TrimRight(u.Path, "/") + "/portal/leases/" + url.PathEscape(leaseID) + "/vnc"
|
||||
u.RawQuery = ""
|
||||
u.Fragment = ""
|
||||
u.RawFragment = ""
|
||||
if strings.TrimSpace(username) != "" || strings.TrimSpace(password) != "" {
|
||||
values := url.Values{}
|
||||
if strings.TrimSpace(username) != "" {
|
||||
@ -256,7 +258,10 @@ func webVNCPortalURL(base, leaseID, username, password string) string {
|
||||
if strings.TrimSpace(password) != "" {
|
||||
values.Set("password", strings.TrimSpace(password))
|
||||
}
|
||||
u.Fragment = values.Encode()
|
||||
u.RawFragment = values.Encode()
|
||||
if fragment, err := url.PathUnescape(u.RawFragment); err == nil {
|
||||
u.Fragment = fragment
|
||||
}
|
||||
}
|
||||
return u.String()
|
||||
}
|
||||
|
||||
@ -22,6 +22,12 @@ func TestWebVNCURLs(t *testing.T) {
|
||||
if got := webVNCPortalURL("https://crabbox.openclaw.ai/", "cbx_abcdef123456", "ec2-user", "secret value"); got != "https://crabbox.openclaw.ai/portal/leases/cbx_abcdef123456/vnc#password=secret+value&username=ec2-user" {
|
||||
t.Fatalf("portal URL=%q", got)
|
||||
}
|
||||
if got := webVNCPortalURL("https://crabbox.openclaw.ai/", "cbx_abcdef123456", "", "Cb1!abc"); got != "https://crabbox.openclaw.ai/portal/leases/cbx_abcdef123456/vnc#password=Cb1%21abc" {
|
||||
t.Fatalf("portal URL=%q", got)
|
||||
}
|
||||
if got := webVNCPortalURL("https://crabbox.openclaw.ai/#stale", "cbx_abcdef123456", "", ""); got != "https://crabbox.openclaw.ai/portal/leases/cbx_abcdef123456/vnc" {
|
||||
t.Fatalf("portal URL=%q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnectWebVNCBridgeRegistersAgentBeforeServe(t *testing.T) {
|
||||
|
||||
@ -120,6 +120,8 @@ $sshPorts = ${windowsSSHPortsPowerShell(config)}
|
||||
$vncPasswordPath = "C:\\ProgramData\\crabbox\\vnc.password"
|
||||
$windowsUsernamePath = "C:\\ProgramData\\crabbox\\windows.username"
|
||||
$windowsPasswordPath = "C:\\ProgramData\\crabbox\\windows.password"
|
||||
$userVNCStartupPath = "C:\\ProgramData\\crabbox\\start-user-vnc.ps1"
|
||||
$userVNCStartupCommandPath = Join-Path (Join-Path (Join-Path "C:\\Users" $user) "AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup") "crabbox-user-vnc.cmd"
|
||||
$setupCompletePath = "C:\\ProgramData\\crabbox\\setup-complete"
|
||||
$openSSHZip = "$env:TEMP\\OpenSSH-Win64.zip"
|
||||
$gitInstaller = "$env:TEMP\\Git-2.52.0-64-bit.exe"
|
||||
@ -209,8 +211,43 @@ if (-not (Test-Path -LiteralPath "C:\\Program Files\\TightVNC\\tvnserver.exe"))
|
||||
"SET_ACCEPTHTTPCONNECTIONS=1", "VALUE_OF_ACCEPTHTTPCONNECTIONS=0"
|
||||
) -Wait
|
||||
}
|
||||
Get-Service -Name tvnserver -ErrorAction SilentlyContinue | Set-Service -StartupType Automatic
|
||||
Start-Service -Name tvnserver -ErrorAction SilentlyContinue
|
||||
$userVNCStartup = @'
|
||||
$ErrorActionPreference = "SilentlyContinue"
|
||||
$serverKey = "HKCU:\\Software\\TightVNC\\Server"
|
||||
$serviceKey = "HKLM:\\Software\\TightVNC\\Server"
|
||||
$serviceConfig = Get-ItemProperty -Path $serviceKey -ErrorAction SilentlyContinue
|
||||
function Set-TightVNCBinaryValue($Name) {
|
||||
$hex = ""
|
||||
if ($serviceConfig -and $serviceConfig.$Name) {
|
||||
$bytes = [byte[]]$serviceConfig.$Name
|
||||
if ($bytes -and $bytes.Length -gt 0) {
|
||||
$hex = -join ($bytes | ForEach-Object { $_.ToString("X2") })
|
||||
}
|
||||
}
|
||||
if ($hex) {
|
||||
& reg.exe add "HKCU\\Software\\TightVNC\\Server" /v $Name /t REG_BINARY /d $hex /f | Out-Null
|
||||
}
|
||||
}
|
||||
New-Item -Force -Path $serverKey | Out-Null
|
||||
New-ItemProperty -Force -Path $serverKey -Name UseVncAuthentication -PropertyType DWord -Value 1 | Out-Null
|
||||
Set-TightVNCBinaryValue "Password"
|
||||
New-ItemProperty -Force -Path $serverKey -Name UseControlAuthentication -PropertyType DWord -Value 1 | Out-Null
|
||||
Set-TightVNCBinaryValue "ControlPassword"
|
||||
New-ItemProperty -Force -Path $serverKey -Name AllowLoopback -PropertyType DWord -Value 1 | Out-Null
|
||||
New-ItemProperty -Force -Path $serverKey -Name AcceptHttpConnections -PropertyType DWord -Value 0 | Out-Null
|
||||
$exe = "C:\\Program Files\\TightVNC\\tvnserver.exe"
|
||||
Get-Process tvnserver -ErrorAction SilentlyContinue | Where-Object { $_.SessionId -eq (Get-Process -Id $PID).SessionId } | Stop-Process -Force -ErrorAction SilentlyContinue
|
||||
Start-Sleep -Milliseconds 500
|
||||
Start-Process -FilePath $exe -ArgumentList "-run" -WindowStyle Minimized
|
||||
'@
|
||||
Set-Content -Encoding UTF8 -LiteralPath $userVNCStartupPath -Value $userVNCStartup
|
||||
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $userVNCStartupCommandPath) | Out-Null
|
||||
Set-Content -Encoding ASCII -LiteralPath $userVNCStartupCommandPath -Value ('@echo off' + [Environment]::NewLine + 'powershell.exe -NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File "' + $userVNCStartupPath + '"' + [Environment]::NewLine)
|
||||
$startupTask = "CrabboxUserVNC"
|
||||
cmd.exe /c "schtasks.exe /Delete /TN $startupTask /F 2>NUL" | Out-Null
|
||||
schtasks.exe /Create /TN $startupTask /SC ONCE /ST ((Get-Date).AddMinutes(1).ToString("HH:mm")) /TR "powershell.exe -NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File $userVNCStartupPath" /RU $user /IT /F | Out-Null
|
||||
Get-Service -Name tvnserver -ErrorAction SilentlyContinue | Set-Service -StartupType Disabled
|
||||
Stop-Service -Name tvnserver -Force -ErrorAction SilentlyContinue
|
||||
$winlogon = "HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon"
|
||||
Set-ItemProperty -Path $winlogon -Name AutoAdminLogon -Value "1" -Type String
|
||||
Set-ItemProperty -Path $winlogon -Name ForceAutoLogon -Value "1" -Type String
|
||||
|
||||
@ -143,6 +143,12 @@ describe("cloud-init bootstrap", () => {
|
||||
expect(got).toContain("tightvnc-2.8.85-gpl-setup-64bit.msi");
|
||||
expect(got).toContain("VALUE_OF_PASSWORD=$vncPassword");
|
||||
expect(got).toContain("VALUE_OF_ALLOWLOOPBACK=1");
|
||||
expect(got).toContain("CrabboxUserVNC");
|
||||
expect(got).toContain("crabbox-user-vnc.cmd");
|
||||
expect(got).toContain("start-user-vnc.ps1");
|
||||
expect(got).toContain("Set-TightVNCBinaryValue");
|
||||
expect(got).toContain('reg.exe add "HKCU\\Software\\TightVNC\\Server"');
|
||||
expect(got).toContain('$hex = -join ($bytes | ForEach-Object { $_.ToString("X2") })');
|
||||
expect(got).toContain("New-CrabboxPassword");
|
||||
expect(got).toContain("${userSID}:F");
|
||||
expect(got).toContain("C:\\ProgramData\\crabbox\\windows.username");
|
||||
|
||||
Loading…
Reference in New Issue
Block a user