Merge pull request #8821 from rhatdan/caps

Containers should not get inheritable caps by default
This commit is contained in:
OpenShift Merge Robot
2021-01-07 09:44:37 -05:00
committed by GitHub
8 changed files with 253 additions and 35 deletions

View File

@ -8,7 +8,6 @@ import (
"strconv"
"time"
"github.com/containers/common/pkg/capabilities"
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/libpod/events"
"github.com/containers/storage/pkg/stringid"
@ -973,20 +972,12 @@ func (c *Container) removeAllExecSessions() error {
// Make an ExecOptions struct to start the OCI runtime and prepare its exec
// bundle.
func prepareForExec(c *Container, session *ExecSession) (*ExecOptions, error) {
// TODO: check logic here - should we set Privileged if the container is
// privileged?
var capList []string
if session.Config.Privileged || c.config.Privileged {
capList = capabilities.AllCapabilities()
}
if err := c.createExecBundle(session.ID()); err != nil {
return nil, err
}
opts := new(ExecOptions)
opts.Cmd = session.Config.Command
opts.CapAdd = capList
opts.Env = session.Config.Environment
opts.Terminal = session.Config.Terminal
opts.Cwd = session.Config.WorkDir
@ -995,6 +986,7 @@ func prepareForExec(c *Container, session *ExecSession) (*ExecOptions, error) {
opts.DetachKeys = session.Config.DetachKeys
opts.ExitCommand = session.Config.ExitCommand
opts.ExitCommandDelay = session.Config.ExitCommandDelay
opts.Privileged = session.Config.Privileged
return opts, nil
}

View File

@ -151,8 +151,6 @@ type OCIRuntime interface {
type ExecOptions struct {
// Cmd is the command to execute.
Cmd []string
// CapAdd is a set of capabilities to add to the executed command.
CapAdd []string
// Env is a set of environment variables to add to the container.
Env map[string]string
// Terminal is whether to create a new TTY for the exec session.
@ -181,6 +179,8 @@ type ExecOptions struct {
// ExitCommandDelay is a delay (in seconds) between the exec session
// exiting, and the exit command being invoked.
ExitCommandDelay uint
// Privileged indicates the execed process will be launched in Privileged mode
Privileged bool
}
// HTTPAttachStreams informs the HTTPAttach endpoint which of the container's

View File

@ -398,10 +398,6 @@ func (r *ConmonOCIRuntime) startExec(c *Container, sessionID string, options *Ex
args = append(args, formatRuntimeOpts("--preserve-fds", fmt.Sprintf("%d", options.PreserveFDs))...)
}
for _, capability := range options.CapAdd {
args = append(args, formatRuntimeOpts("--cap", capability)...)
}
if options.Terminal {
args = append(args, "-t")
}

View File

@ -22,6 +22,7 @@ import (
"text/template"
"time"
"github.com/containers/common/pkg/capabilities"
"github.com/containers/common/pkg/config"
conmonConfig "github.com/containers/conmon/runner/config"
"github.com/containers/podman/v2/libpod/define"
@ -1201,13 +1202,7 @@ func prepareProcessExec(c *Container, options *ExecOptions, env []string, sessio
}
pspec.SelinuxLabel = c.config.ProcessLabel
pspec.Args = options.Cmd
for _, cap := range options.CapAdd {
pspec.Capabilities.Bounding = append(pspec.Capabilities.Bounding, cap)
pspec.Capabilities.Effective = append(pspec.Capabilities.Effective, cap)
pspec.Capabilities.Inheritable = append(pspec.Capabilities.Inheritable, cap)
pspec.Capabilities.Permitted = append(pspec.Capabilities.Permitted, cap)
pspec.Capabilities.Ambient = append(pspec.Capabilities.Ambient, cap)
}
// We need to default this to false else it will inherit terminal as true
// from the container.
pspec.Terminal = false
@ -1263,6 +1258,31 @@ func prepareProcessExec(c *Container, options *ExecOptions, env []string, sessio
pspec.User = processUser
}
ctrSpec, err := c.specFromState()
if err != nil {
return nil, err
}
allCaps := capabilities.AllCapabilities()
if options.Privileged {
pspec.Capabilities.Bounding = allCaps
} else {
pspec.Capabilities.Bounding = ctrSpec.Process.Capabilities.Bounding
}
if execUser.Uid == 0 {
pspec.Capabilities.Effective = pspec.Capabilities.Bounding
pspec.Capabilities.Inheritable = pspec.Capabilities.Bounding
pspec.Capabilities.Permitted = pspec.Capabilities.Bounding
pspec.Capabilities.Ambient = pspec.Capabilities.Bounding
} else {
if user == c.config.User {
pspec.Capabilities.Effective = ctrSpec.Process.Capabilities.Effective
pspec.Capabilities.Inheritable = ctrSpec.Process.Capabilities.Effective
pspec.Capabilities.Permitted = ctrSpec.Process.Capabilities.Effective
pspec.Capabilities.Ambient = ctrSpec.Process.Capabilities.Effective
}
}
hasHomeSet := false
for _, s := range pspec.Env {
if strings.HasPrefix(s, "HOME=") {

View File

@ -133,13 +133,13 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator,
configSpec := g.Config
configSpec.Process.Capabilities.Ambient = []string{}
configSpec.Process.Capabilities.Bounding = caplist
configSpec.Process.Capabilities.Inheritable = caplist
user := strings.Split(s.User, ":")[0]
if (user == "" && s.UserNS.NSMode != specgen.KeepID) || user == "root" || user == "0" {
configSpec.Process.Capabilities.Effective = caplist
configSpec.Process.Capabilities.Permitted = caplist
configSpec.Process.Capabilities.Inheritable = caplist
} else {
userCaps, err := capabilities.MergeCapabilities(nil, s.CapAdd, nil)
if err != nil {
@ -147,6 +147,7 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator,
}
configSpec.Process.Capabilities.Effective = userCaps
configSpec.Process.Capabilities.Permitted = userCaps
configSpec.Process.Capabilities.Inheritable = userCaps
// Ambient capabilities were added to Linux 4.3. Set ambient
// capabilities only when the kernel supports them.

View File

@ -378,10 +378,17 @@ func GetRandomIPAddress() string {
// RunTopContainer runs a simple container in the background that
// runs top. If the name passed != "", it will have a name
func (p *PodmanTestIntegration) RunTopContainer(name string) *PodmanSessionIntegration {
return p.RunTopContainerWithArgs(name, nil)
}
// RunTopContainerWithArgs runs a simple container in the background that
// runs top. If the name passed != "", it will have a name, command args can also be passed in
func (p *PodmanTestIntegration) RunTopContainerWithArgs(name string, args []string) *PodmanSessionIntegration {
var podmanArgs = []string{"run"}
if name != "" {
podmanArgs = append(podmanArgs, "--name", name)
}
podmanArgs = append(podmanArgs, args...)
podmanArgs = append(podmanArgs, "-d", ALPINE, "top")
return p.Podman(podmanArgs)
}
@ -538,12 +545,7 @@ func (p *PodmanTestIntegration) CreatePodWithLabels(name string, labels map[stri
}
func (p *PodmanTestIntegration) RunTopContainerInPod(name, pod string) *PodmanSessionIntegration {
var podmanArgs = []string{"run", "--pod", pod}
if name != "" {
podmanArgs = append(podmanArgs, "--name", name)
}
podmanArgs = append(podmanArgs, "-d", ALPINE, "top")
return p.Podman(podmanArgs)
return p.RunTopContainerWithArgs(name, []string{"--pod", pod})
}
func (p *PodmanTestIntegration) RunHealthCheck(cid string) error {

View File

@ -120,18 +120,200 @@ var _ = Describe("Podman exec", func() {
})
It("podman exec --privileged", func() {
hostCap := SystemExec("awk", []string{"/^CapEff/ { print $2 }", "/proc/self/status"})
Expect(hostCap.ExitCode()).To(Equal(0))
session := podmanTest.Podman([]string{"run", "--privileged", "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
bndPerms := session.OutputToString()
session = podmanTest.Podman([]string{"run", "--privileged", "--rm", ALPINE, "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
effPerms := session.OutputToString()
setup := podmanTest.RunTopContainer("test-privileged")
setup.WaitWithDefaultTimeout()
Expect(setup.ExitCode()).To(Equal(0))
session := podmanTest.Podman([]string{"exec", "--privileged", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"})
session = podmanTest.Podman([]string{"exec", "--privileged", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(effPerms))
containerCapMatchesHost(session.OutputToString(), hostCap.OutputToString())
session = podmanTest.Podman([]string{"exec", "--privileged", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(bndPerms))
})
It("podman exec --privileged", func() {
session := podmanTest.Podman([]string{"run", "--privileged", "--user=bin", "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
bndPerms := session.OutputToString()
session = podmanTest.Podman([]string{"run", "--privileged", "--user=bin", "--rm", ALPINE, "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
effPerms := session.OutputToString()
setup := podmanTest.RunTopContainer("test-privileged")
setup.WaitWithDefaultTimeout()
Expect(setup.ExitCode()).To(Equal(0))
session = podmanTest.Podman([]string{"exec", "--privileged", "--user=bin", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(effPerms))
session = podmanTest.Podman([]string{"exec", "--privileged", "--user=bin", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(bndPerms))
})
It("podman exec --privileged", func() {
session := podmanTest.Podman([]string{"run", "--privileged", "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
bndPerms := session.OutputToString()
setup := podmanTest.RunTopContainer("test-privileged")
setup.WaitWithDefaultTimeout()
Expect(setup.ExitCode()).To(Equal(0))
session = podmanTest.Podman([]string{"exec", "--privileged", "--user=bin", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("00000000"))
session = podmanTest.Podman([]string{"exec", "--privileged", "--user=bin", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(bndPerms))
})
It("podman exec --privileged container not running as root", func() {
session := podmanTest.Podman([]string{"run", "--privileged", "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
bndPerms := session.OutputToString()
setup := podmanTest.RunTopContainerWithArgs("test-privileged", []string{"--user=bin"})
setup.WaitWithDefaultTimeout()
Expect(setup.ExitCode()).To(Equal(0))
session = podmanTest.Podman([]string{"exec", "--privileged", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("00000000"))
session = podmanTest.Podman([]string{"exec", "--privileged", "--user=bin", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("00000000"))
session = podmanTest.Podman([]string{"exec", "--privileged", "--user=root", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(bndPerms))
session = podmanTest.Podman([]string{"exec", "--privileged", "--user=bin", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(bndPerms))
})
It("podman exec with user with cap-add", func() {
capAdd := "--cap-add=net_bind_service"
session := podmanTest.Podman([]string{"run", "--user=bin", capAdd, "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
bndPerms := session.OutputToString()
session = podmanTest.Podman([]string{"run", "--user=bin", capAdd, "--rm", ALPINE, "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
effPerms := session.OutputToString()
setup := podmanTest.RunTopContainerWithArgs("test-privileged", []string{"--user=bin", capAdd})
setup.WaitWithDefaultTimeout()
Expect(setup.ExitCode()).To(Equal(0))
session = podmanTest.Podman([]string{"exec", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(bndPerms))
session = podmanTest.Podman([]string{"exec", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(effPerms))
})
It("podman exec with user with and cap-drop cap-add", func() {
capAdd := "--cap-add=net_bind_service"
capDrop := "--cap-drop=all"
session := podmanTest.Podman([]string{"run", "--user=bin", capDrop, capAdd, "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
bndPerms := session.OutputToString()
session = podmanTest.Podman([]string{"run", "--user=bin", capDrop, capAdd, "--rm", ALPINE, "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
effPerms := session.OutputToString()
setup := podmanTest.RunTopContainerWithArgs("test-privileged", []string{"--user=bin", capDrop, capAdd})
setup.WaitWithDefaultTimeout()
Expect(setup.ExitCode()).To(Equal(0))
session = podmanTest.Podman([]string{"exec", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(bndPerms))
session = podmanTest.Podman([]string{"exec", "--privileged", "test-privileged", "sh", "-c", "grep ^CapInh /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(effPerms))
session = podmanTest.Podman([]string{"exec", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(effPerms))
session = podmanTest.Podman([]string{"exec", "test-privileged", "sh", "-c", "grep ^CapPrm /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(effPerms))
session = podmanTest.Podman([]string{"exec", "test-privileged", "sh", "-c", "grep ^CapAmb /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(effPerms))
})
It("podman exec --privileged with user", func() {
session := podmanTest.Podman([]string{"run", "--privileged", "--user=bin", "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
bindPerms := session.OutputToString()
setup := podmanTest.RunTopContainerWithArgs("test-privileged", []string{"--privileged", "--user=bin"})
setup.WaitWithDefaultTimeout()
Expect(setup.ExitCode()).To(Equal(0))
session = podmanTest.Podman([]string{"exec", "--privileged", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(bindPerms))
session = podmanTest.Podman([]string{"exec", "--privileged", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("0000000000000000"))
})
It("podman exec terminal doesn't hang", func() {

View File

@ -342,6 +342,11 @@ var _ = Describe("Podman run", func() {
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("0000000000000000"))
session = podmanTest.Podman([]string{"run", "--rm", "--user", "bin", ALPINE, "grep", "CapInh", "/proc/self/status"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("0000000000000000"))
session = podmanTest.Podman([]string{"run", "--rm", "--user", "root", ALPINE, "grep", "CapBnd", "/proc/self/status"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@ -352,6 +357,11 @@ var _ = Describe("Podman run", func() {
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb"))
session = podmanTest.Podman([]string{"run", "--rm", "--user", "root", ALPINE, "grep", "CapInh", "/proc/self/status"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb"))
session = podmanTest.Podman([]string{"run", "--rm", ALPINE, "grep", "CapBnd", "/proc/self/status"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
@ -367,10 +377,10 @@ var _ = Describe("Podman run", func() {
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("0000000000000002"))
session = podmanTest.Podman([]string{"run", "--user=1000:1000", "--rm", ALPINE, "grep", "CapAmb", "/proc/self/status"})
session = podmanTest.Podman([]string{"run", "--user=1000:1000", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapInh", "/proc/self/status"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("0000000000000000"))
Expect(session.OutputToString()).To(ContainSubstring("0000000000000002"))
session = podmanTest.Podman([]string{"run", "--user=0", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapAmb", "/proc/self/status"})
session.WaitWithDefaultTimeout()
@ -382,6 +392,11 @@ var _ = Describe("Podman run", func() {
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("0000000000000000"))
session = podmanTest.Podman([]string{"run", "--user=0:0", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapInh", "/proc/self/status"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb"))
if os.Geteuid() > 0 {
if os.Getenv("SKIP_USERNS") != "" {
Skip("Skip userns tests.")
@ -393,6 +408,16 @@ var _ = Describe("Podman run", func() {
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("0000000000000002"))
session = podmanTest.Podman([]string{"run", "--userns=keep-id", "--privileged", "--rm", ALPINE, "grep", "CapInh", "/proc/self/status"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("0000000000000000"))
session = podmanTest.Podman([]string{"run", "--userns=keep-id", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapInh", "/proc/self/status"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("0000000000000002"))
}
})