ntp: drop privileges to nobody/nogroup, retain CAP_SYS_TIME
This commit is contained in:
parent
83adedae5d
commit
3ed1e381b5
@ -23,6 +23,8 @@ func set() error {
|
|||||||
func main() {
|
func main() {
|
||||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||||
|
|
||||||
|
mustDropPrivileges()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if err := set(); err != nil {
|
if err := set(); err != nil {
|
||||||
log.Fatalf("setting time failed: %v", err)
|
log.Fatalf("setting time failed: %v", err)
|
||||||
|
84
cmd/ntp/privdrop.go
Normal file
84
cmd/ntp/privdrop.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// +build go1.9
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type capHeader struct {
|
||||||
|
version uint32
|
||||||
|
pid int
|
||||||
|
}
|
||||||
|
|
||||||
|
type capData struct {
|
||||||
|
effective uint32
|
||||||
|
permitted uint32
|
||||||
|
inheritable uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type caps struct {
|
||||||
|
hdr capHeader
|
||||||
|
data [2]capData
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCaps() (caps, error) {
|
||||||
|
var c caps
|
||||||
|
|
||||||
|
// Get capability version
|
||||||
|
if _, _, errno := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(&c.hdr)), uintptr(unsafe.Pointer(nil)), 0); errno != 0 {
|
||||||
|
return c, fmt.Errorf("SYS_CAPGET: %v", errno)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current capabilities
|
||||||
|
if _, _, errno := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(&c.hdr)), uintptr(unsafe.Pointer(&c.data[0])), 0); errno != 0 {
|
||||||
|
return c, fmt.Errorf("SYS_CAPGET: %v", errno)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// mustDropPrivileges executes the program in a child process, dropping root
|
||||||
|
// privileges, but retaining the CAP_SYS_TIME capability to change the system
|
||||||
|
// clock.
|
||||||
|
func mustDropPrivileges() {
|
||||||
|
if os.Getenv("NTP_PRIVILEGES_DROPPED") == "1" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// From include/uapi/linux/capability.h:
|
||||||
|
// Allow setting the real-time clock
|
||||||
|
const CAP_SYS_TIME = 25
|
||||||
|
|
||||||
|
caps, err := getCaps()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add CAP_SYS_TIME to the permitted and inheritable capability mask,
|
||||||
|
// otherwise we will not be able to add it to the ambient capability mask.
|
||||||
|
caps.data[0].permitted |= 1 << uint(CAP_SYS_TIME)
|
||||||
|
caps.data[0].inheritable |= 1 << uint(CAP_SYS_TIME)
|
||||||
|
|
||||||
|
if _, _, errno := syscall.Syscall(syscall.SYS_CAPSET, uintptr(unsafe.Pointer(&caps.hdr)), uintptr(unsafe.Pointer(&caps.data[0])), 0); errno != 0 {
|
||||||
|
log.Fatalf("SYS_CAPSET: %v", errno)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command(os.Args[0])
|
||||||
|
cmd.Env = append(os.Environ(), "NTP_PRIVILEGES_DROPPED=1")
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||||
|
Credential: &syscall.Credential{
|
||||||
|
Uid: 65534,
|
||||||
|
Gid: 65534,
|
||||||
|
},
|
||||||
|
AmbientCaps: []uintptr{CAP_SYS_TIME},
|
||||||
|
}
|
||||||
|
log.Fatal(cmd.Run())
|
||||||
|
}
|
8
cmd/ntp/privdrop_dummy.go
Normal file
8
cmd/ntp/privdrop_dummy.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
// +build !go1.9
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
// mustDropPrivileges (on Go < 1.9) does nothing, as the AmbientCaps field in
|
||||||
|
// syscall.SysProcAttr was only introduced in Go 1.9.
|
||||||
|
func mustDropPrivileges() {
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user