Ctrl K
ComunidadGuías…

tests/connector_ssh_test.go

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
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))
}