launch

package
v0.31.2 Latest Latest
Warning

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

Go to latest
Published: Oct 26, 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