ssh install works over sftp and runs nothing on the host (closes #10)
All checks were successful
check / check (push) Successful in 4s
All checks were successful
check / check (push) Successful in 4s
ssh install no longer runs a command on the host. It reads .ssh/authorized_keys over sftp, takes the empty reading only from sftp's own message about that path, appends the derived key locally when it is not already present, uploads the result beside the file with mode 0600 and renames it over the original. Any other failure prints what sftp said, writes nothing and exits 1. sftp batch mode disables password prompts, so a key or agent is required; a directory the owner cannot enter reads as a host with no file, which README.md states. Model: opus-5 (implementation); fable-5-1 (landing)
This commit was merged in pull request #11.
This commit is contained in:
78
internal/cli/ssh/install_test.go
Normal file
78
internal/cli/ssh/install_test.go
Normal file
@@ -0,0 +1,78 @@
|
||||
//nolint:testpackage // absent is what these wordings are read by
|
||||
package ssh
|
||||
|
||||
import "testing"
|
||||
|
||||
// What a session says besides its report on the file that was asked
|
||||
// for: sftp echoes the command it is running, and ssh warns about an
|
||||
// identity file it cannot find in the words of a missing file even
|
||||
// though the session goes on to authenticate.
|
||||
const (
|
||||
echoed = `sftp> get .ssh/authorized_keys "/tmp/keyfunc/authorized_keys"
|
||||
`
|
||||
warning = `Warning: Identity file /gone not accessible: ` +
|
||||
"No such file or directory.\n"
|
||||
)
|
||||
|
||||
// TestAbsenceIsReadOnlyFromWhatSFTPSaidAboutAuthorizedKeys holds the
|
||||
// wordings the OpenSSH client was seen to use against a real server:
|
||||
// a file it cannot find is reported one way, naming the path the
|
||||
// server expanded, and everything else it says is a failure.
|
||||
func TestAbsenceIsReadOnlyFromWhatSFTPSaidAboutAuthorizedKeys(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
sessions := map[string]struct {
|
||||
said string
|
||||
want bool
|
||||
}{
|
||||
"the file is not there": {
|
||||
said: echoed +
|
||||
`File "/home/someone/.ssh/authorized_keys" not found.` + "\n",
|
||||
want: true,
|
||||
},
|
||||
"the file is not there, named as it was asked for": {
|
||||
said: echoed + `File ".ssh/authorized_keys" not found.` + "\n",
|
||||
want: true,
|
||||
},
|
||||
"the file is not there and an identity file is not either": {
|
||||
said: warning + echoed +
|
||||
`File "/home/someone/.ssh/authorized_keys" not found.` + "\n",
|
||||
want: true,
|
||||
},
|
||||
"the file is there and cannot be read": {
|
||||
said: echoed +
|
||||
`remote open "/home/someone/.ssh/authorized_keys": ` +
|
||||
"Permission denied\n",
|
||||
want: false,
|
||||
},
|
||||
"only an identity file is not there": {
|
||||
said: warning + echoed +
|
||||
`remote open "/home/someone/.ssh/authorized_keys": ` +
|
||||
"Permission denied\n",
|
||||
want: false,
|
||||
},
|
||||
"some other file is not there": {
|
||||
said: echoed + `File "/home/someone/.ssh/known_hosts" not found.` +
|
||||
"\n",
|
||||
want: false,
|
||||
},
|
||||
"the connection did not come up": {
|
||||
said: "ssh: connect to host example.com port 22: " +
|
||||
"Connection refused\nConnection closed\n",
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
|
||||
for name, session := range sessions {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if absent(session.said) != session.want {
|
||||
t.Errorf(
|
||||
"read as absent: %t, wanted %t, from:\n%s",
|
||||
!session.want, session.want, session.said,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user