fix: land .hushlogin on work disk so vm run is quiet

The work disk mounts at /root, so the .hushlogin written to the
rootfs overlay was shadowed and never reached the guest — pam_motd
kept printing the Debian banner on `banger vm run`. Move the write
to the work disk root inode (= /root in the guest) and run it from
PrepareHost so existing VMs pick it up on next start.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Thales Maciel 2026-05-02 14:39:46 -03:00
parent 696593b365
commit 71e073ac49
No known key found for this signature in database
GPG key ID: 33112E6833C34679
3 changed files with 17 additions and 8 deletions

View file

@ -247,6 +247,9 @@ func (c workDiskCapability) PrepareHost(ctx context.Context, vm *model.VMRecord,
if err := c.ws.ensureAuthorizedKeyOnWorkDisk(ctx, vm, image, prep); err != nil {
return err
}
if err := c.ws.ensureHushLoginOnWorkDisk(ctx, vm); err != nil {
return err
}
if err := c.ws.ensureGitIdentityOnWorkDisk(ctx, vm); err != nil {
return err
}

View file

@ -86,6 +86,15 @@ func provisionAuthorizedKey(ctx context.Context, runner system.CommandRunner, im
return system.WriteExt4FileOwned(ctx, runner, imagePath, "/.ssh/authorized_keys", 0o600, 0, 0, merged)
}
// ensureHushLoginOnWorkDisk lands /root/.hushlogin in the guest by
// writing /.hushlogin at the root of the work disk (which mounts at
// /root inside the guest). pam_motd checks $HOME/.hushlogin and stays
// silent when it exists — combined with sshd's PrintMotd no / PrintLastLog no
// that suppresses the Debian-style banner on `banger vm run`.
func (s *WorkspaceService) ensureHushLoginOnWorkDisk(ctx context.Context, vm *model.VMRecord) error {
return system.WriteExt4FileOwned(ctx, s.runner, vm.Runtime.WorkDiskPath, "/.hushlogin", 0o644, 0, 0, nil)
}
func (s *WorkspaceService) ensureGitIdentityOnWorkDisk(ctx context.Context, vm *model.VMRecord) error {
runner := s.runner
if runner == nil {

View file

@ -50,11 +50,6 @@ func (s *VMService) patchRootOverlay(ctx context.Context, vm model.VMRecord, ima
builder.WriteFile(guestnet.ConfigPath, guestnet.ConfigFile(vm.Runtime.GuestIP, s.config.BridgeIP, s.config.DefaultDNS))
builder.WriteFile(guestnet.GuestScriptPath, []byte(guestnet.BootstrapScript()))
builder.WriteFile("/etc/ssh/sshd_config.d/99-banger.conf", sshdConfig)
// pam_motd reads /etc/motd + /etc/update-motd.d on Debian-family
// guests independent of sshd's PrintMotd. .hushlogin in $HOME tells
// pam_motd to stay quiet for that user — root is the only login on
// banger VMs, so a single file suffices.
builder.WriteFile("/root/.hushlogin", []byte{})
builder.DropMountTarget("/home")
builder.DropMountTarget("/var")
builder.AddMount(guestconfig.MountSpec{
@ -169,9 +164,11 @@ func (s *VMService) ensureWorkDisk(ctx context.Context, vm *model.VMRecord, imag
// Banger VMs are short-lived sandboxes. The Debian-style MOTD
// ("Linux ... GNU/Linux comes with ABSOLUTELY NO WARRANTY …") and
// the "Last login" line are pure noise for `vm run -- echo hi`
// style invocations. Pair this with the .hushlogin written below
// so pam_motd also stays silent on distros that read /etc/motd
// through PAM rather than sshd.
// style invocations. Pair this with the .hushlogin landed on the
// work disk (see ensureHushLoginOnWorkDisk) so pam_motd also stays
// silent on distros that read /etc/motd through PAM rather than
// sshd. The work disk mounts at /root, so the file has to live on
// that disk — a write to the rootfs overlay would be shadowed.
func sshdGuestConfig() string {
return strings.Join([]string{
"PermitRootLogin prohibit-password",