guest sshd: drop DEBUG3 + StrictModes no; normalise /root perms
Previously /etc/ssh/sshd_config.d/99-banger.conf landed with: LogLevel DEBUG3 PermitRootLogin yes PubkeyAuthentication yes AuthorizedKeysFile /root/.ssh/authorized_keys StrictModes no DEBUG3 was debug leftover that floods journald in normal use. StrictModes no was a workaround for /root perm drift on the work disk — the real fix is to make those perms correct at provisioning time. New drop-in: PermitRootLogin prohibit-password PubkeyAuthentication yes PasswordAuthentication no KbdInteractiveAuthentication no AuthorizedKeysFile /root/.ssh/authorized_keys prohibit-password blocks password root login even if PasswordAuth gets flipped on elsewhere; KbdInteractiveAuth no closes the last interactive fallback; StrictModes is now on (sshd's default). normaliseHomeDirPerms chown/chmods /root to 0755 root:root at every work-disk mount (ensureAuthorizedKeyOnWorkDisk, seedAuthorizedKeyOnExt4Image); the .ssh dir also explicitly chown'd root:root. Verified end-to-end against a real VM: `sshd -T` reports strictmodes yes and all five directives match. Regression test (sshd_config_test.go) pins the allow-list and the deny-list (DEBUG3, StrictModes no, bare `PermitRootLogin yes`) so the next accidental reintroduction fails fast. README's Security section updated to reflect the new posture.
This commit is contained in:
parent
6cd52d12f4
commit
2e6e64bc04
6 changed files with 175 additions and 22 deletions
59
internal/daemon/sshd_config_test.go
Normal file
59
internal/daemon/sshd_config_test.go
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
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 in normaliseHomeDirPerms",
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue