makepad/tools/arch_usb/ssh.sh
Admin 384d0e031c tools: the Builder replaces makepad_loader, the web server moves to makepad/webserver, fleet scripts, docs and the workspace members
tools/makepad_builder replaces tools/makepad_loader: one build target
shared across app builds, workspace package selection, checkout
progress on the public Git API, detached built apps with a completion
state, waits for Windows security scans, manual retry after compiler
locks, dedicated-folder installer checks, catalog and runtime fixes.
tools/web_server and its scripts leave for github.com/makepad/webserver.
Arch USB clone/restore scripts, the qwen38 box scripts and the G-belt
serial test join tools/. docs/agents records the agent workflow and the
remote-control handoff protocol; AGENTS.md forbids vendored sources and
bulk imports. Cargo.toml lists apps/wm-dyn, libs/code_language,
libs/search, libs/tar, libs/loader_bundle and tools/makepad_builder,
and drops the two removed crates.

Squashed from work:
- Share Builder target across Makepad app builds
- Fix Builder workspace package selection
- Align Builder checkout progress with public Git API
- Detach built apps and show completion state
- Wait for Windows security scans
- Offer manual retry after Windows compiler locks
- docs: the agent workflow of record and the remote-control handoff protocol
- builder: dedicated-folder installer checks, catalog and runtime fixes; Windows job objects hold c_void handles
- tools: Arch USB clone/restore scripts, the qwen38 box scripts, and the G-belt serial test
- tools: the web server moves to makepad/webserver
- AGENTS.md: no vendored sources or bulk imports in the tree

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-18 12:17:59 +02:00

152 lines
6.4 KiB
Bash

