debian-forge-cli/bib/internal/setup/setup.go
Colin Walters 9b280f0ba3 setup: Simplify container-storage mounted check
I ran into the `--local` option not working at least in my
setup with the current container image (which for some reason
hasn't been updated, and predates the latest tip commit
which talks about btrfs)

On this current test system (MacOS + podman 5 + default podman-machine)
things do work with the tip commit.

However...I don't quite understand the need to *both* try to
run `systemd-detect-virt` *and* parse `/proc/self/mountinfo`.

(BTW, the logic for `insideContainer` was really confusing me because
 it only returned `true` if we were *not* in a container...I wonder
 if that was really intentional?)

Anyways, I think the goal here is just a friendly direct error
message if it doesn't look like the mount is there, which we can
do by just checking for the file path.  If it isn't mounted
from the host then nothing will be there.

Signed-off-by: Colin Walters <walters@verbum.org>
2025-03-31 18:19:33 +00:00

117 lines
4.1 KiB
Go

package setup
import (
"fmt"
"os"
"path/filepath"
"golang.org/x/sys/unix"
"github.com/osbuild/bootc-image-builder/bib/internal/podmanutil"
"github.com/osbuild/bootc-image-builder/bib/internal/util"
)
// EnsureEnvironment mutates external filesystem state as necessary
// to run in a container environment. This function is idempotent.
func EnsureEnvironment(storePath string) error {
osbuildPath := "/usr/bin/osbuild"
if util.IsMountpoint(osbuildPath) {
return nil
}
// Forcibly label the store to ensure we're not grabbing container labels
rootType := "system_u:object_r:root_t:s0"
// This papers over the lack of ensuring correct labels for the /ostree root
// in the existing pipeline
if err := util.RunCmdSync("chcon", rootType, storePath); err != nil {
return err
}
// A hardcoded security label from Fedora derivatives for osbuild
// TODO: Avoid hardcoding this by using either host policy lookup
// Or eventually depend on privileged containers just having this capability.
//
// 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.
installType := "system_u:object_r:install_exec_t:s0"
// Where we dump temporary files; this must be an overlayfs as we cannot
// write security contexts on overlayfs.
runTmp := "/run/osbuild/"
if err := os.MkdirAll(runTmp, 0o755); err != nil {
return err
}
if !util.IsMountpoint(runTmp) {
if err := util.RunCmdSync("mount", "-t", "tmpfs", "tmpfs", runTmp); err != nil {
return err
}
}
destPath := filepath.Join(runTmp, "osbuild")
if err := util.RunCmdSync("cp", "-p", "/usr/bin/osbuild", destPath); err != nil {
return err
}
if err := util.RunCmdSync("chcon", installType, destPath); err != nil {
return err
}
// Ensure we have devfs inside the container to get dynamic loop
// loop devices inside the container.
if err := util.RunCmdSync("mount", "-t", "devtmpfs", "devtmpfs", "/dev"); err != nil {
return err
}
// Create a bind mount into our target location; we can't copy it because
// again we have to perserve the SELinux label.
if err := util.RunCmdSync("mount", "--bind", destPath, osbuildPath); err != nil {
return err
}
// NOTE: Don't add new code here, do it before the bind mount which acts as the final success indicator
return nil
}
// Validate checks that the environment is supported (e.g. caller set up the
// container correctly)
func Validate() error {
isRootless, err := podmanutil.IsRootless()
if err != nil {
return fmt.Errorf("checking rootless: %w", err)
}
if isRootless {
return fmt.Errorf("this command must be run in rootful (not rootless) podman")
}
// Having /sys be writable is an easy to check proxy for privileges; more effective
// is really looking for CAP_SYS_ADMIN, but that involves more Go libraries.
var stvfsbuf unix.Statfs_t
if err := unix.Statfs("/sys", &stvfsbuf); err != nil {
return fmt.Errorf("failed to stat /sys: %w", err)
}
if (stvfsbuf.Flags & unix.ST_RDONLY) > 0 {
return fmt.Errorf("this command requires a privileged container")
}
return nil
}
// ValidateHasContainerStorageMounted checks that the hostcontainer storage
// is mounted inside the container
func ValidateHasContainerStorageMounted() error {
// Just look for the overlay backend, which we expect by default.
// In theory, one could be using a different backend, but we don't
// really need to worry about this right now. If it turns out
// we do need to care, then we can probably handle this by
// just trying to query the image.
overlayPath := "/var/lib/containers/storage/overlay"
if _, err := os.Stat(overlayPath); err != nil {
if os.IsNotExist(err) {
return fmt.Errorf("cannot find %q (missing -v /var/lib/containers/storage:/var/lib/containers/storage mount?)", overlayPath)
}
return fmt.Errorf("failed to stat %q: %w", overlayPath, err)
}
return nil
}