From 08e9dfbbfe5234548a20f489f0bf7903e5d72470 Mon Sep 17 00:00:00 2001 From: Nikolay Edigaryev Date: Mon, 27 Oct 2025 23:07:43 +0400 Subject: [PATCH] Support "tart run"'s --net-softnet-allow and --net-softnet-block (#361) * Support "tart run"'s --net-softnet-allow and --net-softnet-block * Use ghcr.io/cirruslabs/macos-tahoe-base:latest by default --- api/openapi.yaml | 34 ++++++++++++++++++++++- internal/command/create/vm.go | 37 ++++++++++++++++--------- internal/command/get/vm.go | 4 ++- internal/controller/api_vms.go | 7 +++++ internal/imageconstant/imageconstant.go | 2 +- internal/worker/vmmanager/vm.go | 8 +++++- pkg/resource/v1/v1.go | 21 ++++++++------ 7 files changed, 87 insertions(+), 26 deletions(-) diff --git a/api/openapi.yaml b/api/openapi.yaml index 7863ead..64c9b97 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -471,8 +471,40 @@ components: example: 100 net-softnet: type: boolean - description: Whether to use Softnet network isolation + description: Please use `netSoftnet` instead default: false + deprecated: true + netSoftnet: + type: boolean + description: | + Whether to use Softnet network isolation. + + See `tart run`'s help for `--net-softnet` for more details. + default: false + netSoftnetAllow: + type: array + description: | + List of CIDRs to allow the traffic to when using Softnet isolation. + + See `tart run`'s help for `--net-softnet-allow` for more details. + + Enables `netSoftnet`. + example: + - "192.168.0.0/24" + items: + type: string + netSoftnetBlock: + type: array + description: | + List of CIDRs to block the traffic to when using Softnet isolation. + + See `tart run`'s help for `--net-softnet-block` for more details. + + Enables `netSoftnet`. + example: + - "66.66.0.0/16" + items: + type: string net-bridged: type: string description: Whether to use bridged network mode diff --git a/internal/command/create/vm.go b/internal/command/create/vm.go index be8ec3f..49eff4e 100644 --- a/internal/command/create/vm.go +++ b/internal/command/create/vm.go @@ -20,6 +20,8 @@ var cpu uint64 var memory uint64 var diskSize uint64 var netSoftnet bool +var netSoftnetAllow []string +var netSoftnetBlock []string var netBridged string var headless bool var nested bool @@ -47,6 +49,12 @@ func newCreateVMCommand() *cobra.Command { command.Flags().Uint64Var(&diskSize, "disk-size", 0, "resize the VMs disk to the specified size in GB "+ "(no resizing is done by default and VM's image default size is used)") command.Flags().BoolVar(&netSoftnet, "net-softnet", false, "whether to use Softnet network isolation") + command.Flags().StringSliceVar(&netSoftnetAllow, "net-softnet-allow", []string{}, + "comma-separated list of CIDRs to allow the traffic to when using Softnet isolation, see "+ + "\"tart run\"'s help for \"--net-softnet-block\" for more details; automatically enables --net-softnet") + command.Flags().StringSliceVar(&netSoftnetBlock, "net-softnet-block", []string{}, + "comma-separated list of CIDRs to block the traffic to when using Softnet isolation, see "+ + "\"tart run\"'s help for \"--net-softnet-block\" for more details; automatically enables --net-softnet") command.Flags().StringVar(&netBridged, "net-bridged", "", "whether to use Bridged network mode") command.Flags().BoolVar(&headless, "headless", true, "whether to run without graphics") command.Flags().BoolVar(&nested, "nested", false, "enable nested virtualization") @@ -101,19 +109,22 @@ func runCreateVM(cmd *cobra.Command, args []string) error { Meta: v1.Meta{ Name: name, }, - Image: image, - CPU: cpu, - Memory: memory, - DiskSize: diskSize, - NetSoftnet: netSoftnet, - NetBridged: netBridged, - Headless: headless, - Nested: nested, - Username: username, - Password: password, - RandomSerial: randomSerial, - Labels: labels, - HostDirs: hostDirs, + Image: image, + CPU: cpu, + Memory: memory, + DiskSize: diskSize, + NetSoftnetDeprecated: netSoftnet, + NetSoftnet: netSoftnet, + NetSoftnetAllow: netSoftnetAllow, + NetSoftnetBlock: netSoftnetBlock, + NetBridged: netBridged, + Headless: headless, + Nested: nested, + Username: username, + Password: password, + RandomSerial: randomSerial, + Labels: labels, + HostDirs: hostDirs, } // Convert resources diff --git a/internal/command/get/vm.go b/internal/command/get/vm.go index ae10486..b9243f7 100644 --- a/internal/command/get/vm.go +++ b/internal/command/get/vm.go @@ -92,7 +92,9 @@ func runGetVM(cmd *cobra.Command, args []string) error { table.AddRow("Disk size", diskSize) - table.AddRow("Softnet enabled", vm.NetSoftnet) + table.AddRow("Softnet enabled", vm.NetSoftnetDeprecated || vm.NetSoftnet) + table.AddRow("Softnet allowed CIDRs", strings.Join(vm.NetSoftnetAllow, "\n")) + table.AddRow("Softnet blocked CIDRs", strings.Join(vm.NetSoftnetBlock, "\n")) table.AddRow("Bridged networking interface", nonEmptyOrNone(vm.NetBridged)) table.AddRow("Headless mode", vm.Headless) table.AddRow("Nested virtualization", vm.Nested) diff --git a/internal/controller/api_vms.go b/internal/controller/api_vms.go index 74e7c09..c277d41 100644 --- a/internal/controller/api_vms.go +++ b/internal/controller/api_vms.go @@ -43,6 +43,13 @@ func (controller *Controller) createVM(ctx *gin.Context) responder.Responder { vm.RestartCount = 0 vm.UID = uuid.New().String() + // Softnet-specific logic: automatically enable Softnet when NetSoftnetAllow or NetSoftnetBlock are set + // and propagate deprecated and non-deprecated boolean fields into each other + if vm.NetSoftnetDeprecated || vm.NetSoftnet || len(vm.NetSoftnetAllow) != 0 || len(vm.NetSoftnetBlock) != 0 { + vm.NetSoftnetDeprecated = true + vm.NetSoftnet = true + } + // Provide resource defaults if vm.Resources == nil { vm.Resources = make(v1.Resources) diff --git a/internal/imageconstant/imageconstant.go b/internal/imageconstant/imageconstant.go index ea5872f..325b529 100644 --- a/internal/imageconstant/imageconstant.go +++ b/internal/imageconstant/imageconstant.go @@ -1,3 +1,3 @@ package imageconstant -const DefaultMacosImage = "ghcr.io/cirruslabs/macos-sequoia-base:latest" +const DefaultMacosImage = "ghcr.io/cirruslabs/macos-tahoe-base:latest" diff --git a/internal/worker/vmmanager/vm.go b/internal/worker/vmmanager/vm.go index 98aeb5b..e0b936b 100644 --- a/internal/worker/vmmanager/vm.go +++ b/internal/worker/vmmanager/vm.go @@ -336,9 +336,15 @@ func (vm *VM) cloneAndConfigure(ctx context.Context) error { func (vm *VM) run(ctx context.Context) error { var runArgs = []string{"run"} - if vm.Resource.NetSoftnet { + if vm.Resource.NetSoftnetDeprecated || vm.Resource.NetSoftnet { runArgs = append(runArgs, "--net-softnet") } + if len(vm.Resource.NetSoftnetAllow) != 0 { + runArgs = append(runArgs, "--net-softnet-allow", strings.Join(vm.Resource.NetSoftnetAllow, ",")) + } + if len(vm.Resource.NetSoftnetBlock) != 0 { + runArgs = append(runArgs, "--net-softnet-block", strings.Join(vm.Resource.NetSoftnetBlock, ",")) + } if vm.Resource.NetBridged != "" { runArgs = append(runArgs, fmt.Sprintf("--net-bridged=%s", vm.Resource.NetBridged)) } diff --git a/pkg/resource/v1/v1.go b/pkg/resource/v1/v1.go index 4a7eff7..b8b43b4 100644 --- a/pkg/resource/v1/v1.go +++ b/pkg/resource/v1/v1.go @@ -22,15 +22,18 @@ type Meta struct { } type VM struct { - Image string `json:"image,omitempty"` - ImagePullPolicy ImagePullPolicy `json:"imagePullPolicy,omitempty"` - CPU uint64 `json:"cpu,omitempty"` - Memory uint64 `json:"memory,omitempty"` - DiskSize uint64 `json:"diskSize,omitempty"` - NetSoftnet bool `json:"net-softnet,omitempty"` - NetBridged string `json:"net-bridged,omitempty"` - Headless bool `json:"headless,omitempty"` - Nested bool `json:"nested,omitempty"` + Image string `json:"image,omitempty"` + ImagePullPolicy ImagePullPolicy `json:"imagePullPolicy,omitempty"` + CPU uint64 `json:"cpu,omitempty"` + Memory uint64 `json:"memory,omitempty"` + DiskSize uint64 `json:"diskSize,omitempty"` + NetSoftnetDeprecated bool `json:"net-softnet,omitempty"` + NetSoftnet bool `json:"netSoftnet,omitempty"` + NetSoftnetAllow []string `json:"netSoftnetAllow,omitempty"` + NetSoftnetBlock []string `json:"netSoftnetBlock,omitempty"` + NetBridged string `json:"net-bridged,omitempty"` + Headless bool `json:"headless,omitempty"` + Nested bool `json:"nested,omitempty"` // Status field is used to track the lifecycle of the VM associated with this resource. Status VMStatus `json:"status,omitempty"`