-
Notifications
You must be signed in to change notification settings - Fork 18.7k
Description
Motivation
I’d like to use capabilities in order to run unprivileged programs under unprivileged user accounts with only precisely the capabilities the program needs. A concrete example is removing all privileges but CAP_SYS_TIME from an NTP client.
Example/details on what’s not working
Unfortunately, it seems like Go currently doesn’t provide a way to do this.
Here are 2 example programs:
- https://play.golang.org/p/_EN2RzKPE3 prints the current capabilities and tries to enable
CAP_SYS_TIME - https://play.golang.org/p/Yk6AKHyJGw sets the
CAP_SYS_TIMEcapability in the inheritable and ambient capability set, then runs the first example program.
The output of program 2 is:
2017/03/25 17:58:00 ntp.go:17: caps = { effective="empty" permitted="empty" inheritable="sys_time" bounding="full" }
2017/03/25 17:58:00 ntp.go:22: could not apply caps: operation not permitted
2017/03/25 17:58:00 exit status 1
I would have expected the program to include sys_time in its effective capabilities set and not report an error.
Upon closer investigation, it turns out that setuid(2) clears the ambient capability set. Since forkAndExecInChild (src/syscall/exec_linux.go) applies cmd.SysProcAttr.Credential using setuid(2), the ambient capability set is cleared before my program is started.
Looking at systemd, you can see that they restore the ambient capability set after applying credentials: https://github.com/systemd/systemd/blob/1539a651a9d31c18273df917bbfe175ab3606025/src/core/execute.c#L2711-L2748
Proof of concept
Directly modifying the standard library to set the CAP_SYS_TIME capability as follows…
--- i/src/syscall/exec_linux.go
+++ w/src/syscall/exec_linux.go
@@ -227,6 +227,11 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr
if err1 != 0 {
goto childerror
}
+
+ _, _, err1 = RawSyscall(SYS_PRCTL, 47 /* PR_CAP_AMBIENT */, uintptr(2), uintptr(25) /* TIME */)
+ if err1 != 0 {
+ goto childerror
+ }
}
// Chdir…results in my test programs working:
2017/03/25 17:50:52 ntp.go:18: caps = { effective="sys_time" permitted="sys_time" inheritable="sys_time" bounding="full" }
2017/03/25 17:50:52 ntp.go:22: now: { effective="sys_time" permitted="sys_time" inheritable="sys_time" bounding="full" }
Question
Would a CL which adds an ambient capability struct member to syscall.SysProcAttr be accepted? If not, which other way would you recommend for starting processes with an ambient capability set?
Further reading
- https://lwn.net/Articles/636533/ (the patch set which actually introduced ambient capabilities)
- The prctl(2) manpage, notably its PR_CAP_AMBIENT section