108 lines
3.2 KiB
PowerShell
108 lines
3.2 KiB
PowerShell
# Small helper for driving the agent: enqueues one command and waits for the ACK.
|
|
#
|
|
# powershell -File agent-cli.ps1 -Command "STATUS"
|
|
# powershell -File agent-cli.ps1 -Status
|
|
# powershell -File agent-cli.ps1 -Command "PS Get-Process | Select -First 3"
|
|
#
|
|
# Uses the same HMAC scheme the agent does, so it works against the mock worker
|
|
# and the real Cloudflare Worker alike.
|
|
|
|
param(
|
|
[string]$Worker = "http://127.0.0.1:8788",
|
|
[string]$Agent = "testagent",
|
|
[string]$Secret = "testsecret",
|
|
[string]$Command = "",
|
|
[switch]$Status,
|
|
[int]$TimeoutSec = 30
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
function Get-Sha256Hex([byte[]]$Bytes)
|
|
{
|
|
$sha = [System.Security.Cryptography.SHA256]::Create()
|
|
try
|
|
{
|
|
if ($null -eq $Bytes -or $Bytes.Length -eq 0) { $stream = New-Object System.IO.MemoryStream }
|
|
else { $stream = New-Object System.IO.MemoryStream(, $Bytes) }
|
|
return ([BitConverter]::ToString($sha.ComputeHash($stream)) -replace "-", "").ToLower()
|
|
}
|
|
finally { $sha.Dispose() }
|
|
}
|
|
|
|
function Get-HmacHex([string]$Key, [string]$Message)
|
|
{
|
|
$hmac = New-Object System.Security.Cryptography.HMACSHA256
|
|
try
|
|
{
|
|
$hmac.Key = [System.Text.Encoding]::UTF8.GetBytes($Key)
|
|
return ([BitConverter]::ToString($hmac.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($Message))) -replace "-", "").ToLower()
|
|
}
|
|
finally { $hmac.Dispose() }
|
|
}
|
|
|
|
function Invoke-Worker([string]$Path, [string]$Method = "GET", [byte[]]$Body)
|
|
{
|
|
if ($null -eq $Body) { $Body = [byte[]]@() }
|
|
|
|
$ts = [string][DateTimeOffset]::UtcNow.ToUnixTimeSeconds()
|
|
$canonical = "$Agent`n$ts`n$Method`n$($Path.TrimStart('/'))`n$(Get-Sha256Hex $Body)"
|
|
$headers = @{
|
|
"X-Agent-Id" = $Agent
|
|
"X-Agent-Ts" = $ts
|
|
"X-Agent-Sign" = (Get-HmacHex $Secret $canonical)
|
|
}
|
|
|
|
if ($Method -eq "POST")
|
|
{
|
|
return Invoke-WebRequest -Method Post -Uri "$Worker$Path" -Body ([byte[]]$Body) `
|
|
-ContentType "application/octet-stream" -Headers $headers -UseBasicParsing
|
|
}
|
|
|
|
return Invoke-WebRequest -Uri "$Worker$Path" -Headers $headers -UseBasicParsing
|
|
}
|
|
|
|
function Decode-Last([string]$Value)
|
|
{
|
|
if (-not $Value) { return "" }
|
|
|
|
try { return [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($Value)) }
|
|
catch { return $Value }
|
|
}
|
|
|
|
if ($Status)
|
|
{
|
|
$s = (Invoke-Worker -Path "/status").Content | ConvertFrom-Json
|
|
"agent=$($s.agent) queued=$($s.queued) pending=$([bool]$s.pending)"
|
|
if ($s.last) { Decode-Last $s.last }
|
|
exit 0
|
|
}
|
|
|
|
if (-not $Command)
|
|
{
|
|
throw "Usage: agent-cli.ps1 -Command '<command>' | -Status"
|
|
}
|
|
|
|
$before = (Invoke-Worker -Path "/status").Content | ConvertFrom-Json
|
|
$initialLast = $before.last
|
|
|
|
$null = Invoke-Worker -Path "/enqueue" -Method POST -Body ([System.Text.Encoding]::UTF8.GetBytes($Command))
|
|
|
|
$deadline = (Get-Date).AddSeconds($TimeoutSec)
|
|
$seenPending = $false
|
|
|
|
while ((Get-Date) -lt $deadline)
|
|
{
|
|
Start-Sleep -Milliseconds 400
|
|
|
|
$s = (Invoke-Worker -Path "/status").Content | ConvertFrom-Json
|
|
|
|
if ($s.pending) { $seenPending = $true; continue }
|
|
|
|
if ($s.last -and $s.last -ne $initialLast) { Decode-Last $s.last; exit 0 }
|
|
if ($seenPending -and $s.last) { Decode-Last $s.last; exit 0 }
|
|
}
|
|
|
|
Write-Host "<timeout: agent may still be backing off>"
|
|
exit 1
|