Help us shape the future of Go! Take the 2026 Go Developer Survey

launch

package
v0.32.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 31, 2026 License: BSD-3-Clause Imports: 16 Imported by: 0

Documentation

Overview

Package launch starts the workers that serve a person's files as that person (docs/identity.md). Three parties:

  • the LAUNCHER, root, the parent of everything: it is asked for "a worker for alice, on share photos" over a socketpair only the front holds, resolves alice itself (internal/account), and starts the worker as her;
  • the FRONT, no privilege: it asks (Client.Worker), receives the worker's end of a new socketpair (SCM_RIGHTS), and talks to it through go-filesystems/remote;
  • the WORKER, the person: it checks that it is who it was started as and cannot become root again, opens the share's directory, and serves it (Worker).

A request never carries a uid or a path. The front names a person and a share; the launcher decides the rest.

Unix only: Windows has no setuid, and no descriptor passing in os/exec.

Index

Constants

View Source
const (
	FrontEnv  = "FILESHARE_LAUNCHER_FD"
	WorkerEnv = "FILESHARE_WORKER_FD"
)

The variables that say a descriptor was passed, and which: the launcher sets FrontEnv for the front, Exec sets WorkerEnv for a worker.

Variables

This section is empty.

Functions

func FrontConn

func FrontConn(fd int) (*net.UnixConn, error)

FrontConn is the front's end, from the descriptor it inherited -- which the launcher says it passed, in FrontEnv.

func Inherited

func Inherited(fd int, env string) (*os.File, error)

Inherited is descriptor fd, if env says it was passed and it is a socket.

⛔ Never take a descriptor on faith. A process started by hand has nothing on fd 3, and the Go runtime puts its own poller there (kqueue, epoll): os.NewFile(3) and Close then closes the runtime's poller, and the process dies in netpoll -- measured, running serve-front by hand.

func Pair

func Pair() (launcher *net.UnixConn, front *os.File, err error)

Pair is the socket between a launcher and the front it starts: the launcher's end, to Serve on, and the front's, for its ExtraFiles. Both are close-on-exec; the front's is inherited only because ExtraFiles names it.

func Verify

func Verify(e Expect) error

Verify is the worker's check, before it touches a file: it runs as exactly e -- real and effective, uid and gid, and exactly e.Groups -- it is not root, and it cannot become root again. Each of these has failed in some server before (CVE-2006-2607, CVE-2012-2653, CVE-2009-3589, CERT POS37-C).

func Worker

func Worker(e Expect, dir string, readOnly bool) error

Worker is a worker's whole life, after Verify: open the tree, as the person, and serve it on the inherited socket until the front closes it.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is the front's side: it asks the launcher for workers, one request at a time.

func NewClient

func NewClient(conn *net.UnixConn) *Client

NewClient asks for workers over conn, the front's end of the socketpair the launcher started it with.

func (*Client) Worker

func (c *Client) Worker(person, share string) (*remote.FS, error)

Worker is person's worker for share: the filesystem it serves, as them. The error says why the launcher refused, if it did.

type Expect

type Expect struct {
	UID, GID uint32
	Groups   []uint32
}

Expect is what a worker must find itself running as.

func ParseExpect

func ParseExpect(uid, gid uint64, groups string) (Expect, error)

ParseExpect reads the ids a worker was told, as workerArgs wrote them.

type Launcher

type Launcher struct {
	Resolve func(person string) (account.Account, error)
	// Share is the share named, if the configuration has it and it serves
	// a directory as its people (identity = "system").
	Share func(name string) (Share, bool)
	Spawn Spawn
	// Log says what was started and what was refused; nil says nothing.
	Log func(format string, args ...any)
}

Launcher answers a front's requests for workers.

func (*Launcher) Serve

func (l *Launcher) Serve(conn *net.UnixConn) error

Serve answers requests on conn, one at a time, until conn closes. A request that does not decode, or that comes with descriptors, ends it: only the front holds this socket, and a front that sends those is not the front.

type Refused

type Refused struct{ Reason string }

Refused is the launcher's refusal, as the front receives it.

func (*Refused) Error

func (r *Refused) Error() string

type Share

type Share struct {
	Name      string
	Directory string
	ReadOnly  bool
}

Share is a share as the launcher knows it, from the configuration.

type Spawn

type Spawn func(a account.Account, s Share) (*os.File, error)

Spawn starts a worker as a, serving s, and returns the launcher's end of the socket to it -- the end the front is handed.

func Exec

func Exec(exe string, args ...string) Spawn

Exec is the Spawn that starts exe -- this very binary -- with args (the subcommand that runs Worker) as a: setgroups, setgid, setuid in the child, each checked by the Go runtime before exec (SysProcAttr.Credential).

  • The environment is EMPTY but for PATH and WorkerEnv: a worker running as a person must not inherit the launcher's, whatever is in it.
  • Its only descriptor besides 0-2 is its end of the socket; every other one is close-on-exec, which Go guarantees for what it opens.
  • It is in a session of its own, so a signal to the launcher's process group is not one to it.
  • Groups is set, never NoSetGroups: an empty list clears root's groups, NoSetGroups would keep them.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL