From 3c071d8812859905b007b69d8ce2403b0251f760 Mon Sep 17 00:00:00 2001 From: Janez T Date: Fri, 3 Jan 2025 10:25:22 +0100 Subject: [PATCH] feat: enhance SSH configuration checks and add unit tests --- checks/ssh_config.go | 29 ++++++++++++++--- checks/ssh_config_test.go | 67 +++++++++++++++++++++++++++++++++++++++ shared/system.go | 4 +++ 3 files changed, 95 insertions(+), 5 deletions(-) create mode 100644 checks/ssh_config_test.go diff --git a/checks/ssh_config.go b/checks/ssh_config.go index c56d68a..1ac57a0 100644 --- a/checks/ssh_config.go +++ b/checks/ssh_config.go @@ -1,7 +1,6 @@ package checks import ( - "os" "strings" "github.com/caarlos0/log" @@ -72,11 +71,31 @@ func (s *SSHConfigCheck) Passed() bool { } func (s *SSHConfigCheck) IsRunnable() bool { - if _, err := os.Stat("/etc/ssh/sshd_config"); os.IsNotExist(err) { - s.status = "/etc/ssh/sshd_config not found" - return false + + // Check if sshd service is running via systemd + sshdStatus, err := shared.RunCommand("systemctl", "is-active", "sshd") + if err != nil || strings.TrimSpace(string(sshdStatus)) == "active" { + return true } - return true + + // Check if ssh service is running via systemd + sshStatus, err := shared.RunCommand("systemctl", "is-active", "ssh") + if err != nil || strings.TrimSpace(string(sshStatus)) == "active" { + return true + } + // Check if ssh socket service is enabled via systemd + sshSocketStatus, err := shared.RunCommand("systemctl", "is-enabled", "sshd.socket") + if err != nil || strings.TrimSpace(string(sshSocketStatus)) == "enabled" { + return true + } + + // Check if ssh socket service is enabled via systemd + sshSocketStatus, err = shared.RunCommand("systemctl", "is-enabled", "ssh.socket") + if err != nil || strings.TrimSpace(string(sshSocketStatus)) == "enabled" { + return true + } + + return false } func (s *SSHConfigCheck) ReportIfDisabled() bool { diff --git a/checks/ssh_config_test.go b/checks/ssh_config_test.go new file mode 100644 index 0000000..ff9f3d0 --- /dev/null +++ b/checks/ssh_config_test.go @@ -0,0 +1,67 @@ +package checks + +import ( + "testing" + + "github.com/ParetoSecurity/pareto-linux/shared" + "github.com/stretchr/testify/assert" +) + +func TestCheckSSHConfig(t *testing.T) { + + tests := []struct { + name string + setupMocks map[string]string + expectedPassed bool + expectedDetail string + }{ + { + name: "All ok", + setupMocks: map[string]string{ + "sshd -T": "PasswordAuthentication no\nPermitRootLogin no", + }, + expectedPassed: true, + expectedDetail: "", + }, + { + name: "PasswordAuthentication is enabled", + setupMocks: map[string]string{ + "sshd -T": "PasswordAuthentication yes\nPermitRootLogin no", + }, + expectedPassed: false, + expectedDetail: "PasswordAuthentication is enabled", + }, + { + name: "PermitRootLogin is enabled", + setupMocks: map[string]string{ + "sshd -T": "PasswordAuthentication no\nPermitRootLogin yes", + }, + expectedPassed: false, + expectedDetail: "Root login is enabled", + }, + + { + name: "PermitEmptyPasswords is enabled", + setupMocks: map[string]string{ + "sshd -T": "PasswordAuthentication no\nPermitRootLogin no\nPermitEmptyPasswords yes", + }, + expectedPassed: false, + expectedDetail: "Empty passwords are allowed", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + shared.RunCommandMocks = tt.setupMocks + lookPathMock = func(file string) (string, error) { + return file, nil + } + su := &SSHConfigCheck{} + + err := su.Run() + assert.Nil(t, err) + assert.Equal(t, tt.expectedPassed, su.passed) + assert.Equal(t, tt.expectedDetail, su.status) + }) + } +} diff --git a/shared/system.go b/shared/system.go index 0566eb7..979238b 100644 --- a/shared/system.go +++ b/shared/system.go @@ -4,6 +4,7 @@ import ( "fmt" "net" "os" + "testing" "strings" @@ -62,6 +63,9 @@ func SystemSerial() (string, error) { } func IsRoot() bool { + if testing.Testing() { + return true + } return os.Geteuid() == 0 }