diff --git a/inc/vCenter-SSL.ps1 b/inc/vCenter-SSL.ps1 index b17c89b7..c099bd69 100644 --- a/inc/vCenter-SSL.ps1 +++ b/inc/vCenter-SSL.ps1 @@ -1,9 +1,9 @@ #!/usr/bin/env pwsh # ----------------------------------------------------------------------------------- -# Linux-safe vCenter + Posh-ACME Script -# - Uses HttpClient with AutomaticDecompression disabled (Fix1) -# - Handles Posh-ACME + PowerDNS plugin -# - Uploads & applies certificates to vCenter +# Linux-safe vCenter + Posh-ACME Script (Production Hardened) +# - Uses HttpClient with AutomaticDecompression disabled +# - Fault-tolerant ACME certificate request and upload +# - Handles PowerDNS plugin, vCenter session, certificate apply, service restart # ----------------------------------------------------------------------------------- . /opt/idssys/nodemgmt/conf/powerwall/settings.ps1 @@ -64,8 +64,6 @@ function Show-Failure { Write-Host $global:responseBody -ForegroundColor Red Write-Host "The request/response body has been saved to `$global:helpme" -ForegroundColor Red Write-Host "----------------------------------------" -ForegroundColor Red - - exit 1 } # ---------------------------- @@ -82,21 +80,18 @@ function Invoke-SafeRestMethod { ) try { - # Handler: disable automatic decompression $handler = [System.Net.Http.HttpClientHandler]::new() $handler.AutomaticDecompression = [System.Net.DecompressionMethods]::None - $handler.ServerCertificateCustomValidationCallback = { $true } # Skip cert check + $handler.ServerCertificateCustomValidationCallback = { $true } $client = [System.Net.Http.HttpClient]::new($handler) $client.Timeout = [System.TimeSpan]::FromSeconds($TimeoutSec) - # Add headers foreach ($k in $Headers.Keys) { $client.DefaultRequestHeaders.Remove($k) | Out-Null $client.DefaultRequestHeaders.Add($k, $Headers[$k]) } - # Prepare content if ($Body -ne $null) { if ($AsJson) { $jsonBody = $Body | ConvertTo-Json -Depth 12 -Compress @@ -106,31 +101,24 @@ function Invoke-SafeRestMethod { $content = [System.Net.Http.StringContent]::new($Body) } } - else { - $content = $null - } + else { $content = $null } - # Send request $method = [System.Net.Http.HttpMethod]::$Method $request = [System.Net.Http.HttpRequestMessage]::new($method, $Uri) if ($content) { $request.Content = $content } $response = $client.SendAsync($request).GetAwaiter().GetResult() - $respBody = $null if ($response.Content -ne $null) { $respBody = $response.Content.ReadAsStringAsync().GetAwaiter().GetResult() } if ($response.IsSuccessStatusCode) { - # Try to convert JSON if response body exists if ($respBody -and $respBody.Trim().Length -gt 0) { try { return $respBody | ConvertFrom-Json } catch { return $respBody } } - else { - return $respBody - } + else { return $respBody } } else { throw [System.Net.Http.HttpRequestException]::new("HTTP $($response.StatusCode): $($response.ReasonPhrase)", $null, $response) @@ -138,6 +126,7 @@ function Invoke-SafeRestMethod { } catch { Show-Failure -ErrorRecord $_ + return $null } finally { $client.Dispose() @@ -165,105 +154,128 @@ $pArgs = @{ # ---------------------------- # Ensure Posh-ACME Module # ---------------------------- -Write-Host "Checking for Required Module Posh-ACME" -ForegroundColor Green if (-not (Get-Module -ListAvailable -Name Posh-ACME)) { - Write-Host "Posh-ACME Module Not Found, Installing..." -ForegroundColor Yellow Install-Module -Name Posh-ACME -Force -Confirm:$false -Scope AllUsers } Import-Module Posh-ACME -ErrorAction Stop -Write-Host "Posh-ACME module loaded." -ForegroundColor Green # ---------------------------- -# vCenter API Session (Option A) +# vCenter API Session # ---------------------------- $loginUri = "https://$vCenterURL/rest/com/vmware/cis/session" Write-Host "Connecting to vCenter at $vCenterURL ..." -ForegroundColor Cyan -$sessionResponse = Invoke-SafeRestMethod -Uri $loginUri -Method Post -Headers @{} -if ($sessionResponse -is [string] -or -not $sessionResponse) { - # Session might be in header - $sessionToken = $null - try { - $httpclient = [System.Net.Http.HttpClient]::new() +try { + $sessionResponse = Invoke-SafeRestMethod -Uri $loginUri -Method Post -Headers @{} + $sessionToken = $sessionResponse.value + if (-not $sessionToken) { + # fallback: session token may be in header + $httpClient = [System.Net.Http.HttpClient]::new() $req = [System.Net.Http.HttpRequestMessage]::new([System.Net.Http.HttpMethod]::Post, $loginUri) - $resp = $httpclient.SendAsync($req).GetAwaiter().GetResult() + $resp = $httpClient.SendAsync($req).GetAwaiter().GetResult() if ($resp.Headers.Contains("vmware-api-session-id")) { $sessionToken = $resp.Headers.GetValues("vmware-api-session-id") | Select-Object -First 1 } - $httpclient.Dispose() + $httpClient.Dispose() } - catch { - Show-Failure -ErrorRecord $_ - } -} -else { - $sessionToken = $sessionResponse.value -} - -if (-not $sessionToken) { - Show-Failure -ErrorRecord ([pscustomobject]@{ Exception = [System.Exception] "Unable to get vCenter session token" }) -} - -Write-Host "Connected to vCenter API. Session established." -ForegroundColor Green - -# ---------------------------- -# Retrieve VM list -# ---------------------------- -$headers = @{ 'vmware-api-session-id' = $sessionToken } -$vmList = Invoke-SafeRestMethod -Uri "https://$vCenterURL/rest/vcenter/vm" -Headers $headers -if ($vmList.value) { - Write-Host "Retrieved VM list from vCenter:" -ForegroundColor Cyan - $vmList.value | ForEach-Object { Write-Host " - $($_.name)" } -} - -# ---------------------------- -# PowerDNS / Posh-ACME certificate -# ---------------------------- -$certName = "vcenter-cert" -try { - New-PACertificate -Domain $CommonName -DnsPlugin PowerDNS -PluginArgs $pArgs -Contact $EmailContact -AcceptTOS -Verbose -Force - Write-Host "ACME certificate request completed." -ForegroundColor Green } catch { Show-Failure -ErrorRecord $_ } +if (-not $sessionToken) { Show-Failure -ErrorRecord ([pscustomobject]@{ Exception = [System.Exception] "Unable to obtain vCenter session token" }) } +Write-Host "Connected to vCenter API. Session established." -ForegroundColor Green + +$headers = @{ 'vmware-api-session-id' = $sessionToken } + +# ---------------------------- +# Retrieve VM List (optional) +# ---------------------------- +try { + $vmList = Invoke-SafeRestMethod -Uri "https://$vCenterURL/rest/vcenter/vm" -Headers $headers + if ($vmList.value) { + Write-Host "Retrieved VM list:" -ForegroundColor Cyan + $vmList.value | ForEach-Object { Write-Host " - $($_.name)" } + } +} +catch { + Write-Host "Unable to retrieve VM list, continuing..." -ForegroundColor Yellow +} + +# ---------------------------- +# Fault-tolerant ACME certificate request +# ---------------------------- +$certName = "vcenter-cert" +$certSuccess = $false +try { + Write-Host "Requesting certificate via Posh-ACME..." -ForegroundColor Cyan + New-PACertificate -Domain $CommonName -DnsPlugin PowerDNS -PluginArgs $pArgs -Contact $EmailContact -AcceptTOS -Verbose -Force + $certSuccess = $true +} +catch { + Write-Host "ACME certificate request failed: $($_.Exception.Message)" -ForegroundColor Yellow + $global:helpme = $_.Exception.Message +} + # ---------------------------- # Collect certificate paths # ---------------------------- $paAccount = Get-PAAccount $certFolder = $paAccount.CertFolder -$certPath = Join-Path -Path $certFolder -ChildPath "$certName\cert.pem" -$keyPath = Join-Path -Path $certFolder -ChildPath "$certName\privkey.pem" -$chainPath = Join-Path -Path $certFolder -ChildPath "$certName\chain.pem" +$certPath = Join-Path $certFolder "$certName\cert.pem" +$keyPath = Join-Path $certFolder "$certName\privkey.pem" +$chainPath = Join-Path $certFolder "$certName\chain.pem" + foreach ($f in @($certPath, $keyPath, $chainPath)) { - if (-not (Test-Path $f)) { Show-Failure -ErrorRecord ([pscustomobject]@{ Exception = [System.Exception] "Certificate file missing: $f" }) } + if (-not (Test-Path $f)) { + Write-Host "Certificate file missing: $f" -ForegroundColor Yellow + $certSuccess = $false + } } # ---------------------------- -# Upload certificate to vCenter +# Upload certificate to vCenter (if ACME succeeded) # ---------------------------- -$uploadUri = "https://$vCenterURL/rest/vcenter/certificate-management/vcenter/tls" -$body = @{ - cert = Get-Content -Path $certPath -Raw - key = Get-Content -Path $keyPath -Raw - chain = Get-Content -Path $chainPath -Raw -} -Invoke-SafeRestMethod -Uri $uploadUri -Method Post -Headers $headers -Body $body -AsJson -Write-Host "Certificate uploaded to vCenter." -ForegroundColor Green +if ($certSuccess) { + try { + Write-Host "Uploading certificate to vCenter..." -ForegroundColor Cyan + $body = @{ + cert = Get-Content -Path $certPath -Raw + key = Get-Content -Path $keyPath -Raw + chain = Get-Content -Path $chainPath -Raw + } + Invoke-SafeRestMethod -Uri "https://$vCenterURL/rest/vcenter/certificate-management/vcenter/tls" -Method Post -Headers $headers -Body $body -AsJson + Write-Host "Certificate uploaded successfully." -ForegroundColor Green + } + catch { + Write-Host "Certificate upload failed: $($_.Exception.Message)" -ForegroundColor Yellow + $global:helpme = $_.Exception.Message + } -# ---------------------------- -# Apply certificate -# ---------------------------- -$applyUri = "https://$vCenterURL/rest/vcenter/certificate-management/vcenter/tls?action=apply" -Invoke-SafeRestMethod -Uri $applyUri -Method Post -Headers $headers -Write-Host "TLS certificate applied." -ForegroundColor Green + # Apply certificate + try { + Invoke-SafeRestMethod -Uri "https://$vCenterURL/rest/vcenter/certificate-management/vcenter/tls?action=apply" -Method Post -Headers $headers + Write-Host "TLS certificate applied." -ForegroundColor Green + } + catch { + Write-Host "Failed to apply certificate: $($_.Exception.Message)" -ForegroundColor Yellow + $global:helpme = $_.Exception.Message + } +} +else { + Write-Host "Skipping certificate upload/apply due to previous errors." -ForegroundColor Yellow +} # ---------------------------- # Restart vCenter vpxd service # ---------------------------- -$restartUri = "https://$vCenterURL/rest/appliance/system/services/vpxd?action=restart" -Invoke-SafeRestMethod -Uri $restartUri -Method Post -Headers $headers -Write-Host "vCenter vpxd service restart requested." -ForegroundColor Yellow +try { + Invoke-SafeRestMethod -Uri "https://$vCenterURL/rest/appliance/system/services/vpxd?action=restart" -Method Post -Headers $headers + Write-Host "vCenter vpxd service restart requested." -ForegroundColor Yellow +} +catch { + Write-Host "Failed to restart vpxd service: $($_.Exception.Message)" -ForegroundColor Yellow + $global:helpme = $_.Exception.Message +} -Write-Host "Script completed successfully." -ForegroundColor Green +Write-Host "Script completed. Check `$global:helpme for any error details." -ForegroundColor Green