Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions gqt/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ type GdnRunnerConfig struct {
CleanupProcessDirsOnWait *bool `flag:"cleanup-process-dirs-on-wait"`
DisablePrivilegedContainers *bool `flag:"disable-privileged-containers"`
AppArmor string `flag:"apparmor"`
NoNewPrivileges *bool `flag:"no-new-privileges"`
Tag string `flag:"tag"`
NetworkPool string `flag:"network-pool"`
ContainerdSocket string `flag:"containerd-socket"`
Expand Down
105 changes: 105 additions & 0 deletions gqt/security_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,111 @@ var _ = Describe("Security", func() {
})
})

Describe("NoNewPrivileges", func() {
Context("when the --no-new-privileges flag is set", func() {
BeforeEach(func() {
config.NoNewPrivileges = boolptr(true)
})

Context("when running processes in unprivileged containers", func() {
var (
container garden.Container
err error
)

JustBeforeEach(func() {
container, err = client.Create(garden.ContainerSpec{})
Expect(err).NotTo(HaveOccurred())
})

It("should set NoNewPrivileges on the process", func() {
buffer := gbytes.NewBuffer()

_, err = container.Run(garden.ProcessSpec{
Path: "grep",
Args: []string{"NoNewPrivs", "/proc/self/status"},
}, garden.ProcessIO{
Stdout: io.MultiWriter(GinkgoWriter, buffer),
Stderr: GinkgoWriter,
})
Expect(err).NotTo(HaveOccurred())

Eventually(buffer).Should(gbytes.Say(`NoNewPrivs:\s+1`))
})

Context("when running a pea", func() {
var peaRootfs string

BeforeEach(func() {
peaRootfs = createPeaRootfsTar()
})

AfterEach(func() {
Expect(os.RemoveAll(filepath.Dir(peaRootfs))).To(Succeed())
})

It("should set NoNewPrivileges on the process", func() {
buffer := gbytes.NewBuffer()

_, err = container.Run(garden.ProcessSpec{
Path: "grep",
Args: []string{"NoNewPrivs", "/proc/self/status"},
Image: garden.ImageRef{URI: peaRootfs},
}, garden.ProcessIO{
Stdout: io.MultiWriter(GinkgoWriter, buffer),
Stderr: GinkgoWriter,
})
Expect(err).NotTo(HaveOccurred())

Eventually(buffer).Should(gbytes.Say(`NoNewPrivs:\s+1`))
})
})
})

Context("when running processes in privileged containers", func() {
It("should not set NoNewPrivileges", func() {
container, err := client.Create(garden.ContainerSpec{
Privileged: true,
})
Expect(err).NotTo(HaveOccurred())

buffer := gbytes.NewBuffer()

_, err = container.Run(garden.ProcessSpec{
Path: "grep",
Args: []string{"NoNewPrivs", "/proc/self/status"},
}, garden.ProcessIO{
Stdout: io.MultiWriter(GinkgoWriter, buffer),
Stderr: GinkgoWriter,
})
Expect(err).NotTo(HaveOccurred())

Eventually(buffer).Should(gbytes.Say(`NoNewPrivs:\s+0`))
})
})
})

Context("when the --no-new-privileges flag is not set", func() {
It("should not set NoNewPrivileges on processes in unprivileged containers", func() {
container, err := client.Create(garden.ContainerSpec{})
Expect(err).NotTo(HaveOccurred())

buffer := gbytes.NewBuffer()

_, err = container.Run(garden.ProcessSpec{
Path: "grep",
Args: []string{"NoNewPrivs", "/proc/self/status"},
}, garden.ProcessIO{
Stdout: io.MultiWriter(GinkgoWriter, buffer),
Stderr: GinkgoWriter,
})
Expect(err).NotTo(HaveOccurred())

Eventually(buffer).Should(gbytes.Say(`NoNewPrivs:\s+0`))
})
})
})

Describe("ptrace in seccomp allow rules", func() {
It("should allow the ptrace syscall without CAP_SYS_PTRACE", func() {
container, err := client.Create(garden.ContainerSpec{
Expand Down
5 changes: 5 additions & 0 deletions guardiancmd/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ type CommonCommand struct {
DefaultGraceTime time.Duration `long:"default-grace-time" description:"Default time after which idle containers should expire."`
DestroyContainersOnStartup bool `long:"destroy-containers-on-startup" description:"Clean up all the existing containers on startup."`
ApparmorProfile string `long:"apparmor" description:"Apparmor profile to use for unprivileged container processes"`
NoNewPrivileges bool `long:"no-new-privileges" description:"Set NoNewPrivileges on unprivileged container processes"`
} `group:"Container Lifecycle"`

Bin struct {
Expand Down Expand Up @@ -527,6 +528,10 @@ func (cmd *CommonCommand) wireContainerizer(
}
unprivilegedBundle.Spec.Linux.Seccomp = seccomp

if cmd.Containers.NoNewPrivileges {
unprivilegedBundle.Spec.Process.NoNewPrivileges = true
}

if cmd.Containers.ApparmorProfile != "" {
unprivilegedBundle = unprivilegedBundle.WithApparmorProfile(cmd.Containers.ApparmorProfile)
}
Expand Down
1 change: 1 addition & 0 deletions rundmc/processes/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ func (p *ProcBuilder) BuildProcess(bndl goci.Bndl, spec garden.ProcessSpec, user
Rlimits: toRlimits(spec.Limits),
Terminal: spec.TTY != nil,
ApparmorProfile: bndl.Process().ApparmorProfile,
NoNewPrivileges: bndl.Process().NoNewPrivileges,
}
}

Expand Down
14 changes: 14 additions & 0 deletions rundmc/processes/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,20 @@ var _ = Describe("ProcBuilder", func() {
Expect(preparedProc.ApparmorProfile).To(Equal("default-profile"))
})

It("passes NoNewPrivileges from the bundle", func() {
Expect(preparedProc.NoNewPrivileges).To(BeFalse())
})

Context("when the bundle has NoNewPrivileges set", func() {
BeforeEach(func() {
bndl.Spec.Process.NoNewPrivileges = true
})

It("propagates NoNewPrivileges to the process", func() {
Expect(preparedProc.NoNewPrivileges).To(BeTrue())
})
})

It("passes the UID, GID and SGIDs", func() {
Expect(preparedProc.User.UID).To(Equal(uint32(1)))
Expect(preparedProc.User.GID).To(Equal(uint32(2)))
Expand Down