diff --git a/server/README.md b/server/README.md index 84278b2..ef16285 100644 --- a/server/README.md +++ b/server/README.md @@ -19,7 +19,7 @@ We only keep the basic user journey working. Anything that breaks the following user journey is not acceptable: - The user can connect with the SSH key as `root`. -- The prompt: `root@khaos-im-kiez:~#`, which `~/.zshrc` sets. +- The prompt: `root@khaos-im-kiez:~#`, which `~/.zsh/.zshrc` sets. - The home directory the user ends up in: `/home/root`. They cannot change where it is, and they cannot delete it. - `cat README`, `cat README.en`, `cat CODE_OF_CONDUCT` and `cat CODE_OF_CONDUCT.en` always work. The four files are always there and always regular text files. @@ -41,6 +41,10 @@ The scheduled task `KiK restore shell files` runs every 30 minutes and at every The second job should also be able to restore a new server to the expected state. +The files that have to stay belong to the administrators, so the user cannot delete them, and they cannot change the +shell configuration or the key the website logs in with either. The shell reads its configuration from `~/.zsh`, so +the startup files the user could write in the home directory are ignored. + ## Installing the cleanup on the server To deploy the cleanup task an the server, run: diff --git a/server/resources/home/.zshrc b/server/resources/home/.zsh/.zshrc similarity index 100% rename from server/resources/home/.zshrc rename to server/resources/home/.zsh/.zshrc diff --git a/server/resources/home/.zshenv b/server/resources/home/.zshenv index f990c34..3625bba 100644 --- a/server/resources/home/.zshenv +++ b/server/resources/home/.zshenv @@ -1,3 +1,7 @@ # Preset what MSYS2's /etc/profile would otherwise start a helper process for (~90 ms each here). # TZ has to match the Windows timezone (W. Europe Standard Time). export LANG=en_US.UTF-8 TZ=Europe/Berlin + +# The shell reads its configuration from here, not from the home directory: the user may not change it, and the +# files in the home directory that zsh would read otherwise (.zprofile, .zlogin) are ignored. +export ZDOTDIR=~/.zsh diff --git a/server/scripts/install_restore_task.ps1 b/server/scripts/install_restore_task.ps1 index b2aafed..6dd6ced 100644 --- a/server/scripts/install_restore_task.ps1 +++ b/server/scripts/install_restore_task.ps1 @@ -59,10 +59,13 @@ function Restore-Keys($target, $source) { [IO.File]::WriteAllText($target, (($keys + $required) -join "`n") + "`n") } -# The permissions of the home directory and of the files in it that have to stay: the user may read and write them -# and delete their own files, but not the home directory itself and not the files the website needs. Only SYSTEM -# and the administrators own them, so the user cannot take the permissions away again. -function Set-ProtectedAcl($path, $forDirectory) { +# The shell configuration and the key the website logs in with must not change, the four texts may be edited. +$readOnly = '.zshrc', '.zshenv', 'authorized_keys' + +# The permissions of the home directory and of the files in it that have to stay: the user may read them and write +# the ones that may change, and delete their own files, but not the home directory itself and not the files the +# website needs. Only SYSTEM and the administrators own them, so the user cannot take the permissions away again. +function Set-ProtectedAcl($path, $forDirectory, $writable) { if (-not $sid) { return } try { $system = New-Object System.Security.Principal.SecurityIdentifier('S-1-5-18') @@ -78,10 +81,12 @@ function Set-ProtectedAcl($path, $forDirectory) { $inheritOnly = [System.Security.AccessControl.PropagationFlags]::InheritOnly $full = [System.Security.AccessControl.FileSystemRights]::FullControl $some = [System.Security.AccessControl.FileSystemRights]::ReadAndExecute -bor [System.Security.AccessControl.FileSystemRights]::Write + $read = [System.Security.AccessControl.FileSystemRights]::ReadAndExecute $inherit = if ($forDirectory) { $children } else { $none } $acl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule -ArgumentList $system, $full, $inherit, $noPropagate, 'Allow')) $acl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule -ArgumentList $admins, $full, $inherit, $noPropagate, 'Allow')) - $acl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule -ArgumentList $user, $some, $none, $noPropagate, 'Allow')) + $userRights = if ($writable) { $some } else { $read } + $acl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule -ArgumentList $user, $userRights, $none, $noPropagate, 'Allow')) if ($forDirectory) { # the files in it are the user's own, they may delete them $acl.AddAccessRule((New-Object System.Security.AccessControl.FileSystemAccessRule -ArgumentList $user, $full, $children, $inheritOnly, 'Allow')) @@ -96,7 +101,7 @@ function Set-ProtectedAcl($path, $forDirectory) { # gets MSYS2's default files from /etc/skel, which MSYS2 itself only copies into a home that does not exist yet. function New-Home($path) { New-Item -ItemType Directory -Path $path | Out-Null - Set-ProtectedAcl $path $true + Set-ProtectedAcl $path $true $true Get-ChildItem -LiteralPath 'C:\msys2\etc\skel' -Force | Copy-Item -Destination $path -Recurse -Force } @@ -108,7 +113,7 @@ if (-not (Test-Path -LiteralPath $HomeDir)) { New-Home $HomeDir Write-Log "replaced the link $HomeDir" } else { - Set-ProtectedAcl $HomeDir $true + Set-ProtectedAcl $HomeDir $true $true } foreach ($copy in Get-ChildItem -LiteralPath $copies -Recurse -File -Force) { @@ -119,7 +124,7 @@ foreach ($copy in Get-ChildItem -LiteralPath $copies -Recurse -File -Force) { Remove-Anything $dir } if (-not (Test-Path -LiteralPath $dir)) { New-Item -ItemType Directory -Path $dir | Out-Null } - Set-ProtectedAcl $dir $true + Set-ProtectedAcl $dir $true $true if (Test-Path -LiteralPath $target) { $item = Get-Item -LiteralPath $target -Force if ($item.PSIsContainer -or $item.Attributes -band [IO.FileAttributes]::ReparsePoint) { @@ -135,7 +140,7 @@ foreach ($copy in Get-ChildItem -LiteralPath $copies -Recurse -File -Force) { } else { Copy-Item -LiteralPath $copy.FullName -Destination $target -Force } - Set-ProtectedAcl $target $false + Set-ProtectedAcl $target $false ($readOnly -notcontains $copy.Name) Write-Log "restored $($copy.Name)" } catch { Write-Log "failed $($copy.Name) : $_" @@ -192,6 +197,11 @@ if (-not ($keys | Where-Object { ($_ -split '\s+')[1] -eq ($required[0] -split ' } [IO.File]::WriteAllText((Join-Path $installDir 'restore.ps1'), $restoreScript) +# The shell configuration used to be in the home directory, the shell reads it from ~/.zsh now. +if (Test-Path -LiteralPath (Join-Path $HomeDir '.zsh\.zshrc')) { + Remove-Item -LiteralPath (Join-Path $HomeDir '.zshrc') -Force -ErrorAction SilentlyContinue +} + $action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument "-NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$installDir\restore.ps1`" -HomeDir `"$HomeDir`"" $triggers = @( New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 30) -RepetitionDuration (New-TimeSpan -Days 3650)