@@ -12,6 +12,7 @@ import (
1212 "os"
1313 "os/exec"
1414 "path/filepath"
15+ "strconv"
1516 "strings"
1617 "syscall"
1718 "time"
@@ -30,6 +31,11 @@ import (
3031 "github.com/lima-vm/lima/v2/pkg/ptr"
3132)
3233
34+ const (
35+ vSockPort = 2222
36+ sshVsockSock = "ssh-vsock.sock"
37+ )
38+
3339type LimaKrunkitDriver struct {
3440 Instance * limatype.Instance
3541 SSHLocalPort int
@@ -64,8 +70,8 @@ func (l *LimaKrunkitDriver) CreateDisk(ctx context.Context) error {
6470}
6571
6672func (l * LimaKrunkitDriver ) Start (ctx context.Context ) (chan error , error ) {
67- if l . Instance . Config . SSH . OverVsock != nil && * l . Instance . Config . SSH . OverVsock {
68- logrus .Warn (".ssh.overVsock is not implemented yet for krunkit driver " )
73+ if err := l . cleanupStaleSockets (); err != nil {
74+ logrus .WithError ( err ). Warn ("Failed to clean up stale krunkit sockets before start " )
6975 }
7076
7177 var err error
@@ -112,7 +118,27 @@ func (l *LimaKrunkitDriver) Start(ctx context.Context) (chan error, error) {
112118 l .krunkitWaitCh <- krunkitCmd .Wait ()
113119 }()
114120
115- err = l .usernetClient .ConfigureDriver (ctx , l .Instance , l .SSHLocalPort )
121+ usernetSSHLocalPort := l .SSHLocalPort
122+ useSSHOverVsock := l .Instance .Config .SSH .OverVsock != nil && * l .Instance .Config .SSH .OverVsock
123+
124+ if ! useSSHOverVsock {
125+ logrus .Info ("ssh.overVsock is false, using usernet forwarder for SSH" )
126+ } else if err := l .usernetClient .WaitOpeningSSHPort (ctx , l .Instance ); err == nil {
127+ hostAddress := net .JoinHostPort (l .Instance .SSHAddress , strconv .Itoa (usernetSSHLocalPort ))
128+ sshVsockPath := filepath .Join (l .Instance .Dir , sshVsockSock )
129+ if err := startVsockForwarder (ctx , sshVsockPath , hostAddress ); err == nil {
130+ logrus .Infof ("Detected SSH server is listening on the vsock port; changed %s to proxy for the vsock port" , hostAddress )
131+ usernetSSHLocalPort = 0 // disable gvisor ssh port forwarding
132+ } else {
133+ logrus .WithError (err ).WithField ("hostAddress" , hostAddress ).
134+ Debugf ("Failed to start vsock forwarder (systemd is older than v256?)" )
135+ logrus .Info ("SSH server does not seem to be running on vsock port, using usernet forwarder" )
136+ }
137+ } else {
138+ logrus .WithError (err ).Warn ("Failed to wait for the guest SSH server to become available, falling back to usernet forwarder" )
139+ }
140+
141+ err = l .usernetClient .ConfigureDriver (ctx , l .Instance , usernetSSHLocalPort )
116142 if err != nil {
117143 l .krunkitWaitCh <- fmt .Errorf ("failed to configure usernet: %w" , err )
118144 }
@@ -156,6 +182,29 @@ func (l *LimaKrunkitDriver) Validate(_ context.Context) error {
156182 return validateConfig (l .Instance .Config )
157183}
158184
185+ func checkKrunkitVersion () error {
186+ cmd := exec .Command (vmType , "--version" )
187+ output , err := cmd .Output ()
188+ if err != nil {
189+ return fmt .Errorf ("failed to check krunkit version: %w" , err )
190+ }
191+ versionStr := strings .TrimSpace (string (output ))
192+ versionStr = strings .TrimPrefix (versionStr , "krunkit " )
193+
194+ minVersion := semver .New ("1.2.1" )
195+ currentVersion , err := semver .NewVersion (versionStr )
196+ if err != nil {
197+ logrus .WithError (err ).Warnf ("Failed to parse krunkit version %q, skipping version check" , versionStr )
198+ return nil
199+ }
200+
201+ if currentVersion .LessThan (* minVersion ) {
202+ return fmt .Errorf ("krunkit version %q is older than required minimum 1.2.1 (needed for vsock forwarder feature)" , currentVersion )
203+ }
204+
205+ return nil
206+ }
207+
159208func validateConfig (cfg * limatype.LimaYAML ) error {
160209 if cfg == nil {
161210 return errors .New ("configuration is nil" )
@@ -171,7 +220,11 @@ func validateConfig(cfg *limatype.LimaYAML) error {
171220 return fmt .Errorf ("unsupported arch: %q (krunkit requires native arch)" , * cfg .Arch )
172221 }
173222 if _ , err := exec .LookPath (vmType ); err != nil {
174- return errors .New ("krunkit CLI not found in PATH. Install it via:\n brew tap slp/krunkit\n brew install krunkit" )
223+ return errors .New ("krunkit CLI not found in PATH. Install it via:\n brew tap slp/krun\n brew install krunkit" )
224+ }
225+ // Also check if krunkit version >= 1.2.1 because of the vsock forwarder feature
226+ if err := checkKrunkitVersion (); err != nil {
227+ return err
175228 }
176229
177230 if cfg .MountType != nil && (* cfg .MountType != limatype .VIRTIOFS && * cfg .MountType != limatype .REVSSHFS ) {
@@ -216,6 +269,10 @@ func (l *LimaKrunkitDriver) FillConfig(_ context.Context, cfg *limatype.LimaYAML
216269
217270 cfg .VMType = ptr .Of (vmType )
218271
272+ if cfg .SSH .OverVsock == nil {
273+ cfg .SSH .OverVsock = ptr .Of (cfg .OS != nil && * cfg .OS == limatype .LINUX )
274+ }
275+
219276 return validateConfig (cfg )
220277}
221278
@@ -260,6 +317,7 @@ func (l *LimaKrunkitDriver) Create(_ context.Context) error {
260317func (l * LimaKrunkitDriver ) Info () driver.Info {
261318 var info driver.Info
262319 info .Name = vmType
320+ info .VsockPort = vSockPort
263321 if l .Instance != nil && l .Instance .Dir != "" {
264322 info .InstanceDir = l .Instance .Dir
265323 }
@@ -277,7 +335,7 @@ func (l *LimaKrunkitDriver) SSHAddress(_ context.Context) (string, error) {
277335}
278336
279337func (l * LimaKrunkitDriver ) ForwardGuestAgent () bool {
280- return true
338+ return false
281339}
282340
283341func (l * LimaKrunkitDriver ) Delete (_ context.Context ) error {
@@ -324,10 +382,32 @@ func (l *LimaKrunkitDriver) Unregister(_ context.Context) error {
324382 return nil
325383}
326384
327- func (l * LimaKrunkitDriver ) GuestAgentConn (_ context.Context ) (net.Conn , string , error ) {
328- return nil , "unix" , nil
385+ func (l * LimaKrunkitDriver ) GuestAgentConn (ctx context.Context ) (net.Conn , string , error ) {
386+ var d net.Dialer
387+ conn , err := d .DialContext (ctx , "unix" , filepath .Join (l .Instance .Dir , filenames .GuestAgentSock ))
388+ return conn , "unix" , err
329389}
330390
331391func (l * LimaKrunkitDriver ) AdditionalSetupForSSH (_ context.Context ) error {
332392 return nil
333393}
394+
395+ // Currently, Krunkit does not clean-up the vsock unix socket when the VM stops
396+ // Issue: https://github.com/containers/krunkit/issues/101
397+ func (l * LimaKrunkitDriver ) cleanupStaleSockets () error {
398+ if l .Instance == nil || l .Instance .Dir == "" {
399+ return nil
400+ }
401+
402+ var errs []error
403+ for _ , sock := range []string {
404+ filepath .Join (l .Instance .Dir , filenames .GuestAgentSock ),
405+ filepath .Join (l .Instance .Dir , sshVsockSock ),
406+ } {
407+ if err := os .RemoveAll (sock ); err != nil {
408+ errs = append (errs , fmt .Errorf ("failed to remove socket %q: %w" , sock , err ))
409+ }
410+ }
411+
412+ return errors .Join (errs ... )
413+ }
0 commit comments