Check in the keys for both accounts

The keys of the people who deploy are in the repository now: in home/ for
the root user and in admin/ for the Administrator. The resources mirror
both home directories, and the deploy script no longer reads any private
key.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
Zebreus 2026-10-25 13:58:05 +01:00
commit a17b2d76dc
4 changed files with 58 additions and 51 deletions

View file

@ -6,13 +6,13 @@
# The job runs on the box as SYSTEM, never as the root user. The Administrator account sets it up; its shell is a
# Windows one, so the commands we run there are Windows commands.
#
# The Administrator logs in with the personal key of the deployer, by default ~/.ssh/id_ed25519. The key of the
# website never gets administrator rights: it only logs in as the root user.
# The Administrator logs in with the personal key of the deployer, from the agent or from -i. The key of the website
# never gets administrator rights: it only logs in as the root user.
set -euo pipefail
HOST=nowhere.wirs.ing
USER=Administrator
KEYFILE="$HOME/.ssh/id_ed25519"
KEYFILE=''
UNINSTALL=0
while [ $# -gt 0 ]; do
@ -30,11 +30,6 @@ remote='C:\Users\Administrator\kik' # where the installer is put, in the home
remotePath='/c/Users\Administrator\kik' # the same place, as the file transfer sees it
temporary=()
if [ ! -f "$KEYFILE" ]; then
echo "no key file for the login as $USER: $KEYFILE" >&2
exit 1
fi
# The host key is the one the website pins, so that the deployment cannot be intercepted.
expected=$(sed -n "s/.*const HOST_KEY = '\([^']*\)'.*/\1/p" "$repo/src/scripts/terminal.ts")
known=$(mktemp)
@ -47,19 +42,15 @@ if [ "$actual" != "$expected" ]; then
exit 1
fi
# The site logs in with the key in the authorized_keys of the user on the box, so the two have to be the same.
site=$(mktemp)
chmod 600 "$site"
temporary+=("$site")
boring=$(sed -n "s/.*const boringData = '\([^']*\)'.*/\1/p" "$repo/src/scripts/terminal.ts")
printf '%s' "$boring" | base64 -d | tr 'A-Za-z' 'N-ZA-Mn-za-m' > "$site"
public=$(ssh-keygen -y -f "$site" | awk 'NF {print $1" "$2}')
if ! awk 'NF {print $1" "$2}' "$server/resources/.ssh/authorized_keys" | grep -qxF "$public"; then
echo "the key in server/resources/.ssh/authorized_keys is not the one the website uses" >&2
# The site logs in with the first key in the authorized_keys of the user on the box, so it has to be the right one.
sitekey=$(ssh-keygen -lf "$server/resources/home/.ssh/authorized_keys" | awk 'NR==1 {print $2}')
if [ "$sitekey" != 'SHA256:KUEv4d4+75h41l0OGTWCA2FbtEOQZ047ujkeh+kkFW0' ]; then
echo "the key of the website is not the first one in server/resources/home/.ssh/authorized_keys" >&2
exit 1
fi
options=(-i "$KEYFILE" -o "UserKnownHostsFile=$known" -o StrictHostKeyChecking=yes)
options=(-o "UserKnownHostsFile=$known" -o StrictHostKeyChecking=yes)
if [ -n "$KEYFILE" ]; then options+=(-i "$KEYFILE"); fi
ssh=(ssh "${options[@]}" "$USER@$HOST")
scp=(scp "${options[@]}")
@ -68,13 +59,9 @@ if [ "$UNINSTALL" = 1 ]; then
exit
fi
# The installer looks for the resources next to itself, so both directories go to the server.
"${ssh[@]}" "if not exist $remote mkdir $remote"
# The installer looks for the resources next to itself, so both directories go to the server. What was there
# before is removed, so that files that are gone from the repository do not stay on the box.
"${ssh[@]}" "if exist $remote rmdir /s /q $remote"
"${ssh[@]}" "mkdir $remote"
"${scp[@]}" -r "$server/scripts" "$server/resources" "$USER@$HOST:$remotePath/"
"${ssh[@]}" "powershell -NoProfile -ExecutionPolicy Bypass -File $remote\\scripts\\install_restore_task.ps1"
# The Administrator logs in with the key of the deployer, so that it is there after every deployment.
keyline=$(ssh-keygen -y -f "$KEYFILE")
blob=$(echo "$keyline" | awk 'NF {print $2}')
"${ssh[@]}" "if not exist C:\\Users\\Administrator\\.ssh mkdir C:\\Users\\Administrator\\.ssh"
"${ssh[@]}" "findstr /c:\"$blob\" C:\\Users\\Administrator\\.ssh\\authorized_keys >nul 2>&1 || echo $keyline >> C:\\Users\\Administrator\\.ssh\\authorized_keys"

View file

@ -25,7 +25,7 @@ $restoreScript = @'
# 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'
$copies = Join-Path $PSScriptRoot 'files\home'
# The account of the shell server's root user, whose id the files in its home directory belong to.
$passwd = Get-Content 'C:\msys2\etc\passwd' -ErrorAction SilentlyContinue | Where-Object { $_ -like 'root:*' }
@ -51,10 +51,10 @@ function Remove-Anything($path) {
# 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]
$blobs = $required | ForEach-Object { ($_ -split '\s+')[1] }
$keys = @()
if (Test-Path -LiteralPath $target) {
$keys = @(Get-Content -LiteralPath $target | Where-Object { $_.Trim() -and ($_ -split '\s+')[1] -ne $blob })
$keys = @(Get-Content -LiteralPath $target | Where-Object { $_.Trim() -and $blobs -notcontains ($_ -split '\s+')[1] })
}
[IO.File]::WriteAllText($target, (($keys + $required) -join "`n") + "`n")
}
@ -127,7 +127,7 @@ if ($Uninstall) {
}
$resources = Join-Path $PSScriptRoot '..\resources'
if (-not (Get-ChildItem -LiteralPath $resources -File -Force)) { throw "No files in $resources." }
if (-not (Get-ChildItem -LiteralPath $resources -Recurse -File -Force)) { throw "No files in $resources." }
# The root user may have prepared the directory or the file names, so nothing that looks like a link is used.
$prepared = Get-Item -LiteralPath $installDir -Force -ErrorAction SilentlyContinue
@ -152,6 +152,16 @@ foreach ($item in Get-ChildItem -LiteralPath $copiesDir -Recurse -Force) {
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 key of the people who deploy, which is in the resources. The keys that are
# already there stay.
$adminKeys = 'C:\Users\Administrator\.ssh\authorized_keys'
$required = @(Get-Content -LiteralPath (Join-Path $resources 'admin\.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)
$action = New-ScheduledTaskAction -Execute 'powershell.exe' -Argument "-NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$installDir\restore.ps1`" -HomeDir `"$HomeDir`""

View file

@ -1,6 +1,6 @@
# Builds the files for the home directory of the shell server's root user in ../resources: README, README.en,
# CODE_OF_CONDUCT and CODE_OF_CONDUCT.en as the website's pages in terminal text with escape sequences, and the
# files of ../home, which are copied as they are.
# Builds the files for the home directories of the shell server in ../resources: the home of the root user with
# README, README.en, CODE_OF_CONDUCT and CODE_OF_CONDUCT.en as the website's pages in terminal text with escape
# sequences, and the files of ../home and ../admin, which are copied as they are.
# Usage: python3 server/scripts/make_files.py
import pathlib
import shutil
@ -332,16 +332,18 @@ COC_EN = {
}
out = pathlib.Path(__file__).parent.parent / 'resources'
out.mkdir(exist_ok=True)
(out / 'home').mkdir(parents=True, exist_ok=True)
for name, text in [('README', readme(DE)), ('README.en', readme(EN)),
('CODE_OF_CONDUCT', code_of_conduct(COC_DE)), ('CODE_OF_CONDUCT.en', code_of_conduct(COC_EN))]:
(out / name).write_text(text, encoding='utf-8')
# The files for the home directory are not generated, they are copied from ../home, where they are edited.
home = pathlib.Path(__file__).parent.parent / 'home'
for path in home.rglob('*'):
target = out / path.relative_to(home)
if path.is_dir():
target.mkdir(parents=True, exist_ok=True)
else:
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(path, target)
(out / 'home' / name).write_text(text, encoding='utf-8')
# The files of the home directories are not generated, they are copied from ../home and ../admin, where they are
# edited.
for source in 'home', 'admin':
root = pathlib.Path(__file__).parent.parent / source
for path in root.rglob('*'):
target = out / source / path.relative_to(root)
if path.is_dir():
target.mkdir(parents=True, exist_ok=True)
else:
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(path, target)

View file

@ -1,15 +1,19 @@
@echo off
REM Puts the SSH access back that a deleted home directory took with it: the key the website logs in with, in the
REM home of the root user. Run it as Administrator on the box, for example over RDP. The cleanup job puts the key
REM back by itself from then on.
REM home of the root user, and the key of the deployer in the home of the Administrator. Run it as Administrator on
REM the box, for example over RDP. The cleanup job puts the key of the website back by itself from then on.
setlocal EnableDelayedExpansion
set KEY=ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJDweEH8bhZAHVaAedhTVDb2j/oMEn3vm97xCwCw9LC9 root@nowhere.wirs.ing cygwin-2222
set OWN=ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIBTHzLm8QMhHIo7kFAvtAFnqpspeR3L3gM8kLoG1137 lennart@erms
set BLOB=AAAAC3NzaC1lZDI1NTE5AAAAIIBTHzLm8QMhHIo7kFAvtAFnqpspeR3L3gM8kLoG1137
if not exist C:\msys2\home\root\.ssh mkdir C:\msys2\home\root\.ssh
> C:\msys2\home\root\.ssh\authorized_keys echo %KEY%
echo %OWN% >> C:\msys2\home\root\.ssh\authorized_keys
if not exist C:\Users\Administrator\.ssh mkdir C:\Users\Administrator\.ssh
findstr /c:"%BLOB%" C:\Users\Administrator\.ssh\authorized_keys >nul 2>&1 || echo %OWN% >> C:\Users\Administrator\.ssh\authorized_keys
REM The user owns their .ssh, so that they can add keys of their own.
for /f "tokens=5" %%i in ('findstr /b "root:" C:\msys2\etc\passwd') do set SID=%%i
set SID=!SID:*,=!