#!/usr/bin/env bash
# Password access for the Makepad USB's existing arch account.
# Run `sudo bash tools/arch_usb/ssh.sh enable` to update an existing USB.
set -Eeuo pipefail
export PATH=/usr/local/sbin:/usr/local/bin:/usr/bin
umask 077
action=${1:-show}
case "$action" in
enable|install|disable|show|reset-password) ;;
-h|--help) echo 'Usage: sudo makepad-ssh [enable|disable|show|reset-password]'; exit 0 ;;
*) echo 'Usage: makepad-ssh [enable|disable|show|reset-password]' >&2; exit 2 ;;
esac
if test "$EUID" -ne 0; then
echo 'Run with sudo (or from the root console).' >&2
exit 1
fi
state=/var/lib/makepad-ssh
password_file=$state/password
issue=/etc/issue.d/50-makepad-ssh.issue
show_access() {
printf '\nMakepad network SSH: %s\n' "$(systemctl is-active sshd.service || true)"
if ! systemctl is-active --quiet sshd.service; then
printf 'Enable with: sudo makepad-ssh enable\n'
return
fi
printf 'Login: arch (use sudo for root)\n'
if test -s "$password_file"; then
printf 'SSH / sudo password: %s\n' "$(cat "$password_file")"
fi
local address found=no
while read -r address; do
test -n "$address" || continue
printf ' ssh arch@%s\n' "$address"
found=yes
done < <(ip -o address show scope global | awk '{split($4, address, "/"); print address[1]}')
if test "$found" = no; then
printf 'Waiting for a network address. Connect Ethernet, or configure Wi-Fi with iwctl.\n'
fi
printf 'Show again: sudo makepad-ssh show\nDisable: sudo makepad-ssh disable\n\n'
}
if test "$action" = show; then
show_access
exit 0
fi
if test "$action" = disable; then
systemctl disable --now sshd.service
rm -f "$issue"
agetty --reload || true
echo 'Network SSH disabled. Enable with: sudo makepad-ssh enable'
exit 0
fi
id arch >/dev/null
install -d -m 0700 "$state"
install -d -m 0755 /usr/local/sbin /etc/ssh/sshd_config.d /etc/issue.d /etc/profile.d
if test "$(readlink -f "$0")" != /usr/local/sbin/makepad-ssh; then
install -m 0755 "$0" /usr/local/sbin/makepad-ssh
fi
# Draw a fresh passphrase on the target, never in a shared image or build log.
# Use the user's requested two-word, dash-separated format.
if test ! -s "$password_file" || test "$action" = reset-password; then
python3 - <<'PY' > "$state/password.new"
import secrets
words = '''
acorn amber apple apron arrow atlas bacon badge bagel baker beach berry birch bloom blue boat
boots bread brick brook brush cabin camel candy canoe cedar chalk charm cherry chess chili cloud
clover cocoa coral crane creek crown daisy dance dawn deer delta diner dough dream drift drum
eagle earth ember fairy fern field finch flame flute forest fox frost fruit garden gecko glass
globe grape grass green grove guitar hazel heart heron honey horse house ice iris ivory jacket
jade jazz jelly jewel jolly juice kite kiwi lake lamp laser lemon lilac lime linen lion
lotus lucky lunar mango maple marble melon mint mist moon moss mouse music navy nest night
ocean olive onion orbit otter owl panda paper peach pearl pebble pepper piano pine plum polar
'''.split()
print('-'.join(secrets.choice(words) for _ in range(2)))
PY
chmod 0600 "$state/password.new"
mv "$state/password.new" "$password_file"
fi
# Also retries an interrupted setup without losing the displayed password.
printf 'arch:%s\n' "$(cat "$password_file")" | chpasswd
chage -E -1 -I -1 -M 99999 arch
cat > "$state/sshd.conf" <<'EOF'
PubkeyAuthentication no
PasswordAuthentication yes
KbdInteractiveAuthentication no
AuthenticationMethods password
UsePAM yes
PermitEmptyPasswords no
PermitRootLogin no
AllowUsers arch
Banner none
EOF
# OpenSSH uses the first value obtained, so the managed policy goes first.
install -m 0644 "$state/sshd.conf" /etc/ssh/sshd_config.d/00-makepad.conf
if ! head -1 /etc/ssh/sshd_config | grep -Fxq 'Include /etc/ssh/sshd_config.d/00-makepad.conf'; then
{ printf 'Include /etc/ssh/sshd_config.d/00-makepad.conf\n'; cat /etc/ssh/sshd_config; } > "$state/sshd_config.new"
install -m 0600 "$state/sshd_config.new" /etc/ssh/sshd_config
fi
ssh-keygen -A
sshd -t
sshd -T -C user=arch,host=makepad-arch,addr=127.0.0.1 > "$state/sshd-effective.txt"
for policy in 'pubkeyauthentication no' 'passwordauthentication yes' 'kbdinteractiveauthentication no' 'usepam yes' 'permitrootlogin no' 'permitemptypasswords no' 'authenticationmethods password' 'allowusers arch' 'banner none'; do
grep -Fxiq "$policy" "$state/sshd-effective.txt"
done
# This is the account/key file owned by the USB builder, not the Mac's key.
rm -f /home/arch/.ssh/authorized_keys
# agetty reads this as root. It is deliberately absent from /etc/motd and
# the SSH Banner: network logins must not reveal the console passphrase.
printf 'Makepad Arch USB - login: arch\nIPv4: \\4\nRun makepad-status for package setup progress.\n\n' > /etc/issue
printf 'Makepad Arch USB\nSSH / sudo password is shown on the local console.\nRun sudo makepad-ssh show to display connection details.\nRun makepad-status for package setup progress.\nWi-Fi connections are configured with iwctl.\n' > /etc/motd
chmod 0644 /etc/issue /etc/motd
{
printf '\nMakepad network SSH - login: arch (sudo for root)\n'
printf 'SSH / sudo password: %s\n' "$(cat "$password_file")"
printf 'Connect: ssh arch@\\4\n'
printf 'Show addresses: sudo makepad-ssh show\n'
printf 'Disable: sudo makepad-ssh disable\n\n'
} > "$issue"
chmod 0600 "$issue"
cat > /etc/profile.d/makepad-ssh.sh <<'EOF'
# Show credentials only on a local, interactive root console, never SSH.
if [ "$(id -u)" = 0 ] && [ -z "${SSH_CONNECTION:-}" ]; then
case "$-" in
*i*) case "$(tty 2>/dev/null)" in
/dev/tty[0-9]*|/dev/console) /usr/local/sbin/makepad-ssh show ;;
esac ;;
esac
fi
EOF
chmod 0644 /etc/profile.d/makepad-ssh.sh
systemctl --root=/ unmask sshd.service
systemctl --root=/ enable sshd.service
agetty --reload || true
if test "$action" != install; then
systemctl reload-or-restart sshd.service
show_access
# Also update an already logged-in root TTY when enabling remotely.
# Login prompts use issue.d; future root logins use the profile hook.
current_tty=$(tty 2>/dev/null || true)
while read -r console; do
if test "/dev/$console" != "$current_tty" && test -c "/dev/$console"; then
show_access > "/dev/$console"
fi
done < <(who | awk '$1 == "root" && $2 ~ /^tty[0-9]+$/ {print $2}' | sort -u)
fi