github.com/containers/podman/v4@v4.9.4/test/e2e/run_privileged_test.go (about)

     1  package integration
     2  
     3  import (
     4  	"os"
     5  	"strconv"
     6  	"strings"
     7  
     8  	. "github.com/containers/podman/v4/test/utils"
     9  	. "github.com/onsi/ginkgo/v2"
    10  	. "github.com/onsi/gomega"
    11  	"github.com/syndtr/gocapability/capability"
    12  )
    13  
    14  // helper function for confirming that container capabilities are equal
    15  // to those of the host, but only to the extent of caps we (podman)
    16  // know about at compile time. That is: the kernel may have more caps
    17  // available than we are aware of, leading to host=FFF... and ctr=3FF...
    18  // because the latter is all we request. Accept that.
    19  func containerCapMatchesHost(ctrCap string, hostCap string) {
    20  	if isRootless() {
    21  		return
    22  	}
    23  	ctrCapN, err := strconv.ParseUint(ctrCap, 16, 64)
    24  	Expect(err).NotTo(HaveOccurred(), "Error parsing %q as hex", ctrCap)
    25  
    26  	hostCapN, err := strconv.ParseUint(hostCap, 16, 64)
    27  	Expect(err).NotTo(HaveOccurred(), "Error parsing %q as hex", hostCap)
    28  
    29  	// host caps can never be zero (except rootless).
    30  	// and host caps must always be a superset (inclusive) of container
    31  	Expect(hostCapN).To(BeNumerically(">", 0), "host cap %q should be nonzero", hostCap)
    32  	Expect(hostCapN).To(BeNumerically(">=", ctrCapN), "host cap %q should never be less than container cap %q", hostCap, ctrCap)
    33  	hostCapMasked := hostCapN & (1<<len(capability.List()) - 1)
    34  	Expect(ctrCapN).To(Equal(hostCapMasked), "container cap %q is not a subset of host cap %q", ctrCap, hostCap)
    35  }
    36  
    37  var _ = Describe("Podman privileged container tests", func() {
    38  
    39  	It("podman privileged make sure sys is mounted rw", func() {
    40  		session := podmanTest.Podman([]string{"run", "--privileged", BB, "mount"})
    41  		session.WaitWithDefaultTimeout()
    42  		Expect(session).Should(ExitCleanly())
    43  		Expect(session.OutputToString()).To(ContainSubstring("sysfs (rw,"))
    44  	})
    45  
    46  	It("podman privileged CapEff", func() {
    47  		hostCap := SystemExec("awk", []string{"/^CapEff/ { print $2 }", "/proc/self/status"})
    48  		Expect(hostCap).Should(ExitCleanly())
    49  
    50  		session := podmanTest.Podman([]string{"run", "--privileged", BB, "awk", "/^CapEff/ { print $2 }", "/proc/self/status"})
    51  		session.WaitWithDefaultTimeout()
    52  		Expect(session).Should(ExitCleanly())
    53  
    54  		containerCapMatchesHost(session.OutputToString(), hostCap.OutputToString())
    55  	})
    56  
    57  	It("podman cap-add CapEff", func() {
    58  		// Get caps of current process
    59  		hostCap := SystemExec("awk", []string{"/^CapEff/ { print $2 }", "/proc/self/status"})
    60  		Expect(hostCap).Should(ExitCleanly())
    61  
    62  		session := podmanTest.Podman([]string{"run", "--cap-add", "all", BB, "awk", "/^CapEff/ { print $2 }", "/proc/self/status"})
    63  		session.WaitWithDefaultTimeout()
    64  		Expect(session).Should(ExitCleanly())
    65  
    66  		containerCapMatchesHost(session.OutputToString(), hostCap.OutputToString())
    67  	})
    68  
    69  	It("podman cap-add CapEff with --user", func() {
    70  		// Get caps of current process
    71  		hostCap := SystemExec("awk", []string{"/^CapEff/ { print $2 }", "/proc/self/status"})
    72  		Expect(hostCap).Should(ExitCleanly())
    73  
    74  		session := podmanTest.Podman([]string{"run", "--user=bin", "--cap-add", "all", BB, "awk", "/^CapEff/ { print $2 }", "/proc/self/status"})
    75  		session.WaitWithDefaultTimeout()
    76  		Expect(session).Should(ExitCleanly())
    77  
    78  		containerCapMatchesHost(session.OutputToString(), hostCap.OutputToString())
    79  	})
    80  
    81  	It("podman cap-drop CapEff", func() {
    82  		session := podmanTest.Podman([]string{"run", "--cap-drop", "all", BB, "grep", "CapEff", "/proc/self/status"})
    83  		session.WaitWithDefaultTimeout()
    84  		Expect(session).Should(ExitCleanly())
    85  		capEff := strings.Split(session.OutputToString(), " ")
    86  		Expect("0000000000000000").To(Equal(capEff[1]))
    87  	})
    88  
    89  	It("podman privileged should disable seccomp by default", func() {
    90  		hostSeccomp := SystemExec("grep", []string{"-Ei", "^Seccomp:\\s+0$", "/proc/self/status"})
    91  		Expect(hostSeccomp).Should(ExitCleanly())
    92  
    93  		session := podmanTest.Podman([]string{"run", "--privileged", ALPINE, "grep", "-Ei", "^Seccomp:\\s+0$", "/proc/self/status"})
    94  		session.WaitWithDefaultTimeout()
    95  		Expect(session).Should(ExitCleanly())
    96  	})
    97  
    98  	It("podman non-privileged should have very few devices", func() {
    99  		session := podmanTest.Podman([]string{"run", "-t", BB, "ls", "-l", "/dev"})
   100  		session.WaitWithDefaultTimeout()
   101  		Expect(session).Should(ExitCleanly())
   102  		Expect(session.OutputToStringArray()).To(HaveLen(17))
   103  	})
   104  
   105  	It("podman privileged should inherit host devices", func() {
   106  		session := podmanTest.Podman([]string{"run", "--privileged", ALPINE, "ls", "-l", "/dev"})
   107  		session.WaitWithDefaultTimeout()
   108  		Expect(session).Should(ExitCleanly())
   109  		Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 20))
   110  	})
   111  
   112  	It("podman privileged should restart after host devices change", func() {
   113  		containerName := "privileged-restart-test"
   114  		SkipIfRootless("Cannot create devices in /dev in rootless mode")
   115  		// path must be unique to this test, not used anywhere else
   116  		devdir := "/dev/devdirprivrestart"
   117  		Expect(os.MkdirAll(devdir, os.ModePerm)).To(Succeed())
   118  		defer os.RemoveAll(devdir)
   119  
   120  		mknod := SystemExec("mknod", []string{devdir + "/null", "c", "1", "3"})
   121  		mknod.WaitWithDefaultTimeout()
   122  		Expect(mknod).Should(ExitCleanly())
   123  
   124  		session := podmanTest.Podman([]string{"run", "--name=" + containerName, "--privileged", fedoraMinimal, "ls", "/dev"})
   125  		session.WaitWithDefaultTimeout()
   126  		Expect(session).Should(ExitCleanly())
   127  
   128  		deviceFiles := session.OutputToStringArray()
   129  
   130  		os.RemoveAll(devdir)
   131  		session = podmanTest.Podman([]string{"start", "--attach", containerName})
   132  		session.WaitWithDefaultTimeout()
   133  		Expect(session).Should(ExitCleanly())
   134  
   135  		deviceFilesAfterRemoval := session.OutputToStringArray()
   136  		Expect(deviceFiles).To(Not(Equal(deviceFilesAfterRemoval)))
   137  	})
   138  
   139  	It("run no-new-privileges test", func() {
   140  		// Check if our kernel is new enough
   141  		k, err := IsKernelNewerThan("4.14")
   142  		Expect(err).ToNot(HaveOccurred())
   143  		if !k {
   144  			Skip("Kernel is not new enough to test this feature")
   145  		}
   146  
   147  		cap := SystemExec("grep", []string{"NoNewPrivs", "/proc/self/status"})
   148  		if cap.ExitCode() != 0 {
   149  			Skip("Can't determine NoNewPrivs")
   150  		}
   151  
   152  		session := podmanTest.Podman([]string{"run", BB, "grep", "NoNewPrivs", "/proc/self/status"})
   153  		session.WaitWithDefaultTimeout()
   154  		Expect(session).Should(ExitCleanly())
   155  
   156  		privs := strings.Split(session.OutputToString(), ":")
   157  		session = podmanTest.Podman([]string{"run", "--security-opt", "no-new-privileges", BB, "grep", "NoNewPrivs", "/proc/self/status"})
   158  		session.WaitWithDefaultTimeout()
   159  		Expect(session).Should(ExitCleanly())
   160  
   161  		noprivs := strings.Split(session.OutputToString(), ":")
   162  		Expect(privs[1]).To(Not(Equal(noprivs[1])))
   163  	})
   164  
   165  })