Files
Bose-SoundTouch/pkg/service/setup/enable_ssh_test.go
T
Tobias GesellchenandClaude Opus 4.8 6c8a50c049 feat(cli): opt-in hardening for setup enable-ssh (--close-17000, --authorized-key) (refs #471)
Adds the #471 "secure" steps as opt-in flags on `setup enable-ssh`, off by
default (per the decision that closing 17000 must be opt-in):

- --close-17000: blocks port 17000 from the LAN. Manager.Close17000 remounts /
  read-write, persists an idempotent iptables rule in
  /etc/init.d/Firewalls/update_iptables (keyed on a marker), and applies it
  immediately; loopback access is kept.
- --authorized-key <pubkey>: Manager.InstallAuthorizedKey writes the key to
  /home/root/.ssh/authorized_keys so root SSH no longer relies on the
  empty-password login.

Both run over the SSH the enable step just opened. Default output reminds the
user that 17000 is left open and how to close it. Unit tests cover the
firewall command sequence and the key upload path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 21:11:19 +02:00

107 lines
3.0 KiB
Go

package setup
import (
"strings"
"testing"
)
func TestEnableSSHViaTelnet_BuildsInjectedCommand(t *testing.T) {
const svc = "https://192.0.2.10:8443"
want := `envswitch boseurls set "https://192.0.2.10:8443;touch /tmp/remote_services;/etc/init.d/sshd start" "https://192.0.2.10:8443/update"`
f := &fakeTelnet{responses: map[string]string{want: "OK\n"}}
m := newFakeTelnetManager(f)
if _, err := m.EnableSSHViaTelnet("192.0.2.10", svc); err != nil {
t.Fatalf("EnableSSHViaTelnet: %v", err)
}
if len(f.commands) != 1 || f.commands[0] != want {
t.Errorf("sent %q\n want %q", f.commands, want)
}
}
func TestResetBoseURLs_BuildsCleanCommand(t *testing.T) {
const svc = "https://192.0.2.10:8443"
want := `envswitch boseurls set "https://192.0.2.10:8443" "https://192.0.2.10:8443/update"`
f := &fakeTelnet{responses: map[string]string{want: "OK\n"}}
m := newFakeTelnetManager(f)
if _, err := m.ResetBoseURLs("192.0.2.10", svc); err != nil {
t.Fatalf("ResetBoseURLs: %v", err)
}
if len(f.commands) != 1 || f.commands[0] != want {
t.Errorf("sent %q\n want %q", f.commands, want)
}
}
func TestSetBoseURLs_RejectsDoubleQuote(t *testing.T) {
m := newFakeTelnetManager(&fakeTelnet{})
if _, err := m.EnableSSHViaTelnet("192.0.2.10", `https://x"evil`); err == nil {
t.Fatal("expected an error when the service URL contains a double quote")
}
}
func TestClose17000_RunsFirewallSteps(t *testing.T) {
var ran []string
m := &Manager{NewSSH: func(string) SSHClient {
return &mockSSH{runFunc: func(cmd string) (string, error) {
ran = append(ran, cmd)
return "", nil
}}
}}
if _, err := m.Close17000("192.0.2.10"); err != nil {
t.Fatalf("Close17000: %v", err)
}
joined := strings.Join(ran, "\n")
for _, want := range []string{
"mount / -o rw,remount",
block17000Marker,
"iptables -I INPUT -p tcp --dport 17000 -j DROP",
"--dport 17000 -i lo -j ACCEPT",
} {
if !strings.Contains(joined, want) {
t.Errorf("Close17000 commands missing %q\nran:\n%s", want, joined)
}
}
}
func TestInstallAuthorizedKey_UploadsKey(t *testing.T) {
m := &Manager{NewSSH: func(string) SSHClient {
return &mockSSH{runFunc: func(string) (string, error) { return "", nil }}
}}
if _, err := m.InstallAuthorizedKey("192.0.2.10", " ssh-ed25519 AAAATEST comment "); err != nil {
t.Fatalf("InstallAuthorizedKey: %v", err)
}
// A fresh mockSSH is created per NewSSH call, so re-run against a captured
// one to assert the upload.
var captured *mockSSH
m.NewSSH = func(string) SSHClient {
captured = &mockSSH{runFunc: func(string) (string, error) { return "", nil }}
return captured
}
if _, err := m.InstallAuthorizedKey("192.0.2.10", "ssh-ed25519 AAAATEST comment"); err != nil {
t.Fatalf("InstallAuthorizedKey: %v", err)
}
got, ok := captured.uploaded["/home/root/.ssh/authorized_keys"]
if !ok {
t.Fatal("authorized_keys was not uploaded")
}
if strings.TrimSpace(string(got)) != "ssh-ed25519 AAAATEST comment" {
t.Errorf("uploaded key = %q", string(got))
}
}