package e2e_test import ( "encoding/base64" "fmt" "html/template" "os" "path" "testing" "time" "github.com/google/uuid" ) const yamlConfigTemplate = ` version: "1.0" pipes: - from: - username: "password_simple" to: host: host-password:2222 username: "user" ignore_hostkey: true - from: - username: "^password_.*_regex$" username_regex_match: true to: host: host-password:2222 username: "user" known_hosts_data: # github.com - fDF8RjRwTmVveUZHVEVHcEIyZ3A4RGE0WlE4TGNVPXxycVZYNU0rWTJoS0dteFphcVFBb0syRHp1TEE9IHNzaC1lZDI1NTE5IEFBQUFDM056YUMxbFpESTFOVEU1QUFBQUlPTXFxbmtWenJtMFNkRzZVT29xS0xzYWJnSDVDOW9rV2kwZGgybDlHS0psCg== - {{ .KnownHostsKey }} - {{ .KnownHostsPass }} - from: - username: "^password_(.+?)_regex_expand$" username_regex_match: true to: host: host-password:2222 username: "$1" known_hosts_data: {{ .KnownHostsPass }} - from: - username: "publickey_simple" authorized_keys: {{ .AuthorizedKeys_Simple }} to: host: host-publickey:2222 username: "user" private_key: {{ .PrivateKey }} known_hosts_data: {{ .KnownHostsKey }} - from: - username: "cert" trusted_user_ca_keys: {{ .TrustedUserCAKeys }} to: host: host-publickey:2222 username: "user" ignore_hostkey: true private_key: {{ .PrivateKey }} - from: - groupname: "testgroup" authorized_keys: {{ .AuthorizedKeys_Simple }} to: host: host-publickey:2222 username: "user" private_key: {{ .PrivateKey }} known_hosts_data: {{ .KnownHostsKey }} - from: - groupname: "testgroup" to: host: host-password:2222 username: "user" ignore_hostkey: true - from: - username: ".*" username_regex_match: true authorized_keys: - {{ .AuthorizedKeys_Simple }} - {{ .AuthorizedKeys_Catchall }} to: host: host-publickey:2222 username: "user" ignore_hostkey: true private_key: {{ .PrivateKey }} ` func TestYaml(t *testing.T) { yamldir, err := os.MkdirTemp("", "") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) } yamlfile, err := os.OpenFile(path.Join(yamldir, "config.yaml"), os.O_RDWR|os.O_CREATE, 0o400) if err != nil { t.Fatalf("Failed to create temp file: %v", err) } { // simple key if err := runCmdAndWait("rm", "-f", path.Join(yamldir, "id_rsa_simple")); err != nil { t.Errorf("failed to remove id_rsa: %v", err) } if err := runCmdAndWait( "ssh-keygen", "-N", "", "-f", path.Join(yamldir, "id_rsa_simple"), ); err != nil { t.Errorf("failed to generate private key: %v", err) } // catch all key if err := runCmdAndWait("rm", "-f", path.Join(yamldir, "id_rsa_catchall")); err != nil { t.Errorf("failed to remove id_rsa: %v", err) } if err := runCmdAndWait( "ssh-keygen", "-N", "", "-f", path.Join(yamldir, "id_rsa_catchall"), ); err != nil { t.Errorf("failed to generate private key: %v", err) } // upstream key if err := runCmdAndWait("rm", "-f", path.Join(yamldir, "id_rsa")); err != nil { t.Errorf("failed to remove id_rsa: %v", err) } if err := runCmdAndWait( "ssh-keygen", "-N", "", "-f", path.Join(yamldir, "id_rsa"), ); err != nil { t.Errorf("failed to generate private key: %v", err) } if err := runCmdAndWait( "/bin/cp", path.Join(yamldir, "id_rsa.pub"), "/publickey_authorized_keys/authorized_keys", ); err != nil { t.Errorf("failed to copy public key: %v", err) } // ssh ca if err := runCmdAndWait( "ssh-keygen", "-N", "", "-f", path.Join(yamldir, "ca_key"), ); err != nil { t.Errorf("failed to generate ca key: %v", err) } if err := runCmdAndWait( "ssh-keygen", "-N", "", "-f", path.Join(yamldir, "user_ca_key"), ); err != nil { t.Errorf("failed to generate user ca key: %v", err) } if err := runCmdAndWait( "ssh-keygen", "-s", path.Join(yamldir, "ca_key"), "-I", "cert", "-n", "cert", "-V", "+1w", path.Join(yamldir, "user_ca_key.pub"), ); err != nil { t.Errorf("failed to sign user ca key: %v", err) } } knownHostsKeyData, err := runAndGetStdout( "ssh-keyscan", "-p", "2222", "host-publickey", ) if err != nil { t.Errorf("failed to run ssh-keyscan: %v", err) } knownHostsPassData, err := runAndGetStdout( "ssh-keyscan", "-p", "2222", "host-password", ) if err != nil { t.Errorf("failed to run ssh-keyscan : %v", err) } if err := template.Must(template.New("yaml").Parse(yamlConfigTemplate)).ExecuteTemplate(yamlfile, "yaml", struct { KnownHostsKey string KnownHostsPass string PrivateKey string AuthorizedKeys_Simple string AuthorizedKeys_Catchall string TrustedUserCAKeys string }{ KnownHostsKey: base64.StdEncoding.EncodeToString(knownHostsKeyData), KnownHostsPass: base64.StdEncoding.EncodeToString(knownHostsPassData), PrivateKey: path.Join(yamldir, "id_rsa"), AuthorizedKeys_Simple: path.Join(yamldir, "id_rsa_simple.pub"), AuthorizedKeys_Catchall: path.Join(yamldir, "id_rsa_catchall.pub"), TrustedUserCAKeys: path.Join(yamldir, "ca_key.pub"), }); err != nil { t.Fatalf("Failed to write yaml file %v", err) } // dump config.yaml to stdout _ = runCmdAndWait("cat", "-n", path.Join(yamldir, "config.yaml")) piperaddr, piperport := nextAvailablePiperAddress() piper, _, _, err := runCmd("/sshpiperd/sshpiperd", "-p", piperport, "/sshpiperd/plugins/yaml", "--config", yamlfile.Name(), ) if err != nil { t.Errorf("failed to run sshpiperd: %v", err) } defer killCmd(piper) waitForEndpointReady(piperaddr) t.Run("password_simple", func(t *testing.T) { randtext := uuid.New().String() targetfie := uuid.New().String() c, stdin, stdout, err := runCmd( "ssh", "-v", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-p", piperport, "-l", "password_simple", "127.0.0.1", fmt.Sprintf(`sh -c "echo -n %v > /shared/%v"`, randtext, targetfie), ) if err != nil { t.Errorf("failed to ssh to piper, %v", err) } defer killCmd(c) enterPassword(stdin, stdout, "pass") time.Sleep(time.Second) // wait for file flush checkSharedFileContent(t, targetfie, randtext) }) t.Run("password_regex", func(t *testing.T) { randtext := uuid.New().String() targetfie := uuid.New().String() c, stdin, stdout, err := runCmd( "ssh", "-v", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-p", piperport, "-l", "password_XXX_regex", "127.0.0.1", fmt.Sprintf(`sh -c "echo -n %v > /shared/%v"`, randtext, targetfie), ) if err != nil { t.Errorf("failed to ssh to piper, %v", err) } defer killCmd(c) enterPassword(stdin, stdout, "pass") time.Sleep(time.Second) // wait for file flush checkSharedFileContent(t, targetfie, randtext) }) t.Run("password_regex_expand", func(t *testing.T) { randtext := uuid.New().String() targetfie := uuid.New().String() c, stdin, stdout, err := runCmd( "ssh", "-v", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-p", piperport, "-l", "password_user_regex_expand", "127.0.0.1", fmt.Sprintf(`sh -c "echo -n %v > /shared/%v"`, randtext, targetfie), ) if err != nil { t.Errorf("failed to ssh to piper, %v", err) } defer killCmd(c) enterPassword(stdin, stdout, "pass") time.Sleep(time.Second) // wait for file flush checkSharedFileContent(t, targetfie, randtext) }) t.Run("publickey_simple", func(t *testing.T) { randtext := uuid.New().String() targetfie := uuid.New().String() c, _, _, err := runCmd( "ssh", "-v", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-p", piperport, "-l", "publickey_simple", "-i", path.Join(yamldir, "id_rsa_simple"), "127.0.0.1", fmt.Sprintf(`sh -c "echo -n %v > /shared/%v"`, randtext, targetfie), ) if err != nil { t.Errorf("failed to ssh to piper, %v", err) } defer killCmd(c) time.Sleep(time.Second) // wait for file flush checkSharedFileContent(t, targetfie, randtext) }) t.Run("catch_all", func(t *testing.T) { randtext := uuid.New().String() targetfie := uuid.New().String() c, _, _, err := runCmd( "ssh", "-v", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-p", piperport, "-l", "anyusername", "-i", path.Join(yamldir, "id_rsa_catchall"), "127.0.0.1", fmt.Sprintf(`sh -c "echo -n %v > /shared/%v"`, randtext, targetfie), ) if err != nil { t.Errorf("failed to ssh to piper, %v", err) } defer killCmd(c) time.Sleep(time.Second) // wait for file flush checkSharedFileContent(t, targetfie, randtext) }) t.Run("publickey_simple_withmultiple_keyfile", func(t *testing.T) { randtext := uuid.New().String() targetfie := uuid.New().String() wrongkeydir, err := os.MkdirTemp("", "") if err != nil { t.Errorf("failed to create temp key file: %v", err) } wrongkeyfile := path.Join(wrongkeydir, "key") if err := runCmdAndWait( "ssh-keygen", "-N", "", "-f", wrongkeyfile, ); err != nil { t.Errorf("failed to generate key: %v", err) } c, _, _, err := runCmd( "ssh", "-v", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-p", piperport, "-l", "publickey_simple", "-i", wrongkeyfile, "-i", path.Join(yamldir, "id_rsa_simple"), "127.0.0.1", fmt.Sprintf(`sh -c "echo -n %v > /shared/%v"`, randtext, targetfie), ) if err != nil { t.Errorf("failed to ssh to piper, %v", err) } defer killCmd(c) time.Sleep(time.Second) // wait for file flush checkSharedFileContent(t, targetfie, randtext) }) t.Run("ssh_cert", func(t *testing.T) { randtext := uuid.New().String() targetfie := uuid.New().String() c, _, _, err := runCmd( "ssh", "-v", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-o", fmt.Sprintf("CertificateFile=%v", path.Join(yamldir, "user_ca_key-cert.pub")), "-p", piperport, "-l", "cert", "-i", path.Join(yamldir, "user_ca_key"), "127.0.0.1", fmt.Sprintf(`sh -c "echo -n %v > /shared/%v"`, randtext, targetfie), ) if err != nil { t.Errorf("failed to ssh to piper, %v", err) } defer killCmd(c) time.Sleep(time.Second) // wait for file flush checkSharedFileContent(t, targetfie, randtext) }) t.Run("group_routing_key", func(t *testing.T) { randtext := uuid.New().String() targetfie := uuid.New().String() c, _, _, err := runCmd( "ssh", "-v", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-p", piperport, "-l", "testgroupuser", "-i", path.Join(yamldir, "id_rsa_simple"), "127.0.0.1", fmt.Sprintf(`sh -c "echo -n %v > /shared/%v"`, randtext, targetfie), ) if err != nil { t.Errorf("failed to ssh to piper, %v", err) } defer killCmd(c) time.Sleep(time.Second) // wait for file flush checkSharedFileContent(t, targetfie, randtext) }) t.Run("group_routing_password", func(t *testing.T) { randtext := uuid.New().String() targetfie := uuid.New().String() c, stdin, stdout, err := runCmd( "ssh", "-v", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-p", piperport, "-l", "testgroupuser", "127.0.0.1", fmt.Sprintf(`sh -c "echo -n %v > /shared/%v"`, randtext, targetfie), ) if err != nil { t.Errorf("failed to ssh to piper, %v", err) } defer killCmd(c) enterPassword(stdin, stdout, "pass") time.Sleep(time.Second) // wait for file flush checkSharedFileContent(t, targetfie, randtext) }) }