diff options
-rw-r--r-- | .cirrus.yml | 2 | ||||
-rw-r--r-- | cmd/podman/common/create.go | 2 | ||||
-rw-r--r-- | cmd/podman/common/specgen.go | 6 | ||||
-rw-r--r-- | contrib/cirrus/packer/Makefile | 4 | ||||
-rw-r--r-- | contrib/cirrus/packer/fedora_packaging.sh | 5 | ||||
-rw-r--r-- | contrib/cirrus/packer/libpod_images.yml | 12 | ||||
-rw-r--r-- | contrib/cirrus/packer/ubuntu_packaging.sh | 6 | ||||
-rwxr-xr-x | contrib/cirrus/setup_environment.sh | 1 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 2 | ||||
-rw-r--r-- | pkg/specgen/container_validate.go | 4 | ||||
-rw-r--r-- | pkg/specgen/generate/oci.go | 7 | ||||
-rw-r--r-- | pkg/specgen/generate/security.go | 30 | ||||
-rw-r--r-- | test/e2e/run_apparmor_test.go | 158 |
13 files changed, 217 insertions, 22 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 9a5e0472f..37c9108eb 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -39,7 +39,7 @@ env: UBUNTU_NAME: "ubuntu-20" PRIOR_UBUNTU_NAME: "ubuntu-19" - _BUILT_IMAGE_SUFFIX: "libpod-6508632441356288" + _BUILT_IMAGE_SUFFIX: "podman-5869602141896704" FEDORA_CACHE_IMAGE_NAME: "${FEDORA_NAME}-${_BUILT_IMAGE_SUFFIX}" PRIOR_FEDORA_CACHE_IMAGE_NAME: "${PRIOR_FEDORA_NAME}-${_BUILT_IMAGE_SUFFIX}" UBUNTU_CACHE_IMAGE_NAME: "${UBUNTU_NAME}-${_BUILT_IMAGE_SUFFIX}" diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go index 46f78cdba..e248e621f 100644 --- a/cmd/podman/common/create.go +++ b/cmd/podman/common/create.go @@ -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( diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go index 731085731..e694317cc 100644 --- a/cmd/podman/common/specgen.go +++ b/cmd/podman/common/specgen.go @@ -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] diff --git a/contrib/cirrus/packer/Makefile b/contrib/cirrus/packer/Makefile index a911cafdb..c5a8e4cac 100644 --- a/contrib/cirrus/packer/Makefile +++ b/contrib/cirrus/packer/Makefile @@ -5,7 +5,8 @@ PACKER_DIST_FILENAME := packer_${PACKER_VER}_linux_${GOARCH}.zip # Only needed for libpod_base_images target TIMESTAMP := $(shell date +%s) -GOSRC ?= $(shell realpath "./../../../") +GOPATH ?= /var/tmp/go +GOSRC ?= $(GOPATH)/src/github.com/containers/libpod PACKER_BASE ?= contrib/cirrus/packer SCRIPT_BASE ?= contrib/cirrus POST_MERGE_BUCKET_SUFFIX ?= @@ -54,6 +55,7 @@ libpod_images: guard-PACKER_BUILDS libpod_images.json packer ./packer build \ -force \ $(shell test -z "${PACKER_BUILDS}" || echo "-only=${PACKER_BUILDS}") \ + -var GOPATH=$(GOPATH) \ -var GOSRC=$(GOSRC) \ -var PACKER_BASE=$(PACKER_BASE) \ -var SCRIPT_BASE=$(SCRIPT_BASE) \ diff --git a/contrib/cirrus/packer/fedora_packaging.sh b/contrib/cirrus/packer/fedora_packaging.sh index aecaaef93..b4a3a2062 100644 --- a/contrib/cirrus/packer/fedora_packaging.sh +++ b/contrib/cirrus/packer/fedora_packaging.sh @@ -74,6 +74,7 @@ INSTALL_PACKAGES=(\ gpgme-devel grubby hostname + httpd-tools iproute iptables jq @@ -168,5 +169,9 @@ fi echo "Installing runtime tooling" # Save some runtime by having these already available cd $GOSRC +# Required since initially go was not installed +source $GOSRC/$SCRIPT_BASE/lib.sh +echo "Go environment has been setup:" +go env $SUDO make install.tools $SUDO $GOSRC/hack/install_catatonit.sh diff --git a/contrib/cirrus/packer/libpod_images.yml b/contrib/cirrus/packer/libpod_images.yml index 754626a2e..38f5a8250 100644 --- a/contrib/cirrus/packer/libpod_images.yml +++ b/contrib/cirrus/packer/libpod_images.yml @@ -3,6 +3,7 @@ # All of these are required variables: BUILT_IMAGE_SUFFIX: '{{env `BUILT_IMAGE_SUFFIX`}}' + GOPATH: '{{env `GOPATH`}}' GOSRC: '{{env `GOSRC`}}' PACKER_BASE: '{{env `PACKER_BASE`}}' SCRIPT_BASE: '{{env `SCRIPT_BASE`}}' @@ -62,15 +63,22 @@ builders: # The brains of the operation, making actual modifications to the base-image. provisioners: + - type: 'shell' + inline: + - 'set -ex' + # The 'file' provisioner item (below) will create the final component + - 'mkdir -vp $(dirname {{user `GOSRC`}})' + - type: 'file' source: '{{user `GOSRC`}}' - destination: '/tmp/libpod' + destination: '{{user `GOSRC`}}' - type: 'shell' script: '{{user `GOSRC`}}/{{user `PACKER_BASE`}}/{{split build_name "-" 0}}_setup.sh' environment_vars: - 'PACKER_BUILDER_NAME={{build_name}}' - - 'GOSRC=/tmp/libpod' + - 'GOPATH={{user `GOPATH`}}' + - 'GOSRC={{user `GOSRC`}}' - 'PACKER_BASE={{user `PACKER_BASE`}}' - 'SCRIPT_BASE={{user `SCRIPT_BASE`}}' diff --git a/contrib/cirrus/packer/ubuntu_packaging.sh b/contrib/cirrus/packer/ubuntu_packaging.sh index 09f9aab9f..d11c612c5 100644 --- a/contrib/cirrus/packer/ubuntu_packaging.sh +++ b/contrib/cirrus/packer/ubuntu_packaging.sh @@ -36,6 +36,7 @@ ooe.sh curl -L -o /tmp/Release.key "https://download.opensuse.org/repositories/d ooe.sh $SUDO apt-key add - < /tmp/Release.key INSTALL_PACKAGES=(\ + apache2-utils apparmor aufs-tools autoconf @@ -153,7 +154,12 @@ if [[ ${#DOWNLOAD_PACKAGES[@]} -gt 0 ]]; then fi echo "Installing runtime tooling" +# Save some runtime by having these already available cd $GOSRC +# Required since initially go was not installed +source $GOSRC/$SCRIPT_BASE/lib.sh +echo "Go environment has been setup:" +go env $SUDO hack/install_catatonit.sh $SUDO make install.libseccomp.sudo $SUDO make install.tools diff --git a/contrib/cirrus/setup_environment.sh b/contrib/cirrus/setup_environment.sh index ea2c7d8e0..fbdae83fa 100755 --- a/contrib/cirrus/setup_environment.sh +++ b/contrib/cirrus/setup_environment.sh @@ -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 diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index edea62a0d..09bf33728 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -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 diff --git a/pkg/specgen/container_validate.go b/pkg/specgen/container_validate.go index 622313a04..eddc4ad5d 100644 --- a/pkg/specgen/container_validate.go +++ b/pkg/specgen/container_validate.go @@ -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") diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index 4953735b1..f279aac1c 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -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 { diff --git a/pkg/specgen/generate/security.go b/pkg/specgen/generate/security.go index 70493cd5f..fcd1622f9 100644 --- a/pkg/specgen/generate/security.go +++ b/pkg/specgen/generate/security.go @@ -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" @@ -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 @@ -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 diff --git a/test/e2e/run_apparmor_test.go b/test/e2e/run_apparmor_test.go new file mode 100644 index 000000000..344309b93 --- /dev/null +++ b/test/e2e/run_apparmor_test.go @@ -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("")) + }) +}) |