drop

package
v0.12.2 Latest Latest
Warning

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

Go to latest
Published: Jun 29, 2022 License: BSD-3-Clause, MIT, MIT-0, + 1 more Imports: 12 Imported by: 0

README

drop - drop privileges and inherit handles

go get -u "tawesoft.co.uk/go"
import "tawesoft.co.uk/go/drop"
Links License Stable?
homedocssrc MIT candidate

About

Package drop implements the ability to start a process as root, open privileged resources as files, drop privileges to become a given user account, and inherit file handles across the dropping of privileges.

NOTE: This package has only been tested on Linux. YMMV.

NOTE: This package WILL NOT WORK on Windows.

WARNING: if a process opens a config file as root, that file must be writable by root or system accounts only. The safest way to do this is change it to be root-owned with permissions 0644 (or 0600).

Examples

Opens privileged files and ports as root, then drops privileges

package main

import (
    "fmt"
    "os"

    "tawesoft.co.uk/go/drop"
)

// Define structures and methods that meets the drop.Inheritable interface
// by implementing Name(), Open(), Inherit() and Close()...

// These are implemented here for example purposes - but you can use
// the builtins from drop instead.

// InheritableFile is a file handle that survives a dropping of
// privileges.
type InheritableFile struct {
    Path  string
    Flags int // e.g.  os.O_RDWR|os.O_CREATE
    Perm  os.FileMode // for os.O_CREATE, e.g. 0600

    handle *os.File
}

func (h InheritableFile) String() string {
    return h.Path
}

func (h *InheritableFile) Open() (*os.File, error) {
    f, err := os.OpenFile(h.Path, h.Flags, h.Perm)
    h.handle = f
    return f, err
}

func (h *InheritableFile) Inherit(f *os.File) error {
    f.Seek(0, 0)
    h.handle = f
    return nil
}

func (h *InheritableFile) Close() error {
    return h.handle.Close()
}

func main() {
    if len(os.Args) < 2 {
        panic(fmt.Sprintf("USAGE: sudo %s username\n", os.Args[0]))
    }

    // what user to drop to
    username := os.Args[1]

    // resources to be opened as root and persist after privileges are dropped
    privilegedFile := &InheritableFile{"/tmp/privileged-file-example", os.O_RDWR|os.O_CREATE, 0600, nil}
    privilegedPort := drop.NewInheritableTCPListener(":81")

    // If the program is run as root, open privileged resources as root, then
    // start a child process as `username` that inherits these resources and
    // the parent process's stdio, and immediately exit.
    //
    // If the program is run as non-root, inherit these resources and continue.
    shouldExit, closer, err := drop.Drop(username, privilegedFile, privilegedPort)
    if err != nil {
        panic(fmt.Sprintf("error dropping privileges (try running as root): %v", err))
    }
    defer closer()
    if shouldExit { return }

    // At this point, the program is no longer running as root, but it still
    // has access to these privileged resources.

    // do things with privilegedFile
    privilegedFile.handle.WriteString("hello world\n")
    privilegedFile.Close()

    // do things with privilegedPort
    // privilegedPort.handle ....
    privilegedPort.Close()
}

Changes

2021-07-09
  • The Inheritable interface has changed. It now has a Close() method. The Name() method has also been renamed String() to satisfy the stringer interface.

  • The Drop() function now returns an extra value before the error value. This closer can be used by the child process to close all Inheritable handles. Alternatively, it is possible to ignore this and close each handle by calling their Close() method.

  • The package now exports the builtins InheritableFile and InheritableNetListener that implement the Inheritable interface for Files and net.Listeners. These are created by the functions NewInheritableFile, NewInheritableTCPListener and NewInheritableUnixListener.

  • Drop() no longer panics on non-Linux platforms. However, it has only been tested on Linux so YMMV. It will continue to panic on Windows. Listeners also cannot be inherited on the JS platform target as they are not backed by files.

2021-03-17
  • Drop() now returns a (bool, error) 2-tuple. The first return value, if true, indicates that the caller should immediately exit.
2020-11-27
  • Drop() functionality has been moved to tawesoft.co.uk/go/drop with changes to Inheritables from a struct to an interface.

Getting Help

This package is part of tawesoft.co.uk/go, a monorepo for small Go modules maintained by Tawesoft®. Check out that URL for more information about other Go modules from Tawesoft plus community and commercial support options.

Documentation

Overview

Package drop implements the ability to start a process as root, open privileged resources as files, drop privileges to become a given user account, and inherit file handles across the dropping of privileges.

NOTE: This package has only been tested on Linux. YMMV.

NOTE: This package WILL NOT WORK on Windows.

WARNING: if a process opens a config file as root, that file must be writable by root or system accounts only. The safest way to do this is change it to be root-owned with permissions 0644 (or 0600).

Examples

Opens privileged files and ports as root, then drops privileges

https://www.tawesoft.co.uk/go/doc/drop/examples/drop/

Package Information

License: MIT (see LICENSE.txt)

Stable: candidate

For more information, documentation, source code, examples, support, links, etc. please see https://www.tawesoft.co.uk/go and https://www.tawesoft.co.uk/go/drop

