v0.106.1: ssh-copy-id -s requires ~/.ssh locally — create it in the installer
Live finding: SFTP-mode ssh-copy-id mktemp's under ~/.ssh; the container image has no /root/.ssh, so the install died locally AFTER the one-time password was consumed (fail-safe held; password never transmitted). Probe confirmed the pinned known_hosts + StrictHostKeyChecking=yes chain is sound against the real box once ~/.ssh exists. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
@@ -1,5 +1,17 @@
|
|||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
### v0.106.1 — offsite apply-bridge: ssh-copy-id -s needs ~/.ssh to exist (live finding F3) (2026-07-09)
|
||||||
|
|
||||||
|
First supervised live apply: scan+verify passed, the one-time password was consumed, then `ssh-copy-id -s`
|
||||||
|
died **locally** — SFTP mode mktemp's its batch file under `~/.ssh`, and the container image ships without
|
||||||
|
`/root/.ssh`. The fail-safe held (loud "password is spent" signal, no marker, no offbox config) and the
|
||||||
|
password never left the box, but the install could never succeed.
|
||||||
|
|
||||||
|
- `internal/offsiteapply.SSHCopyIDInstaller`: ensure `~/.ssh` (0700) exists before running `ssh-copy-id`.
|
||||||
|
- Live diagnosis (container, no secrets): with `~/.ssh` present, the pinned single-line known_hosts +
|
||||||
|
`StrictHostKeyChecking=yes` verifies cleanly and a wrong password fails as `Permission denied` (sshpass
|
||||||
|
exit 5) — the TOCTOU-hardened pin mechanics are sound end-to-end against the real box.
|
||||||
|
|
||||||
### v0.106.0 — offsite provisioning SLICE 2: controller apply-bridge (2026-07-09)
|
### v0.106.0 — offsite provisioning SLICE 2: controller apply-bridge (2026-07-09)
|
||||||
|
|
||||||
Pairs with hub v0.38.0. On startup the controller reconciles the hub-served `offsite:` descriptor into a
|
Pairs with hub v0.38.0. On startup the controller reconciles the hub-served `offsite:` descriptor into a
|
||||||
|
|||||||
@@ -146,6 +146,14 @@ func (SSHCopyIDInstaller) Install(ctx context.Context, host, user string, port i
|
|||||||
if strings.TrimSpace(knownHosts) == "" {
|
if strings.TrimSpace(knownHosts) == "" {
|
||||||
return fmt.Errorf("ssh-copy-id: empty known_hosts — refusing to install without a pinned host key")
|
return fmt.Errorf("ssh-copy-id: empty known_hosts — refusing to install without a pinned host key")
|
||||||
}
|
}
|
||||||
|
// ssh-copy-id -s (SFTP mode) mktemp's its batch file under ~/.ssh and dies LOCALLY if the directory
|
||||||
|
// doesn't exist — the container image ships without /root/.ssh (live finding: the one-time password was
|
||||||
|
// consumed, then the install failed before ever connecting).
|
||||||
|
if home, err := os.UserHomeDir(); err == nil {
|
||||||
|
if err := os.MkdirAll(filepath.Join(home, ".ssh"), 0o700); err != nil {
|
||||||
|
return fmt.Errorf("ssh-copy-id: ensure ~/.ssh (needed by -s mode): %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
work, err := os.MkdirTemp("", "felhom-keyinstall-")
|
work, err := os.MkdirTemp("", "felhom-keyinstall-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
Reference in New Issue
Block a user