Repository: lima-vm/lima
Pull request #877 (2022-06-03 14:36:28)

Allow forwarding X11 to VM
This allows people who have XQuartz installed to easily install and run X11 applications inside lima.

This is a nicer replacement for https://github.com/lima-vm/lima/pull/875 - it adds two new settings to the `ssh` section of the template - `forwardX11` and `forwardX11Trusted` correspond to the similarly-named ssh options, like `forwardAgent` does.


Patch:
diff --git a/cmd/limactl/copy.go b/cmd/limactl/copy.go
index 383253d1..e118e1cd 100644
--- a/cmd/limactl/copy.go
+++ b/cmd/limactl/copy.go
@@ -108,7 +108,7 @@ func copyAction(cmd *cobra.Command, args []string) error {
 		// arguments such as ControlPath.  This is preferred as we can multiplex
 		// sessions without re-authenticating (MaxSessions permitting).
 		for _, instDir := range instDirs {
-			sshOpts, err = sshutil.SSHOpts(instDir, false, false)
+			sshOpts, err = sshutil.SSHOpts(instDir, false, false, false, false)
 			if err != nil {
 				return err
 			}
diff --git a/cmd/limactl/shell.go b/cmd/limactl/shell.go
index ce136481..80fcd75a 100644
--- a/cmd/limactl/shell.go
+++ b/cmd/limactl/shell.go
@@ -131,7 +131,7 @@ func shellAction(cmd *cobra.Command, args []string) error {
 		return err
 	}
 
-	sshOpts, err := sshutil.SSHOpts(inst.Dir, *y.SSH.LoadDotSSHPubKeys, *y.SSH.ForwardAgent)
+	sshOpts, err := sshutil.SSHOpts(inst.Dir, *y.SSH.LoadDotSSHPubKeys, *y.SSH.ForwardAgent, *y.SSH.ForwardX11, *y.SSH.ForwardX11Trusted)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/limactl/show_ssh.go b/cmd/limactl/show_ssh.go
index a27c6e1d..1558b9be 100644
--- a/cmd/limactl/show_ssh.go
+++ b/cmd/limactl/show_ssh.go
@@ -87,7 +87,7 @@ func showSSHAction(cmd *cobra.Command, args []string) error {
 	if err != nil {
 		return err
 	}
-	opts, err := sshutil.SSHOpts(inst.Dir, *y.SSH.LoadDotSSHPubKeys, *y.SSH.ForwardAgent)
+	opts, err := sshutil.SSHOpts(inst.Dir, *y.SSH.LoadDotSSHPubKeys, *y.SSH.ForwardAgent, *y.SSH.ForwardX11, *y.SSH.ForwardX11Trusted)
 	if err != nil {
 		return err
 	}
diff --git a/examples/default.yaml b/examples/default.yaml
index 48891285..7853c9f1 100644
--- a/examples/default.yaml
+++ b/examples/default.yaml
@@ -106,6 +106,12 @@ ssh:
   # Forward ssh agent into the instance.
   # 🟢 Builtin default: false
   forwardAgent: null
+  # Forward X11 into the instance
+  # 🟢 Builtin default: false
+  forwardX11: null
+  # Trust forwarded X11 clients
+  # 🟢 Builtin default: false
+  forwardX11Trusted: null
 
 # ===================================================================== #
 # ADVANCED CONFIGURATION
diff --git a/pkg/hostagent/hostagent.go b/pkg/hostagent/hostagent.go
index 3755edc8..da877338 100644
--- a/pkg/hostagent/hostagent.go
+++ b/pkg/hostagent/hostagent.go
@@ -120,7 +120,7 @@ func New(instName string, stdout io.Writer, sigintCh chan os.Signal, opts ...Opt
 		return nil, err
 	}
 
-	sshOpts, err := sshutil.SSHOpts(inst.Dir, *y.SSH.LoadDotSSHPubKeys, *y.SSH.ForwardAgent)
+	sshOpts, err := sshutil.SSHOpts(inst.Dir, *y.SSH.LoadDotSSHPubKeys, *y.SSH.ForwardAgent, *y.SSH.ForwardX11, *y.SSH.ForwardX11Trusted)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/limayaml/defaults.go b/pkg/limayaml/defaults.go
index 040da5f6..128360ba 100644
--- a/pkg/limayaml/defaults.go
+++ b/pkg/limayaml/defaults.go
@@ -206,6 +206,26 @@ func FillDefault(y, d, o *LimaYAML, filePath string) {
 		y.SSH.ForwardAgent = pointer.Bool(false)
 	}
 
+	if y.SSH.ForwardX11 == nil {
+		y.SSH.ForwardX11 = d.SSH.ForwardX11
+	}
+	if o.SSH.ForwardX11 != nil {
+		y.SSH.ForwardX11 = o.SSH.ForwardX11
+	}
+	if y.SSH.ForwardX11 == nil {
+		y.SSH.ForwardX11 = pointer.Bool(false)
+	}
+
+	if y.SSH.ForwardX11Trusted == nil {
+		y.SSH.ForwardX11Trusted = d.SSH.ForwardX11Trusted
+	}
+	if o.SSH.ForwardX11Trusted != nil {
+		y.SSH.ForwardX11Trusted = o.SSH.ForwardX11Trusted
+	}
+	if y.SSH.ForwardX11Trusted == nil {
+		y.SSH.ForwardX11Trusted = pointer.Bool(false)
+	}
+
 	hosts := make(map[string]string)
 	// Values can be either names or IP addresses. Name values are canonicalized in the hostResolver.
 	for k, v := range d.HostResolver.Hosts {
diff --git a/pkg/limayaml/limayaml.go b/pkg/limayaml/limayaml.go
index 17030804..20ea3a4f 100644
--- a/pkg/limayaml/limayaml.go
+++ b/pkg/limayaml/limayaml.go
@@ -96,6 +96,8 @@ type SSH struct {
 	// LoadDotSSHPubKeys loads ~/.ssh/*.pub in addition to $LIMA_HOME/_config/user.pub .
 	LoadDotSSHPubKeys *bool `yaml:"loadDotSSHPubKeys,omitempty" json:"loadDotSSHPubKeys,omitempty"` // default: true
 	ForwardAgent      *bool `yaml:"forwardAgent,omitempty" json:"forwardAgent,omitempty"`           // default: false
+	ForwardX11        *bool `yaml:"forwardX11,omitempty" json:"forwardX11,omitempty"`               // default: false
+	ForwardX11Trusted *bool `yaml:"forwardX11Trusted,omitempty" json:"forwardX11Trusted,omitempty"` // default: false
 }
 
 type Firmware struct {
diff --git a/pkg/sshutil/sshutil.go b/pkg/sshutil/sshutil.go
index 629e99d2..1675bbe5 100644
--- a/pkg/sshutil/sshutil.go
+++ b/pkg/sshutil/sshutil.go
@@ -201,7 +201,7 @@ func CommonOpts(useDotSSH bool) ([]string, error) {
 }
 
 // SSHOpts adds the following options to CommonOptions: User, ControlMaster, ControlPath, ControlPersist
-func SSHOpts(instDir string, useDotSSH, forwardAgent bool) ([]string, error) {
+func SSHOpts(instDir string, useDotSSH, forwardAgent bool, forwardX11 bool, forwardX11Trusted bool) ([]string, error) {
 	controlSock := filepath.Join(instDir, filenames.SSHSock)
 	if len(controlSock) >= osutil.UnixPathMax {
 		return nil, fmt.Errorf("socket path %q is too long: >= UNIX_PATH_MAX=%d", controlSock, osutil.UnixPathMax)
@@ -223,6 +223,12 @@ func SSHOpts(instDir string, useDotSSH, forwardAgent bool) ([]string, error) {
 	if forwardAgent {
 		opts = append(opts, "ForwardAgent=yes")
 	}
+	if forwardX11 {
+		opts = append(opts, "ForwardX11=yes")
+	}
+	if forwardX11Trusted {
+		opts = append(opts, "ForwardX11Trusted=yes")
+	}
 	return opts, nil
 }
 


Test patch:
diff --git a/pkg/limayaml/defaults_test.go b/pkg/limayaml/defaults_test.go
index f352838c..16794cdd 100644
--- a/pkg/limayaml/defaults_test.go
+++ b/pkg/limayaml/defaults_test.go
@@ -65,6 +65,8 @@ func TestFillDefault(t *testing.T) {
 			LocalPort:         pointer.Int(0),
 			LoadDotSSHPubKeys: pointer.Bool(true),
 			ForwardAgent:      pointer.Bool(false),
+			ForwardX11:        pointer.Bool(false),
+			ForwardX11Trusted: pointer.Bool(false),
 		},
 		Firmware: Firmware{
 			LegacyBIOS: pointer.Bool(false),
@@ -230,6 +232,8 @@ func TestFillDefault(t *testing.T) {
 			LocalPort:         pointer.Int(888),
 			LoadDotSSHPubKeys: pointer.Bool(false),
 			ForwardAgent:      pointer.Bool(true),
+			ForwardX11:        pointer.Bool(false),
+			ForwardX11Trusted: pointer.Bool(false),
 		},
 		Firmware: Firmware{
 			LegacyBIOS: pointer.Bool(true),
@@ -376,6 +380,8 @@ func TestFillDefault(t *testing.T) {
 			LocalPort:         pointer.Int(4433),
 			LoadDotSSHPubKeys: pointer.Bool(true),
 			ForwardAgent:      pointer.Bool(true),
+			ForwardX11:        pointer.Bool(false),
+			ForwardX11Trusted: pointer.Bool(false),
 		},
 		Firmware: Firmware{
 			LegacyBIOS: pointer.Bool(true),