Tracked the harness artifacts accumulated on blankpad (blank@), matching the existing convention for the --home-dank-* memory stores and the 36 already-tracked managed-skills. managed-skills (9): authentik-proxy-provider-app-link, dankclaw-guard-chain-layer-diagnosis, e2e-arr-rawip-reachability-triage, halogen-kv-pool-fit, lan-client-split-dns-fix, noctalia-reference, rootless-quadlet-log-retrieval, ssh-key-bootstrap-no-sshpass, swapfile-resize-safe-ordering. memories (2 stores): --home-blank-.omp-- (dotfiles_git, handy_lifecycle, input_permissions) and --home-blank-Projects-serverAppConfig-- (frank_large_quadlet). Secret sweep before staging: no PEM blocks, no credential literals. Content is fleet topology/IPs only, same class as the tracked dank stores.
65 lines
2.5 KiB
Markdown
65 lines
2.5 KiB
Markdown
---
|
|
name: ssh-key-bootstrap-no-sshpass
|
|
description: "Bootstrap passwordless SSH from a dev box to fleet hosts when sshpass/expect are unavailable, using OpenSSH SSH_ASKPASS_REQUIRE=force so the password never touches disk."
|
|
---
|
|
|
|
# Passwordless SSH bootstrap without sshpass/expect
|
|
|
|
Use when you must install your pubkey on remote hosts but `sshpass` and `expect`
|
|
are not installed, and you only have a password. Modern OpenSSH (>= 8.4) can
|
|
drive the password prompt from a helper program, so the password flows through
|
|
the **process env only** — never a file.
|
|
|
|
## Procedure
|
|
|
|
1. **Generate a key** (if none):
|
|
```bash
|
|
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N "" -C "host-deploy-$(date +%Y%m%d)"
|
|
```
|
|
|
|
2. **Create an askpass helper** that echoes the password from an env var
|
|
(the helper itself stores NO secret):
|
|
```bash
|
|
printf '#!/bin/sh\necho "$DEPLOY_PW"\n' > /tmp/askpass.sh
|
|
chmod 700 /tmp/askpass.sh
|
|
```
|
|
|
|
3. **Copy the pubkey**, forcing the askpass path and detaching from the TTY
|
|
with `setsid` (ssh only uses SSH_ASKPASS when there is no controlling TTY,
|
|
unless `SSH_ASKPASS_REQUIRE=force`):
|
|
```bash
|
|
env DEPLOY_PW='<password>' \
|
|
SSH_ASKPASS=/tmp/askpass.sh \
|
|
SSH_ASKPASS_REQUIRE=force \
|
|
setsid ssh-copy-id -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 user@host
|
|
```
|
|
Pass the password via the `env` argument (transient), never in a committed
|
|
file or shell history.
|
|
|
|
4. **Add host aliases** in `~/.ssh/config` so deploy scripts resolve names:
|
|
```
|
|
Host myhost
|
|
HostName 10.0.0.5
|
|
User deployuser
|
|
IdentityFile ~/.ssh/id_ed25519
|
|
IdentitiesOnly yes
|
|
```
|
|
|
|
5. **Verify** key auth with no prompt:
|
|
```bash
|
|
ssh -o BatchMode=yes myhost 'echo OK; hostname'
|
|
```
|
|
`BatchMode=yes` fails fast if a password would be required — a clean
|
|
success proves the key works.
|
|
|
|
## Gotchas
|
|
- Without `SSH_ASKPASS_REQUIRE=force`, ssh ignores SSH_ASKPASS when a TTY is
|
|
present and just prompts interactively (hangs in a non-interactive tool).
|
|
- `setsid` detaches the controlling terminal — required on older OpenSSH even
|
|
with `force`, harmless otherwise.
|
|
- The helper must be executable (`chmod 700`) and print ONLY the password.
|
|
- `IdentitiesOnly yes` stops ssh offering other agent keys that could be
|
|
rejected before yours, avoiding "Too many authentication failures".
|
|
- Passwordless **sudo** on the target is a separate concern (NOPASSWD /
|
|
`FRANK_SUDO_PW`); this skill only covers SSH key auth.
|