Skip to content

Commit

Permalink
hotdog: CVE-2022-0071
Browse files Browse the repository at this point in the history
Signed-off-by: Arnaldo Garcia Rincon <[email protected]>
  • Loading branch information
arnaldo2792 committed Apr 1, 2022
1 parent 6e3d6ed commit 1a3f35b
Show file tree
Hide file tree
Showing 10 changed files with 1,389 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
From 6ce5ac28431bfb18a0ba0233636e238fc18082d6 Mon Sep 17 00:00:00 2001
From: Arnaldo Garcia Rincon <[email protected]>
Date: Wed, 23 Feb 2022 19:55:25 +0000
Subject: [PATCH 1/9] poststart-hook: silently exit under certain conditions

Since hotdog patches java processes on a best-effort basis, the hotdog
poststart hook won't exit with a non-zero error code if:

- The process fails to constrain itself
- An error occurred while reading the container's capabilities
- An error occurred while the hotpatch was applied

Signed-off-by: Arnaldo Garcia Rincon <[email protected]>
Reviewed-by: Ben Cressey <[email protected]>
Reviewed-by: Samuel Karp <[email protected]>
---
cmd/hotdog-poststart-hook/main.go | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/cmd/hotdog-poststart-hook/main.go b/cmd/hotdog-poststart-hook/main.go
index 49b3606..2e559d4 100644
--- a/cmd/hotdog-poststart-hook/main.go
+++ b/cmd/hotdog-poststart-hook/main.go
@@ -20,6 +20,7 @@ func main() {
}