2021-07-09

    * The Inheritable interface has changed. It now has a Close() method. The
      Name() method has also been renamed String() to satisfy the stringer
      interface.

    * The Drop() function now returns an extra value before the error value.
      This `closer` can be used by the child process to close all Inheritable
      handles. Alternatively, it is possible to ignore this and close each
      handle by calling their Close() method.

    * The package now exports the builtins InheritableFile and
      InheritableNetListener that implement the Inheritable interface for
      Files and net.Listeners. These are created by the functions
      NewInheritableFile, NewInheritableTCPListener and
      NewInheritableUnixListener.

    * Drop() no longer panics on non-Linux platforms. However, it has only been
      tested on Linux so YMMV. It will continue to panic on Windows. Listeners
      also cannot be inherited on the JS platform target as they are not backed
      by files.

2021-03-17

    * Drop() now returns a (bool, error) 2-tuple. The first return value,
      if true, indicates that the caller should immediately exit.

2020-11-27

    * Drop() functionality has been moved to tawesoft.co.uk/go/drop with
      changes to Inheritables from a struct to an interface.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Drop

func Drop(username string, files ...Inheritable) (bool, func() []error, error)

Drop works in two ways, depending on the privileges of the current process.

As the superuser (root), drop executes a new copy of the running program as the given user, blocks until that child program exits, then returns.

As the child process, drop returns without blocking. The current stdio streams, and zero-or-more "inheritable" files, are persisted and inherited by the new process.

If the first return argument is true, the caller is the root process and should exit immediately (e.g. by returning from main(), with os.Exit(), etc.)

The second return argument is a function. It is nil if the third argument is an error. In the child process, this calls the Close() method on each of the supplied Inheritable files. The caller may either Close() each Inheritable manually or typically simply defer this returned function. The returned []error argument from this function contains the errors returned by Inheritable Close() methods. In the root process, this returned function is always a no-op and does not need to be called.

func IsSuperuser added in v0.12.1

func IsSuperuser() bool

IsSuperuser returns true iff the current user is root

func UserLookup added in v0.12.1

func UserLookup(username string) (uid int, gid int, gids []int, err error)

UserLookup returns UID, GID, and Supplementary Group IDs

Types

type Inheritable

type Inheritable interface {
	// String returns some description of the resource.
	String() string

	// Open is called by the root process
	Open() (*os.File, error)

	// Inherit is called by the child process
	Inherit(*os.File) error

	// Close closes any resource generated by a call to Open or Inherit.
	Close() error
}

Inheritable interface describes a File-based handle that can be passed between a parent and child process across a dropping of privileges.

type InheritableFile added in v0.11.0

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

InheritableFile is a file handle that survives a dropping of privileges.

func NewInheritableFile added in v0.12.1

func NewInheritableFile(path string, flags int, uid int, gid int, mode os.FileMode) *InheritableFile

NewInheritableFile returns an InhertiableFile that wraps a file handle that survives a dropping of privileges.

The parameters uid, gid, and mode, unless equal to -1, -1, or 0, set the user, group, and permissions of the file after it has been opened.

WARNING: if these values are supplied from a config file, that config file should be writable to root or system accounts only - otherwise, an attacker may edit the config file in such a way as to set the permissions of arbitrary files.

func (InheritableFile) Close added in v0.11.0

func (h InheritableFile) Close() error

func (InheritableFile) Handle added in v0.11.0

func (h InheritableFile) Handle() *os.File

func (*InheritableFile) Inherit added in v0.11.0

func (h *InheritableFile) Inherit(f *os.File) error

func (*InheritableFile) Open added in v0.11.0

func (h *InheritableFile) Open() (*os.File, error)

func (InheritableFile) String added in v0.12.1

func (h InheritableFile) String() string

type InheritableNetListener added in v0.11.0

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

InheritableNetListener is a net.Listener that survives a dropping of privileges.

Note that On JS and Windows, the File method of most Listeners are not implemented, so this will not work.

func NewInheritableTCPListener added in v0.12.1

func NewInheritableTCPListener(address string) *InheritableNetListener

func NewInheritableUnixListener added in v0.12.1

func NewInheritableUnixListener(address string, uid int, gid int, mode os.FileMode) *InheritableNetListener

NewInheritableUnixListener returns an InheritableNetListener for a UNIX socket.

The parameters uid, gid, and mode, unless equal to -1, -1, or 0, set the user, group, and permissions of the socket after it has been opened.

WARNING: if these values are supplied from a config file, that config file should be writable to root or system accounts only - otherwise, an attacker may edit the config file in such a way as to set the permissions of arbitrary files.

func (*InheritableNetListener) Close added in v0.11.0

func (h *InheritableNetListener) Close() error

func (InheritableNetListener) Handle added in v0.11.0

func (h InheritableNetListener) Handle() net.Listener

func (*InheritableNetListener) Inherit added in v0.11.0

func (h *InheritableNetListener) Inherit(f *os.File) error

func (*InheritableNetListener) Open added in v0.11.0

func (h *InheritableNetListener) Open() (*os.File, error)

func (InheritableNetListener) String added in v0.12.1

func (h InheritableNetListener) String() string

Directories

Path Synopsis
examples
drop
Opens privileged files and ports as root, then drops privileges
Opens privileged files and ports as root, then drops privileges

Jump to

Keyboard shortcuts

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