Test deployment public URL (Linux) / test-linux (push) Canceled after 0s
Test start.ps1 (Windows) / test-windows (push) Canceled after 0s
Test deployment public URL (Linux) / test-linux (pull_request) Canceled after 0s
Test start.ps1 (Windows) / test-windows (pull_request) Canceled after 0s
首次安装默认生成独立加密密钥,更新旧部署保留原配置,通过显式开关进入迁移窗口。增加生产迁移包装入口和完整密钥环备份恢复工具,并说明旧镜像升级、严格迁移、轮换及还原流程。 已通过 Bash 安全环境及密钥备份回归;对应 PowerShell 5.1 和 7 脚本已在 Windows 原生环境各通过 49 项验收。宁波服务器验收尚未执行。
51 lines
3.9 KiB
PowerShell
51 lines
3.9 KiB
PowerShell
#Requires -Version 5.1
|
|
$ErrorActionPreference = 'Stop'
|
|
. (Join-Path $PSScriptRoot 'Initialize-SecurityEnv.ps1')
|
|
$temp = Join-Path ([System.IO.Path]::GetTempPath()) ('easyai-key-recovery-' + [Guid]::NewGuid().ToString('N'))
|
|
[System.IO.Directory]::CreateDirectory($temp) | Out-Null
|
|
$envFile = Join-Path $temp '.env'; $keyFile = Join-Path $temp 'keys.env'; $logFile = Join-Path $temp 'result.log'
|
|
function Assert-KeyTest([bool]$Value, [string]$Label) { if (-not $Value) { throw "FAILED: $Label" } }
|
|
try {
|
|
[System.IO.File]::WriteAllText($envFile, '')
|
|
Initialize-SecurityEnv -Path $envFile -Mode new *> $logFile
|
|
$original = Get-Content $envFile -Raw
|
|
$ring = Get-EnvValue $original 'CONFIG_CREDENTIAL_ENCRYPTION_KEYRING'
|
|
$second = [Convert]::FromBase64String((New-SecuritySecret))
|
|
$second = [Convert]::ToBase64String($second[0..31])
|
|
$multi = $ring.Substring(0, $ring.Length - 1) + ',"v2":"' + $second + '"}'
|
|
$content = Set-EnvValue $original 'CONFIG_CREDENTIAL_ENCRYPTION_KEYRING' ("'" + $multi + "'")
|
|
$content = Set-EnvValue $content 'CONFIG_CREDENTIAL_ENCRYPTION_ACTIVE_KEY_ID' 'v2'
|
|
$content = Set-EnvValue $content 'UNRELATED_CONFIG' 'keep-me'
|
|
[System.IO.File]::WriteAllText($envFile, $content)
|
|
& (Join-Path $PSScriptRoot 'Manage-CredentialKeys.ps1') -Mode export -Path $keyFile -EnvPath $envFile *>> $logFile
|
|
Assert-KeyTest ((Get-EnvValue (Get-Content $keyFile -Raw) 'CONFIG_CREDENTIAL_ENCRYPTION_KEYRING') -ceq $multi) 'full ring exported'
|
|
$refused = $false
|
|
try { & (Join-Path $PSScriptRoot 'Manage-CredentialKeys.ps1') -Mode export -Path $keyFile -EnvPath $envFile *>> $logFile } catch { $refused = $true }
|
|
Assert-KeyTest $refused 'existing backup never overwritten'
|
|
[System.IO.File]::WriteAllText($envFile, (Set-EnvValue $original 'UNRELATED_CONFIG' 'keep-me'))
|
|
& (Join-Path $PSScriptRoot 'Manage-CredentialKeys.ps1') -Mode restore -Path $keyFile -EnvPath $envFile *>> $logFile
|
|
$restored = Get-Content $envFile -Raw
|
|
foreach ($name in @('CONFIG_SECURITY_CONFIG_ENCRYPTION_KEY','CONFIG_JWT_SECRET')) { Assert-KeyTest ((Get-EnvValue $restored $name) -ceq (Get-EnvValue $original $name)) 'original config master and JWT retained' }
|
|
Assert-KeyTest ((Get-EnvValue $restored 'CONFIG_CREDENTIAL_ENCRYPTION_KEYRING') -ceq $multi) 'both original keys restored'
|
|
Assert-KeyTest ((Get-EnvValue $restored 'CONFIG_CREDENTIAL_ENCRYPTION_ACTIVE_KEY_ID') -ceq 'v2') 'active key restored'
|
|
Assert-KeyTest ((Get-EnvValue $restored 'UNRELATED_CONFIG') -ceq 'keep-me') 'unrelated config preserved'
|
|
$backups = @(Get-ChildItem $temp -Force -Filter '.env.pre-credential-restore.*')
|
|
Assert-KeyTest ($backups.Count -eq 1) 'previous env saved'
|
|
$bad = Join-Path $temp 'bad.env'
|
|
[System.IO.File]::WriteAllText($bad, "CONFIG_CREDENTIAL_ENCRYPTION_ACTIVE_KEY_ID=v2`nCONFIG_CREDENTIAL_ENCRYPTION_KEYRING='{}'`n")
|
|
$refused = $false
|
|
try { & (Join-Path $PSScriptRoot 'Manage-CredentialKeys.ps1') -Mode restore -Path $bad -EnvPath $envFile *>> $logFile } catch { $refused = $true }
|
|
Assert-KeyTest $refused 'invalid recovery refused'
|
|
Assert-KeyTest ((Get-Content $envFile -Raw) -ceq $restored) 'invalid recovery never mutates env'
|
|
Assert-KeyTest (-not (Get-Content $logFile -Raw).Contains($second)) 'no key leaked to log'
|
|
if ($env:OS -eq 'Windows_NT') {
|
|
$sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value
|
|
foreach ($file in @($envFile, $keyFile, $backups[0].FullName)) {
|
|
$acl = Get-Acl -LiteralPath $file
|
|
Assert-KeyTest $acl.AreAccessRulesProtected 'private DACL'
|
|
foreach ($rule in $acl.Access) { Assert-KeyTest ($rule.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]).Value -eq $sid) 'only current user access' }
|
|
}
|
|
}
|
|
Write-Host 'Credential key backup/restore self-test passed.'
|
|
} finally { Remove-Item -LiteralPath $temp -Recurse -Force }
|