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:
parent
44a2d83ae6
commit
a17b2d76dc
4 changed files with 58 additions and 51 deletions
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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`""
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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:*,=!
|
||||
|
|
|
|||
Loading…
Reference in a new issue