Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support default profile for apparmor #6992

Merged
merged 1 commit into from
Jul 22, 2020
Merged
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
2 changes: 1 addition & 1 deletion cmd/podman/common/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet {
)
createFlags.StringArrayVar(
&cf.SecurityOpt,
"security-opt", containerConfig.SecurityOptions(),
"security-opt", []string{},
"Security Options",
)
createFlags.String(
Expand Down
6 changes: 2 additions & 4 deletions cmd/podman/common/specgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -512,10 +512,8 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
s.ContainerSecurityConfig.SelinuxOpts = append(s.ContainerSecurityConfig.SelinuxOpts, con[1])
s.Annotations[define.InspectAnnotationLabel] = strings.Join(s.ContainerSecurityConfig.SelinuxOpts, ",label=")
case "apparmor":
if !c.Privileged {
s.ContainerSecurityConfig.ApparmorProfile = con[1]
s.Annotations[define.InspectAnnotationApparmor] = con[1]
}
s.ContainerSecurityConfig.ApparmorProfile = con[1]
s.Annotations[define.InspectAnnotationApparmor] = con[1]
case "seccomp":
s.SeccompProfilePath = con[1]
s.Annotations[define.InspectAnnotationSeccomp] = con[1]
Expand Down
1 change: 0 additions & 1 deletion contrib/cirrus/setup_environment.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ case "${OS_RELEASE_ID}" in
ubuntu)
apt-get update
apt-get install -y containers-common
sed -ie 's/^\(# \)\?apparmor_profile =.*/apparmor_profile = ""/' /etc/containers/containers.conf
if [[ "$OS_RELEASE_VER" == "19" ]]; then
apt-get purge -y --auto-remove golang*
apt-get install -y golang-1.13
Expand Down
2 changes: 1 addition & 1 deletion libpod/container_internal_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
}

