diff options
| author | Lena <lena@omega> | 2026-08-16 00:00:00 +0000 |
|---|---|---|
| committer | Lena <lena@omega> | 2026-08-16 00:00:00 +0000 |
| commit | beaa0c970d6c3538119b8a34a3f2f754b45e54cf (patch) | |
| tree | 0a4be0a63a5c5b40f3db66a7e2b6c603e60d8473 /rsh/sshserver_test.go | |
| parent | 0890d139e5fa501561e469aca7854791a936fcb6 (diff) | |
| download | rsend-beaa0c970d6c3538119b8a34a3f2f754b45e54cf.tar.gz | |
native: harden rsync and SSH transport
Update rsync to 3.5.0 and Go to 1.26.6. Bound SSH handshakes, pin
host-key types, and build 16 KB-aligned hardened executables.
Diffstat (limited to 'rsh/sshserver_test.go')
| -rw-r--r-- | rsh/sshserver_test.go | 42 |
1 files changed, 34 insertions, 8 deletions
diff --git a/rsh/sshserver_test.go b/rsh/sshserver_test.go index 087bff4..f238e0b 100644 --- a/rsh/sshserver_test.go +++ b/rsh/sshserver_test.go @@ -1,7 +1,9 @@ package main import ( + "crypto/ecdsa" "crypto/ed25519" + "crypto/elliptic" "crypto/rand" "errors" "fmt" @@ -20,14 +22,24 @@ type execHandler func(cmd string, stdin io.Reader, stdout, stderr io.Writer) int // testServer is a minimal in-process SSH server for tests: one host key, one // accepted client public key, and a pluggable exec handler. No sshd needed. type testServer struct { - ln net.Listener - hostKey ssh.Signer - authPub ssh.PublicKey - handle execHandler + ln net.Listener + hostKey ssh.Signer + extraKey ssh.Signer + authPub ssh.PublicKey + handle execHandler } func newTestServer(t *testing.T, clientPub ssh.PublicKey, handle execHandler) *testServer { t.Helper() + return newTestServerKeys(t, clientPub, handle, false) +} + +// newTestServerKeys builds the server with an ed25519 host key and, when +// alsoECDSA is set, an ECDSA one too. A server offering both is the stock sshd +// case: x/crypto's default client preference would pick the ECDSA key, so this +// is what proves rsh's host-key algorithm policy is doing its job. +func newTestServerKeys(t *testing.T, clientPub ssh.PublicKey, handle execHandler, alsoECDSA bool) *testServer { + t.Helper() _, hpriv, err := ed25519.GenerateKey(rand.Reader) if err != nil { t.Fatal(err) @@ -36,11 +48,21 @@ func newTestServer(t *testing.T, clientPub ssh.PublicKey, handle execHandler) *t if err != nil { t.Fatal(err) } + var extra ssh.Signer + if alsoECDSA { + epriv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + if extra, err = ssh.NewSignerFromKey(epriv); err != nil { + t.Fatal(err) + } + } ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatal(err) } - s := &testServer{ln: ln, hostKey: signer, authPub: clientPub, handle: handle} + s := &testServer{ln: ln, hostKey: signer, extraKey: extra, authPub: clientPub, handle: handle} go s.serve() t.Cleanup(func() { ln.Close() }) return s @@ -58,6 +80,9 @@ func (s *testServer) serve() { }, } cfg.AddHostKey(s.hostKey) + if s.extraKey != nil { + cfg.AddHostKey(s.extraKey) + } for { nConn, err := s.ln.Accept() if err != nil { @@ -107,15 +132,16 @@ func (s *testServer) handleSession(ch ssh.Channel, requests <-chan *ssh.Request) } } -// shellExec runs cmd via /bin/sh -c, exactly as sshd would, wiring the channel -// to the process. Used by the real-rsync end-to-end test. +// shellExecIn runs cmd via /bin/sh -c, exactly as sshd would, wiring the +// channel to the process. Used by the real-rsync end-to-end test. // // stdin is fed through a detached goroutine rather than c.Stdin so that Wait // does not block on the stdin copy: rsync's remote receiver exits while the // client still holds the channel open, and a c.Run with a non-file Stdin would // deadlock waiting for that copy to finish. -func shellExec(cmd string, stdin io.Reader, stdout, stderr io.Writer) int { +func shellExecIn(dir, cmd string, stdin io.Reader, stdout, stderr io.Writer) int { c := exec.Command("/bin/sh", "-c", cmd) + c.Dir = dir c.Stdout = stdout c.Stderr = stderr inPipe, err := c.StdinPipe() |