From 71e073ac49b1c5515472e639fad1aebe1b9cc5bc Mon Sep 17 00:00:00 2001 From: Thales Maciel Date: Sat, 2 May 2026 14:39:46 -0300 Subject: [PATCH] fix: land .hushlogin on work disk so vm run is quiet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- internal/daemon/capabilities.go | 3 +++ internal/daemon/vm_authsync.go | 9 +++++++++ internal/daemon/vm_disk.go | 13 +++++-------- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/internal/daemon/capabilities.go b/internal/daemon/capabilities.go index 89fa5e9..b99ba4a 100644 --- a/internal/daemon/capabilities.go +++ b/internal/daemon/capabilities.go @@ -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 } diff --git a/internal/daemon/vm_authsync.go b/internal/daemon/vm_authsync.go index b4feaaa..117014a 100644 --- a/internal/daemon/vm_authsync.go +++ b/internal/daemon/vm_authsync.go @@ -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 { diff --git a/internal/daemon/vm_disk.go b/internal/daemon/vm_disk.go index e86b8b3..fe5db6d 100644 --- a/internal/daemon/vm_disk.go +++ b/internal/daemon/vm_disk.go @@ -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",