package daemon import ( "strings" "testing" ) // TestSshdGuestConfig_Hardened is a regression guard for the guest // SSH posture. An earlier version shipped `LogLevel DEBUG3` and // `StrictModes no`; both are gone and must not come back without an // explicit call-out. func TestSshdGuestConfig_Hardened(t *testing.T) { cfg := sshdGuestConfig() // Posture: key-only, root via pubkey, no password / keyboard- // interactive fallback, pinned authorized_keys path. mustContain := []string{ "PermitRootLogin prohibit-password", "PubkeyAuthentication yes", "PasswordAuthentication no", "KbdInteractiveAuthentication no", "AuthorizedKeysFile /root/.ssh/authorized_keys", } for _, line := range mustContain { if !strings.Contains(cfg, line) { t.Errorf("sshd drop-in missing %q:\n%s", line, cfg) } } // Things that must NOT appear. Each has a history and a reason. mustNotContain := map[string]string{ "LogLevel DEBUG3": "was debug leftover; floods journald", "StrictModes no": "masked a /root perm drift; real fix is EnsureExt4RootPerms at authsync time", // Blanket "PermitRootLogin yes" (without prohibit-password) // would re-enable password root login if something else // flipped PasswordAuthentication back to yes. "PermitRootLogin yes": "use prohibit-password instead", } for needle, why := range mustNotContain { if strings.Contains(cfg, needle) { t.Errorf("sshd drop-in contains %q (%s):\n%s", needle, why, cfg) } } } func TestSshdGuestConfig_IsCompleteLines(t *testing.T) { // Every directive should be a full line on its own. Trailing // newline matters — sshd_config.d files without a newline sometimes // get misparsed when concatenated with other drop-ins. cfg := sshdGuestConfig() if !strings.HasSuffix(cfg, "\n") { t.Errorf("sshd drop-in should end with newline:\n%q", cfg) } for _, line := range strings.Split(strings.TrimRight(cfg, "\n"), "\n") { if strings.TrimSpace(line) == "" { t.Errorf("sshd drop-in has blank line:\n%s", cfg) } } }