mirror of
https://github.com/saphid/frame-control.git
synced 2026-10-06 05:02:50 +02:00
The headset desktop is nested Plasma in gamescope with no wl-copy/xclip, so paste-to-frame.sh now calls Klipper over plasmashell's D-Bus bus. connect.sh, push.sh, paste-to-frame.sh and install-apps.sh were exercised on SteamOS 0.3.0 (build 20260922); docs record what was confirmed. Cross-provider review skipped at Alex's request (Astra quota exhausted). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
133 lines
4.4 KiB
Bash
Executable File
133 lines
4.4 KiB
Bash
Executable File
#!/usr/bin/env zsh
|
|
# Mac-side: find the Steam Frame, create a key, add a `Host frame` alias to
|
|
# ~/.ssh/config, copy the key, and optionally disable SSH password logins.
|
|
#
|
|
# Verified on a Frame 2026-09-25 (except --harden). Idempotent: safe to re-run.
|
|
#
|
|
# Usage:
|
|
# scripts/connect.sh [HOST_OR_IP] # set up key + alias
|
|
# scripts/connect.sh [HOST_OR_IP] --harden # also disable password auth
|
|
#
|
|
# Env: FRAME_USER (default steamos), FRAME_ALIAS (default frame).
|
|
set -euo pipefail
|
|
|
|
FRAME_USER=${FRAME_USER:-steamos}
|
|
FRAME_ALIAS=${FRAME_ALIAS:-frame}
|
|
KEY="$HOME/.ssh/id_ed25519_frame"
|
|
CONFIG="$HOME/.ssh/config"
|
|
BEGIN_MARK="# >>> steam-frame ($FRAME_ALIAS) >>>"
|
|
END_MARK="# <<< steam-frame ($FRAME_ALIAS) <<<"
|
|
|
|
harden=0
|
|
host_arg=""
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--harden) harden=1 ;;
|
|
-h|--help) sed -n '2,11p' "$0"; exit 0 ;;
|
|
*) host_arg="$arg" ;;
|
|
esac
|
|
done
|
|
|
|
port_open() {
|
|
# nc resolves through the system resolver (including mDNS for .local).
|
|
nc -z -G 3 "$1" 22 >/dev/null 2>&1
|
|
}
|
|
|
|
pick_host() {
|
|
local candidates=()
|
|
[[ -n "$host_arg" ]] && candidates+=("$host_arg")
|
|
candidates+=("$FRAME_ALIAS.local" "$FRAME_ALIAS")
|
|
local h
|
|
for h in "${candidates[@]}"; do
|
|
if port_open "$h"; then
|
|
print -r -- "$h"; return 0
|
|
fi
|
|
print -u2 " - $h: not resolvable or port 22 closed"
|
|
done
|
|
return 1
|
|
}
|
|
|
|
print "==> Looking for the Steam Frame"
|
|
if ! HOST=$(pick_host); then
|
|
print -u2 "Could not reach the Frame on port 22."
|
|
print -u2 "Check: Developer Mode on + user password set; same Wi-Fi; no client isolation."
|
|
print -u2 "Then re-run with the IP from Quick Settings: scripts/connect.sh 192.168.x.y"
|
|
exit 1
|
|
fi
|
|
print " found: $HOST"
|
|
|
|
print "==> SSH key"
|
|
mkdir -p "$HOME/.ssh" && chmod 700 "$HOME/.ssh"
|
|
if [[ ! -f "$KEY" ]]; then
|
|
ssh-keygen -q -t ed25519 -N '' -C "mac->steam-frame" -f "$KEY"
|
|
print " created $KEY"
|
|
else
|
|
print " exists: $KEY"
|
|
fi
|
|
|
|
print "==> ~/.ssh/config alias '$FRAME_ALIAS' -> $HOST"
|
|
touch "$CONFIG" && chmod 600 "$CONFIG"
|
|
tmp=$(mktemp)
|
|
# Drop any previous managed block, then PREPEND a fresh one: ssh uses the first
|
|
# value it sees per option, so this block must precede any other "Host frame"
|
|
# or "Host *". The trailing "Host *" returns the rest of the file to global scope.
|
|
awk -v b="$BEGIN_MARK" -v e="$END_MARK" '
|
|
$0==b {skip=1; next}
|
|
$0==e {skip=0; next}
|
|
!skip {print}
|
|
' "$CONFIG" > "$tmp"
|
|
{
|
|
print -r -- "$BEGIN_MARK"
|
|
print -r -- "Host $FRAME_ALIAS"
|
|
print -r -- " HostName $HOST"
|
|
print -r -- " User $FRAME_USER"
|
|
print -r -- " IdentityFile $KEY"
|
|
print -r -- " IdentitiesOnly yes"
|
|
print -r -- " ServerAliveInterval 30"
|
|
print -r -- "Host *"
|
|
print -r -- "$END_MARK"
|
|
cat "$tmp"
|
|
} > "$CONFIG"
|
|
rm -f "$tmp"
|
|
|
|
print "==> Checking key login"
|
|
if ssh -o BatchMode=yes -o ConnectTimeout=5 "$FRAME_ALIAS" true 2>/dev/null; then
|
|
print " key login already works"
|
|
else
|
|
print " copying key (enter the Developer Mode password once)"
|
|
ssh-copy-id -i "$KEY.pub" -o IdentitiesOnly=yes "$FRAME_USER@$HOST"
|
|
ssh -o BatchMode=yes -o ConnectTimeout=5 "$FRAME_ALIAS" true \
|
|
|| { print -u2 "Key login still failing after ssh-copy-id."; exit 1; }
|
|
print " key login OK"
|
|
fi
|
|
|
|
if (( harden )); then
|
|
print "==> Disabling SSH password auth (sudo password asked on the Frame)"
|
|
# shellcheck disable=SC2016
|
|
if ! ssh -t "$FRAME_ALIAS" '
|
|
set -e
|
|
grep -Eiq "^[[:space:]]*Include[[:space:]]+/etc/ssh/sshd_config\.d/\*\.conf" /etc/ssh/sshd_config \
|
|
|| { echo "sshd_config has no sshd_config.d include; not hardening."; exit 1; }
|
|
printf "PasswordAuthentication no\nKbdInteractiveAuthentication no\n" \
|
|
| { sudo mkdir -p /etc/ssh/sshd_config.d; sudo tee /etc/ssh/sshd_config.d/01-frame-keys-only.conf >/dev/null; }
|
|
sudo sshd -t
|
|
sudo systemctl reload sshd
|
|
echo "password auth disabled"
|
|
'; then
|
|
print -u2 "!! Hardening failed. If the drop-in was written, it will disable password SSH"
|
|
print -u2 "!! on the next sshd restart. To undo it:"
|
|
print -u2 "!! ssh $FRAME_ALIAS 'sudo rm -f /etc/ssh/sshd_config.d/01-frame-keys-only.conf'"
|
|
exit 1
|
|
fi
|
|
if ssh -o BatchMode=yes -o ConnectTimeout=5 "$FRAME_ALIAS" true; then
|
|
print " key login still OK after hardening"
|
|
else
|
|
print -u2 "!! Key login FAILED after hardening. Password SSH is now off."
|
|
print -u2 "!! Recover via RDP or 'adb shell' (USB-C), then run:"
|
|
print -u2 "!! sudo rm /etc/ssh/sshd_config.d/01-frame-keys-only.conf && sudo systemctl reload sshd"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
print "\nDone. Try: ssh $FRAME_ALIAS"
|