sshcli_test.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. // Copyright 2023 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package test
  5. import (
  6. "bytes"
  7. "fmt"
  8. "os"
  9. "os/exec"
  10. "path/filepath"
  11. "runtime"
  12. "testing"
  13. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/internal/testenv"
  14. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/ssh"
  15. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/ssh/testdata"
  16. )
  17. func sshClient(t *testing.T) string {
  18. if testing.Short() {
  19. t.Skip("Skipping test that executes OpenSSH in -short mode")
  20. }
  21. sshCLI := os.Getenv("SSH_CLI_PATH")
  22. if sshCLI == "" {
  23. sshCLI = "ssh"
  24. }
  25. var err error
  26. sshCLI, err = exec.LookPath(sshCLI)
  27. if err != nil {
  28. t.Skipf("Can't find an ssh(1) client to test against: %v", err)
  29. }
  30. return sshCLI
  31. }
  32. func TestSSHCLIAuth(t *testing.T) {
  33. if runtime.GOOS == "windows" {
  34. t.Skipf("always fails on Windows, see #64403")
  35. }
  36. sshCLI := sshClient(t)
  37. dir := t.TempDir()
  38. keyPrivPath := filepath.Join(dir, "rsa")
  39. for fn, content := range map[string][]byte{
  40. keyPrivPath: testdata.PEMBytes["rsa"],
  41. keyPrivPath + ".pub": ssh.MarshalAuthorizedKey(testPublicKeys["rsa"]),
  42. filepath.Join(dir, "rsa-cert.pub"): testdata.SSHCertificates["rsa-user-testcertificate"],
  43. } {
  44. if err := os.WriteFile(fn, content, 0600); err != nil {
  45. t.Fatalf("WriteFile(%q): %v", fn, err)
  46. }
  47. }
  48. certChecker := ssh.CertChecker{
  49. IsUserAuthority: func(k ssh.PublicKey) bool {
  50. return bytes.Equal(k.Marshal(), testPublicKeys["ca"].Marshal())
  51. },
  52. UserKeyFallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
  53. if conn.User() == "testpubkey" && bytes.Equal(key.Marshal(), testPublicKeys["rsa"].Marshal()) {
  54. return nil, nil
  55. }
  56. return nil, fmt.Errorf("pubkey for %q not acceptable", conn.User())
  57. },
  58. }
  59. config := &ssh.ServerConfig{
  60. PublicKeyCallback: certChecker.Authenticate,
  61. }
  62. config.AddHostKey(testSigners["rsa"])
  63. server, err := newTestServer(config)
  64. if err != nil {
  65. t.Fatalf("unable to start test server: %v", err)
  66. }
  67. defer server.Close()
  68. port, err := server.port()
  69. if err != nil {
  70. t.Fatalf("unable to get server port: %v", err)
  71. }
  72. // test public key authentication.
  73. cmd := testenv.Command(t, sshCLI, "-vvv", "-i", keyPrivPath, "-o", "StrictHostKeyChecking=no",
  74. "-p", port, "testpubkey@127.0.0.1", "true")
  75. out, err := cmd.CombinedOutput()
  76. if err != nil {
  77. t.Fatalf("public key authentication failed, error: %v, command output %q", err, string(out))
  78. }
  79. // Test SSH user certificate authentication.
  80. // The username must match one of the principals included in the certificate.
  81. // The certificate "rsa-user-testcertificate" has "testcertificate" as principal.
  82. cmd = testenv.Command(t, sshCLI, "-vvv", "-i", keyPrivPath, "-o", "StrictHostKeyChecking=no",
  83. "-p", port, "testcertificate@127.0.0.1", "true")
  84. out, err = cmd.CombinedOutput()
  85. if err != nil {
  86. t.Fatalf("user certificate authentication failed, error: %v, command output %q", err, string(out))
  87. }
  88. }