You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
699 lines
51 KiB
699 lines
51 KiB
param(
|
|
[string]$StagingRoot = "",
|
|
[ValidateSet("systemd", "buildroot")]
|
|
[string]$InitSystem = "systemd",
|
|
[switch]$KeepStaging
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
function Assert-FileNonEmpty {
|
|
param([string]$Path)
|
|
|
|
if (-not (Test-Path -LiteralPath $Path)) {
|
|
throw "Expected staged file is missing: $Path"
|
|
}
|
|
$item = Get-Item -LiteralPath $Path
|
|
if ($item.Length -le 0) {
|
|
throw "Expected staged file is empty: $Path"
|
|
}
|
|
}
|
|
|
|
function Assert-Contains {
|
|
param(
|
|
[string]$Text,
|
|
[string]$Expected,
|
|
[string]$Message
|
|
)
|
|
|
|
if (-not $Text.Contains($Expected)) {
|
|
throw $Message
|
|
}
|
|
}
|
|
|
|
function Assert-NoSecretEnvironmentValues {
|
|
param([string]$Text)
|
|
|
|
foreach ($line in ($Text -split "`r?`n")) {
|
|
if ($line -notmatch "^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$") {
|
|
continue
|
|
}
|
|
$key = $Matches[1]
|
|
$value = $Matches[2].Trim()
|
|
if ($value -eq "") {
|
|
continue
|
|
}
|
|
if ($key -eq "TERA_API_PASSWORD_KDF" -or $key -eq "TERA_API_MIN_PASSWORD_LENGTH" -or $key -eq "TERA_API_SECRET_STORE") {
|
|
continue
|
|
}
|
|
if ($key -match "(?i)(PASSWORD|TOKEN|SECRET|PRIVATE_KEY)") {
|
|
throw "target env must not contain secret material in $key"
|
|
}
|
|
}
|
|
}
|
|
|
|
$scriptApiRoot = Resolve-Path (Join-Path $PSScriptRoot "..")
|
|
$workingApiRoot = Resolve-Path (Get-Location)
|
|
if (Test-Path -LiteralPath (Join-Path $workingApiRoot "go.mod")) {
|
|
$apiRoot = $workingApiRoot
|
|
} elseif (Test-Path -LiteralPath (Join-Path $scriptApiRoot "go.mod")) {
|
|
$apiRoot = $scriptApiRoot
|
|
} else {
|
|
throw "Run this script from services\tera-api, or place it under services\tera-api\scripts."
|
|
}
|
|
$repoRoot = Resolve-Path (Join-Path $apiRoot "..\..")
|
|
$projectRoot = Resolve-Path (Join-Path $apiRoot "..")
|
|
|
|
$buildScript = Join-Path $apiRoot "scripts\check-linux-arm64-build.ps1"
|
|
$unitSource = Join-Path $apiRoot "deploy\systemd\tera-api.service"
|
|
$envSource = Join-Path $apiRoot "deploy\etc\tera\tera-api.env"
|
|
$accessdUnitSource = Join-Path $apiRoot "deploy\systemd\tera-accessd.service"
|
|
$accessdEnvSource = Join-Path $apiRoot "deploy\etc\tera\tera-accessd.env"
|
|
$hwdUnitSource = Join-Path $apiRoot "deploy\systemd\tera-hwd.service"
|
|
$hwdEnvSource = Join-Path $apiRoot "deploy\etc\tera\tera-hwd.env"
|
|
$netdUnitSource = Join-Path $apiRoot "deploy\systemd\tera-netd.service"
|
|
$netdEnvSource = Join-Path $apiRoot "deploy\etc\tera\tera-netd.env"
|
|
$timedUnitSource = Join-Path $apiRoot "deploy\systemd\tera-timed.service"
|
|
$timedEnvSource = Join-Path $apiRoot "deploy\etc\tera\tera-timed.env"
|
|
$alarmdUnitSource = Join-Path $apiRoot "deploy\systemd\tera-alarmd.service"
|
|
$alarmdEnvSource = Join-Path $apiRoot "deploy\etc\tera\tera-alarmd.env"
|
|
$snmpdUnitSource = Join-Path $apiRoot "deploy\systemd\tera-snmpd.service"
|
|
$snmpdBuildrootEnvSource = Join-Path $apiRoot "deploy\etc\tera\tera-snmpd.env"
|
|
$snmpdSystemdEnvSource = Join-Path $apiRoot "deploy\etc\tera\tera-snmpd-systemd.env"
|
|
$logdUnitSource = Join-Path $apiRoot "deploy\systemd\tera-logd.service"
|
|
$logdEnvSource = Join-Path $apiRoot "deploy\etc\tera\tera-logd.env"
|
|
$updatedUnitSource = Join-Path $apiRoot "deploy\systemd\tera-updated.service"
|
|
$updatedEnvSource = Join-Path $apiRoot "deploy\etc\tera\tera-updated.env"
|
|
$serialdUnitSource = Join-Path $apiRoot "deploy\systemd\tera-seriald.service"
|
|
$serialdEnvSource = Join-Path $apiRoot "deploy\etc\tera\tera-seriald.env"
|
|
$sessiondUnitSource = Join-Path $apiRoot "deploy\systemd\tera-sessiond.service"
|
|
$sessiondEnvSource = Join-Path $apiRoot "deploy\etc\tera\tera-sessiond.env"
|
|
$webUnitSource = Join-Path $apiRoot "deploy\systemd\tera-web.service"
|
|
$webEnvSource = Join-Path $apiRoot "deploy\etc\tera\tera-web.env"
|
|
$cloneDemoWebBuildScript = Join-Path $projectRoot "frontend\scripts\check-linux-arm64-build.ps1"
|
|
$cloneDemoWebUnitSource = Join-Path $apiRoot "deploy\systemd\tera-clone-demo-web.service"
|
|
$cloneDemoWebEnvSource = Join-Path $apiRoot "deploy\etc\tera\tera-clone-demo-web.env"
|
|
$webSource = Join-Path $repoRoot "webapp"
|
|
$serialMapSource = Join-Path $apiRoot "deploy\etc\tera\serial-map.json"
|
|
$updateTrustSource = Join-Path $apiRoot "deploy\etc\tera\update-trust"
|
|
$updateTrustReadmeSource = Join-Path $updateTrustSource "README"
|
|
$updateTrustAnchorSources = @(Get-ChildItem -LiteralPath $updateTrustSource -Force -File -Filter "*.json")
|
|
if ($updateTrustAnchorSources.Count -eq 0) {
|
|
throw "At least one reviewed public update trust anchor is required for target staging."
|
|
}
|
|
$buildrootSerialdSource = Join-Path $apiRoot "deploy\buildroot\etc\init.d\S40tera-seriald"
|
|
$buildrootHwdSource = Join-Path $apiRoot "deploy\buildroot\etc\init.d\S42tera-hwd"
|
|
$buildrootNetdSource = Join-Path $apiRoot "deploy\buildroot\etc\init.d\S43tera-netd"
|
|
$buildrootTimedSource = Join-Path $apiRoot "deploy\buildroot\etc\init.d\S45tera-timed"
|
|
$buildrootAlarmdSource = Join-Path $apiRoot "deploy\buildroot\etc\init.d\S47tera-alarmd"
|
|
$buildrootSNMPDSource = Join-Path $apiRoot "deploy\buildroot\etc\init.d\S46tera-snmpd"
|
|
$buildrootLogdSource = Join-Path $apiRoot "deploy\buildroot\etc\init.d\S44tera-logd"
|
|
$buildrootUpdatedSource = Join-Path $apiRoot "deploy\buildroot\etc\init.d\S48tera-updated"
|
|
$buildrootAPISource = Join-Path $apiRoot "deploy\buildroot\etc\init.d\S50tera-api"
|
|
$buildrootAccessdSource = Join-Path $apiRoot "deploy\buildroot\etc\init.d\S49tera-accessd"
|
|
$buildrootSessiondSource = Join-Path $apiRoot "deploy\buildroot\etc\init.d\S55tera-sessiond"
|
|
$buildrootWebSource = Join-Path $apiRoot "deploy\buildroot\etc\init.d\S60tera-web"
|
|
$buildrootCloneDemoWebSource = Join-Path $apiRoot "deploy\buildroot\etc\init.d\S61tera-clone-demo-web"
|
|
$buildrootUdevSource = Join-Path $apiRoot "deploy\buildroot\etc\udev\rules.d\99-tera-serial.rules"
|
|
|
|
$requiredPaths = @($buildScript, $cloneDemoWebBuildScript, $envSource, $accessdEnvSource, $hwdEnvSource, $netdEnvSource, $timedEnvSource, $alarmdEnvSource, $snmpdBuildrootEnvSource, $snmpdSystemdEnvSource, $logdEnvSource, $updatedEnvSource, $serialdEnvSource, $sessiondEnvSource, $webEnvSource, $cloneDemoWebEnvSource, $webSource, $serialMapSource, $updateTrustReadmeSource) + $updateTrustAnchorSources.FullName
|
|
if ($InitSystem -eq "systemd") {
|
|
$requiredPaths += @($unitSource, $accessdUnitSource, $hwdUnitSource, $netdUnitSource, $timedUnitSource, $alarmdUnitSource, $snmpdUnitSource, $logdUnitSource, $updatedUnitSource, $serialdUnitSource, $sessiondUnitSource, $webUnitSource, $cloneDemoWebUnitSource)
|
|
} else {
|
|
$requiredPaths += @($buildrootSerialdSource, $buildrootHwdSource, $buildrootNetdSource, $buildrootLogdSource, $buildrootTimedSource, $buildrootAlarmdSource, $buildrootSNMPDSource, $buildrootUpdatedSource, $buildrootAccessdSource, $buildrootAPISource, $buildrootSessiondSource, $buildrootWebSource, $buildrootCloneDemoWebSource, $buildrootUdevSource)
|
|
}
|
|
foreach ($requiredPath in $requiredPaths) {
|
|
if (-not (Test-Path -LiteralPath $requiredPath)) {
|
|
throw "Required packaging input is missing: $requiredPath"
|
|
}
|
|
}
|
|
|
|
$createdTempRoot = $false
|
|
if ($StagingRoot -eq "") {
|
|
$StagingRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("tera-api-rootfs-" + [System.Guid]::NewGuid().ToString("N"))
|
|
$createdTempRoot = $true
|
|
}
|
|
|
|
if (Test-Path -LiteralPath $StagingRoot) {
|
|
throw "Staging root already exists; choose an empty path: $StagingRoot"
|
|
}
|
|
|
|
$binTarget = Join-Path $StagingRoot "usr\bin\tera-api"
|
|
$accessdBinTarget = Join-Path $StagingRoot "usr\bin\tera-accessd"
|
|
$hwdBinTarget = Join-Path $StagingRoot "usr\bin\tera-hwd"
|
|
$netdBinTarget = Join-Path $StagingRoot "usr\bin\tera-netd"
|
|
$timedBinTarget = Join-Path $StagingRoot "usr\bin\tera-timed"
|
|
$alarmdBinTarget = Join-Path $StagingRoot "usr\bin\tera-alarmd"
|
|
$snmpdBinTarget = Join-Path $StagingRoot "usr\bin\tera-snmpd"
|
|
$logdBinTarget = Join-Path $StagingRoot "usr\bin\tera-logd"
|
|
$updatedBinTarget = Join-Path $StagingRoot "usr\bin\tera-updated"
|
|
$serialdBinTarget = Join-Path $StagingRoot "usr\bin\tera-seriald"
|
|
$sessiondBinTarget = Join-Path $StagingRoot "usr\bin\tera-sessiond"
|
|
$webBinTarget = Join-Path $StagingRoot "usr\bin\tera-web"
|
|
$cloneDemoWebBinTarget = Join-Path $StagingRoot "usr\bin\tera-clone-demo-web"
|
|
$serialCheckBinTarget = Join-Path $StagingRoot "usr\bin\tera-serial-check"
|
|
$serialLineCheckBinTarget = Join-Path $StagingRoot "usr\bin\tera-serial-line-check"
|
|
$unitTarget = Join-Path $StagingRoot "etc\systemd\system\tera-api.service"
|
|
$accessdUnitTarget = Join-Path $StagingRoot "etc\systemd\system\tera-accessd.service"
|
|
$hwdUnitTarget = Join-Path $StagingRoot "etc\systemd\system\tera-hwd.service"
|
|
$netdUnitTarget = Join-Path $StagingRoot "etc\systemd\system\tera-netd.service"
|
|
$timedUnitTarget = Join-Path $StagingRoot "etc\systemd\system\tera-timed.service"
|
|
$alarmdUnitTarget = Join-Path $StagingRoot "etc\systemd\system\tera-alarmd.service"
|
|
$snmpdUnitTarget = Join-Path $StagingRoot "etc\systemd\system\tera-snmpd.service"
|
|
$logdUnitTarget = Join-Path $StagingRoot "etc\systemd\system\tera-logd.service"
|
|
$updatedUnitTarget = Join-Path $StagingRoot "etc\systemd\system\tera-updated.service"
|
|
$serialdUnitTarget = Join-Path $StagingRoot "etc\systemd\system\tera-seriald.service"
|
|
$sessiondUnitTarget = Join-Path $StagingRoot "etc\systemd\system\tera-sessiond.service"
|
|
$webUnitTarget = Join-Path $StagingRoot "etc\systemd\system\tera-web.service"
|
|
$cloneDemoWebUnitTarget = Join-Path $StagingRoot "etc\systemd\system\tera-clone-demo-web.service"
|
|
$envTarget = Join-Path $StagingRoot "etc\tera\tera-api.env"
|
|
$accessdEnvTarget = Join-Path $StagingRoot "etc\tera\tera-accessd.env"
|
|
$hwdEnvTarget = Join-Path $StagingRoot "etc\tera\tera-hwd.env"
|
|
$netdEnvTarget = Join-Path $StagingRoot "etc\tera\tera-netd.env"
|
|
$timedEnvTarget = Join-Path $StagingRoot "etc\tera\tera-timed.env"
|
|
$alarmdEnvTarget = Join-Path $StagingRoot "etc\tera\tera-alarmd.env"
|
|
$snmpdEnvTarget = Join-Path $StagingRoot "etc\tera\tera-snmpd.env"
|
|
$logdEnvTarget = Join-Path $StagingRoot "etc\tera\tera-logd.env"
|
|
$updatedEnvTarget = Join-Path $StagingRoot "etc\tera\tera-updated.env"
|
|
$serialdEnvTarget = Join-Path $StagingRoot "etc\tera\tera-seriald.env"
|
|
$sessiondEnvTarget = Join-Path $StagingRoot "etc\tera\tera-sessiond.env"
|
|
$webEnvTarget = Join-Path $StagingRoot "etc\tera\tera-web.env"
|
|
$cloneDemoWebEnvTarget = Join-Path $StagingRoot "etc\tera\tera-clone-demo-web.env"
|
|
$webRootTarget = Join-Path $StagingRoot "usr\share\tera-web"
|
|
$serialMapTarget = Join-Path $StagingRoot "etc\tera\serial-map.json"
|
|
$updateTrustReadmeTarget = Join-Path $StagingRoot "etc\tera\update-trust\README"
|
|
$updateTrustAnchorTargets = @($updateTrustAnchorSources | ForEach-Object { Join-Path (Split-Path -Parent $updateTrustReadmeTarget) $_.Name })
|
|
$buildrootSerialdTarget = Join-Path $StagingRoot "etc\init.d\S40tera-seriald"
|
|
$buildrootHwdTarget = Join-Path $StagingRoot "etc\init.d\S42tera-hwd"
|
|
$buildrootNetdTarget = Join-Path $StagingRoot "etc\init.d\S43tera-netd"
|
|
$buildrootTimedTarget = Join-Path $StagingRoot "etc\init.d\S45tera-timed"
|
|
$buildrootAlarmdTarget = Join-Path $StagingRoot "etc\init.d\S47tera-alarmd"
|
|
$buildrootSNMPDTarget = Join-Path $StagingRoot "etc\init.d\S46tera-snmpd"
|
|
$buildrootLogdTarget = Join-Path $StagingRoot "etc\init.d\S44tera-logd"
|
|
$buildrootUpdatedTarget = Join-Path $StagingRoot "etc\init.d\S48tera-updated"
|
|
$buildrootAPITarget = Join-Path $StagingRoot "etc\init.d\S50tera-api"
|
|
$buildrootAccessdTarget = Join-Path $StagingRoot "etc\init.d\S49tera-accessd"
|
|
$buildrootSessiondTarget = Join-Path $StagingRoot "etc\init.d\S55tera-sessiond"
|
|
$buildrootWebTarget = Join-Path $StagingRoot "etc\init.d\S60tera-web"
|
|
$buildrootCloneDemoWebTarget = Join-Path $StagingRoot "etc\init.d\S61tera-clone-demo-web"
|
|
$buildrootUdevTarget = Join-Path $StagingRoot "etc\udev\rules.d\99-tera-serial.rules"
|
|
$stateDir = Join-Path $StagingRoot "var\lib\tera-api"
|
|
$logDir = Join-Path $StagingRoot "var\log\tera"
|
|
|
|
try {
|
|
foreach ($directory in @(
|
|
(Split-Path -Parent $binTarget),
|
|
(Split-Path -Parent $unitTarget),
|
|
(Split-Path -Parent $envTarget),
|
|
$webRootTarget,
|
|
(Split-Path -Parent $buildrootSerialdTarget),
|
|
(Split-Path -Parent $buildrootUdevTarget),
|
|
$stateDir,
|
|
$logDir
|
|
)) {
|
|
New-Item -ItemType Directory -Force $directory | Out-Null
|
|
}
|
|
|
|
& $buildScript -OutputPath $binTarget -Command tera-api
|
|
if ($LASTEXITCODE -ne 0) {
|
|
exit $LASTEXITCODE
|
|
}
|
|
& $buildScript -OutputPath $accessdBinTarget -Command tera-accessd
|
|
if ($LASTEXITCODE -ne 0) {
|
|
exit $LASTEXITCODE
|
|
}
|
|
& $buildScript -OutputPath $hwdBinTarget -Command tera-hwd
|
|
if ($LASTEXITCODE -ne 0) {
|
|
exit $LASTEXITCODE
|
|
}
|
|
& $buildScript -OutputPath $netdBinTarget -Command tera-netd
|
|
if ($LASTEXITCODE -ne 0) {
|
|
exit $LASTEXITCODE
|
|
}
|
|
& $buildScript -OutputPath $timedBinTarget -Command tera-timed
|
|
if ($LASTEXITCODE -ne 0) {
|
|
exit $LASTEXITCODE
|
|
}
|
|
& $buildScript -OutputPath $alarmdBinTarget -Command tera-alarmd
|
|
if ($LASTEXITCODE -ne 0) {
|
|
exit $LASTEXITCODE
|
|
}
|
|
& $buildScript -OutputPath $snmpdBinTarget -Command tera-snmpd
|
|
if ($LASTEXITCODE -ne 0) {
|
|
exit $LASTEXITCODE
|
|
}
|
|
& $buildScript -OutputPath $logdBinTarget -Command tera-logd
|
|
if ($LASTEXITCODE -ne 0) {
|
|
exit $LASTEXITCODE
|
|
}
|
|
& $buildScript -OutputPath $updatedBinTarget -Command tera-updated
|
|
if ($LASTEXITCODE -ne 0) {
|
|
exit $LASTEXITCODE
|
|
}
|
|
& $buildScript -OutputPath $serialdBinTarget -Command tera-seriald
|
|
if ($LASTEXITCODE -ne 0) {
|
|
exit $LASTEXITCODE
|
|
}
|
|
& $buildScript -OutputPath $sessiondBinTarget -Command tera-sessiond
|
|
if ($LASTEXITCODE -ne 0) {
|
|
exit $LASTEXITCODE
|
|
}
|
|
& $buildScript -OutputPath $webBinTarget -Command tera-web
|
|
if ($LASTEXITCODE -ne 0) {
|
|
exit $LASTEXITCODE
|
|
}
|
|
& $cloneDemoWebBuildScript -OutputPath $cloneDemoWebBinTarget
|
|
if ($LASTEXITCODE -ne 0) {
|
|
exit $LASTEXITCODE
|
|
}
|
|
& $buildScript -OutputPath $serialCheckBinTarget -Command tera-serial-check
|
|
if ($LASTEXITCODE -ne 0) {
|
|
exit $LASTEXITCODE
|
|
}
|
|
& $buildScript -OutputPath $serialLineCheckBinTarget -Command tera-serial-line-check
|
|
if ($LASTEXITCODE -ne 0) {
|
|
exit $LASTEXITCODE
|
|
}
|
|
|
|
Copy-Item -LiteralPath $envSource -Destination $envTarget -Force
|
|
Copy-Item -LiteralPath $accessdEnvSource -Destination $accessdEnvTarget -Force
|
|
Copy-Item -LiteralPath $hwdEnvSource -Destination $hwdEnvTarget -Force
|
|
Copy-Item -LiteralPath $netdEnvSource -Destination $netdEnvTarget -Force
|
|
Copy-Item -LiteralPath $timedEnvSource -Destination $timedEnvTarget -Force
|
|
Copy-Item -LiteralPath $alarmdEnvSource -Destination $alarmdEnvTarget -Force
|
|
Copy-Item -LiteralPath $logdEnvSource -Destination $logdEnvTarget -Force
|
|
Copy-Item -LiteralPath $updatedEnvSource -Destination $updatedEnvTarget -Force
|
|
Copy-Item -LiteralPath $serialdEnvSource -Destination $serialdEnvTarget -Force
|
|
Copy-Item -LiteralPath $sessiondEnvSource -Destination $sessiondEnvTarget -Force
|
|
Copy-Item -LiteralPath $webEnvSource -Destination $webEnvTarget -Force
|
|
Copy-Item -LiteralPath $cloneDemoWebEnvSource -Destination $cloneDemoWebEnvTarget -Force
|
|
Copy-Item -LiteralPath (Join-Path $webSource "index.html") -Destination (Join-Path $webRootTarget "index.html") -Force
|
|
Copy-Item -LiteralPath (Join-Path $webSource "src") -Destination (Join-Path $webRootTarget "src") -Recurse -Force
|
|
Copy-Item -LiteralPath $serialMapSource -Destination $serialMapTarget -Force
|
|
New-Item -ItemType Directory -Force (Split-Path -Parent $updateTrustReadmeTarget) | Out-Null
|
|
Copy-Item -LiteralPath $updateTrustReadmeSource -Destination $updateTrustReadmeTarget -Force
|
|
foreach ($anchorSource in $updateTrustAnchorSources) {
|
|
if (($anchorSource.Attributes -band [IO.FileAttributes]::ReparsePoint) -or
|
|
$anchorSource.Name.EndsWith(".private.json", [StringComparison]::OrdinalIgnoreCase)) {
|
|
throw "Update trust anchor must be a public regular file: $($anchorSource.FullName)"
|
|
}
|
|
$anchor = Get-Content -LiteralPath $anchorSource.FullName -Raw -Encoding UTF8 | ConvertFrom-Json
|
|
if ($anchor.format_version -ne 1 -or $anchor.algorithm -ne "ed25519" -or
|
|
$anchorSource.Name -cne "$($anchor.key_id).json" -or
|
|
$anchor.allowed_products -notcontains "tera-core-x48-pro" -or
|
|
$anchor.allowed_profiles -notcontains "fet3568j-c") {
|
|
throw "Update trust anchor policy is invalid: $($anchorSource.FullName)"
|
|
}
|
|
Copy-Item -LiteralPath $anchorSource.FullName -Destination (Join-Path (Split-Path -Parent $updateTrustReadmeTarget) $anchorSource.Name) -Force
|
|
}
|
|
if ($InitSystem -eq "systemd") {
|
|
Copy-Item -LiteralPath $snmpdSystemdEnvSource -Destination $snmpdEnvTarget -Force
|
|
Copy-Item -LiteralPath $unitSource -Destination $unitTarget -Force
|
|
Copy-Item -LiteralPath $accessdUnitSource -Destination $accessdUnitTarget -Force
|
|
Copy-Item -LiteralPath $hwdUnitSource -Destination $hwdUnitTarget -Force
|
|
Copy-Item -LiteralPath $netdUnitSource -Destination $netdUnitTarget -Force
|
|
Copy-Item -LiteralPath $timedUnitSource -Destination $timedUnitTarget -Force
|
|
Copy-Item -LiteralPath $alarmdUnitSource -Destination $alarmdUnitTarget -Force
|
|
Copy-Item -LiteralPath $snmpdUnitSource -Destination $snmpdUnitTarget -Force
|
|
Copy-Item -LiteralPath $logdUnitSource -Destination $logdUnitTarget -Force
|
|
Copy-Item -LiteralPath $updatedUnitSource -Destination $updatedUnitTarget -Force
|
|
Copy-Item -LiteralPath $serialdUnitSource -Destination $serialdUnitTarget -Force
|
|
Copy-Item -LiteralPath $sessiondUnitSource -Destination $sessiondUnitTarget -Force
|
|
Copy-Item -LiteralPath $webUnitSource -Destination $webUnitTarget -Force
|
|
Copy-Item -LiteralPath $cloneDemoWebUnitSource -Destination $cloneDemoWebUnitTarget -Force
|
|
} else {
|
|
Copy-Item -LiteralPath $snmpdBuildrootEnvSource -Destination $snmpdEnvTarget -Force
|
|
Copy-Item -LiteralPath $buildrootSerialdSource -Destination $buildrootSerialdTarget -Force
|
|
Copy-Item -LiteralPath $buildrootHwdSource -Destination $buildrootHwdTarget -Force
|
|
Copy-Item -LiteralPath $buildrootNetdSource -Destination $buildrootNetdTarget -Force
|
|
Copy-Item -LiteralPath $buildrootTimedSource -Destination $buildrootTimedTarget -Force
|
|
Copy-Item -LiteralPath $buildrootAlarmdSource -Destination $buildrootAlarmdTarget -Force
|
|
Copy-Item -LiteralPath $buildrootSNMPDSource -Destination $buildrootSNMPDTarget -Force
|
|
Copy-Item -LiteralPath $buildrootLogdSource -Destination $buildrootLogdTarget -Force
|
|
Copy-Item -LiteralPath $buildrootUpdatedSource -Destination $buildrootUpdatedTarget -Force
|
|
Copy-Item -LiteralPath $buildrootAPISource -Destination $buildrootAPITarget -Force
|
|
Copy-Item -LiteralPath $buildrootAccessdSource -Destination $buildrootAccessdTarget -Force
|
|
Copy-Item -LiteralPath $buildrootSessiondSource -Destination $buildrootSessiondTarget -Force
|
|
Copy-Item -LiteralPath $buildrootWebSource -Destination $buildrootWebTarget -Force
|
|
Copy-Item -LiteralPath $buildrootCloneDemoWebSource -Destination $buildrootCloneDemoWebTarget -Force
|
|
Copy-Item -LiteralPath $buildrootUdevSource -Destination $buildrootUdevTarget -Force
|
|
}
|
|
|
|
Assert-FileNonEmpty $binTarget
|
|
Assert-FileNonEmpty $accessdBinTarget
|
|
Assert-FileNonEmpty $hwdBinTarget
|
|
Assert-FileNonEmpty $netdBinTarget
|
|
Assert-FileNonEmpty $timedBinTarget
|
|
Assert-FileNonEmpty $alarmdBinTarget
|
|
Assert-FileNonEmpty $snmpdBinTarget
|
|
Assert-FileNonEmpty $logdBinTarget
|
|
Assert-FileNonEmpty $updatedBinTarget
|
|
Assert-FileNonEmpty $serialdBinTarget
|
|
Assert-FileNonEmpty $sessiondBinTarget
|
|
Assert-FileNonEmpty $webBinTarget
|
|
Assert-FileNonEmpty $cloneDemoWebBinTarget
|
|
Assert-FileNonEmpty $serialCheckBinTarget
|
|
Assert-FileNonEmpty $serialLineCheckBinTarget
|
|
Assert-FileNonEmpty $envTarget
|
|
Assert-FileNonEmpty $accessdEnvTarget
|
|
Assert-FileNonEmpty $hwdEnvTarget
|
|
Assert-FileNonEmpty $netdEnvTarget
|
|
Assert-FileNonEmpty $timedEnvTarget
|
|
Assert-FileNonEmpty $alarmdEnvTarget
|
|
Assert-FileNonEmpty $snmpdEnvTarget
|
|
Assert-FileNonEmpty $logdEnvTarget
|
|
Assert-FileNonEmpty $updatedEnvTarget
|
|
Assert-FileNonEmpty $serialdEnvTarget
|
|
Assert-FileNonEmpty $sessiondEnvTarget
|
|
Assert-FileNonEmpty $webEnvTarget
|
|
Assert-FileNonEmpty $cloneDemoWebEnvTarget
|
|
Assert-FileNonEmpty (Join-Path $webRootTarget "index.html")
|
|
Assert-FileNonEmpty (Join-Path $webRootTarget "src\direct-stream.js")
|
|
Assert-FileNonEmpty $serialMapTarget
|
|
Assert-FileNonEmpty $updateTrustReadmeTarget
|
|
foreach ($anchorTarget in $updateTrustAnchorTargets) {
|
|
Assert-FileNonEmpty $anchorTarget
|
|
}
|
|
if ($InitSystem -eq "systemd") {
|
|
Assert-FileNonEmpty $unitTarget
|
|
Assert-FileNonEmpty $accessdUnitTarget
|
|
Assert-FileNonEmpty $hwdUnitTarget
|
|
Assert-FileNonEmpty $netdUnitTarget
|
|
Assert-FileNonEmpty $timedUnitTarget
|
|
Assert-FileNonEmpty $alarmdUnitTarget
|
|
Assert-FileNonEmpty $snmpdUnitTarget
|
|
Assert-FileNonEmpty $logdUnitTarget
|
|
Assert-FileNonEmpty $updatedUnitTarget
|
|
Assert-FileNonEmpty $serialdUnitTarget
|
|
Assert-FileNonEmpty $sessiondUnitTarget
|
|
Assert-FileNonEmpty $webUnitTarget
|
|
Assert-FileNonEmpty $cloneDemoWebUnitTarget
|
|
} else {
|
|
Assert-FileNonEmpty $buildrootSerialdTarget
|
|
Assert-FileNonEmpty $buildrootHwdTarget
|
|
Assert-FileNonEmpty $buildrootNetdTarget
|
|
Assert-FileNonEmpty $buildrootTimedTarget
|
|
Assert-FileNonEmpty $buildrootAlarmdTarget
|
|
Assert-FileNonEmpty $buildrootSNMPDTarget
|
|
Assert-FileNonEmpty $buildrootLogdTarget
|
|
Assert-FileNonEmpty $buildrootUpdatedTarget
|
|
Assert-FileNonEmpty $buildrootAPITarget
|
|
Assert-FileNonEmpty $buildrootAccessdTarget
|
|
Assert-FileNonEmpty $buildrootSessiondTarget
|
|
Assert-FileNonEmpty $buildrootWebTarget
|
|
Assert-FileNonEmpty $buildrootCloneDemoWebTarget
|
|
Assert-FileNonEmpty $buildrootUdevTarget
|
|
}
|
|
|
|
$envText = Get-Content -Raw -LiteralPath $envTarget
|
|
$accessdEnvText = Get-Content -Raw -LiteralPath $accessdEnvTarget
|
|
$hwdEnvText = Get-Content -Raw -LiteralPath $hwdEnvTarget
|
|
$netdEnvText = Get-Content -Raw -LiteralPath $netdEnvTarget
|
|
$timedEnvText = Get-Content -Raw -LiteralPath $timedEnvTarget
|
|
$alarmdEnvText = Get-Content -Raw -LiteralPath $alarmdEnvTarget
|
|
$snmpdEnvText = Get-Content -Raw -LiteralPath $snmpdEnvTarget
|
|
$logdEnvText = Get-Content -Raw -LiteralPath $logdEnvTarget
|
|
$updatedEnvText = Get-Content -Raw -LiteralPath $updatedEnvTarget
|
|
$serialdEnvText = Get-Content -Raw -LiteralPath $serialdEnvTarget
|
|
$sessiondEnvText = Get-Content -Raw -LiteralPath $sessiondEnvTarget
|
|
$webEnvText = Get-Content -Raw -LiteralPath $webEnvTarget
|
|
$cloneDemoWebEnvText = Get-Content -Raw -LiteralPath $cloneDemoWebEnvTarget
|
|
$serialMapText = Get-Content -Raw -LiteralPath $serialMapTarget
|
|
|
|
Assert-Contains $envText "TERA_API_LISTEN_ADDR=127.0.0.1:8082" "target API must listen only behind the same-origin gateway"
|
|
Assert-Contains $envText "TERA_API_GREEN_LED_SOCKET=/run/tera-hwd/green-leds.sock" "target env must configure the hardware LED socket"
|
|
Assert-Contains $envText "TERA_API_SERIAL_SOCKET=/run/tera-seriald/serial-ports.sock" "target env must configure the serial controller socket"
|
|
Assert-Contains $envText "TERA_API_TIME_SOCKET=/run/tera-timed/time.sock" "target env must configure the system time socket"
|
|
Assert-Contains $envText "TERA_API_NETWORK_SOCKET=/run/tera-netd/control.sock" "target env must configure the privileged network socket"
|
|
Assert-Contains $envText "TERA_API_MANAGEMENT_SOCKET=/run/tera-accessd/control.sock" "target env must configure the management access socket"
|
|
Assert-Contains $accessdEnvText "TERA_ACCESSD_BIND_HOST=0.0.0.0" "management access listeners must bind the target interfaces"
|
|
Assert-Contains $envText "TERA_API_TLS_STORE=/var/lib/tera-api/tls/library" "TLS inventory must stay in protected API service state"
|
|
Assert-Contains $envText "TERA_API_TLS_ACTIVE_KEY=/var/lib/tera-api/tls/active.key" "active TLS private key must stay in protected API service state"
|
|
Assert-Contains $accessdEnvText "TERA_ACCESSD_TLS_KEY=/var/lib/tera-api/tls/active.key" "management listener must use the API-managed active TLS private key"
|
|
Assert-Contains $envText "TERA_API_ALARM_SOCKET=/run/tera-alarmd/alarm-delivery.sock" "target env must configure the alarm delivery socket"
|
|
Assert-Contains $envText "TERA_API_LOG_SOCKET=/run/tera-logd/log-store.sock" "target env must configure the authoritative log socket"
|
|
Assert-Contains $envText "TERA_API_LOG_SCHEDULER_ENABLED=true" "target env must enable configured status-event scheduling"
|
|
Assert-Contains $envText "TERA_API_LOG_ETHERNET_INTERFACES=Interface-1=eth0,Interface-2=eth1" "target env must bind logical Ethernet identities before reading carrier"
|
|
Assert-Contains $envText "TERA_API_UPDATE_SOCKET=/run/tera-updated/control.sock" "target env must configure the protected update socket"
|
|
Assert-Contains $envText "TERA_API_UPDATE_MAX_BYTES=1342177280" "target API must enforce the update package maximum"
|
|
Assert-Contains $envText "TERA_API_FIRMWARE_TIMEOUT_SECONDS=1800" "target API must use a route-specific bounded firmware timeout"
|
|
if ($envText.Contains("TERA_API_READ_TIMEOUT_SECONDS=1800") -or $envText.Contains("TERA_API_WRITE_TIMEOUT_SECONDS=1800")) {
|
|
throw "firmware upload must not expand the global API read/write timeouts"
|
|
}
|
|
Assert-Contains $envText "TERA_API_DIAGNOSTICS_ENABLED=true" "target env must enable the bounded diagnostics executor"
|
|
Assert-Contains $envText "TERA_API_DIAGNOSTICS_PING_PATH=/bin/ping" "target env must use a fixed ping executable"
|
|
Assert-Contains $envText "TERA_API_DIAGNOSTICS_WORKERS=2" "target diagnostics workers must remain bounded"
|
|
Assert-Contains $envText "TERA_API_DIAGNOSTICS_QUEUE=16" "target diagnostics queue must remain bounded"
|
|
Assert-Contains $envText "TERA_API_SUPPORT_BUNDLE_DIR=/var/lib/tera-api/support-bundles" "target support bundles must use the protected API state directory"
|
|
Assert-Contains $envText "TERA_API_SUPPORT_BUNDLE_MAX_ARTIFACTS=4" "target support bundle retention count must remain bounded"
|
|
Assert-Contains $envText "TERA_API_SUPPORT_BUNDLE_MAX_BYTES=8388608" "target support bundle artifact size must remain bounded"
|
|
Assert-Contains $envText "TERA_API_SUPPORT_BUNDLE_MAX_SECTION_BYTES=2097152" "target support bundle section size must remain bounded"
|
|
Assert-Contains $envText "TERA_API_AUTH_STORE=/var/lib/tera-api/auth.json" "target local users must persist across service restarts"
|
|
Assert-Contains $envText "TERA_API_SECRET_STORE=/var/lib/tera-api/secrets.json" "protected integration secrets must use the service-owned store path"
|
|
Assert-Contains $envText "TERA_API_MIN_PASSWORD_LENGTH=8" "target local password policy must enforce the approved eight-character minimum"
|
|
Assert-Contains $envText "TERA_API_PASSWORD_KDF=argon2id" "target env must default to Argon2id"
|
|
Assert-Contains $envText "TERA_API_ALLOW_MOCK_ACTORS=false" "target env must disable mock actors"
|
|
Assert-Contains $hwdEnvText "TERA_HWD_GREEN_LED_SYSFS_ROOT=/sys/class/leds" "hardware env must use kernel gpio-leds entries"
|
|
Assert-Contains $hwdEnvText "TERA_HWD_RESET_BUTTON_VALUE_PATH=/sys/class/gpio/manual_KeyReset/value" "hardware env must bind the active-low SW2 reset input"
|
|
Assert-Contains $envText "TERA_API_RESET_BUTTON_POLL_MILLISECONDS=100" "API env must poll the SW2 reset input promptly"
|
|
Assert-Contains $envText "TERA_API_RESET_BUTTON_HOLD_MILLISECONDS=1000" "API env must debounce SW2 before resetting settings"
|
|
Assert-Contains $netdEnvText "TERA_NETD_INTERFACES=/etc/network/interfaces" "network adapter must own the target interface file"
|
|
Assert-Contains $netdEnvText "TERA_NETD_PENDING=/var/lib/tera-network/pending.json" "network adapter must persist rollback state"
|
|
Assert-Contains $timedEnvText "TERA_TIMED_ZONEINFO_ROOT=/usr/share/zoneinfo" "time adapter must use the target zoneinfo database"
|
|
Assert-Contains $timedEnvText "TERA_TIMED_LOCALTIME_PATH=/etc/localtime" "time adapter must own only the product localtime path"
|
|
Assert-Contains $alarmdEnvText "TERA_ALARMD_STATE=/var/lib/tera-alarmd/deliveries.json" "alarm delivery retries must use the protected service state path"
|
|
Assert-Contains $alarmdEnvText "TERA_ALARMD_MAX_JOBS=256" "alarm delivery history and queue must remain bounded"
|
|
Assert-Contains $envText "TERA_API_SNMP_SOCKET=/run/tera-snmpd/control.sock" "API must use the privileged SNMP control socket"
|
|
Assert-Contains $snmpdEnvText "TERA_SNMPD_CONFIG=/etc/snmp/snmpd.conf" "SNMP adapter must use the Net-SNMP configuration path"
|
|
Assert-Contains $snmpdEnvText "TERA_SNMPD_PERSISTENT_CONFIG=/var/lib/net-snmp/snmpd.conf" "SNMPv3 bootstrap credentials must use the protected Net-SNMP state path"
|
|
Assert-Contains $logdEnvText "TERA_LOGD_ROOT=/userdata/log" "log service must use only the userdata log root"
|
|
Assert-Contains $logdEnvText "TERA_LOGD_STORAGE_POLICY=/userdata/storage-layout.json" "log service must consume the shared capacity policy"
|
|
Assert-Contains $logdEnvText "TERA_LOGD_SEGMENT_BYTES=4194304" "log service closed segments must remain bounded"
|
|
Assert-Contains $updatedEnvText "TERA_UPDATED_STORAGE_POLICY=/userdata/storage-layout.json" "updater must consume the shared capacity policy"
|
|
Assert-Contains $updatedEnvText "TERA_UPDATED_TRUST_DIR=/etc/tera/update-trust" "updater trust anchors must remain outside userdata"
|
|
Assert-Contains $updatedEnvText "TERA_UPDATED_PROFILE=fet3568j-c" "updater must bind packages to the target profile"
|
|
Assert-Contains $updatedEnvText "TERA_UPDATED_RECOVERY_APPLY_ENABLED=false" "recovery Apply must remain default-off until target evidence promotion"
|
|
Assert-Contains $updatedEnvText "TERA_UPDATED_RECOVERY_GATE=/etc/tera/recovery-apply-enabled.json" "recovery Apply must require the target evidence gate"
|
|
Assert-Contains $updatedEnvText "TERA_UPDATED_MISC_DEVICE=/dev/mmcblk0p2" "updater must bind the BCB request to the fixed misc partition"
|
|
Assert-Contains $serialdEnvText "TERA_SERIALD_MAPPING=/etc/tera/serial-map.json" "serial controller must require an explicit mapping artifact"
|
|
Assert-Contains $serialdEnvText "TERA_SERIALD_STREAM_SOCKET=/run/tera-seriald/serial-stream.sock" "serial controller must configure the separate data-plane socket"
|
|
Assert-Contains $serialdEnvText "TERA_SERIALD_EVENT_QUEUE=1024" "serial metadata observation must use a bounded queue"
|
|
Assert-Contains $serialdEnvText "TERA_SERIALD_REPORT_INTERVAL_SECONDS=10" "serial metadata observation must use a bounded report interval"
|
|
Assert-Contains $serialdEnvText "TERA_SERIALD_LOG_SOCKET=/run/tera-logd/log-store.sock" "serial payload capture must use the authoritative bounded log service"
|
|
Assert-Contains $envText "TERA_API_DIRECT_TICKET_SOCKET=/run/tera-api/direct-tickets.sock" "API must configure the Direct Mode ticket socket"
|
|
Assert-Contains $envText "TERA_API_SESSIOND_STATUS_URL=http://127.0.0.1:8081/readyz" "API must read actual session runtime health over loopback"
|
|
Assert-Contains $envText "TERA_API_SERIAL_RECONCILE_INTERVAL_SECONDS=30" "API must repair serial profiles after seriald restarts"
|
|
Assert-Contains $sessiondEnvText "TERA_SESSIOND_TICKET_SOCKET=/run/tera-api/direct-tickets.sock" "session broker must consume API tickets locally"
|
|
Assert-Contains $sessiondEnvText "TERA_SESSIOND_SERIAL_STREAM_SOCKET=/run/tera-seriald/serial-stream.sock" "session broker must use the serial data-plane socket"
|
|
Assert-Contains $webEnvText "TERA_WEB_LISTEN_ADDR=0.0.0.0:8080" "same-origin gateway must own the public listener"
|
|
Assert-Contains $webEnvText "TERA_WEB_API_UPSTREAM=http://127.0.0.1:8082" "same-origin gateway must route REST to loopback tera-api"
|
|
Assert-Contains $webEnvText "TERA_WEB_SESSION_UPSTREAM=http://127.0.0.1:8081" "same-origin gateway must route streams to loopback tera-sessiond"
|
|
if ($webEnvText -match "(?m)^PORT=" -or $webEnvText -match "(?m)^TERACLONE_DB_PATH=") {
|
|
throw "static tera-web env must not contain Clone Demo runtime settings"
|
|
}
|
|
Assert-Contains $cloneDemoWebEnvText "PORT=8083" "Clone Demo web must use its dedicated public port"
|
|
Assert-Contains $cloneDemoWebEnvText "TERACLONE_API_UPSTREAM=http://127.0.0.1:8082" "Clone Demo web must route REST to loopback tera-api"
|
|
Assert-Contains $cloneDemoWebEnvText "TERACLONE_SESSION_UPSTREAM=http://127.0.0.1:8081" "Clone Demo web must route streams to loopback tera-sessiond"
|
|
Assert-Contains $cloneDemoWebEnvText "TERACLONE_DB_PATH=/var/lib/tera-clone-demo-web/teraclone.db" "Clone Demo web must use isolated state"
|
|
|
|
$serialMap = $serialMapText | ConvertFrom-Json
|
|
if ($serialMap.format_version -ne 1 -or $serialMap.hardware_profile -ne "fet3568j-c" -or -not $serialMap.validated) {
|
|
throw "serial mapping metadata is invalid"
|
|
}
|
|
if ($serialMap.channels.Count -ne 48) {
|
|
throw "serial mapping must contain exactly 48 channels"
|
|
}
|
|
for ($index = 1; $index -le 48; $index++) {
|
|
$entry = $serialMap.channels[$index - 1]
|
|
$expectedChannel = "CH{0:d2}" -f $index
|
|
$expectedTTY = if ($index -le 24) { $index + 3 } else { $index + 7 }
|
|
$expectedDevice = "/dev/ttyWCH$expectedTTY"
|
|
$expectedBDF = if ($index -le 24) { "0000:01:00.0" } else { "0002:21:00.0" }
|
|
if ($entry.channel -ne $expectedChannel -or $entry.device -ne $expectedDevice -or $entry.pci_bdf -ne $expectedBDF) {
|
|
throw "serial mapping entry $expectedChannel does not match the canonical 24+24 layout"
|
|
}
|
|
}
|
|
|
|
if ($InitSystem -eq "systemd") {
|
|
$unitText = Get-Content -Raw -LiteralPath $unitTarget
|
|
$accessdUnitText = Get-Content -Raw -LiteralPath $accessdUnitTarget
|
|
$hwdUnitText = Get-Content -Raw -LiteralPath $hwdUnitTarget
|
|
$netdUnitText = Get-Content -Raw -LiteralPath $netdUnitTarget
|
|
$timedUnitText = Get-Content -Raw -LiteralPath $timedUnitTarget
|
|
$alarmdUnitText = Get-Content -Raw -LiteralPath $alarmdUnitTarget
|
|
$snmpdUnitText = Get-Content -Raw -LiteralPath $snmpdUnitTarget
|
|
$logdUnitText = Get-Content -Raw -LiteralPath $logdUnitTarget
|
|
$updatedUnitText = Get-Content -Raw -LiteralPath $updatedUnitTarget
|
|
$serialdUnitText = Get-Content -Raw -LiteralPath $serialdUnitTarget
|
|
$sessiondUnitText = Get-Content -Raw -LiteralPath $sessiondUnitTarget
|
|
$webUnitText = Get-Content -Raw -LiteralPath $webUnitTarget
|
|
$cloneDemoWebUnitText = Get-Content -Raw -LiteralPath $cloneDemoWebUnitTarget
|
|
Assert-Contains $unitText "ExecStart=/usr/bin/tera-api" "systemd unit must execute the installed API binary"
|
|
Assert-Contains $unitText "EnvironmentFile=-/etc/tera/tera-api.env" "systemd unit must load the target environment file"
|
|
Assert-Contains $unitText "User=tera" "systemd unit must run tera-api as the product service user"
|
|
Assert-Contains $unitText "NoNewPrivileges=true" "systemd unit must enable basic privilege hardening"
|
|
Assert-Contains $unitText "UMask=0007" "API ticket socket must remain group-restricted"
|
|
Assert-Contains $unitText "Wants=network-online.target tera-hwd.service tera-seriald.service tera-netd.service tera-logd.service tera-timed.service tera-alarmd.service tera-snmpd.service tera-updated.service tera-accessd.service" "tera-api must request its local service adapters"
|
|
Assert-Contains $accessdUnitText "ExecStart=/usr/bin/tera-accessd" "management access unit must execute the bounded listener service"
|
|
Assert-Contains $accessdUnitText "CapabilityBoundingSet=CAP_NET_BIND_SERVICE" "management access unit must retain only privileged-port binding capability"
|
|
Assert-Contains $accessdUnitText "ReadWritePaths=/run/tera-accessd /var/lib/tera-api/tls" "management access unit writes must stay in declared runtime and TLS state paths"
|
|
Assert-Contains $hwdUnitText "ExecStart=/usr/bin/tera-hwd" "hardware unit must execute the installed helper"
|
|
Assert-Contains $hwdUnitText "User=root" "hardware unit must own privileged gpio-led writes"
|
|
Assert-Contains $hwdUnitText "Group=tera" "hardware unit socket must be accessible to tera-api"
|
|
Assert-Contains $hwdUnitText "NoNewPrivileges=true" "hardware unit must enable basic privilege hardening"
|
|
Assert-Contains $hwdUnitText "ReadWritePaths=/run/tera-hwd /sys/class/leds" "hardware unit must only write its socket and gpio-led entries"
|
|
Assert-Contains $netdUnitText "ExecStart=/usr/bin/tera-netd" "network unit must execute the privileged adapter"
|
|
Assert-Contains $netdUnitText "CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW" "network unit must retain only network administration capabilities"
|
|
Assert-Contains $netdUnitText "ReadWritePaths=/run/tera-netd /var/lib/tera-network /etc/network" "network unit must restrict declared write paths"
|
|
Assert-Contains $timedUnitText "ExecStart=/usr/bin/tera-timed" "time unit must execute the installed helper"
|
|
Assert-Contains $timedUnitText "User=root" "time unit must own privileged clock changes"
|
|
Assert-Contains $timedUnitText "Group=tera" "time unit socket must be accessible to tera-api"
|
|
Assert-Contains $timedUnitText "CapabilityBoundingSet=CAP_SYS_TIME" "time unit must retain only the system-time capability"
|
|
Assert-Contains $timedUnitText "ReadWritePaths=/run/tera-timed /etc/localtime" "time unit must restrict declared write paths"
|
|
Assert-Contains $alarmdUnitText "ExecStart=/usr/bin/tera-alarmd" "alarm unit must execute the bounded delivery service"
|
|
Assert-Contains $alarmdUnitText "User=tera" "alarm delivery must run without root privileges"
|
|
Assert-Contains $alarmdUnitText "ReadWritePaths=/var/lib/tera-alarmd /run/tera-alarmd" "alarm unit must restrict declared write paths"
|
|
Assert-Contains $snmpdUnitText "ExecStart=/usr/bin/tera-snmpd" "SNMP unit must execute the privileged adapter"
|
|
Assert-Contains $snmpdUnitText "User=root" "SNMP adapter must own protected Net-SNMP configuration"
|
|
Assert-Contains $snmpdUnitText "ReadWritePaths=/run/tera-snmpd /etc/snmp /var/lib/net-snmp" "SNMP adapter writes must stay in declared paths"
|
|
Assert-Contains $logdUnitText "ExecStart=/usr/bin/tera-logd" "log unit must execute the authoritative storage service"
|
|
Assert-Contains $logdUnitText "User=tera-logd" "log unit must use the dedicated userdata owner"
|
|
Assert-Contains $logdUnitText "ReadWritePaths=/userdata/log /run/tera-logd" "log unit must never spill into rootfs"
|
|
Assert-Contains $updatedUnitText "ExecStart=/usr/bin/tera-updated" "update unit must execute the signed package validator"
|
|
Assert-Contains $updatedUnitText "User=tera-updated" "update unit must use the dedicated storage owner"
|
|
Assert-Contains $updatedUnitText "ReadWritePaths=/userdata/update /run/tera-updated" "update unit must restrict write paths"
|
|
Assert-Contains $serialdUnitText "ExecStart=/usr/bin/tera-seriald" "serial unit must execute the installed controller"
|
|
Assert-Contains $serialdUnitText "User=tera" "serial controller must run without root privileges"
|
|
Assert-Contains $serialdUnitText "DevicePolicy=closed" "serial controller must close unrelated devices"
|
|
Assert-Contains $serialdUnitText "DeviceAllow=/dev/ttyWCH* rw" "serial controller must only access WCH tty devices"
|
|
Assert-Contains $serialdUnitText "UMask=0007" "serial controller sockets must remain group-restricted"
|
|
Assert-Contains $serialdUnitText "Wants=tera-logd.service" "serial payload capture must request the authoritative log service"
|
|
Assert-Contains $sessiondUnitText "ExecStart=/usr/bin/tera-sessiond" "session unit must execute the bounded broker"
|
|
Assert-Contains $sessiondUnitText "User=tera" "session broker must run without root privileges"
|
|
Assert-Contains $sessiondUnitText "UMask=0007" "session broker local IPC must remain group-restricted"
|
|
Assert-Contains $webUnitText "ExecStart=/usr/bin/tera-web" "web unit must execute the static same-origin gateway"
|
|
Assert-Contains $webUnitText "ReadOnlyPaths=/usr/share/tera-web" "web unit must treat static assets as read-only"
|
|
Assert-Contains $webUnitText "UMask=0027" "static gateway files must not become world-writable"
|
|
Assert-Contains $cloneDemoWebUnitText "ExecStart=/usr/bin/tera-clone-demo-web" "Clone Demo unit must execute its dedicated binary"
|
|
Assert-Contains $cloneDemoWebUnitText "EnvironmentFile=-/etc/tera/tera-clone-demo-web.env" "Clone Demo unit must load its dedicated environment"
|
|
Assert-Contains $cloneDemoWebUnitText "ReadWritePaths=/var/lib/tera-clone-demo-web" "Clone Demo unit must isolate its writable state"
|
|
} else {
|
|
$buildrootSerialdText = Get-Content -Raw -LiteralPath $buildrootSerialdTarget
|
|
$buildrootHwdText = Get-Content -Raw -LiteralPath $buildrootHwdTarget
|
|
$buildrootNetdText = Get-Content -Raw -LiteralPath $buildrootNetdTarget
|
|
$buildrootTimedText = Get-Content -Raw -LiteralPath $buildrootTimedTarget
|
|
$buildrootAlarmdText = Get-Content -Raw -LiteralPath $buildrootAlarmdTarget
|
|
$buildrootSNMPDText = Get-Content -Raw -LiteralPath $buildrootSNMPDTarget
|
|
$buildrootLogdText = Get-Content -Raw -LiteralPath $buildrootLogdTarget
|
|
$buildrootUpdatedText = Get-Content -Raw -LiteralPath $buildrootUpdatedTarget
|
|
$buildrootAPIText = Get-Content -Raw -LiteralPath $buildrootAPITarget
|
|
$buildrootAccessdText = Get-Content -Raw -LiteralPath $buildrootAccessdTarget
|
|
$buildrootSessiondText = Get-Content -Raw -LiteralPath $buildrootSessiondTarget
|
|
$buildrootWebText = Get-Content -Raw -LiteralPath $buildrootWebTarget
|
|
$buildrootCloneDemoWebText = Get-Content -Raw -LiteralPath $buildrootCloneDemoWebTarget
|
|
$buildrootUdevText = Get-Content -Raw -LiteralPath $buildrootUdevTarget
|
|
Assert-Contains $buildrootSerialdText "DAEMON=/usr/bin/tera-seriald" "Buildroot serial init must execute tera-seriald"
|
|
Assert-Contains $buildrootSerialdText "RUN_AS=tera:tera" "Buildroot serial init must drop privileges"
|
|
Assert-Contains $buildrootSerialdText "normalize_service_account_password" "Buildroot service account setup must avoid BusyBox bad-salt sentinels"
|
|
Assert-Contains $buildrootSerialdText 'mkpasswd -m sha-256' "Buildroot service account sentinel must use a supported non-plaintext hash"
|
|
Assert-Contains $buildrootSerialdText '-s /bin/false' "Buildroot tera service account must remain non-interactive"
|
|
Assert-Contains $buildrootSerialdText "start-stop-daemon" "Buildroot serial init must use BusyBox supervision"
|
|
Assert-Contains $buildrootHwdText "DAEMON=/usr/bin/tera-hwd" "Buildroot hardware init must execute tera-hwd"
|
|
Assert-Contains $buildrootHwdText "RUN_AS=root:tera" "Buildroot hardware init must retain root UID with only the shared service group"
|
|
Assert-Contains $buildrootHwdText "umask 0007" "Buildroot hardware socket must not be world accessible"
|
|
Assert-Contains $buildrootNetdText "DAEMON=/usr/bin/tera-netd" "Buildroot network init must execute tera-netd"
|
|
Assert-Contains $buildrootNetdText "RUN_AS=root:tera" "Buildroot network adapter must retain root UID with only the shared service group"
|
|
Assert-Contains $buildrootNetdText "chmod 0700 /var/lib/tera-network" "Buildroot rollback state must be root-only"
|
|
Assert-Contains $buildrootTimedText "DAEMON=/usr/bin/tera-timed" "Buildroot time init must execute tera-timed"
|
|
Assert-Contains $buildrootTimedText "chown root:tera /run/tera-timed" "Buildroot time socket directory must be group restricted"
|
|
Assert-Contains $buildrootTimedText "RUN_AS=root:tera" "Buildroot time adapter must retain root UID with only the shared service group"
|
|
Assert-Contains $buildrootTimedText "umask 0007" "Buildroot time socket must not be world accessible"
|
|
Assert-Contains $buildrootAlarmdText "DAEMON=/usr/bin/tera-alarmd" "Buildroot alarm init must execute tera-alarmd"
|
|
Assert-Contains $buildrootAlarmdText "start-stop-daemon" "Buildroot alarm init must use BusyBox supervision"
|
|
Assert-Contains $buildrootSNMPDText "DAEMON=/usr/bin/tera-snmpd" "Buildroot SNMP init must execute tera-snmpd"
|
|
Assert-Contains $buildrootSNMPDText "RUN_AS=root:tera" "Buildroot SNMP adapter must retain root UID with the shared service group"
|
|
Assert-Contains $buildrootSNMPDText "chmod 0700 /var/lib/net-snmp" "Buildroot SNMPv3 state must remain root-only"
|
|
Assert-Contains $buildrootLogdText "DAEMON=/usr/bin/tera-logd" "Buildroot log init must execute tera-logd"
|
|
Assert-Contains $buildrootLogdText "RUN_AS=tera-logd:tera" "Buildroot log init must isolate storage ownership while sharing the API socket group"
|
|
Assert-Contains $buildrootLogdText "missing /userdata/log" "Buildroot log init must fail closed without userdata logging"
|
|
Assert-Contains $buildrootUpdatedText "DAEMON=/usr/bin/tera-updated" "Buildroot update init must execute tera-updated"
|
|
Assert-Contains $buildrootUpdatedText "RUN_AS=tera-updated:tera" "Buildroot update init must isolate storage ownership while sharing the API socket group"
|
|
Assert-Contains $buildrootUpdatedText "/usr/libexec/tera-storage-init --check" "Buildroot update init must fail closed when storage policy is invalid"
|
|
Assert-Contains $buildrootAPIText "DAEMON=/usr/bin/tera-api" "Buildroot API init must execute tera-api"
|
|
Assert-Contains $buildrootAPIText "RUN_AS=tera:tera" "Buildroot API init must drop privileges"
|
|
Assert-Contains $buildrootAPIText "chown -R tera:tera /var/lib/tera-api /var/log/tera" "Buildroot API init must repair restored state ownership"
|
|
Assert-Contains $buildrootAPIText 'wait_for_socket "$TERA_API_LOG_SOCKET"' "Buildroot API init must wait for the authoritative log service before startup"
|
|
Assert-Contains $buildrootAccessdText "DAEMON=/usr/bin/tera-accessd" "Buildroot management access init must execute tera-accessd"
|
|
Assert-Contains $buildrootAccessdText "chown -R tera:tera /var/lib/tera-api/tls" "Buildroot TLS state must be owned by the restricted service account"
|
|
Assert-Contains $buildrootAccessdText "chmod 0700 /var/lib/tera-api/tls /var/lib/tera-api/tls/library" "Buildroot TLS private state must remain service-account-only"
|
|
Assert-Contains $buildrootAccessdText "chown root:tera /run/tera-accessd" "Buildroot management control directory must be group traversable"
|
|
Assert-Contains $buildrootSessiondText "DAEMON=/usr/bin/tera-sessiond" "Buildroot session init must execute tera-sessiond"
|
|
Assert-Contains $buildrootSessiondText "RUN_AS=tera:tera" "Buildroot session init must drop privileges"
|
|
Assert-Contains $buildrootWebText "DAEMON=/usr/bin/tera-web" "Buildroot web init must execute tera-web"
|
|
Assert-Contains $buildrootWebText "RUN_AS=tera:tera" "Buildroot web init must drop privileges"
|
|
Assert-Contains $buildrootCloneDemoWebText "DAEMON=/usr/bin/tera-clone-demo-web" "Buildroot Clone Demo init must execute its dedicated binary"
|
|
Assert-Contains $buildrootCloneDemoWebText "ENVFILE=/etc/tera/tera-clone-demo-web.env" "Buildroot Clone Demo init must load its dedicated environment"
|
|
Assert-Contains $buildrootCloneDemoWebText "STATE_DIR=/var/lib/tera-clone-demo-web" "Buildroot Clone Demo init must isolate its writable state"
|
|
Assert-Contains $buildrootUdevText 'KERNEL=="ttyWCH[0-9]*", GROUP="tera", MODE="0660"' "Buildroot udev rule must restrict WCH access to the tera group"
|
|
foreach ($initText in @($buildrootSerialdText, $buildrootHwdText, $buildrootNetdText, $buildrootLogdText, $buildrootTimedText, $buildrootAlarmdText, $buildrootSNMPDText, $buildrootUpdatedText, $buildrootAccessdText, $buildrootAPIText, $buildrootSessiondText, $buildrootWebText, $buildrootCloneDemoWebText)) {
|
|
Assert-Contains $initText 'rm -f "$PIDFILE"' "Buildroot service init must remove stale PID files before start"
|
|
}
|
|
}
|
|
|
|
if ($envText -match "(?im)^\s*TERA_API_CORS_ORIGINS\s*=\s*\*") {
|
|
throw "target env must not enable wildcard CORS"
|
|
}
|
|
Assert-NoSecretEnvironmentValues $envText
|
|
Assert-NoSecretEnvironmentValues $accessdEnvText
|
|
Assert-NoSecretEnvironmentValues $hwdEnvText
|
|
Assert-NoSecretEnvironmentValues $netdEnvText
|
|
Assert-NoSecretEnvironmentValues $timedEnvText
|
|
Assert-NoSecretEnvironmentValues $alarmdEnvText
|
|
Assert-NoSecretEnvironmentValues $snmpdEnvText
|
|
Assert-NoSecretEnvironmentValues $logdEnvText
|
|
Assert-NoSecretEnvironmentValues $updatedEnvText
|
|
Assert-NoSecretEnvironmentValues $serialdEnvText
|
|
Assert-NoSecretEnvironmentValues $sessiondEnvText
|
|
Assert-NoSecretEnvironmentValues $webEnvText
|
|
Assert-NoSecretEnvironmentValues $cloneDemoWebEnvText
|
|
|
|
$artifact = Get-Item -LiteralPath $binTarget
|
|
$accessdArtifact = Get-Item -LiteralPath $accessdBinTarget
|
|
$hwdArtifact = Get-Item -LiteralPath $hwdBinTarget
|
|
$netdArtifact = Get-Item -LiteralPath $netdBinTarget
|
|
$timedArtifact = Get-Item -LiteralPath $timedBinTarget
|
|
$alarmdArtifact = Get-Item -LiteralPath $alarmdBinTarget
|
|
$snmpdArtifact = Get-Item -LiteralPath $snmpdBinTarget
|
|
$logdArtifact = Get-Item -LiteralPath $logdBinTarget
|
|
$updatedArtifact = Get-Item -LiteralPath $updatedBinTarget
|
|
$serialdArtifact = Get-Item -LiteralPath $serialdBinTarget
|
|
$sessiondArtifact = Get-Item -LiteralPath $sessiondBinTarget
|
|
$webArtifact = Get-Item -LiteralPath $webBinTarget
|
|
$cloneDemoWebArtifact = Get-Item -LiteralPath $cloneDemoWebBinTarget
|
|
$serialCheckArtifact = Get-Item -LiteralPath $serialCheckBinTarget
|
|
$serialLineCheckArtifact = Get-Item -LiteralPath $serialLineCheckBinTarget
|
|
if ($createdTempRoot -and -not $KeepStaging) {
|
|
Write-Host "[check-target-rootfs-staging] Verified temporary tera-api $InitSystem rootfs staging at $StagingRoot"
|
|
} else {
|
|
Write-Host "[check-target-rootfs-staging] Staged tera-api $InitSystem rootfs at $StagingRoot"
|
|
}
|
|
Write-Host "[check-target-rootfs-staging] Binary size: $($artifact.Length) bytes"
|
|
Write-Host "[check-target-rootfs-staging] Management access service size: $($accessdArtifact.Length) bytes"
|
|
Write-Host "[check-target-rootfs-staging] Hardware helper size: $($hwdArtifact.Length) bytes"
|
|
Write-Host "[check-target-rootfs-staging] Network helper size: $($netdArtifact.Length) bytes"
|
|
Write-Host "[check-target-rootfs-staging] System time helper size: $($timedArtifact.Length) bytes"
|
|
Write-Host "[check-target-rootfs-staging] Alarm delivery service size: $($alarmdArtifact.Length) bytes"
|
|
Write-Host "[check-target-rootfs-staging] SNMP agent adapter size: $($snmpdArtifact.Length) bytes"
|
|
Write-Host "[check-target-rootfs-staging] Authoritative log service size: $($logdArtifact.Length) bytes"
|
|
Write-Host "[check-target-rootfs-staging] Firmware validation service size: $($updatedArtifact.Length) bytes"
|
|
Write-Host "[check-target-rootfs-staging] Serial controller size: $($serialdArtifact.Length) bytes"
|
|
Write-Host "[check-target-rootfs-staging] Session broker size: $($sessiondArtifact.Length) bytes"
|
|
Write-Host "[check-target-rootfs-staging] Static gateway size: $($webArtifact.Length) bytes"
|
|
Write-Host "[check-target-rootfs-staging] Clone Demo web size: $($cloneDemoWebArtifact.Length) bytes"
|
|
Write-Host "[check-target-rootfs-staging] Read-only serial checker size: $($serialCheckArtifact.Length) bytes"
|
|
Write-Host "[check-target-rootfs-staging] Restoring serial line checker size: $($serialLineCheckArtifact.Length) bytes"
|
|
} finally {
|
|
if ($createdTempRoot -and -not $KeepStaging -and (Test-Path -LiteralPath $StagingRoot)) {
|
|
Remove-Item -LiteralPath $StagingRoot -Recurse -Force
|
|
}
|
|
}
|
|
|