Files
crypto/ssh/test/sshcli_test.go
T
Nicola Murino cf8dcb0f7d ssh: add test case against ssh CLI
These tests try to ensure better compatibility of our server implementation
with the ssh CLI.

With these tests in place:

1) before merging CL 447757 we would have noticed that our server
   implementation was broken with OpenSSH 8.8+
2) after merging CL 447757 we would have noticed that our server
   implementation was broken with OpenSSH 7.2-7.7

The ssh CLI from $PATH is used by default, but can be overridden using
the SSH_CLI_PATH environment variable.

Change-Id: I93d64be41c7613132b0364afac8397f57c2dcbca
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/506837
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Benny Siegert <bsiegert@gmail.com>
Reviewed-by: Han-Wen Nienhuys <hanwen@google.com>
Run-TryBot: Nicola Murino <nicola.murino@gmail.com>
2023-10-24 07:12:59 +00:00

97 lines
2.8 KiB
Go

// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package test
import (
"bytes"
"fmt"
"os"
"os/exec"
"path/filepath"
"testing"
"golang.org/x/crypto/internal/testenv"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/testdata"
)
func sshClient(t *testing.T) string {
if testing.Short() {
t.Skip("Skipping test that executes OpenSSH in -short mode")
}
sshCLI := os.Getenv("SSH_CLI_PATH")
if sshCLI == "" {
sshCLI = "ssh"
}
var err error
sshCLI, err = exec.LookPath(sshCLI)
if err != nil {
t.Skipf("Can't find an ssh(1) client to test against: %v", err)
}
return sshCLI
}
func TestSSHCLIAuth(t *testing.T) {
sshCLI := sshClient(t)
dir := t.TempDir()
keyPrivPath := filepath.Join(dir, "rsa")
for fn, content := range map[string][]byte{
keyPrivPath: testdata.PEMBytes["rsa"],
keyPrivPath + ".pub": ssh.MarshalAuthorizedKey(testPublicKeys["rsa"]),
filepath.Join(dir, "rsa-cert.pub"): testdata.SSHCertificates["rsa-user-testcertificate"],
} {
if err := os.WriteFile(fn, content, 0600); err != nil {
t.Fatalf("WriteFile(%q): %v", fn, err)
}
}
certChecker := ssh.CertChecker{
IsUserAuthority: func(k ssh.PublicKey) bool {
return bytes.Equal(k.Marshal(), testPublicKeys["ca"].Marshal())
},
UserKeyFallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
if conn.User() == "testpubkey" && bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
return nil, nil
}
return nil, fmt.Errorf("pubkey for %q not acceptable", conn.User())
},
}
config := &ssh.ServerConfig{
PublicKeyCallback: certChecker.Authenticate,
}
config.AddHostKey(testSigners["rsa"])
server, err := newTestServer(config)
if err != nil {
t.Fatalf("unable to start test server: %v", err)
}
defer server.Close()
port, err := server.port()
if err != nil {
t.Fatalf("unable to get server port: %v", err)
}
// test public key authentication.
cmd := testenv.Command(t, sshCLI, "-vvv", "-i", keyPrivPath, "-o", "StrictHostKeyChecking=no",
"-p", port, "testpubkey@127.0.0.1", "true")
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("public key authentication failed, error: %v, command output %q", err, string(out))
}
// Test SSH user certificate authentication.
// The username must match one of the principals included in the certificate.
// The certificate "rsa-user-testcertificate" has "testcertificate" as principal.
cmd = testenv.Command(t, sshCLI, "-vvv", "-i", keyPrivPath, "-o", "StrictHostKeyChecking=no",
"-p", port, "testcertificate@127.0.0.1", "true")
out, err = cmd.CombinedOutput()
if err != nil {
t.Fatalf("user certificate authentication failed, error: %v, command output %q", err, string(out))
}
}