mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix: fix oom_score adjustments failing if caps set (#15758)
- Fixes an issue where oom scores would fail to be adjusted in cases where the `coder` binary has capabilities set on it. This is because `PR_SET_DUMPABLE` is set to `0` when a process is executed with elevated capabilities. The fix is to flip `PR_SET_DUMPABLE` to `1` prior to writing to `oom_score_adj`.
This commit is contained in:
@@ -15,6 +15,7 @@ import (
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"golang.org/x/xerrors"
|
||||
"kernel.org/pub/linux/libs/security/libcap/cap"
|
||||
)
|
||||
|
||||
// CLI runs the agent-exec command. It should only be called by the cli package.
|
||||
@@ -48,14 +49,6 @@ func CLI() error {
|
||||
return xerrors.Errorf("no exec command provided %+v", os.Args)
|
||||
}
|
||||
|
||||
if *nice == unset {
|
||||
// If an explicit nice score isn't set, we use the default.
|
||||
*nice, err = defaultNiceScore()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("get default nice score: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if *oom == unset {
|
||||
// If an explicit oom score isn't set, we use the default.
|
||||
*oom, err = defaultOOMScore()
|
||||
@@ -64,12 +57,31 @@ func CLI() error {
|
||||
}
|
||||
}
|
||||
|
||||
err = unix.Setpriority(unix.PRIO_PROCESS, 0, *nice)
|
||||
if *nice == unset {
|
||||
// If an explicit nice score isn't set, we use the default.
|
||||
*nice, err = defaultNiceScore()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("get default nice score: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// We drop effective caps prior to setting dumpable so that we limit the
|
||||
// impact of someone attempting to hijack the process (i.e. with a debugger)
|
||||
// to take advantage of the capabilities of the agent process. We encourage
|
||||
// users to set cap_net_admin on the agent binary for improved networking
|
||||
// performance and doing so results in the process having its SET_DUMPABLE
|
||||
// attribute disabled (meaning we cannot adjust the oom score).
|
||||
err = dropEffectiveCaps()
|
||||
if err != nil {
|
||||
// We alert the user instead of failing the command since it can be difficult to debug
|
||||
// for a template admin otherwise. It's quite possible (and easy) to set an
|
||||
// inappriopriate value for niceness.
|
||||
printfStdErr("failed to adjust niceness to %d for cmd %+v: %v", *nice, args, err)
|
||||
printfStdErr("failed to drop effective caps: %v", err)
|
||||
}
|
||||
|
||||
// Set dumpable to 1 so that we can adjust the oom score. If the process
|
||||
// doesn't have capabilities or has an suid/sgid bit set, this is already
|
||||
// set.
|
||||
err = unix.Prctl(unix.PR_SET_DUMPABLE, 1, 0, 0, 0)
|
||||
if err != nil {
|
||||
printfStdErr("failed to set dumpable: %v", err)
|
||||
}
|
||||
|
||||
err = writeOOMScoreAdj(*oom)
|
||||
@@ -77,7 +89,21 @@ func CLI() error {
|
||||
// We alert the user instead of failing the command since it can be difficult to debug
|
||||
// for a template admin otherwise. It's quite possible (and easy) to set an
|
||||
// inappriopriate value for oom_score_adj.
|
||||
printfStdErr("failed to adjust oom score to %d for cmd %+v: %v", *oom, args, err)
|
||||
printfStdErr("failed to adjust oom score to %d for cmd %+v: %v", *oom, execArgs(os.Args), err)
|
||||
}
|
||||
|
||||
// Set dumpable back to 0 just to be safe. It's not inherited for execve anyways.
|
||||
err = unix.Prctl(unix.PR_SET_DUMPABLE, 0, 0, 0, 0)
|
||||
if err != nil {
|
||||
printfStdErr("failed to unset dumpable: %v", err)
|
||||
}
|
||||
|
||||
err = unix.Setpriority(unix.PRIO_PROCESS, 0, *nice)
|
||||
if err != nil {
|
||||
// We alert the user instead of failing the command since it can be difficult to debug
|
||||
// for a template admin otherwise. It's quite possible (and easy) to set an
|
||||
// inappriopriate value for niceness.
|
||||
printfStdErr("failed to adjust niceness to %d for cmd %+v: %v", *nice, args, err)
|
||||
}
|
||||
|
||||
path, err := exec.LookPath(args[0])
|
||||
@@ -151,3 +177,16 @@ func execArgs(args []string) []string {
|
||||
func printfStdErr(format string, a ...any) {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "coder-agent: %s\n", fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
func dropEffectiveCaps() error {
|
||||
proc := cap.GetProc()
|
||||
err := proc.ClearFlag(cap.Effective)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("clear effective caps: %w", err)
|
||||
}
|
||||
err = proc.SetProc()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("set proc: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user