Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pkg/cmd/system_details_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func runSystemDetails() error {
hostInfoManager := application.NewHostInfoManager(hostInfoService)

// Generate system status information with our enhanced implementation
info, err := system.GenerateSystemStatus(hostInfoManager)
info, err := system.GenerateSystemStatus(hostInfoManager, provider.Commander)
if err != nil {
return fmt.Errorf("failed to generate system status: %w", err)
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/infrastructure/menu_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (f *MenuFactory) CreateHelpMenu() *menu.HelpMenu {
func (f *MenuFactory) CreateSystemDetailsMenu() *menu.SystemDetailsMenu {
// Get the host info manager from the service factory
hostInfoManager := f.serviceFactory.CreateHostInfoManager()
return menu.NewSystemDetailsMenu(f.config, f.osInfo, hostInfoManager)
return menu.NewSystemDetailsMenu(f.config, f.osInfo, hostInfoManager, f.serviceFactory.provider.Commander)
}

// CreateMainMenu creates the main menu with all dependencies wired up
Expand Down Expand Up @@ -83,5 +83,5 @@ func (f *MenuFactory) CreateMainMenu(versionService *version.Service) *menu.Main
hostInfoManager)

// Create menu with all necessary fields initialized
return menu.NewMainMenu(menuManager, f.config, f.osInfo, versionService)
return menu.NewMainMenu(menuManager, f.config, f.osInfo, versionService, f.serviceFactory.provider.Commander)
}
8 changes: 6 additions & 2 deletions pkg/menu/main_menu.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/abbott/hardn/pkg/application"
"github.com/abbott/hardn/pkg/config"
"github.com/abbott/hardn/pkg/domain/model"
"github.com/abbott/hardn/pkg/interfaces"
"github.com/abbott/hardn/pkg/osdetect"
"github.com/abbott/hardn/pkg/security"
"github.com/abbott/hardn/pkg/style"
Expand All @@ -19,6 +20,7 @@ type MainMenu struct {
menuManager *application.MenuManager
config *config.Config
osInfo *osdetect.OSInfo
commander interfaces.Commander

// Version service for update checks
versionService *version.Service
Expand All @@ -40,12 +42,14 @@ func NewMainMenu(
config *config.Config,
osInfo *osdetect.OSInfo,
versionService *version.Service,
commander interfaces.Commander,
) *MainMenu {
return &MainMenu{
menuManager: menuManager,
config: config,
osInfo: osInfo,
versionService: versionService,
commander: commander,
}
}

Expand Down Expand Up @@ -391,7 +395,7 @@ func (m *MainMenu) ShowMainMenu(currentVersion, buildDate, gitCommit string) {
utils.ClearScreen()

// Get security status
securityStatus, err := security.CheckSecurityStatus(m.config, m.osInfo)
securityStatus, err := security.CheckSecurityStatus(m.config, m.osInfo, m.commander)

// Create formatter for security status
formatter := style.NewStatusFormatter([]string{
Expand Down Expand Up @@ -524,7 +528,7 @@ func (m *MainMenu) handleMenuChoice(choice string) bool {
envMenu.Show()

case "9": // Host Info
systemDetailsMenu := NewSystemDetailsMenu(m.config, m.osInfo, m.menuManager.GetHostInfoManager())
systemDetailsMenu := NewSystemDetailsMenu(m.config, m.osInfo, m.menuManager.GetHostInfoManager(), m.commander)
systemDetailsMenu.Show()

case "10": // Logs
Expand Down
6 changes: 5 additions & 1 deletion pkg/menu/system_details_menu.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/abbott/hardn/pkg/application"
"github.com/abbott/hardn/pkg/config"
"github.com/abbott/hardn/pkg/interfaces"
"github.com/abbott/hardn/pkg/osdetect"
"github.com/abbott/hardn/pkg/style"
"github.com/abbott/hardn/pkg/system"
Expand All @@ -18,18 +19,21 @@ type SystemDetailsMenu struct {
config *config.Config
osInfo *osdetect.OSInfo
hostInfoManager *application.HostInfoManager
commander interfaces.Commander
}

// NewSystemDetailsMenu creates a new SystemDetailsMenu
func NewSystemDetailsMenu(
config *config.Config,
osInfo *osdetect.OSInfo,
hostInfoManager *application.HostInfoManager,
commander interfaces.Commander,
) *SystemDetailsMenu {
return &SystemDetailsMenu{
config: config,
osInfo: osInfo,
hostInfoManager: hostInfoManager,
commander: commander,
}
}

Expand All @@ -38,7 +42,7 @@ func (m *SystemDetailsMenu) Show() {
utils.ClearScreen()

// Get detailed system information using our enhanced status package
systemInfo, err := system.GenerateSystemStatus(m.hostInfoManager)
systemInfo, err := system.GenerateSystemStatus(m.hostInfoManager, m.commander)
if err != nil {
fmt.Printf("\n%s Error retrieving system status: %v\n",
style.Colored(style.Red, style.SymCrossMark), err)
Expand Down
36 changes: 13 additions & 23 deletions pkg/security/security.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ package security
import (
"fmt"
"os"
"os/exec"
"path/filepath"

"github.com/abbott/hardn/pkg/config"
"github.com/abbott/hardn/pkg/interfaces"
"github.com/abbott/hardn/pkg/logging"
"github.com/abbott/hardn/pkg/osdetect"
)

// SetupAppArmor installs and configures AppArmor
func SetupAppArmor(cfg *config.Config, osInfo *osdetect.OSInfo) error {
func SetupAppArmor(cfg *config.Config, osInfo *osdetect.OSInfo, commander interfaces.Commander) error {
if cfg.DryRun {
logging.LogInfo("[DRY-RUN] Install and configure AppArmor:")
if osInfo.OsType == "alpine" {
Expand All @@ -31,19 +31,16 @@ func SetupAppArmor(cfg *config.Config, osInfo *osdetect.OSInfo) error {
// Install AppArmor
if osInfo.OsType == "alpine" {
// Alpine installation
cmd := exec.Command("apk", "add", "apparmor")
if err := cmd.Run(); err != nil {
if _, err := commander.Execute("apk", "add", "apparmor"); err != nil {
return fmt.Errorf("failed to install AppArmor on Alpine: %w", err)
}

// Enable AppArmor in OpenRC
rcUpdateCmd := exec.Command("rc-update", "add", "apparmor", "default")
if err := rcUpdateCmd.Run(); err != nil {
if _, err := commander.Execute("rc-update", "add", "apparmor", "default"); err != nil {
logging.LogError("Failed to add AppArmor to Alpine boot services: %v", err)
}

rcServiceCmd := exec.Command("rc-service", "apparmor", "start")
if err := rcServiceCmd.Run(); err != nil {
if _, err := commander.Execute("rc-service", "apparmor", "start"); err != nil {
logging.LogError("Failed to start AppArmor service on Alpine: %v", err)
}

Expand All @@ -57,8 +54,7 @@ func SetupAppArmor(cfg *config.Config, osInfo *osdetect.OSInfo) error {
for _, file := range files {
if !file.IsDir() {
profilePath := filepath.Join(profilesDir, file.Name())
aaEnforceCmd := exec.Command("aa_enforce", profilePath)
if err := aaEnforceCmd.Run(); err != nil {
if _, err := commander.Execute("aa_enforce", profilePath); err != nil {
logging.LogError("Failed to enforce AppArmor profile %s: %v", profilePath, err)
}
}
Expand All @@ -67,14 +63,12 @@ func SetupAppArmor(cfg *config.Config, osInfo *osdetect.OSInfo) error {
}
} else {
// Debian/Ubuntu installation
cmd := exec.Command("apt-get", "install", "-y", "apparmor")
if err := cmd.Run(); err != nil {
if _, err := commander.Execute("apt-get", "install", "-y", "apparmor"); err != nil {
return fmt.Errorf("failed to install AppArmor on Debian/Ubuntu: %w", err)
}

// Apply profiles
aaEnforceCmd := exec.Command("aa-enforce", "/etc/apparmor.d/*")
if err := aaEnforceCmd.Run(); err != nil {
if _, err := commander.Execute("aa-enforce", "/etc/apparmor.d/*"); err != nil {
logging.LogError("Failed to enforce AppArmor profiles with wildcard: %v", err)
// Try individual profiles if wildcard fails
profilesDir := "/etc/apparmor.d"
Expand All @@ -86,8 +80,7 @@ func SetupAppArmor(cfg *config.Config, osInfo *osdetect.OSInfo) error {
for _, file := range files {
if !file.IsDir() {
profilePath := filepath.Join(profilesDir, file.Name())
aaEnforceCmd := exec.Command("aa-enforce", profilePath)
if err := aaEnforceCmd.Run(); err != nil {
if _, err := commander.Execute("aa-enforce", profilePath); err != nil {
logging.LogError("Failed to enforce AppArmor profile %s: %v", profilePath, err)
}
}
Expand All @@ -102,7 +95,7 @@ func SetupAppArmor(cfg *config.Config, osInfo *osdetect.OSInfo) error {
}

// SetupLynis installs and runs the Lynis security audit tool
func SetupLynis(cfg *config.Config, osInfo *osdetect.OSInfo) error {
func SetupLynis(cfg *config.Config, osInfo *osdetect.OSInfo, commander interfaces.Commander) error {
if cfg.DryRun {
logging.LogInfo("[DRY-RUN] Install and run Lynis security audit tool:")
if osInfo.OsType == "alpine" {
Expand All @@ -119,20 +112,17 @@ func SetupLynis(cfg *config.Config, osInfo *osdetect.OSInfo) error {

// Install Lynis
if osInfo.OsType == "alpine" {
cmd := exec.Command("apk", "add", "lynis")
if err := cmd.Run(); err != nil {
if _, err := commander.Execute("apk", "add", "lynis"); err != nil {
return fmt.Errorf("failed to install Lynis on Alpine: %w", err)
}
} else {
cmd := exec.Command("apt-get", "install", "-y", "lynis")
if err := cmd.Run(); err != nil {
if _, err := commander.Execute("apt-get", "install", "-y", "lynis"); err != nil {
return fmt.Errorf("failed to install Lynis on Debian/Ubuntu: %w", err)
}
}

// Run Lynis audit
auditCmd := exec.Command("lynis", "audit", "system")
output, err := auditCmd.CombinedOutput()
output, err := commander.Execute("lynis", "audit", "system")
if err != nil {
return fmt.Errorf("failed to run Lynis audit: %w\nOutput: %s", err, string(output))
}
Expand Down
55 changes: 23 additions & 32 deletions pkg/security/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ package security
import (
"bufio"
"os"
"os/exec"
"strconv"
"strings"

"github.com/abbott/hardn/pkg/adapter/secondary"
"github.com/abbott/hardn/pkg/config"
"github.com/abbott/hardn/pkg/interfaces"
"github.com/abbott/hardn/pkg/osdetect"
"github.com/abbott/hardn/pkg/style"
)
Expand All @@ -27,26 +27,26 @@ type SecurityStatus struct {
}

// CheckSecurityStatus examines the system and returns the security status
func CheckSecurityStatus(cfg *config.Config, osInfo *osdetect.OSInfo) (*SecurityStatus, error) {
func CheckSecurityStatus(cfg *config.Config, osInfo *osdetect.OSInfo, commander interfaces.Commander) (*SecurityStatus, error) {
status := &SecurityStatus{}

// Check SSH root login status
status.RootLoginEnabled = checkRootLoginEnabled(osInfo)

// Check firewall status
status.FirewallEnabled, status.FirewallConfigured = checkFirewallStatus()
status.FirewallEnabled, status.FirewallConfigured = checkFirewallStatus(commander)

// Check user security (non-root users with sudo)
status.SecureUsers = checkUserSecurity()
status.SecureUsers = checkUserSecurity(commander)

// Check AppArmor status
status.AppArmorEnabled = checkAppArmorStatus(osInfo)
status.AppArmorEnabled = checkAppArmorStatus(osInfo, commander)

// Check unattended upgrades
status.UnattendedUpgrades = checkUnattendedUpgrades(osInfo)
status.UnattendedUpgrades = checkUnattendedUpgrades(osInfo, commander)

// Check sudo configuration
status.SudoConfigured = checkSudoConfiguration()
status.SudoConfigured = checkSudoConfiguration(commander)

// Check SSH port configuration
status.SshPortNonDefault = (cfg.SshPort != 22)
Expand Down Expand Up @@ -91,8 +91,8 @@ func DisplaySecurityStatusWithCustomPrinter(cfg *config.Config, status *Security
// Display sudo configuration
if !status.SudoConfigured {
indentedPrintFn(formatter.FormatWarning("Sudo", "Not Installed", "", "dark"))
// } else {
// indentedPrintFn(formatter.FormatConfigured("Sudo", "Installed", "", "dark"))
// } else {
// indentedPrintFn(formatter.FormatConfigured("Sudo", "Installed", "", "dark"))
}

// Display sudo method
Expand Down Expand Up @@ -264,13 +264,12 @@ func checkRootLoginEnabled(osInfo *osdetect.OSInfo) bool {
}

// checkFirewallStatus checks if the firewall is enabled and properly configured
func checkFirewallStatus() (bool, bool) {
func checkFirewallStatus(commander interfaces.Commander) (bool, bool) {
enabled := false
configured := false

// Check if UFW is installed and enabled
cmd := exec.Command("ufw", "status", "verbose")
output, err := cmd.CombinedOutput()
output, err := commander.Execute("ufw", "status", "verbose")
if err == nil {
statusOutput := string(output)
enabled = strings.Contains(statusOutput, "Status: active")
Expand Down Expand Up @@ -300,8 +299,7 @@ func checkFirewallStatus() (bool, bool) {

// Check for iptables if UFW not found
if !enabled {
iptablesCmd := exec.Command("iptables", "-L")
iptablesOutput, err := iptablesCmd.CombinedOutput()
iptablesOutput, err := commander.Execute("iptables", "-L")
if err == nil {
rules := strings.Count(string(iptablesOutput), "Chain")
enabled = rules > 3
Expand All @@ -314,7 +312,7 @@ func checkFirewallStatus() (bool, bool) {
}

// checkUserSecurity checks if there are non-root users with sudo access
func checkUserSecurity() bool {
func checkUserSecurity(commander interfaces.Commander) bool {
// Check /etc/sudoers.d for non-root user entries
sudoersDir := "/etc/sudoers.d"
if _, err := os.Stat(sudoersDir); err == nil {
Expand Down Expand Up @@ -352,18 +350,16 @@ func checkUserSecurity() bool {

// checkAppArmorStatus checks if AppArmor is enabled
// checkAppArmorStatus checks if AppArmor is properly configured and enforcing
func checkAppArmorStatus(osInfo *osdetect.OSInfo) bool {
func checkAppArmorStatus(osInfo *osdetect.OSInfo, commander interfaces.Commander) bool {
// If Alpine, check if AppArmor is installed, enabled, and has profiles
if osInfo.OsType == "alpine" {
// Check if AppArmor package is installed
cmd := exec.Command("apk", "info", "-e", "apparmor")
if err := cmd.Run(); err != nil {
if _, err := commander.Execute("apk", "info", "-e", "apparmor"); err != nil {
return false
}

// Check if AppArmor is in runlevel
rcCmd := exec.Command("rc-status", "default")
output, err := rcCmd.CombinedOutput()
output, err := commander.Execute("rc-status", "default")
if err != nil {
return false
}
Expand All @@ -373,8 +369,7 @@ func checkAppArmorStatus(osInfo *osdetect.OSInfo) bool {
}

// Check if AppArmor is running and has profiles loaded
statusCmd := exec.Command("aa-status")
statusOutput, err := statusCmd.CombinedOutput()
statusOutput, err := commander.Execute("aa-status")
if err != nil {
return false
}
Expand All @@ -389,8 +384,7 @@ func checkAppArmorStatus(osInfo *osdetect.OSInfo) bool {
return !strings.Contains(statusText, "0 profiles are in enforce mode")
} else {
// For Debian/Ubuntu, check AppArmor status
cmd := exec.Command("aa-status")
output, err := cmd.CombinedOutput()
output, err := commander.Execute("aa-status")
if err != nil {
return false
}
Expand All @@ -412,7 +406,7 @@ func checkAppArmorStatus(osInfo *osdetect.OSInfo) bool {
}

// checkUnattendedUpgrades checks if unattended upgrades are configured
func checkUnattendedUpgrades(osInfo *osdetect.OSInfo) bool {
func checkUnattendedUpgrades(osInfo *osdetect.OSInfo, commander interfaces.Commander) bool {
if osInfo.OsType == "alpine" {
// Check for daily cron job
if _, err := os.Stat("/etc/periodic/daily/apk-upgrade"); err == nil {
Expand All @@ -421,14 +415,12 @@ func checkUnattendedUpgrades(osInfo *osdetect.OSInfo) bool {
return false
} else {
// Check for unattended-upgrades package and configuration
cmd := exec.Command("dpkg", "-l", "unattended-upgrades")
if err := cmd.Run(); err != nil {
if _, err := commander.Execute("dpkg", "-l", "unattended-upgrades"); err != nil {
return false
}

// Check if service is enabled
svcCmd := exec.Command("systemctl", "is-enabled", "unattended-upgrades")
if err := svcCmd.Run(); err != nil {
if _, err := commander.Execute("systemctl", "is-enabled", "unattended-upgrades"); err != nil {
return false
}

Expand All @@ -437,10 +429,9 @@ func checkUnattendedUpgrades(osInfo *osdetect.OSInfo) bool {
}

// checkSudoConfiguration checks if sudo is configured securely
func checkSudoConfiguration() bool {
func checkSudoConfiguration(commander interfaces.Commander) bool {
// Check if sudo is installed
sudoCmd := exec.Command("which", "sudo")
if err := sudoCmd.Run(); err != nil {
if _, err := commander.Execute("which", "sudo"); err != nil {
return false
}

Expand Down
Loading
Loading