simplified library and added better examples and net.UnixListener support

This commit is contained in:
ston1th 2017-04-19 20:42:19 +02:00
commit 694ac1e8a9
7 changed files with 95 additions and 61 deletions

3
.gitignore vendored
View file

@ -1 +1,2 @@
example/example port80
port80and443

View file

@ -1,4 +1,4 @@
Copyright (C) 2016 Marius Schellenberger Copyright (C) 2017 Marius Schellenberger
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without

View file

@ -1,3 +1,5 @@
# godrop - drop privileges # godrop - drop privileges
Godrop is a simple library to drop privileges on linux maschines. Godrop is a simple library to drop privileges on linux maschines.
See the examples directory on how to use the `Drop` and `MultiDrop` functions.

View file

@ -24,8 +24,9 @@ func main() {
fmt.Println("Failed to listen on FD 3:", err) fmt.Println("Failed to listen on FD 3:", err)
os.Exit(1) os.Exit(1)
} }
_, port, _ := net.SplitHostPort(l.Addr().String())
http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Serve(l, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "pid %d\nuid %d\ngid %d", os.Getpid(), os.Getuid(), os.Getgid()) fmt.Fprintf(w, "port %s\npid %d\nuid %d\ngid %d", port, os.Getpid(), os.Getuid(), os.Getgid())
})) }))
} }

52
example/port80and443.go Normal file
View file

@ -0,0 +1,52 @@
package main
import (
"fmt"
"net"
"net/http"
"os"
"git.giftfish.de/ston1th/godrop"
)
func main() {
cfg := godrop.Config{
User: "nobody",
Group: "nobody",
}
err := godrop.MultiDrop(cfg, func() ([]net.Listener, error) {
l1, err := net.Listen("tcp", ":80")
if err != nil {
return nil, err
}
l2, err := net.Listen("tcp", ":443")
return []net.Listener{l1, l2}, err
})
if err != nil {
fmt.Println(err)
}
l1, err := godrop.GetListener()
if err != nil {
fmt.Println("Failed to listen on FD 3:", err)
os.Exit(1)
}
_, port1, _ := net.SplitHostPort(l1.Addr().String())
l2, err := godrop.GetListenerFd(4)
if err != nil {
fmt.Println("Failed to listen on FD 4:", err)
os.Exit(1)
}
_, port2, _ := net.SplitHostPort(l2.Addr().String())
go func() {
http.Serve(l1, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "port %s\npid %d\nuid %d\ngid %d", port1, os.Getpid(), os.Getuid(), os.Getgid())
}))
}()
http.Serve(l2, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "port %s\npid %d\nuid %d\ngid %d", port2, os.Getpid(), os.Getuid(), os.Getgid())
}))
}

View file

