Work around Sequoia's "Local Network" permission with a helper process (#302)

* Work around Sequoia's "Local Network" permission with a helper process

* README.md: macOS 15 (Sequoia) warning

* Make "orchard dev" unix-specific too, otherwise Release fails

* Fix typo in "localNetworkHerlper"

* Slightly improve the macOS 15 (Sequoia) note

* orchard worker run: better documentation for --user

* Make sure privilege dropping is the first step we do in runWorker()
This commit is contained in:
Nikolay Edigaryev
2025-04-10 18:01:19 +04:00
committed by GitHub
parent fa38fe72ed
commit abcfee677d
16 changed files with 201 additions and 32 deletions
+2
View File
@@ -1,3 +1,5 @@
//go:build unix
package dev
import (
+9
View File
@@ -0,0 +1,9 @@
//go:build !unix
package dev
import "github.com/spf13/cobra"
func NewCommand() *cobra.Command {
return nil
}
@@ -0,0 +1,31 @@
//go:build unix
package localnetworkhelper
import (
"github.com/cirruslabs/chacha/pkg/localnetworkhelper"
"github.com/spf13/cobra"
)
func NewCommand() *cobra.Command {
cmd := &cobra.Command{
Use: localnetworkhelper.CommandName,
Short: "Run the macOS \"Local Network\" permission helper process",
Hidden: true,
RunE: func(cmd *cobra.Command, args []string) error {
// In localnetworkhelper.New(), a macOS "Local Network"
// permission helper process is spawned and receives
// its socketpair(2) end via ExtraFiles field of Golang's
// exec.Cmd[1].
//
// This socketpair(2) end becomes FD number 3 here,
// according to the ExtraFiles documentation[1]:
//
// >If non-nil, entry i becomes file descriptor 3+i.
//
// [1]: https://pkg.go.dev/os/exec#Cmd
return localnetworkhelper.Serve(3)
},
}
return cmd
}
@@ -0,0 +1,11 @@
//go:build !unix
package localnetworkhelper
import (
"github.com/spf13/cobra"
)
func NewCommand() *cobra.Command {
return nil
}
+17 -4
View File
@@ -8,6 +8,7 @@ import (
"github.com/cirruslabs/orchard/internal/command/dev"
"github.com/cirruslabs/orchard/internal/command/get"
"github.com/cirruslabs/orchard/internal/command/list"
"github.com/cirruslabs/orchard/internal/command/localnetworkhelper"
"github.com/cirruslabs/orchard/internal/command/logs"
"github.com/cirruslabs/orchard/internal/command/pause"
"github.com/cirruslabs/orchard/internal/command/portforward"
@@ -37,6 +38,10 @@ func NewRootCmd() *cobra.Command {
},
}
if localNetworkHelperCommand := localnetworkhelper.NewCommand(); localNetworkHelperCommand != nil {
command.AddCommand(localNetworkHelperCommand)
}
addGroupedCommands(command, "Working With Resources:",
create.NewCommand(),
deletepkg.NewCommand(),
@@ -51,12 +56,20 @@ func NewRootCmd() *cobra.Command {
vnc.NewCommand(),
)
addGroupedCommands(command, "Administrative Tasks:",
administrativeCommands := []*cobra.Command{
context.NewCommand(),
controller.NewCommand(),
worker.NewCommand(),
dev.NewCommand(),
)
}
if devCommand := dev.NewCommand(); devCommand != nil {
administrativeCommands = append(administrativeCommands, devCommand)
}
if workerCommand := worker.NewCommand(); workerCommand != nil {
administrativeCommands = append(administrativeCommands, workerCommand)
}
addGroupedCommands(command, "Administrative Tasks:", administrativeCommands...)
return command
}
+39 -10
View File
@@ -1,8 +1,12 @@
//go:build unix
package worker
import (
"errors"
"fmt"
"github.com/cirruslabs/chacha/pkg/localnetworkhelper"
"github.com/cirruslabs/chacha/pkg/privdrop"
"github.com/cirruslabs/orchard/internal/bootstraptoken"
"github.com/cirruslabs/orchard/internal/netconstants"
"github.com/cirruslabs/orchard/internal/worker"
@@ -32,6 +36,7 @@ var labels map[string]string
var noPKI bool
var defaultCPU uint64
var defaultMemory uint64
var username string
var debug bool
func newRunCommand() *cobra.Command {
@@ -62,17 +67,46 @@ func newRunCommand() *cobra.Command {
"that do not explicitly specify a value")
cmd.PersistentFlags().Uint64Var(&defaultMemory, "default-memory", 8*1024, "megabytes of memory "+
"to use for VMs that do not explicitly specify a value")
cmd.Flags().StringVar(&username, "user", "", "username to drop privileges to "+
"(\"Local Network\" permission workaround: requires starting \"orchard worker run\" as \"root\", "+
"the privileges will be then dropped to the specified user after starting the \"orchard localnetworkhelper\" "+
"helper process)")
cmd.PersistentFlags().BoolVar(&debug, "debug", false, "enable debug logging")
return cmd
}
func runWorker(cmd *cobra.Command, args []string) (err error) {
var clientOpts []client.Option
workerOpts := []worker.Option{
worker.WithName(name),
worker.WithLabels(labels),
worker.WithDefaultCPUAndMemory(defaultCPU, defaultMemory),
}
// Run the macOS "Local Network" permission helper
// when privilege dropping is requested
if username != "" {
localNetworkHelper, err := localnetworkhelper.New(cmd.Context())
if err != nil {
return err
}
clientOpts = append(clientOpts, client.WithDialContext(localNetworkHelper.PrivilegedDialContext))
workerOpts = append(workerOpts, worker.WithLocalNetworkHelper(localNetworkHelper))
if err := privdrop.Drop(username); err != nil {
return err
}
}
// Parse controller URL
controllerURL, err := netconstants.NormalizeAddress(args[0])
if err != nil {
return err
}
clientOpts = append(clientOpts, client.WithAddress(controllerURL.String()))
// Parse bootstrap token
bootstrapTokenRaw, err := readBootstrapToken()
@@ -86,17 +120,15 @@ func runWorker(cmd *cobra.Command, args []string) (err error) {
if err != nil {
return err
}
clientOpts = append(clientOpts, client.WithCredentials(bootstrapToken.ServiceAccountName(),
bootstrapToken.ServiceAccountToken()))
// Convert resources
resources, err := v1.NewResourcesFromStringToString(stringToStringResources)
if err != nil {
return fmt.Errorf("%w: %v", ErrRunFailed, err)
}
clientOpts := []client.Option{
client.WithAddress(controllerURL.String()),
client.WithCredentials(bootstrapToken.ServiceAccountName(), bootstrapToken.ServiceAccountToken()),
}
workerOpts = append(workerOpts, worker.WithResources(resources))
if trustedCertificate := bootstrapToken.Certificate(); trustedCertificate != nil {
clientOpts = append(clientOpts, client.WithTrustedCertificate(trustedCertificate))
@@ -120,14 +152,11 @@ func runWorker(cmd *cobra.Command, args []string) (err error) {
err = syncErr
}
}()
workerOpts = append(workerOpts, worker.WithLogger(logger))
workerInstance, err := worker.New(
controllerClient,
worker.WithName(name),
worker.WithResources(resources),
worker.WithLabels(labels),
worker.WithDefaultCPUAndMemory(defaultCPU, defaultMemory),
worker.WithLogger(logger),
workerOpts...,
)
if err != nil {
return err
+2
View File
@@ -1,3 +1,5 @@
//go:build unix
package worker
import (
@@ -0,0 +1,9 @@
//go:build !unix
package worker
import "github.com/spf13/cobra"
func NewCommand() *cobra.Command {
return nil
}