func _main() error {
+ // Fail if an error occurs while the container's state or config are retrieved
state, err := hook.State()
if err != nil {
return err
@@ -28,21 +29,28 @@ func _main() error {
if err != nil {
return err
}
+ // Silently exit if:
+ // - The process fails to constrain itself
+ // - An error occurred while reading the container's capabilities
+ // - An error occurred while the hotpatch was applied
+ // We don't send these errors to the STDOUT because the runtime
+ // only reads it when the hook errors out
if spec.Process.SelinuxLabel != "" {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
if err := selinux.SetExecLabel(spec.Process.SelinuxLabel); err != nil {
- return err
+ return nil
}
}
capJSON, err := json.Marshal(spec.Process.Capabilities)
if err != nil {
- return err
+ return nil
}
hotpatch := exec.Command("nsenter",
"-t", strconv.Itoa(state.Pid),
"-m", "-n", "-i", "-u", "-p",
filepath.Join(hotdog.ContainerDir, hotdog.HotpatchBinary))
hotpatch.Env = []string{hotdog.EnvCapability + "=" + string(capJSON)}
- return hotpatch.Start()
+ hotpatch.Start()
+ return nil
}
--
2.33.1

168 changes: 168 additions & 0 deletions packages/hotdog/0002-poststart-hook-enter-container-s-cgroups.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
From 84a8080e1a8754d605bad861fc528d61d7792ff9 Mon Sep 17 00:00:00 2001
From: Arnaldo Garcia Rincon <[email protected]>
Date: Thu, 27 Jan 2022 22:29:30 +0000
Subject: [PATCH 2/9] poststart-hook: enter container's cgroups

The poststart-hooks processes have to be bound to the resource limits
set to the container to prevent them from consuming all the host's
resources.

Signed-off-by: Arnaldo Garcia Rincon <[email protected]>
Reviewed-by: Ben Cressey <[email protected]>
Reviewed-by: Samuel Karp <[email protected]>
---
cgroups/utils.go | 74 +++++++++++++++++++++++++++++++
cmd/hotdog-poststart-hook/main.go | 32 +++++++++----
2 files changed, 98 insertions(+), 8 deletions(-)
create mode 100644 cgroups/utils.go

diff --git a/cgroups/utils.go b/cgroups/utils.go
new file mode 100644
index 0000000..fc321d0
--- /dev/null
+++ b/cgroups/utils.go
@@ -0,0 +1,74 @@
+package cgroups
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+)
+
+// EnterCgroups writes the current process into the cgroups of
+// the target process
+func EnterCgroups(targetPID string) error {
+ cgroups, err := parseCgroupFile(filepath.Join("/proc", targetPID, "/cgroup"))
+ if err != nil {
+ return nil
+ }
+ pid := os.Getpid()
+
+ for sub, path := range cgroups {
+ if err := os.WriteFile(filepath.Join("/sys/fs/cgroup/", sub, path, "tasks"), []byte(strconv.Itoa(pid)), 0); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// parseCgroupFile returns a map of strings, with the keys being the names of the cgroups
+// controllers, and the values the path name of the control group in the hierarchy
+// to which the process belongs.
+func parseCgroupFile(path string) (map[string]string, error) {
+ f, err := os.Open(path)
+ if err != nil {
+ return nil, err
+ }
+ defer f.Close()
+
+ s := bufio.NewScanner(f)
+ cgroups := make(map[string]string)
+
+ for s.Scan() {
+ text := s.Text()
+ // from cgroups(7):
+ // /proc/[pid]/cgroup
+ // ...
+ // For each cgroup hierarchy ... there is one entry
+ // containing three colon-separated fields of the form:
+ // hierarchy-ID:subsystem-list:cgroup-path
+ parts := strings.SplitN(text, ":", 3)
+ if len(parts) < 3 {
+ return nil, fmt.Errorf("invalid cgroup entry: must contain at least two colons: %v", text)
+ }
+ subsystem := parts[1]
+ path := parts[2]
+ // The `cgroup` file contains lines with empty subsystems
+ if subsystem == "" {
+ continue
+ }
+ // There are subsystems with the form `name=<sub>`
+ if strings.Contains(subsystem, "=") {
+ subParts := strings.SplitN(subsystem, "=", 2)
+ if len(subParts) < 2 {
+ return nil, fmt.Errorf("invalid subsystem format, must have subsystem name: %s", parts[1])
+ }
+ subsystem = subParts[1]
+ }
+ cgroups[subsystem] = path
+ }
+ if err := s.Err(); err != nil {
+ return nil, err
+ }
+ return cgroups, nil
+}
diff --git a/cmd/hotdog-poststart-hook/main.go b/cmd/hotdog-poststart-hook/main.go
index 2e559d4..fc5d9d1 100644
--- a/cmd/hotdog-poststart-hook/main.go
+++ b/cmd/hotdog-poststart-hook/main.go
@@ -8,9 +8,11 @@ import (
"strconv"

"github.com/bottlerocket/hotdog"
+ "github.com/bottlerocket/hotdog/cgroups"
"github.com/bottlerocket/hotdog/hook"

- selinux "github.com/opencontainers/selinux/go-selinux"
+ "github.com/opencontainers/runtime-spec/specs-go"
+ "github.com/opencontainers/selinux/go-selinux"
)

func main() {
@@ -29,28 +31,42 @@ func _main() error {
if err != nil {
return err
}
+ targetPID := strconv.Itoa(state.Pid)
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
// Silently exit if:
// - The process fails to constrain itself
// - An error occurred while reading the container's capabilities
// - An error occurred while the hotpatch was applied
// We don't send these errors to the STDOUT because the runtime
// only reads it when the hook errors out
- if spec.Process.SelinuxLabel != "" {
- runtime.LockOSThread()
- defer runtime.UnlockOSThread()
- if err := selinux.SetExecLabel(spec.Process.SelinuxLabel); err != nil {
- return nil
- }
+ if err := constrainProcess(spec, targetPID); err != nil {
+ return nil
}
capJSON, err := json.Marshal(spec.Process.Capabilities)
if err != nil {
return nil
}
hotpatch := exec.Command("nsenter",
- "-t", strconv.Itoa(state.Pid),
+ "-t", targetPID,
"-m", "-n", "-i", "-u", "-p",
filepath.Join(hotdog.ContainerDir, hotdog.HotpatchBinary))
hotpatch.Env = []string{hotdog.EnvCapability + "=" + string(capJSON)}
hotpatch.Start()
return nil
}
+
+// constrainProcess sets the SELinux label of the running process, and changes
+// its cgroups to be the same as the target container.
+func constrainProcess(spec specs.Spec, targetPID string) error {
+ if err := cgroups.EnterCgroups(targetPID); err != nil {
+ return err
+ }
+ if spec.Process.SelinuxLabel != "" {
+ if err := selinux.SetExecLabel(spec.Process.SelinuxLabel); err != nil {
+ return err
+ }
+ }
+ return nil
+}
--
2.33.1

Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
From 71ac992149a17862a495552f38e747a6d297fb43 Mon Sep 17 00:00:00 2001
From: Arnaldo Garcia Rincon <[email protected]>
Date: Thu, 3 Feb 2022 22:55:40 +0000
Subject: [PATCH 3/9] poststart-hook: set `NO_NEW_PRIVS` in poststart process

The `NO_NEW_PRIVS` flag will prevent the poststart process and its
children from getting more privileges than what they were granted.

Signed-off-by: Arnaldo Garcia Rincon <[email protected]>
Reviewed-by: Ben Cressey <[email protected]>
Reviewed-by: Samuel Karp <[email protected]>
---
cmd/hotdog-poststart-hook/main.go | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/cmd/hotdog-poststart-hook/main.go b/cmd/hotdog-poststart-hook/main.go
index fc5d9d1..ac9d33c 100644
--- a/cmd/hotdog-poststart-hook/main.go
+++ b/cmd/hotdog-poststart-hook/main.go
@@ -13,6 +13,7 @@ import (

"github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/selinux/go-selinux"
+ "golang.org/x/sys/unix"
)

func main() {
@@ -57,8 +58,10 @@ func _main() error {
return nil
}

-// constrainProcess sets the SELinux label of the running process, and changes
-// its cgroups to be the same as the target container.
+// constrainProcess sets the SELinux label of the running process, changes
+// its cgroups to be the same as the target container, and sets the
+// `NO_NEW_PRIVS` flags to prevent the current process to get more
+// privileges.
func constrainProcess(spec specs.Spec, targetPID string) error {
if err := cgroups.EnterCgroups(targetPID); err != nil {
return err
@@ -68,5 +71,8 @@ func constrainProcess(spec specs.Spec, targetPID string) error {
return err
}
}
+ if err := unix.Prctl(unix.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); err != nil {
+ return err
+ }
return nil
}
--
2.33.1

Loading

0 comments on commit 1a3f35b

Please sign in to comment.