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:
Thales Maciel 2026-04-19 13:40:40 -03:00
parent 6cd52d12f4
commit 2e6e64bc04
No known key found for this signature in database
GPG key ID: 33112E6833C34679
6 changed files with 175 additions and 22 deletions

View file

@ -30,14 +30,7 @@ func (d *Daemon) patchRootOverlay(ctx context.Context, vm model.VMRecord, image
resolv := []byte(fmt.Sprintf("nameserver %s\n", d.config.DefaultDNS))
hostname := []byte(vm.Name + "\n")
hosts := []byte(fmt.Sprintf("127.0.0.1 localhost\n127.0.1.1 %s\n", vm.Name))
sshdConfig := []byte(strings.Join([]string{
"LogLevel DEBUG3",
"PermitRootLogin yes",
"PubkeyAuthentication yes",
"AuthorizedKeysFile /root/.ssh/authorized_keys",
"StrictModes no",
"",
}, "\n"))
sshdConfig := []byte(sshdGuestConfig())
fstab, err := system.ReadDebugFSText(ctx, d.runner, vm.Runtime.DMDev, "/etc/fstab")
if err != nil {
fstab = ""
@ -136,6 +129,55 @@ func (d *Daemon) ensureWorkDisk(ctx context.Context, vm *model.VMRecord, image m
return workDiskPreparation{}, nil
}
// sshdGuestConfig is the banger-authored drop-in that lands at
// /etc/ssh/sshd_config.d/99-banger.conf inside every guest.
//
// Banger VMs are single-user root sandboxes reachable only through the
// host bridge (default 172.16.0.0/24). The drop-in sets the minimum
// needed to make that usable while keeping the posture tight enough
// that a misconfigured host bridge does not immediately hand over an
// unauthenticated root shell.
//
// Why each line is here:
//
// - PermitRootLogin prohibit-password
// The guest IS root — there's no other account. prohibit-password
// allows pubkey login and blocks password auth at the source even
// if some future config flips PasswordAuthentication on.
//
// - PubkeyAuthentication yes
// The only auth method we expect. Explicit in case a future
// Debian default or distro package flips it off.
//
// - PasswordAuthentication no
//
// - KbdInteractiveAuthentication no
// Belt-and-braces: every interactive auth path is off, not just
// the PermitRootLogin path. These are already Debian defaults but
// stating them here means the drop-in documents the intent.
//
// - AuthorizedKeysFile /root/.ssh/authorized_keys
// Pins the lookup path so the banger-written file always wins,
// regardless of distro default ($HOME/.ssh/authorized_keys) and
// regardless of any per-image weirdness.
//
// Previously this file also contained `LogLevel DEBUG3` and
// `StrictModes no`. DEBUG3 was a leftover from debugging the
// first-boot flow and flooded journald in normal use. StrictModes no
// was a workaround for perm drift on /root inside the work disk; the
// real fix — normalising /root permissions at provisioning time — is
// in ensureAuthorizedKeyOnWorkDisk / seedAuthorizedKeyOnExt4Image.
func sshdGuestConfig() string {
return strings.Join([]string{
"PermitRootLogin prohibit-password",
"PubkeyAuthentication yes",
"PasswordAuthentication no",
"KbdInteractiveAuthentication no",
"AuthorizedKeysFile /root/.ssh/authorized_keys",
"",
}, "\n")
}
func (d *Daemon) flattenNestedWorkHome(ctx context.Context, workMount string) error {
nestedHome := filepath.Join(workMount, "root")
if !exists(nestedHome) {