Skip to content

Commit e562f3a

Browse files
henrybarretogustavosbarreto
authored andcommitted
tests: add more integration tests on ssh server
1 parent f68d889 commit e562f3a

File tree

1 file changed

+308
-0
lines changed

1 file changed

+308
-0
lines changed

tests/ssh_test.go

Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,314 @@ func TestSSH(t *testing.T) {
997997
assert.Equal(t, unicodeChars, string(output))
998998
},
999999
},
1000+
{
1001+
name: "connection with cipher and MAC preferences",
1002+
run: func(t *testing.T, environment *Environment, device *models.Device) {
1003+
config := &ssh.ClientConfig{
1004+
User: fmt.Sprintf("%s@%s.%s", ShellHubAgentUsername, ShellHubNamespaceName, device.Name),
1005+
Auth: []ssh.AuthMethod{
1006+
ssh.Password(ShellHubAgentPassword),
1007+
},
1008+
HostKeyCallback: ssh.InsecureIgnoreHostKey(), //nolint:gosec
1009+
Config: ssh.Config{
1010+
Ciphers: []string{
1011+
"aes256-ctr", "aes192-ctr", "aes128-ctr",
1012+
"aes256-gcm@openssh.com", "aes128-gcm@openssh.com",
1013+
},
1014+
MACs: []string{
1015+
"hmac-sha2-256-etm@openssh.com",
1016+
"hmac-sha2-512-etm@openssh.com",
1017+
"hmac-sha2-256",
1018+
"hmac-sha2-512",
1019+
},
1020+
},
1021+
}
1022+
1023+
conn, err := ssh.Dial("tcp", fmt.Sprintf("localhost:%s", environment.services.Env("SHELLHUB_SSH_PORT")), config)
1024+
require.NoError(t, err)
1025+
defer conn.Close()
1026+
1027+
sess, err := conn.NewSession()
1028+
require.NoError(t, err)
1029+
defer sess.Close()
1030+
1031+
output, err := sess.Output("echo -n 'cipher test'")
1032+
require.NoError(t, err)
1033+
assert.Equal(t, "cipher test", string(output))
1034+
},
1035+
},
1036+
{
1037+
name: "multiple concurrent SSH sessions",
1038+
run: func(t *testing.T, environment *Environment, device *models.Device) {
1039+
config := &ssh.ClientConfig{
1040+
User: fmt.Sprintf("%s@%s.%s", ShellHubAgentUsername, ShellHubNamespaceName, device.Name),
1041+
Auth: []ssh.AuthMethod{
1042+
ssh.Password(ShellHubAgentPassword),
1043+
},
1044+
HostKeyCallback: ssh.InsecureIgnoreHostKey(), //nolint:gosec
1045+
}
1046+
1047+
const numConnections = 5
1048+
var wg sync.WaitGroup
1049+
errors := make(chan error, numConnections)
1050+
1051+
for i := range numConnections {
1052+
wg.Add(1)
1053+
go func(id int) {
1054+
defer wg.Done()
1055+
1056+
conn, err := ssh.Dial("tcp", fmt.Sprintf("localhost:%s", environment.services.Env("SHELLHUB_SSH_PORT")), config)
1057+
if err != nil {
1058+
errors <- fmt.Errorf("connection %d failed: %w", id, err)
1059+
1060+
return
1061+
}
1062+
defer conn.Close()
1063+
1064+
sess, err := conn.NewSession()
1065+
if err != nil {
1066+
errors <- fmt.Errorf("session %d failed: %w", id, err)
1067+
1068+
return
1069+
}
1070+
defer sess.Close()
1071+
1072+
expected := fmt.Sprintf("session-%d", id)
1073+
output, err := sess.Output(fmt.Sprintf("echo -n '%s'", expected))
1074+
if err != nil {
1075+
errors <- fmt.Errorf("command %d failed: %w", id, err)
1076+
1077+
return
1078+
}
1079+
1080+
if string(output) != expected {
1081+
errors <- fmt.Errorf("unexpected output from session %d: got %q, want %q", id, string(output), expected)
1082+
}
1083+
}(i)
1084+
}
1085+
1086+
wg.Wait()
1087+
close(errors)
1088+
1089+
for err := range errors {
1090+
require.NoError(t, err)
1091+
}
1092+
},
1093+
},
1094+
{
1095+
name: "connection with strict host key checking simulation",
1096+
run: func(t *testing.T, environment *Environment, device *models.Device) {
1097+
var learnedKey ssh.PublicKey
1098+
config1 := &ssh.ClientConfig{
1099+
User: fmt.Sprintf("%s@%s.%s", ShellHubAgentUsername, ShellHubNamespaceName, device.Name),
1100+
Auth: []ssh.AuthMethod{
1101+
ssh.Password(ShellHubAgentPassword),
1102+
},
1103+
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
1104+
learnedKey = key
1105+
1106+
return nil
1107+
},
1108+
}
1109+
1110+
conn1, err := ssh.Dial("tcp", fmt.Sprintf("localhost:%s", environment.services.Env("SHELLHUB_SSH_PORT")), config1)
1111+
require.NoError(t, err)
1112+
conn1.Close()
1113+
1114+
config2 := &ssh.ClientConfig{
1115+
User: fmt.Sprintf("%s@%s.%s", ShellHubAgentUsername, ShellHubNamespaceName, device.Name),
1116+
Auth: []ssh.AuthMethod{
1117+
ssh.Password(ShellHubAgentPassword),
1118+
},
1119+
HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
1120+
if !bytes.Equal(key.Marshal(), learnedKey.Marshal()) {
1121+
return fmt.Errorf("host key mismatch")
1122+
}
1123+
1124+
return nil
1125+
},
1126+
}
1127+
1128+
conn2, err := ssh.Dial("tcp", fmt.Sprintf("localhost:%s", environment.services.Env("SHELLHUB_SSH_PORT")), config2)
1129+
require.NoError(t, err)
1130+
defer conn2.Close()
1131+
},
1132+
},
1133+
{
1134+
name: "connection with keep-alive and heartbeat",
1135+
run: func(t *testing.T, environment *Environment, device *models.Device) {
1136+
config := &ssh.ClientConfig{
1137+
User: fmt.Sprintf("%s@%s.%s", ShellHubAgentUsername, ShellHubNamespaceName, device.Name),
1138+
Auth: []ssh.AuthMethod{
1139+
ssh.Password(ShellHubAgentPassword),
1140+
},
1141+
HostKeyCallback: ssh.InsecureIgnoreHostKey(), //nolint:gosec
1142+
Timeout: 10 * time.Second,
1143+
}
1144+
1145+
conn, err := ssh.Dial("tcp", fmt.Sprintf("localhost:%s", environment.services.Env("SHELLHUB_SSH_PORT")), config)
1146+
require.NoError(t, err)
1147+
defer conn.Close()
1148+
1149+
go func() {
1150+
ticker := time.NewTicker(2 * time.Second)
1151+
defer ticker.Stop()
1152+
for range 3 {
1153+
<-ticker.C
1154+
_, _, err := conn.SendRequest("keepalive@shellhub.io", true, nil)
1155+
if err != nil {
1156+
t.Logf("Keep-alive failed: %v", err)
1157+
1158+
return
1159+
}
1160+
}
1161+
}()
1162+
1163+
time.Sleep(8 * time.Second)
1164+
1165+
sess, err := conn.NewSession()
1166+
require.NoError(t, err)
1167+
defer sess.Close()
1168+
1169+
output, err := sess.Output("echo -n 'alive after keepalive'")
1170+
require.NoError(t, err)
1171+
assert.Equal(t, "alive after keepalive", string(output))
1172+
},
1173+
},
1174+
{
1175+
name: "connection with subsystem request (sftp)",
1176+
run: func(t *testing.T, environment *Environment, device *models.Device) {
1177+
config := &ssh.ClientConfig{
1178+
User: fmt.Sprintf("%s@%s.%s", ShellHubAgentUsername, ShellHubNamespaceName, device.Name),
1179+
Auth: []ssh.AuthMethod{
1180+
ssh.Password(ShellHubAgentPassword),
1181+
},
1182+
HostKeyCallback: ssh.InsecureIgnoreHostKey(), //nolint:gosec
1183+
}
1184+
1185+
conn, err := ssh.Dial("tcp", fmt.Sprintf("localhost:%s", environment.services.Env("SHELLHUB_SSH_PORT")), config)
1186+
require.NoError(t, err)
1187+
defer conn.Close()
1188+
1189+
sess, err := conn.NewSession()
1190+
require.NoError(t, err)
1191+
defer sess.Close()
1192+
1193+
err = sess.RequestSubsystem("sftp")
1194+
require.NoError(t, err)
1195+
1196+
stdin, err := sess.StdinPipe()
1197+
require.NoError(t, err)
1198+
1199+
stdout, err := sess.StdoutPipe()
1200+
require.NoError(t, err)
1201+
1202+
initPacket := []byte{0, 0, 0, 5, 1, 0, 0, 0, 3} // SSH_FXP_INIT with version 3
1203+
_, err = stdin.Write(initPacket)
1204+
require.NoError(t, err)
1205+
1206+
response := make([]byte, 9)
1207+
n, err := stdout.Read(response)
1208+
require.NoError(t, err)
1209+
assert.Equal(t, 9, n)
1210+
assert.Equal(t, byte(2), response[4]) // SSH_FXP_VERSION
1211+
},
1212+
},
1213+
{
1214+
name: "connection with pseudo-terminal modes",
1215+
run: func(t *testing.T, environment *Environment, device *models.Device) {
1216+
config := &ssh.ClientConfig{
1217+
User: fmt.Sprintf("%s@%s.%s", ShellHubAgentUsername, ShellHubNamespaceName, device.Name),
1218+
Auth: []ssh.AuthMethod{
1219+
ssh.Password(ShellHubAgentPassword),
1220+
},
1221+
HostKeyCallback: ssh.InsecureIgnoreHostKey(), //nolint:gosec
1222+
}
1223+
1224+
conn, err := ssh.Dial("tcp", fmt.Sprintf("localhost:%s", environment.services.Env("SHELLHUB_SSH_PORT")), config)
1225+
require.NoError(t, err)
1226+
defer conn.Close()
1227+
1228+
sess, err := conn.NewSession()
1229+
require.NoError(t, err)
1230+
defer sess.Close()
1231+
1232+
modes := ssh.TerminalModes{
1233+
ssh.ECHO: 0, // Disable echo
1234+
ssh.TTY_OP_ISPEED: 14400, // Input speed
1235+
ssh.TTY_OP_OSPEED: 14400, // Output speed
1236+
ssh.ICRNL: 1, // Map CR to NL on input
1237+
ssh.OPOST: 1, // Enable output processing
1238+
}
1239+
1240+
err = sess.RequestPty("xterm-256color", 24, 80, modes)
1241+
require.NoError(t, err)
1242+
1243+
stdin, err := sess.StdinPipe()
1244+
require.NoError(t, err)
1245+
1246+
stdout, err := sess.StdoutPipe()
1247+
require.NoError(t, err)
1248+
1249+
err = sess.Shell()
1250+
require.NoError(t, err)
1251+
1252+
_, err = stdin.Write([]byte("stty -echo && echo 'no echo test' && exit\n"))
1253+
require.NoError(t, err)
1254+
1255+
buffer := make([]byte, 1024)
1256+
n, err := stdout.Read(buffer)
1257+
require.NoError(t, err)
1258+
assert.Greater(t, n, 0)
1259+
},
1260+
},
1261+
{
1262+
name: "connection with signal handling",
1263+
run: func(t *testing.T, environment *Environment, device *models.Device) {
1264+
config := &ssh.ClientConfig{
1265+
User: fmt.Sprintf("%s@%s.%s", ShellHubAgentUsername, ShellHubNamespaceName, device.Name),
1266+
Auth: []ssh.AuthMethod{
1267+
ssh.Password(ShellHubAgentPassword),
1268+
},
1269+
HostKeyCallback: ssh.InsecureIgnoreHostKey(), //nolint:gosec
1270+
}
1271+
1272+
conn, err := ssh.Dial("tcp", fmt.Sprintf("localhost:%s", environment.services.Env("SHELLHUB_SSH_PORT")), config)
1273+
require.NoError(t, err)
1274+
defer conn.Close()
1275+
1276+
sess, err := conn.NewSession()
1277+
require.NoError(t, err)
1278+
defer sess.Close()
1279+
1280+
err = sess.RequestPty("xterm", 24, 80, ssh.TerminalModes{})
1281+
require.NoError(t, err)
1282+
1283+
stdin, err := sess.StdinPipe()
1284+
require.NoError(t, err)
1285+
1286+
err = sess.Shell()
1287+
require.NoError(t, err)
1288+
1289+
_, err = stdin.Write([]byte("sleep 30 &\n"))
1290+
require.NoError(t, err)
1291+
1292+
time.Sleep(100 * time.Millisecond)
1293+
1294+
err = sess.Signal(ssh.SIGINT)
1295+
if err != nil {
1296+
t.Logf("Signal sending not supported: %v", err)
1297+
}
1298+
1299+
err = sess.Signal(ssh.SIGTERM)
1300+
if err != nil {
1301+
t.Logf("Signal sending not supported: %v", err)
1302+
}
1303+
1304+
_, err = stdin.Write([]byte("echo 'signal test done'\n"))
1305+
require.NoError(t, err)
1306+
},
1307+
},
10001308
}
10011309

10021310
ctx := context.Background()

0 commit comments

Comments
 (0)