tests/connector_ssh_test.go
Rama master · réplica de lectura del repositorio original.
package main
import (
"context"
"io"
"strings"
"testing"
"github.com/shellhub-io/shellhub/pkg/models"
"github.com/shellhub-io/shellhub/tests/environment"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/testcontainers/testcontainers-go"
tcexec "github.com/testcontainers/testcontainers-go/exec"
"golang.org/x/crypto/ssh"
)
const (
connectorShadowUser = "shadowed"
connectorShadowPassword = "shadowed-password"
)
// TestConnectorSSH logs in through a connector to the container it serves. The connector checks a
// password against the container's own /etc/shadow, so the password cases log in as a user that
// exists only in that container, with the password set there.
func TestConnectorSSH(t *testing.T) {
t.Parallel()
ctx := context.Background()
compose := newSSHEnvironment(t, ctx, legacyNamespace)
name := "ssh-" + randomHex(t)
target := startLabelledContainer(t, ctx, name)
execInConnectorTarget(t, ctx, target, "adduser -D "+connectorShadowUser+" && echo "+connectorShadowUser+":"+connectorShadowPassword+" | chpasswd")
startConnector(t, ctx, compose, connectorLabel+"="+name)
device := awaitContainerWithStatus(t, compose, models.DeviceStatusPending)
compose.UpdateDeviceStatus(t, device.UID, environment.DeviceActionAccept)
compose.AwaitDeviceOnline(t, device.UID)
t.Run("a password set in the container's /etc/shadow logs in", func(t *testing.T) {
skipUnlessAgentAcceptsPasswords(t)
conn := dialClient(t, ctx, compose.SSHAddress(), connectorShadowLogin(&device, connectorShadowPassword))
defer conn.Close() //nolint:errcheck // the test is over once the command answered
assert.Equal(t, connectorShadowUser, strings.TrimSpace(runOnDevice(t, conn, "id -un")))
})
t.Run("a password the container's /etc/shadow does not hold is refused", func(t *testing.T) {
skipUnlessAgentAcceptsPasswords(t)
err := handshakeWith(ctx, compose.SSHAddress(), connectorShadowLogin(&device, "wrong-password"))
require.ErrorContains(t, err, "ssh: unable to authenticate, attempted methods [none password]")
})
t.Run("a registered public key logs in", func(t *testing.T) {
conn := dialDevice(t, ctx, compose, &device, registerDeviceKey(t, ctx, compose))
defer conn.Close() //nolint:errcheck // the test is over once the command answered
assert.Equal(t, ShellHubAgentUsername, strings.TrimSpace(runOnDevice(t, conn, "id -un")))
})
}
func connectorShadowLogin(device *models.Device, password string) *ssh.ClientConfig {
return &ssh.ClientConfig{ //nolint:exhaustruct // the remaining fields keep their defaults
User: connectorShadowUser + "@" + ShellHubNamespaceName + "." + device.Name,
Auth: []ssh.AuthMethod{ssh.Password(password)},
HostKeyCallback: ssh.InsecureIgnoreHostKey(), //nolint:gosec // the test stack's host key is ephemeral
}
}
func execInConnectorTarget(t *testing.T, ctx context.Context, c testcontainers.Container, script string) {
t.Helper()
code, reader, err := c.Exec(ctx, []string{"sh", "-c", script}, tcexec.Multiplexed())
require.NoError(t, err)
output, err := io.ReadAll(reader)
require.NoError(t, err)
require.Zero(t, code, string(output))
}