Files
Mark-VIII/scripts/deploy.ps1
2026-08-11 20:24:24 -07:00

116 lines
6.1 KiB
PowerShell

param(
[string]$PagerHost = "172.16.52.1",
[string]$User = "root",
[string]$Password = "",
[string]$SshKey = "",
[string]$BuildDir = "",
[switch]$NoPortalRefresh
)
$ErrorActionPreference = "Stop"
$Root = Split-Path -Parent $PSScriptRoot
$PayloadKey = "pager-webui"
$PayloadCategory = "remote_access"
$PayloadDir = Join-Path $Root "payload\user\$PayloadCategory\$PayloadKey"
if (-not (Test-Path $PayloadDir)) { throw "Payload dir not found: $PayloadDir" }
if (-not $BuildDir) { $BuildDir = Join-Path $Root "build" }
$OutDir = Join-Path $BuildDir $PayloadKey
New-Item -ItemType Directory -Force -Path $OutDir | Out-Null
# --- 1. Stage payload tree -------------------------------------------------
$Stage = Join-Path $OutDir "stage"
if (Test-Path $Stage) { Remove-Item -Recurse -Force $Stage }
New-Item -ItemType Directory -Force -Path (Join-Path $Stage "user\$PayloadCategory") | Out-Null
Copy-Item -Recurse $PayloadDir (Join-Path $Stage "user\$PayloadCategory\$PayloadKey")
# --- 2. Build zip (portal format: payload-<b64>.zip) -----------------------
$b64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($PayloadKey)).TrimEnd('=').Replace('+','-').Replace('/','_')
$ZipName = "payload-$b64.zip"
$ZipPath = Join-Path $OutDir $ZipName
if (Test-Path $ZipPath) { Remove-Item -Force $ZipPath }
Add-Type -AssemblyName System.IO.Compression
$zip = New-Object System.IO.Compression.ZipArchive([IO.File]::Open($ZipPath, 'Create'), [IO.Compression.ZipArchiveMode]::Create)
try {
Get-ChildItem -Recurse -File $Stage | Where-Object {
$_.FullName -notmatch '[\\/]__pycache__[\\/]' -and $_.Extension -ne '.pyc'
} | ForEach-Object {
$rel = $_.FullName.Substring($Stage.Length + 1).Replace('\', '/')
$entry = $zip.CreateEntry($rel, [IO.Compression.CompressionLevel]::Optimal)
$es = $entry.Open()
$bytes = [IO.File]::ReadAllBytes($_.FullName)
if ($rel -match '(^|/)(payload\.sh|pagerwebui\.init)$') {
$bytes = [byte[]]($bytes | Where-Object { $_ -ne 13 })
}
$es.Write($bytes, 0, $bytes.Length)
$es.Close()
}
} finally { $zip.Dispose() }
# --- 3. Manifest with generated fields ------------------------------------
$hash = (Get-FileHash -Algorithm SHA256 $ZipPath).Hash.ToLower()
$manifest = Get-Content -Raw (Join-Path $PayloadDir "_hak5_manifest.json") | ConvertFrom-Json
$manifest.time = [int64]([DateTimeOffset]::UtcNow.ToUnixTimeSeconds())
$manifest.last_hash = $hash
$manifest.zip = $ZipName
$manifest = $manifest | ConvertTo-Json
Set-Content -Path (Join-Path $OutDir "_hak5_manifest.json") -Value $manifest -Encoding ascii
Write-Host "Built: $ZipPath"
# --- 4. Credentials / transport -------------------------------------------
if ($SshKey) {
$sshBase = "$User@$PagerHost"
$scp = "scp -i `"$SshKey`""
$ssh = "ssh -i `"$SshKey`""
} elseif (Get-Command sshpass -ErrorAction SilentlyContinue) {
if (-not $Password) { $Password = Read-Host -AsSecureString "Pager root password"; $Password = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password)) }
$sshBase = "$User@$PagerHost"
$scp = "sshpass -p `"$Password`" scp"
$ssh = "sshpass -p `"$Password`" ssh"
} else {
Write-Host "`nNo sshpass or -SshKey found. Run these manually (password prompts appear):"
Write-Host " scp `"$ZipPath`" $User@${PagerHost}:/tmp/"
Write-Host " ssh $User@$PagerHost `"cd /root/payloads && unzip -q -o /tmp/$ZipName && chmod +x user/general/$PayloadKey/payload.sh && rm -f /tmp/$ZipName`""
Write-Host "Then re-run this script with -SshKey, or install sshpass."
exit 1
}
# --- 5. Upload + extract on device ----------------------------------------
& cmd /c "$scp `"$ZipPath`" `"$(Join-Path $OutDir '_hak5_manifest.json')`" ${sshBase}:/tmp/" | Out-Null
if ($LASTEXITCODE -ne 0) { throw "SCP failed" }
$remotePayloadDir = "user/$PayloadCategory/$PayloadKey"
$legacyPayloadDir = "user/general/$PayloadKey"
$remoteCmd = "cd /root/payloads && rm -rf $remotePayloadDir $legacyPayloadDir && unzip -q -o /tmp/$ZipName && cp /tmp/_hak5_manifest.json $remotePayloadDir/_hak5_manifest.json && chmod +x $remotePayloadDir/payload.sh && chmod -R 755 $remotePayloadDir/www && rm -f /tmp/$ZipName /tmp/_hak5_manifest.json && if [ -x /etc/init.d/pagerwebui ] && /etc/init.d/pagerwebui running >/dev/null 2>&1; then /etc/init.d/pagerwebui restart; fi && echo EXTRACT_OK"
& cmd /c "$ssh $sshBase `"$remoteCmd`""
if ($LASTEXITCODE -ne 0) { throw "Remote extraction failed" }
Write-Host "Installed to /root/payloads/$remotePayloadDir/"
# --- 6. Portal refresh (best-effort) --------------------------------------
if (-not $NoPortalRefresh) {
if (-not $Password) {
Write-Host "Skipping portal refresh (no password supplied). Run the payload from the on-device menu to verify."
} else {
# Base64 the remote command so cmd/ssh quoting cannot mangle the JSON body.
$loginCmd = "curl -s -X POST http://127.0.0.1:1471/api/login -d '{""username"":""root"",""password"":""$Password""}'"
$b64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($loginCmd))
$tokenJson = & cmd /c "$ssh $sshBase `"echo $b64 | base64 -d | sh`""
$tokenJson = [string]$tokenJson -replace '\x1b\[[0-9;?]*[A-Za-z]', ''
$token = ($tokenJson | ConvertFrom-Json).token
if ($token) {
$refreshCmd = "curl -fsS -X POST http://127.0.0.1:1471/api/payloads/portal/refresh -H 'Authorization: Bearer $token'"
$b64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($refreshCmd))
& cmd /c "$ssh $sshBase `"echo $b64 | base64 -d | sh`"" | Out-Null
if ($LASTEXITCODE -eq 0) {
Write-Host "Portal refreshed. The payload should appear in the on-device Payloads menu / Virtual Pager portal."
} else {
Write-Warning "Portal refresh failed; the local payload installation is still complete."
}
} else {
Write-Host "Login to portal refresh failed; the payload is installed as a directory - run it from the menu."
}
}
}
Write-Host "Deploy complete. Run payload.sh from the Pager menu, then browse http://${PagerHost}:8080/"