Skip to content

Commit

Permalink
Make the container working on an SELinux-enforcing systems
Browse files Browse the repository at this point in the history
When building an image, we need to make sure that the target system is
correctly labeled. This becomes challenging if the target system contains
labels that are unknown to the host because the process setting the label
needs to have CAP_MAC_ADMIN if the host is SELinux-enforcing.

CAP_MAC_ADMIN isn't a common capability on a SELinux-enforcing system.
Even unconfined_t doesn't have it (same for spc_t - label used by
--privileged containers). Thus, we need to ensure that we transition to
a domain that actually has it.

This commit relabels osbuild as install_t, a domain that has CAP_MAP_ADMIN.
A bit of mount-dancing is needed in order to achieve that, see prepare.sh.

I decided to make prepare.sh a separate script. This is useful for debugging:

host # podman run -it \
  --privileged \
  --security-opt label=type:unconfined_t \
  --entrypoint bash \
  localhost/osbuild-deploy-container
container # ./prepare.sh

This way, you get the same environment as if you run the container the
default way.

See #6 (comment)
and links in this comment for further information.
  • Loading branch information
ondrejbudai committed Nov 24, 2023
1 parent 200546e commit 5347567
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 4 deletions.
2 changes: 1 addition & 1 deletion Containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ RUN ./build.sh
FROM registry.fedoraproject.org/fedora:39
RUN dnf install -y osbuild osbuild-ostree && dnf clean all
COPY --from=builder images/osbuild-deploy-container /usr/bin/osbuild-deploy-container
COPY entrypoint.sh /
COPY prepare.sh entrypoint.sh /
COPY --from=builder images/dnf-json .

ENTRYPOINT ["/entrypoint.sh"]
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ A simpler container for deploying bootable container images.

```
mkdir output
sudo podman run --rm -it --privileged -v $(pwd)/output:/output ghcr.io/osbuild/osbuild-deploy-container -imageref quay.io/centos-boot/fedora-tier-1:eln
sudo podman run --rm -it --privileged --security-opt label=type:unconfined_t -v $(pwd)/output:/output ghcr.io/osbuild/osbuild-deploy-container -imageref quay.io/centos-boot/fedora-tier-1:eln
qemu-system-x86_64 -M accel=kvm -cpu host -smp 2 -m 4096 -bios /usr/share/OVMF/OVMF_CODE.fd -snapshot output/qcow2/disk.qcow2
```
Expand Down Expand Up @@ -40,5 +40,5 @@ Example of such a config:
Save this config as `output/config.json` and run:

```
sudo podman run --rm -it --privileged -v $(pwd)/output:/output ghcr.io/osbuild/osbuild-deploy-container -imageref quay.io/centos-boot/fedora-tier-1:eln -config /output/config.json
sudo podman run --rm -it --privileged --security-opt label=type:unconfined_t -v $(pwd)/output:/output ghcr.io/osbuild/osbuild-deploy-container -imageref quay.io/centos-boot/fedora-tier-1:eln -config /output/config.json
```
5 changes: 4 additions & 1 deletion entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#!/bin/bash

/usr/bin/osbuild-deploy-container -store /store -rpmmd /rpmmd -output /output "$@"
set -euo pipefail

./prepare.sh
/usr/bin/osbuild-deploy-container -store /store -rpmmd /rpmmd -output /output "$@"
30 changes: 30 additions & 0 deletions prepare.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/bash

set -euo pipefail

# Create a new tmpfs. This solves two issues for us:
# - / is mounted as nosuid, this prevents SELinux to transition to `install_t` because domain transitions are
# disallowed if they give more caps to the process and the target executable is on `nosuid` filesystem
# - / can be mounted as OverlayFS that doesn't support overlaying SELinux labels. Thus, we need to ensure that
# the relabeling happens on a mountpoint that's definitely not an OverlayFS.
TMP=/run/suidtmp
mkdir -p "${TMP}"

# The container is mounted as MS_SHARED, this mount as well. Thus, we don't need to care about cleanup, when the
# container dies, it will take this mount with itself.
mount -t tmpfs tmpfs "${TMP}"

# Copy osbuild to the new mountpoint.
cp /usr/bin/osbuild "${TMP}/osbuild"

# Label it as `install_exec_t`. We need this in order to get `install_t` that has `CAP_MAC_ADMIN` for creating SELinux
# labels unknown to the host.
#
# Note that the transition to `install_t` must happen at this point. Osbuild stages run in `bwrap` that creates
# a nosuid, no_new_privs environment. In such an environment, we cannot transition from `unconfined_t` to `install_t`,
# because we would get more privileges.
chcon system_u:object_r:install_exec_t:s0 "${TMP}/osbuild"

# "Copy" back the relabeled osbuild to its right place. We obviously cannot copy it, so let's bind-mount it instead.
# Once again, we don't care about clean-up, this is MS_SHARED.
mount -o bind "${TMP}/osbuild" /usr/bin/osbuild

0 comments on commit 5347567

Please sign in to comment.