Restore the SSH access in the cleanup
The key that the website logs in with lives in the home directory of the user, so a deleted home directory took it away and the site could not connect any more. The key is now one of the copies the cleanup restores, and it is merged into the authorized_keys of the user, so that keys they added stay. The installer writes the same key for the Administrator, and the deploy script checks that it is the key the website uses. Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
parent
e48e0644ba
commit
22106dcd7d
1 changed files with 106 additions and 28 deletions
|
|
@ -21,8 +21,8 @@ $taskName = 'KiK restore shell files'
|
|||
# administrators may read or write.
|
||||
$restoreScript = @'
|
||||
# Puts the shell server's home directory back into shape: the directory itself, the shell configuration that gives
|
||||
# the prompt, and the four text files. Runs as SYSTEM every 30 minutes; the shell server's root user can neither stop
|
||||
# it nor change the copies it restores from.
|
||||
# the prompt, the keys that let the website in, and the four text files. Runs as SYSTEM every 30 minutes; the shell
|
||||
# server's root user can neither stop it nor change the copies it restores from.
|
||||
param([string]$HomeDir = 'C:\msys2\home\root')
|
||||
$log = Join-Path $PSScriptRoot 'restore.log'
|
||||
$copies = Join-Path $PSScriptRoot 'files'
|
||||
|
|
@ -47,8 +47,21 @@ function Remove-Anything($path) {
|
|||
}
|
||||
}
|
||||
|
||||
# The home directory is the one from /etc/passwd, which the user cannot change, but which they can delete.
|
||||
function New-Home($path) {
|
||||
# The website logs in with a key in the authorized_keys of the user. The user may keep keys of their own, so only
|
||||
# the key of the website is put back.
|
||||
function Restore-Keys($target, $source) {
|
||||
$required = @(Get-Content -LiteralPath $source | Where-Object { $_.Trim() })
|
||||
$blob = ($required[0] -split '\s+')[1]
|
||||
$keys = @()
|
||||
if (Test-Path -LiteralPath $target) {
|
||||
$keys = @(Get-Content -LiteralPath $target | Where-Object { $_.Trim() -and ($_ -split '\s+')[1] -ne $blob })
|
||||
}
|
||||
[IO.File]::WriteAllText($target, (($keys + $required) -join "`n") + "`n")
|
||||
}
|
||||
|
||||
# The home directory is the one from /etc/passwd, which the user cannot change, but which they can delete. The
|
||||
# directories in it belong to the user, so that they can put their own keys in .ssh.
|
||||
function New-Dir($path) {
|
||||
New-Item -ItemType Directory -Path $path | Out-Null
|
||||
# The parent directory gives the users read access only, so the owner gets write access on their own home.
|
||||
if ($sid) {
|
||||
|
|
@ -58,17 +71,26 @@ function New-Home($path) {
|
|||
}
|
||||
|
||||
if (-not (Test-Path -LiteralPath $HomeDir)) {
|
||||
New-Home $HomeDir
|
||||
New-Dir $HomeDir
|
||||
Write-Log "created $HomeDir"
|
||||
} elseif ((Get-Item -LiteralPath $HomeDir -Force).Attributes -band [IO.FileAttributes]::ReparsePoint) {
|
||||
Remove-Anything $HomeDir
|
||||
New-Home $HomeDir
|
||||
New-Dir $HomeDir
|
||||
Write-Log "replaced the link $HomeDir"
|
||||
}
|
||||
|
||||
foreach ($copy in Get-ChildItem -LiteralPath $copies -File -Force) {
|
||||
$target = Join-Path $HomeDir $copy.Name
|
||||
foreach ($copy in Get-ChildItem -LiteralPath $copies -Recurse -File -Force) {
|
||||
$target = Join-Path $HomeDir $copy.FullName.Substring($copies.Length).TrimStart('\')
|
||||
try {
|
||||
$dir = Split-Path -Parent $target
|
||||
if ((Test-Path -LiteralPath $dir) -and -not (Get-Item -LiteralPath $dir -Force).PSIsContainer) {
|
||||
Remove-Anything $dir
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $dir)) { New-Item -ItemType Directory -Path $dir | Out-Null }
|
||||
if ($sid) {
|
||||
icacls $dir /grant "*${sid}:(OI)(CI)F" | Out-Null
|
||||
icacls $dir /setowner "*$sid" | Out-Null
|
||||
}
|
||||
if (Test-Path -LiteralPath $target) {
|
||||
$item = Get-Item -LiteralPath $target -Force
|
||||
if ($item.PSIsContainer -or $item.Attributes -band [IO.FileAttributes]::ReparsePoint) {
|
||||
|
|
@ -80,7 +102,11 @@ foreach ($copy in Get-ChildItem -LiteralPath $copies -File -Force) {
|
|||
icacls $target /reset | Out-Null
|
||||
}
|
||||
}
|
||||
Copy-Item -LiteralPath $copy.FullName -Destination $target -Force
|
||||
if ($copy.Name -eq 'authorized_keys') {
|
||||
Restore-Keys $target $copy.FullName
|
||||
} else {
|
||||
Copy-Item -LiteralPath $copy.FullName -Destination $target -Force
|
||||
}
|
||||
if ($sid) { icacls $target /setowner "*$sid" | Out-Null }
|
||||
Write-Log "restored $($copy.Name)"
|
||||
} catch {
|
||||
|
|
@ -113,20 +139,29 @@ New-Item -ItemType Directory -Force -Path $copiesDir | Out-Null
|
|||
$acl = New-Object System.Security.AccessControl.DirectorySecurity
|
||||
$acl.SetAccessRuleProtection($true, $false)
|
||||
foreach ($sid in 'S-1-5-18', 'S-1-5-32-544') {
|
||||
$acl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule -ArgumentList $sid, 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow'))
|
||||
$identity = New-Object System.Security.Principal.SecurityIdentifier $sid
|
||||
$acl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule -ArgumentList $identity, 'FullControl', 'ContainerInherit,ObjectInherit', 'None', 'Allow'))
|
||||
}
|
||||
$acl.SetOwner((New-Object System.Security.Principal.SecurityIdentifier 'S-1-5-32-544'))
|
||||
Set-Acl -LiteralPath $installDir -AclObject $acl
|
||||
|
||||
# Files the root user prepared are removed, the copies are written fresh and inherit the restricted permissions.
|
||||
foreach ($copy in Get-ChildItem -LiteralPath $resources -File -Force) {
|
||||
$path = Join-Path $copiesDir $copy.Name
|
||||
if (Test-Path -LiteralPath $path) {
|
||||
$item = Get-Item -LiteralPath $path -Force
|
||||
if ($item.Attributes -band [IO.FileAttributes]::ReparsePoint) { throw "$path is a link, refusing to install." }
|
||||
Remove-Item -LiteralPath $path -Recurse -Force
|
||||
}
|
||||
Copy-Item -LiteralPath $copy.FullName -Destination $path -Force
|
||||
foreach ($item in Get-ChildItem -LiteralPath $copiesDir -Recurse -Force) {
|
||||
if ($item.Attributes -band [IO.FileAttributes]::ReparsePoint) { throw "$($item.FullName) is a link, refusing to install." }
|
||||
}
|
||||
Remove-Item -LiteralPath $copiesDir -Recurse -Force
|
||||
New-Item -ItemType Directory -Force -Path $copiesDir | Out-Null
|
||||
Copy-Item -Path (Join-Path $resources '*') -Destination $copiesDir -Recurse -Force
|
||||
|
||||
# The Administrator logs in with the same key, so that the deployment keeps working. The keys that are already
|
||||
# there stay.
|
||||
$adminKeys = 'C:\Users\Administrator\.ssh\authorized_keys'
|
||||
$required = @(Get-Content -LiteralPath (Join-Path $resources '.ssh\authorized_keys') | Where-Object { $_.Trim() })
|
||||
$keys = @()
|
||||
if (Test-Path -LiteralPath $adminKeys) { $keys = @(Get-Content -LiteralPath $adminKeys | Where-Object { $_.Trim() }) }
|
||||
if (-not ($keys | Where-Object { ($_ -split '\s+')[1] -eq ($required[0] -split '\s+')[1] })) {
|
||||
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $adminKeys) | Out-Null
|
||||
[IO.File]::WriteAllText($adminKeys, (($keys + $required) -join "`n") + "`n")
|
||||
}
|
||||
[IO.File]::WriteAllText((Join-Path $installDir 'restore.ps1'), $restoreScript)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue