8ca3e530c5
codice: - template/Validate-SetupState.ps1: docblock HTTP/Basic -> HTTPS/Basic, port 5986 - scripts/Validate-HostState.ps1 (nuovo): check ACL SSH key ci_linux, Credential Manager target BuildVMGuest, struttura dir F:\CI\, vmrun.exe raggiungibile docs: - docs/TEST-PLAN-v1.3-to-HEAD.md sezione 3.6: campi JSONL corretti (cloneSec/startSec/ ipSec/winrmSec/destroySec/totalBootSec) per riflettere output reale di Measure-CIBenchmark.ps1 - docs/CI-FLOW.md tabella failure: 'partial artifacts' -> Get-GuestDiagnostics a diagnostics/ - docs/CHANGELOG.md (nuovo): log reverse-cronologico di tutti gli sprint completati - plans/final-master-plan.md: tutti i Low marcati done
191 lines
6.3 KiB
PowerShell
191 lines
6.3 KiB
PowerShell
#Requires -Version 5.1
|
|
<#
|
|
.SYNOPSIS
|
|
Validates host-side security prerequisites for the CI system.
|
|
|
|
.DESCRIPTION
|
|
Checks that:
|
|
1. The SSH private key for Linux guest access exists and has restrictive ACLs
|
|
(only the current user and SYSTEM/Administrators may read it).
|
|
2. The Windows Credential Manager target used by CI guest sessions is accessible.
|
|
3. The CI key directories exist with expected permissions.
|
|
|
|
Run after `Setup-Host.ps1` and any time credentials or keys are rotated.
|
|
Exits with code 0 on all-pass, non-zero if any check fails.
|
|
|
|
.PARAMETER SshKeyPath
|
|
Path to the SSH private key used for Linux guest access.
|
|
Default: F:\CI\keys\ci_linux
|
|
|
|
.PARAMETER GuestCredTarget
|
|
Windows Credential Manager target name for Windows guest credentials.
|
|
Default: BuildVMGuest
|
|
|
|
.PARAMETER CIRootDir
|
|
Root CI directory to verify existence.
|
|
Default: F:\CI
|
|
|
|
.EXAMPLE
|
|
.\Validate-HostState.ps1
|
|
|
|
.EXAMPLE
|
|
.\Validate-HostState.ps1 -SshKeyPath 'F:\CI\keys\ci_linux' -Verbose
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[string] $SshKeyPath = 'F:\CI\keys\ci_linux',
|
|
[string] $GuestCredTarget = 'BuildVMGuest',
|
|
[string] $CIRootDir = 'F:\CI'
|
|
)
|
|
|
|
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
# ── Helpers ───────────────────────────────────────────────────────────────────
|
|
|
|
$script:passCount = 0
|
|
$script:failCount = 0
|
|
|
|
function Write-Check {
|
|
param([string]$Label, [scriptblock]$Test)
|
|
try {
|
|
$result = & $Test
|
|
if ($result) {
|
|
Write-Host " [OK] $Label"
|
|
$script:passCount++
|
|
} else {
|
|
Write-Host " [FAIL] $Label"
|
|
$script:failCount++
|
|
}
|
|
} catch {
|
|
Write-Host " [FAIL] $Label — $_"
|
|
$script:failCount++
|
|
}
|
|
}
|
|
|
|
# ── Check 1: SSH key file exists ──────────────────────────────────────────────
|
|
|
|
Write-Host "`n=== SSH Key ($SshKeyPath) ==="
|
|
|
|
Write-Check "SSH private key file exists" {
|
|
Test-Path $SshKeyPath -PathType Leaf
|
|
}
|
|
|
|
Write-Check "SSH public key file exists" {
|
|
Test-Path "${SshKeyPath}.pub" -PathType Leaf
|
|
}
|
|
|
|
# Check ACLs: only SYSTEM, Administrators, and the current user should have access
|
|
Write-Check "SSH key ACL — no unexpected identities" {
|
|
if (-not (Test-Path $SshKeyPath -PathType Leaf)) { return $false }
|
|
|
|
$acl = Get-Acl -Path $SshKeyPath
|
|
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
|
|
|
|
# Allowed identities (case-insensitive prefix matching)
|
|
$allowedPatterns = @(
|
|
'NT AUTHORITY\SYSTEM',
|
|
'BUILTIN\Administrators',
|
|
$currentUser
|
|
)
|
|
|
|
$unexpected = @()
|
|
foreach ($ace in $acl.Access) {
|
|
$identity = $ace.IdentityReference.Value
|
|
$matched = $false
|
|
foreach ($pattern in $allowedPatterns) {
|
|
if ($identity -ieq $pattern) { $matched = $true; break }
|
|
}
|
|
if (-not $matched) {
|
|
$unexpected += $identity
|
|
}
|
|
}
|
|
|
|
if ($unexpected.Count -gt 0) {
|
|
Write-Verbose "[ACL] Unexpected identities on SSH key: $($unexpected -join ', ')"
|
|
return $false
|
|
}
|
|
return $true
|
|
}
|
|
|
|
Write-Check "SSH key not world-readable (Everyone/Users absent)" {
|
|
if (-not (Test-Path $SshKeyPath -PathType Leaf)) { return $false }
|
|
|
|
$acl = Get-Acl -Path $SshKeyPath
|
|
$broad = $acl.Access | Where-Object {
|
|
$id = $_.IdentityReference.Value
|
|
$id -ilike '*Everyone*' -or $id -ilike 'BUILTIN\Users'
|
|
}
|
|
return ($null -eq $broad -or @($broad).Count -eq 0)
|
|
}
|
|
|
|
# ── Check 2: Credential Manager target ────────────────────────────────────────
|
|
|
|
Write-Host "`n=== Credential Manager (target: $GuestCredTarget) ==="
|
|
|
|
Write-Check "CredentialManager module available" {
|
|
$null -ne (Get-Module -Name CredentialManager -ListAvailable -ErrorAction SilentlyContinue)
|
|
}
|
|
|
|
Write-Check "Credential target '$GuestCredTarget' accessible" {
|
|
# Use cmdkey.exe as a fallback — avoids requiring CredentialManager module
|
|
$cmdkeyOut = & cmdkey.exe /list:$GuestCredTarget 2>&1 | Out-String
|
|
if ($cmdkeyOut -imatch [regex]::Escape($GuestCredTarget)) {
|
|
return $true
|
|
}
|
|
|
|
# Try via CredentialManager module if available
|
|
if ($null -ne (Get-Module -Name CredentialManager -ListAvailable -ErrorAction SilentlyContinue)) {
|
|
Import-Module CredentialManager -ErrorAction SilentlyContinue
|
|
$cred = Get-StoredCredential -Target $GuestCredTarget -ErrorAction SilentlyContinue
|
|
return ($null -ne $cred)
|
|
}
|
|
|
|
return $false
|
|
}
|
|
|
|
# ── Check 3: CI directory structure ───────────────────────────────────────────
|
|
|
|
Write-Host "`n=== CI Directory Structure ($CIRootDir) ==="
|
|
|
|
$requiredDirs = @(
|
|
'BuildVMs',
|
|
'Artifacts',
|
|
'Logs',
|
|
'State',
|
|
'keys',
|
|
'Cache\NuGet',
|
|
'Cache\pip',
|
|
'Templates'
|
|
)
|
|
|
|
foreach ($rel in $requiredDirs) {
|
|
$full = Join-Path $CIRootDir $rel
|
|
Write-Check "Directory exists: $rel" { Test-Path $full -PathType Container }
|
|
}
|
|
|
|
# ── Check 4: vmrun.exe reachable ──────────────────────────────────────────────
|
|
|
|
Write-Host "`n=== VMware ==="
|
|
|
|
Write-Check "vmrun.exe found in PATH or default location" {
|
|
$vmrunDefault = 'C:\Program Files (x86)\VMware\VMware Workstation\vmrun.exe'
|
|
$inPath = $null -ne (Get-Command 'vmrun.exe' -ErrorAction SilentlyContinue)
|
|
$atDefault = Test-Path $vmrunDefault -PathType Leaf
|
|
$inPath -or $atDefault
|
|
}
|
|
|
|
# ── Summary ───────────────────────────────────────────────────────────────────
|
|
|
|
Write-Host "`n=== Summary ==="
|
|
Write-Host " Passed : $($script:passCount)"
|
|
Write-Host " Failed : $($script:failCount)"
|
|
|
|
if ($script:failCount -gt 0) {
|
|
Write-Host "`n[Validate-HostState] $($script:failCount) check(s) failed. Review output above." -ForegroundColor Red
|
|
exit 1
|
|
} else {
|
|
Write-Host "`n[Validate-HostState] All checks passed." -ForegroundColor Green
|
|
exit 0
|
|
}
|