@ -1,7 +1,11 @@
// Copyright (C) 2017 Marius Schellenberger
// Package godrop provides a simple privileges dropping library
package godrop package godrop
import ( import (
"errors" "errors"
"fmt"
"io/ioutil" "io/ioutil"
"net" "net"
"os" "os"
@ -11,6 +15,10 @@ import (
"syscall" "syscall"
) )
func errf(err error) error {
return fmt.Errorf("godrop: %s", err)
}
func readFile(name, file string) (id int, err error) { func readFile(name, file string) (id int, err error) {
id = -1 id = -1
if name == "" { if name == "" {
@ -44,6 +52,7 @@ func GID(group string) (int, error) {
return readFile(group, "/etc/group") return readFile(group, "/etc/group")
} }
// Config represents the drop config
type Config struct { type Config struct {
User string User string
Group string Group string
@ -52,78 +61,43 @@ type Config struct {
// Drop will spawn a new process, hand over the listening socket file descriptor and terminate itself after // Drop will spawn a new process, hand over the listening socket file descriptor and terminate itself after
func Drop(c Config, f func() (net.Listener, error)) error { func Drop(c Config, f func() (net.Listener, error)) error {
uid, err := UID(c.User) return MultiDrop(c, func() ([]net.Listener, error) {
if err != nil { l, err := f()
return err return []net.Listener{l}, err
} })
gid, err := GID(c.Group)
if err != nil {
return err
}
switch os.Getuid() {
case 0:
ln, err := f()
if err != nil {
return err
}
l, ok := ln.(*net.TCPListener)
if !ok {
return errors.New("godrop: interface conversion failed")
}
f, err := l.File()
if err != nil {
return err
}
cmd := exec.Command(os.Args[0], os.Args[1:]...)
cmd.ExtraFiles = []*os.File{f}
cmd.SysProcAttr = &syscall.SysProcAttr{
Chroot: c.Chroot,
Credential: &syscall.Credential{
Uid: uint32(uid),
Gid: uint32(gid),
},
Setsid: true,
}
if err := cmd.Start(); err != nil {
return err
}
cmd.Process.Release()
os.Exit(0)
case uid:
return nil
}
return errors.New("godrop: droping priviledges failed")
} }
// MultiDrop will spawn a new process, hand over the all listening sockets and terminate itself after // MultiDrop will spawn a new process, hand over the all listening sockets and terminate itself after
func MultiDrop(c Config, f func() ([]net.Listener, error)) error { func MultiDrop(c Config, f func() ([]net.Listener, error)) error {
uid, err := UID(c.User) uid, err := UID(c.User)
if err != nil { if err != nil {
return err return errf(err)
} }
gid, err := GID(c.Group) gid, err := GID(c.Group)
if err != nil { if err != nil {
return err return errf(err)
} }
switch os.Getuid() { switch os.Getuid() {
case 0: case 0:
cmd := exec.Command(os.Args[0], os.Args[1:]...) cmd := exec.Command(os.Args[0], os.Args[1:]...)
ln, err := f() ln, err := f()
if err != nil { if err != nil {
return err return errf(err)
} }
for _, v := range ln { for i, v := range ln {
l, ok := v.(*net.TCPListener) var f *os.File
if !ok { switch l := v.(type) {
return errors.New("godrop: interface conversion failed") case *net.TCPListener:
f, err = l.File()
l.Close()
case *net.UnixListener:
f, err = l.File()
l.Close()
default:
return errf(fmt.Errorf("index: %d listener is not type of either *net.TCPListener or *net.UnixListener", i))
} }
f, err := l.File()
if err != nil { if err != nil {
return err return errf(fmt.Errorf("index: %d %s", i, err))
} }
cmd.ExtraFiles = append(cmd.ExtraFiles, f) cmd.ExtraFiles = append(cmd.ExtraFiles, f)
} }
@ -138,7 +112,7 @@ func MultiDrop(c Config, f func() ([]net.Listener, error)) error {
} }
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
return err return errf(err)
} }
cmd.Process.Release() cmd.Process.Release()
@ -146,15 +120,17 @@ func MultiDrop(c Config, f func() ([]net.Listener, error)) error {
case uid: case uid:
return nil return nil
} }
return errors.New("godrop: droping priviledges failed") return errf(errors.New("dropping priviledges failed"))
} }
// GetListener returns the listener socket of file descriptor 3 // GetListener returns the listener socket of file descriptor 3
func GetListener() (net.Listener, error) { func GetListener() (net.Listener, error) {
return net.FileListener(os.NewFile(3, "[socket]")) return GetListenerFd(3)
} }
// GetListenerFd returns the listener socket of the given file descriptor // GetListenerFd returns the listener socket of the given file descriptor
func GetListenerFd(fd int) (net.Listener, error) { func GetListenerFd(fd int) (net.Listener, error) {
return net.FileListener(os.NewFile(uintptr(fd), "[socket]")) f := os.NewFile(uintptr(fd), "")
defer f.Close()
return net.FileListener(f)
} }

View file

@ -1,3 +1,5 @@
// Copyright (C) 2017 Marius Schellenberger
package godrop package godrop
import "testing" import "testing"