diff --git a/bib/internal/podmanutil/podmanutils.go b/bib/internal/podmanutil/podmanutils.go new file mode 100644 index 0000000..ac6c485 --- /dev/null +++ b/bib/internal/podmanutil/podmanutils.go @@ -0,0 +1,38 @@ +package podmanutil + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io/fs" + "os" +) + +// envPath is written by podman +const envPath = "/run/.containerenv" + +// rootlessKey is set when we are rootless +const rootlessKey = "rootless=1" + +// IsRootless detects if we are running rootless in podman; +// other situations (e.g. docker) will successfuly return false. +func IsRootless() (bool, error) { + buf, err := os.ReadFile(envPath) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + return false, nil + } + return false, err + } + scanner := bufio.NewScanner(bytes.NewReader(buf)) + for scanner.Scan() { + if scanner.Text() == rootlessKey { + return true, nil + } + } + if err := scanner.Err(); err != nil { + return false, fmt.Errorf("parsing %s: %w", envPath, err) + } + return false, nil +} diff --git a/bib/internal/setup/setup.go b/bib/internal/setup/setup.go index 71b0b1a..e2a3c23 100644 --- a/bib/internal/setup/setup.go +++ b/bib/internal/setup/setup.go @@ -7,7 +7,9 @@ import ( "os/exec" "path/filepath" + "github.com/osbuild/bootc-image-builder/bib/internal/podmanutil" "github.com/osbuild/bootc-image-builder/bib/internal/utils" + "golang.org/x/sys/unix" ) // EnsureEnvironment mutates external filesystem state as necessary @@ -74,5 +76,23 @@ func EnsureEnvironment() error { // 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 -} \ No newline at end of file +}