// Apply AppArmor checks and load the default profile if needed.
if !c.config.Privileged {
if len(c.config.Spec.Process.ApparmorProfile) > 0 {
updatedProfile, err := apparmor.CheckProfileAndLoadDefault(c.config.Spec.Process.ApparmorProfile)
if err != nil {
return nil, err
Expand Down
4 changes: 0 additions & 4 deletions pkg/specgen/container_validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,6 @@ func (s *SpecGenerator) Validate() error {
if len(s.CapAdd) > 0 && s.Privileged {
return exclusiveOptions("CapAdd", "privileged")
}
// apparmor and privileged are exclusive
if len(s.ApparmorProfile) > 0 && s.Privileged {
return exclusiveOptions("AppArmorProfile", "privileged")
}
// userns and idmappings conflict
if s.UserNS.IsPrivate() && s.IDMappings == nil {
return errors.Wrap(ErrInvalidSpecConfig, "IDMappings are required when not creating a User namespace")
Expand Down
7 changes: 0 additions & 7 deletions pkg/specgen/generate/oci.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,13 +285,6 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt
}
}

// SECURITY OPTS
g.SetProcessNoNewPrivileges(s.NoNewPrivileges)

if !s.Privileged {
g.SetProcessApparmorProfile(s.ApparmorProfile)
}

BlockAccessToKernelFilesystems(s.Privileged, s.PidNS.IsHost(), &g)

for name, val := range s.Env {
Expand Down
30 changes: 30 additions & 0 deletions pkg/specgen/generate/security.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package generate
import (
"strings"

"github.com/containers/common/pkg/apparmor"
"github.com/containers/common/pkg/capabilities"
"github.com/containers/common/pkg/config"
"github.com/containers/libpod/v2/libpod"
Expand Down Expand Up @@ -56,6 +57,28 @@ func setLabelOpts(s *specgen.SpecGenerator, runtime *libpod.Runtime, pidConfig s
return nil
}

func setupApparmor(s *specgen.SpecGenerator, rtc *config.Config, g *generate.Generator) error {
hasProfile := len(s.ApparmorProfile) > 0
if !apparmor.IsEnabled() {
if hasProfile {
return errors.Errorf("Apparmor profile %q specified, but Apparmor is not enabled on this system", s.ApparmorProfile)
}
return nil
}
// If privileged and caller did not specify apparmor profiles return
if s.Privileged && !hasProfile {
return nil
}
if !hasProfile {
s.ApparmorProfile = rtc.Containers.ApparmorProfile
}
if len(s.ApparmorProfile) > 0 {
g.SetProcessApparmorProfile(s.ApparmorProfile)
}

return nil
}

func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, newImage *image.Image, rtc *config.Config) error {
var (
caplist []string
Expand Down Expand Up @@ -105,6 +128,13 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator,
}
}
}

g.SetProcessNoNewPrivileges(s.NoNewPrivileges)

if err := setupApparmor(s, rtc, g); err != nil {
return err
}

configSpec := g.Config
configSpec.Process.Capabilities.Bounding = caplist

Expand Down
158 changes: 158 additions & 0 deletions test/e2e/run_apparmor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// +build !remote

package integration

import (
"fmt"
"io/ioutil"
"os"
"path/filepath"

"github.com/containers/common/pkg/apparmor"
. "github.com/containers/libpod/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

func skipIfAppArmorEnabled() {
if apparmor.IsEnabled() {
Skip("Apparmor is enabled")
}
}
func skipIfAppArmorDisabled() {
if !apparmor.IsEnabled() {
Skip("Apparmor is not enabled")
}
}

var _ = Describe("Podman run", func() {
var (
tempdir string
err error
podmanTest *PodmanTestIntegration
)

BeforeEach(func() {
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
}
podmanTest = PodmanTestCreate(tempdir)
podmanTest.Setup()
podmanTest.SeedImages()
})

AfterEach(func() {
podmanTest.Cleanup()
f := CurrentGinkgoTestDescription()
processTestResult(f)

})

It("podman run apparmor default", func() {
skipIfAppArmorDisabled()
session := podmanTest.Podman([]string{"create", ALPINE, "ls"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))

cid := session.OutputToString()
// Verify that apparmor.Profile is being set
inspect := podmanTest.InspectContainer(cid)
Expect(inspect[0].AppArmorProfile).To(Equal(apparmor.Profile))
})

It("podman run no apparmor --privileged", func() {
skipIfAppArmorDisabled()
session := podmanTest.Podman([]string{"create", "--privileged", ALPINE, "ls"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))

cid := session.OutputToString()
// Verify that apparmor.Profile is being set
inspect := podmanTest.InspectContainer(cid)
Expect(inspect[0].AppArmorProfile).To(Equal(""))
})

It("podman run no apparmor --security-opt=apparmor.Profile --privileged", func() {
skipIfAppArmorDisabled()
session := podmanTest.Podman([]string{"create", "--security-opt", fmt.Sprintf("apparmor=%s", apparmor.Profile), "--privileged", ALPINE, "ls"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))

cid := session.OutputToString()
// Verify that apparmor.Profile is being set
inspect := podmanTest.InspectContainer(cid)
Expect(inspect[0].AppArmorProfile).To(Equal(apparmor.Profile))
})

It("podman run apparmor aa-test-profile", func() {
skipIfAppArmorDisabled()
aaProfile := `
#include <tunables/global>
profile aa-test-profile flags=(attach_disconnected,mediate_deleted) {
#include <abstractions/base>
deny mount,
deny /sys/[^f]*/** wklx,
deny /sys/f[^s]*/** wklx,
deny /sys/fs/[^c]*/** wklx,
deny /sys/fs/c[^g]*/** wklx,
deny /sys/fs/cg[^r]*/** wklx,
deny /sys/firmware/efi/efivars/** rwklx,
deny /sys/kernel/security/** rwklx,
}
`
aaFile := filepath.Join(os.TempDir(), "aaFile")
Expect(ioutil.WriteFile(aaFile, []byte(aaProfile), 0755)).To(BeNil())
parse := SystemExec("apparmor_parser", []string{"-Kr", aaFile})
Expect(parse.ExitCode()).To(Equal(0))

session := podmanTest.Podman([]string{"create", "--security-opt", "apparmor=aa-test-profile", "ls"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))

cid := session.OutputToString()
// Verify that apparmor.Profile is being set
inspect := podmanTest.InspectContainer(cid)
Expect(inspect[0].AppArmorProfile).To(Equal("aa-test-profile"))
})

It("podman run apparmor invalid", func() {
skipIfAppArmorDisabled()
session := podmanTest.Podman([]string{"run", "--security-opt", "apparmor=invalid", ALPINE, "ls"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).ToNot(Equal(0))
})

It("podman run apparmor unconfined", func() {
skipIfAppArmorDisabled()
session := podmanTest.Podman([]string{"create", "--security-opt", "apparmor=unconfined", ALPINE, "ls"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))

cid := session.OutputToString()
// Verify that apparmor.Profile is being set
inspect := podmanTest.InspectContainer(cid)
Expect(inspect[0].AppArmorProfile).To(Equal("unconfined"))
})

It("podman run apparmor disabled --security-opt apparmor fails", func() {
skipIfAppArmorEnabled()
// Should fail if user specifies apparmor on disabled system
session := podmanTest.Podman([]string{"create", "--security-opt", fmt.Sprintf("apparmor=%s", apparmor.Profile), ALPINE, "ls"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).ToNot(Equal(0))
})

It("podman run apparmor disabled no default", func() {
skipIfAppArmorEnabled()
// Should succeed if user specifies apparmor on disabled system
session := podmanTest.Podman([]string{"create", ALPINE, "ls"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))

cid := session.OutputToString()
// Verify that apparmor.Profile is being set
inspect := podmanTest.InspectContainer(cid)
Expect(inspect[0].AppArmorProfile).To(Equal(""))
})
})