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

Default AppArmor profile never loaded & apparmor.IsEnabled() always returns false when rootless #15874

Open
kernelmethod opened this issue Sep 20, 2022 · 15 comments
Labels
kind/bug Categorizes issue or PR as related to a bug.

Comments

@kernelmethod
Copy link

Is this a BUG REPORT or FEATURE REQUEST? (leave only one on its own line)

/kind bug

Description

This is related to a bug I previously reported in containers/common#958. This is really a bug in that library but it's impossible to resolve (without breakage) without making changes to Podman.

There is nominally a default AppArmor profile that gets applied to containers running on AA-supported systems, however, the way that this is currently implemented results in the profile only getting applied to containers that are run as root.

Specifically, apparmor.IsEnabled() calls IsSupported under the hood, and only returns true if the user is running as root:

func (a *ApparmorVerifier) IsSupported() error {
if a.impl.UnshareIsRootless() {
return errors.New("AppAmor is not supported on rootless containers")
}
if !a.impl.RuncIsEnabled() {
return errors.New("AppArmor not supported by the host system")
}
_, err := a.FindAppArmorParserBinary()
return err
}

This in turn causes setupApparmor to skip setting the profile to the default, and so any containers that are run rootlessly are run without any kind of LSM confinement.

The easy way to solve this problem would simply be to fix the apparmor.IsEnabled() function (which in fact I previously tried to do, incorrectly, in containers/common#960), however, there is no way to load a default AppArmor profile that doesn't require root privileges. As such, it seems that fixing this issue would require identifying a method for Podman to load the default profile prior to trying to run the container rootlessly.

Steps to reproduce the issue:

  • On a system using AppArmor, start a new container and inspect the profile that it is running under:
$ ./bin/podman run --rm -d docker.io/library/alpine:latest sleep 5; ps -eo user,label,comm | grep sleep
f2aa019411a1f0f9097fd0647b70820a405b8bd0fa8f298570c8be677e5932ff
kernelm+ unconfined                      sleep
  • Compare against the profile that is enforced when running as root:
$ sudo ./bin/podman run --rm -d docker.io/library/alpine:latest sleep 5; ps -eo user,label,comm | grep sleep
ec3bd60ff00d6156d2a927bc783214af42db3ee9af8fd8d5fbd40e67e670424f
root     containers-default-0.49.2-dev (enforce) sleep

This second command will load the default profile into the kernel, although Podman still won't use that profile for rootless containers.

Describe the results you received: the rootless container runs without AppArmor confinement while the rootful container is confined.

Describe the results you expected: both containers should run with confinement.

Additional information you deem important (e.g. issue happens only occasionally): N/A

Output of podman version:

$ ./bin/podman version
Client:       Podman Engine
Version:      4.3.0-dev
API Version:  4.3.0-dev
Go Version:   go1.19.1
Git Commit:   30231d0da7e6dcf3d6d1f45b10150baae35aaf28
Built:        Tue Sep 20 22:23:35 2022
OS/Arch:      linux/amd64

Output of podman info:

host:
  arch: amd64
  buildahVersion: 1.28.0-dev
  cgroupControllers:
  - memory
  - pids
  cgroupManager: systemd
  cgroupVersion: v2
  conmon:
    package: conmon_2.0.25+ds1-1.1_amd64
    path: /usr/bin/conmon
    version: 'conmon version 2.0.25, commit: unknown'
  cpuUtilization:
    idlePercent: 97.06
    systemPercent: 0.68
    userPercent: 2.27
  cpus: 4
  distribution:
    codename: bullseye
    distribution: debian
    version: "11"
  eventLogger: journald
  hostname: debdev
  idMappings:
    gidmap:
    - container_id: 0
      host_id: 1000
      size: 1
    - container_id: 1
      host_id: 100000
      size: 65536
    uidmap:
    - container_id: 0
      host_id: 1000
      size: 1
    - container_id: 1
      host_id: 100000
      size: 65536
  kernel: 5.10.0-18-amd64
  linkmode: dynamic
  logDriver: journald
  memFree: 6671405056
  memTotal: 8343564288
  networkBackend: cni
  ociRuntime:
    name: crun
    package: crun_0.17+dfsg-1_amd64
    path: /usr/bin/crun
    version: |-
      crun version 0.17
      commit: 0e9229ae34caaebcb86f1fde18de3acaf18c6d9a
      spec: 1.0.0
      +SYSTEMD +SELINUX +APPARMOR +CAP +SECCOMP +EBPF +YAJL
  os: linux
  remoteSocket:
    exists: true
    path: /run/user/1000/podman/podman.sock
  security:
    apparmorEnabled: false
    capabilities: CAP_CHOWN,CAP_DAC_OVERRIDE,CAP_FOWNER,CAP_FSETID,CAP_KILL,CAP_NET_BIND_SERVICE,CAP_SETFCAP,CAP_SETGID,CAP_SETPCAP,CAP_SETUID,CAP_SYS_CHROOT
    rootless: true
    seccompEnabled: true
    seccompProfilePath: ""
    selinuxEnabled: false
  serviceIsRemote: false
  slirp4netns:
    executable: /usr/bin/slirp4netns
    package: slirp4netns_1.0.1-2_amd64
    version: |-
      slirp4netns version 1.0.1
      commit: 6a7b16babc95b6a3056b33fb45b74a6f62262dd4
      libslirp: 4.4.0
  swapFree: 0
  swapTotal: 0
  uptime: 0h 31m 25.00s
plugins:
  authorization: null
  log:
  - k8s-file
  - none
  - passthrough
  - journald
  network:
  - bridge
  - macvlan
  - ipvlan
  volume:
  - local
registries: {}
store:
  configFile: /home/kernelmethod/.config/containers/storage.conf
  containerStore:
    number: 0
    paused: 0
    running: 0
    stopped: 0
  graphDriverName: vfs
  graphOptions: {}
  graphRoot: /home/kernelmethod/.local/share/containers/storage
  graphRootAllocated: 270385668096
  graphRootUsed: 9269370880
  graphStatus: {}
  imageCopyTmpDir: /var/tmp
  imageStore:
    number: 2
  runRoot: /run/user/1000/containers
  volumePath: /home/kernelmethod/.local/share/containers/storage/volumes
version:
  APIVersion: 4.3.0-dev
  Built: 1663712615
  BuiltTime: Tue Sep 20 22:23:35 2022
  GitCommit: 30231d0da7e6dcf3d6d1f45b10150baae35aaf28
  GoVersion: go1.19.1
  Os: linux
  OsArch: linux/amd64
  Version: 4.3.0-dev

Package info (e.g. output of rpm -q podman or apt list podman):

N/A (I have been testing this with a build of the latest version of Podman)

Have you tested with the latest version of Podman and have you checked the Podman Troubleshooting Guide? (https://github.com/containers/podman/blob/main/troubleshooting.md)

Yes

Additional environment details (AWS, VirtualBox, physical, etc.):

None

@openshift-ci openshift-ci bot added the kind/bug Categorizes issue or PR as related to a bug. label Sep 20, 2022
@kernelmethod
Copy link
Author

kernelmethod commented Sep 20, 2022

I ran into this issue while trying to write a fix for AppArmor confinement without root (containers/common#960 was reverted due to some mistakes on my end, see containers/common#969). It would seem like the easiest way to fix this problem would be to either

  1. add a new profile to /etc/apparmor.d upon installing Podman, or
  2. add a subcommand, e.g. podman system apparmor, that requires root privileges and dynamically generates and loads this profile into the kernel.

(I suppose an option that could be combined with (2) would be to install a systemd service to dynamically generate and load the profile into the kernel after boot.)

The more straightforward route (which would also conform to the "normal" method for AppArmor administration) would be to do (1), but I'm unsure of whether or not Podman strictly needs to be a able to dynamically generate and load the AppArmor profile at runtime.

I'm happy to work on a fix for this issue but I'm not sure what the preferred way of loading a default profile would be from your perspective.

@rhatdan
Copy link
Member

rhatdan commented Sep 23, 2022

I would go with 1, Podman can be told to load a different profile via --security-opt apparmor:PROFILE, I believe.

But having a default one would help out rootless users.
Then again I am just an SELinux guy.

@vrothberg @saschagrunert WDYT?

@vrothberg
Copy link
Member

  1. looks like a good way forward but I do not have the time to think this through entirely at the moment. It's probably something distributions should do.

@vrothberg
Copy link
Member

To facilitate packaging, I think we should provide a generated profile in c/common/pkg/apparmor.

Cc: @siretart (Debian/Ubuntu) @sysrich (openSUSE)

@github-actions
Copy link

A friendly reminder that this issue had no activity for 30 days.

@yveszoundi
Copy link

I really like how this functionality is delivered with nerdctl, just from a pure user perspective.

  • It doesn't try to guess or assume much
  • It is flexible enough (I think) for most people
sudo apparmor_parser -r -W /etc/apparmord.d/usr.bin.mystuff
sudo nerdctl apparmor load
nerdctl run --security-opt apparmor=/usr/bin/mystuff docker.io/mystuff /usr/bin/mystuff

Once apparmor load is called, nerdctl becomes aware of ALL loaded apparmor profiles, including possibly a "default podman apparmor profile" already loaded on startup (appamor systemd service).

My use case

  • I'm offering a Live CD for a free document sanitizer solution
  • I'm preloading a container image on the Live CD
  • podman is running the "sanitizer program" inside a container (custom seccomp profile)
  • I now want to add a custom apparmor profile to further restricts application permissions: I would load all the related apparmor initialization logic in a systemd service

nerdctl does address my needs, but apparently containerd needs mandatory data duplication via "snapshots". This doubles both the ISO image size and minimum RAM requirements for my application...

@XSpielinbox
Copy link

We are also running Podman on Ubuntu with enabled AppArmor. I would also appreciate if rootless containers would get confined with some AppArmor profile by default or at least one would have the option to.

When starting a container with --security-opt apparmor=my-custom-profile, I get the error message Error: Apparmor profile "my-custom-profile" specified, but Apparmor is not enabled on this system.
aa-status does prove however that the profile is loaded, AppArmor is enabled and other processes are running confined, even in enforcing mode (as can also be proven with ps aux -Z).
The only thing I could find is this Reddit thread, stating, this is an unsolved Apparmor issue. I was unable to find the container-default profile that containers/common shall be applying by default.

@rhatdan
Copy link
Member

rhatdan commented May 30, 2023

Are you using crun or runc? If one fails could you try the other?

@XSpielinbox
Copy link

I so far have only been using crun. I will test runc as soon as I find the time.

@XSpielinbox
Copy link

When switching to runc, the error stays exactly the same.

@kernelmethod
Copy link
Author

@XSpielinbox I assume that you're running podman as a non-root user? I believe that the issue you're experiencing is the same one I described in containers/common#958, i.e., apparmor.IsEnabled() always returns false if you're not running as root, regardless of whether AppArmor is actually enabled.

@kernelmethod
Copy link
Author

kernelmethod commented Jun 5, 2023

I apologize for not revisiting this issue sooner (I'm just working on this when I'm free; it fell off my radar due to time constraints back in September). I should have some spare time starting next week to fix this, and then revisit containers/common#958 when that's done.

@XSpielinbox
Copy link

XSpielinbox commented Jun 5, 2023

I assume that you're running podman as a non-root user?

Yes, as stated in my first comment, I am running podman rootless.

I believe that the issue you're experiencing is the same one I described in containers/common#958

Yes, exactly, but as it states there, that the changes were merged quite long ago, I was confused. I only quite a while later noticed, that the changes got reverted, but could not find any exact reasoning or explanation. The only somewhat related issue I could find, was this one, but my errors are the ones specified in containers/common#960, but not like the ones in this issue here.

It would be awesome to get some progress on this!
Is there anything I can help with?

Unfortunately, I have no experience at all in Go programming.

@kernelmethod
Copy link
Author

kernelmethod commented Jun 5, 2023

I only quite a while later noticed, that the changes got reverted, but could not find any exact reasoning or explanation. The only somewhat related issue I could find, was this one, but my errors are the ones specified in containers/common#960, but not like the ones in this issue here.

Yeah; the reversion was done in containers/common#969 because it caused a CI job to fail, but there wasn't much discussion at the time into what actually caused the failure. The fundamental problem (which is what this issue is about, and is roughly what caused the CI issue) is that Podman is currently designed such that the default AppArmor profile only gets loaded at runtime. Once that problem is solved, I believe the original issue (containers/common#958), which is the problem you're encountering, can be revisited.

kernelmethod added a commit to kernelmethod/common that referenced this issue Jul 20, 2023
- Set the default AppArmor profile to unconfined; see issue
  containers/podman#15874. Based on the
  discussion there, distros that use AppArmor should supply their own
  AppArmor profile and set it in a default containers.conf, since there
  is no way to load AppArmor profiles rootlessly.
- Remove CheckProfileAndLoadDefault, since this functionality is now
  being offloaded to profile loading in /etc/apparmor.d.
kernelmethod added a commit to kernelmethod/common that referenced this issue Jul 20, 2023
Set the default AppArmor profile to unconfined; see the following
issues:

- containers#958
- containers/podman#15874

Based on the discussion there, distros that use AppArmor should supply
their own AppArmor profile and set it in a default containers.conf,
since there is no way to load AppArmor profiles rootlessly.
kernelmethod added a commit to kernelmethod/common that referenced this issue Jul 20, 2023
Set the default AppArmor profile to unconfined; see the following
issues:

- containers#958
- containers/podman#15874

Based on the discussion there, distros that use AppArmor should supply
their own AppArmor profile and set it in a default containers.conf,
since there is no way to load AppArmor profiles rootlessly.

Signed-off-by: Will Shand <[email protected]>
@kernelmethod
Copy link
Author

Update: I've opened containers/common#1572 and #19303 for this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/bug Categorizes issue or PR as related to a bug.
Projects
None yet
Development

No branches or pull requests

5 participants