From 79fb3c2135db1a9f962ad3ce42e97b20867e9fa6 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Wed, 17 Jan 2024 11:03:06 -0500 Subject: [PATCH] Make buildah match podman for handling of ulimits Podman currently sets the ulimits of nofile and nproc to max in rootless mode, if the user does not override. Buildah on the other hand just passes in the current defaults. Podman build should match podman run, and this will fix that problem. Fixes: https://github.com/containers/buildah/issues/5273 Signed-off-by: Daniel J Walsh --- define/types.go | 3 +++ run_linux.go | 34 ++++++++++++++++++++++++++++++ tests/bud.bats | 15 +++++++++++++ tests/bud/bud.limits/Containerfile | 4 ++++ tests/run.bats | 19 ++++++++++------- 5 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 tests/bud/bud.limits/Containerfile diff --git a/define/types.go b/define/types.go index c34bd2152ff..fc9c5a8c8c3 100644 --- a/define/types.go +++ b/define/types.go @@ -54,6 +54,9 @@ const ( SNP TeeType = "snp" ) +// DefaultRlimitValue is the value set by default for nofile and nproc +const RLimitDefaultValue = uint64(1048576) + // TeeType is a supported trusted execution environment type. type TeeType string diff --git a/run_linux.go b/run_linux.go index 4630ac5efcc..c96fc0bffbe 100644 --- a/run_linux.go +++ b/run_linux.go @@ -854,6 +854,9 @@ func addRlimits(ulimit []string, g *generate.Generator, defaultUlimits []string) var ( ul *units.Ulimit err error + // setup rlimits + nofileSet bool + nprocSet bool ) ulimit = append(defaultUlimits, ulimit...) @@ -862,8 +865,39 @@ func addRlimits(ulimit []string, g *generate.Generator, defaultUlimits []string) return fmt.Errorf("ulimit option %q requires name=SOFT:HARD, failed to be parsed: %w", u, err) } + if strings.ToUpper(ul.Name) == "NOFILE" { + nofileSet = true + } + if strings.ToUpper(ul.Name) == "NPROC" { + nprocSet = true + } g.AddProcessRlimits("RLIMIT_"+strings.ToUpper(ul.Name), uint64(ul.Hard), uint64(ul.Soft)) } + if !nofileSet { + max := define.RLimitDefaultValue + var rlimit unix.Rlimit + if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rlimit); err == nil { + if max < rlimit.Max || unshare.IsRootless() { + max = rlimit.Max + } + } else { + logrus.Warnf("Failed to return RLIMIT_NOFILE ulimit %q", err) + } + g.AddProcessRlimits("RLIMIT_NOFILE", max, max) + } + if !nprocSet { + max := define.RLimitDefaultValue + var rlimit unix.Rlimit + if err := unix.Getrlimit(unix.RLIMIT_NPROC, &rlimit); err == nil { + if max < rlimit.Max || unshare.IsRootless() { + max = rlimit.Max + } + } else { + logrus.Warnf("Failed to return RLIMIT_NPROC ulimit %q", err) + } + g.AddProcessRlimits("RLIMIT_NPROC", max, max) + } + return nil } diff --git a/tests/bud.bats b/tests/bud.bats index 5fbe001d283..097537dbcd2 100644 --- a/tests/bud.bats +++ b/tests/bud.bats @@ -6602,3 +6602,18 @@ _EOF expect_output --substring "localhost/foo/bar" expect_output --substring "localhost/bar" } + +@test "build test default ulimits" { + skip_if_no_runtime + _prefetch alpine + + run podman run --rm alpine sh -c "echo -n Files=; awk '/open files/{print \$4 \"/\" \$5}' /proc/self/limits" + podman_files=$output + + run podman run --rm alpine sh -c "echo -n Processes=; awk '/processes/{print \$3 \"/\" \$4}' /proc/self/limits" + podman_processes=$output + + CONTAINERS_CONF=/dev/null run_buildah build --no-cache --pull=false $WITH_POLICY_JSON -t foo/bar $BUDFILES/bud.limits + expect_output --substring "$podman_files" + expect_output --substring "$podman_processes" +} diff --git a/tests/bud/bud.limits/Containerfile b/tests/bud/bud.limits/Containerfile new file mode 100644 index 00000000000..7d6eedd3586 --- /dev/null +++ b/tests/bud/bud.limits/Containerfile @@ -0,0 +1,4 @@ +# Containerfile +FROM alpine +RUN echo -n "Files="; awk '/open files/{print $4 "/" $5}' /proc/self/limits +RUN echo -n "Processes="; awk '/processes/{print $3 "/" $4}' /proc/self/limits diff --git a/tests/run.bats b/tests/run.bats index dc40c081fea..0ddf920b300 100644 --- a/tests/run.bats +++ b/tests/run.bats @@ -510,7 +510,6 @@ function configure_and_check_user() { } @test "Check if containers run with correct open files/processes limits" { - skip_if_rootless_environment skip_if_no_runtime # we need to not use the list of limits that are set in our default @@ -521,21 +520,25 @@ function configure_and_check_user() { export CONTAINERS_CONF=${TEST_SCRATCH_DIR}/containers.conf _prefetch alpine - maxpids=$(cat /proc/sys/kernel/pid_max) + + run podman run --rm alpine sh -c "awk '/open files/{print \$4 \"/\" \$5}' /proc/self/limits" + podman_files=$output + run_buildah from --quiet --pull=false $WITH_POLICY_JSON alpine cid=$output - run_buildah run $cid awk '/open files/{print $4}' /proc/self/limits - expect_output 1024 "limits: open files (unlimited)" - run_buildah run $cid awk '/processes/{print $3}' /proc/self/limits - expect_output ${maxpids} "limits: processes (unlimited)" + run_buildah run $cid awk '/open files/{print $4 "/" $5}' /proc/self/limits + expect_output "${podman_files}" "limits: podman and buildah should agree on open files" + + run podman run --rm alpine sh -c "awk '/processes/{print \$3 \"/\" \$4}' /proc/self/limits" + podman_processes=$output + run_buildah run $cid awk '/processes/{print $3 "/" $4}' /proc/self/limits + expect_output ${podman_processes} "processes should match podman" run_buildah rm $cid run_buildah from --quiet --ulimit nofile=300:400 --pull=false $WITH_POLICY_JSON alpine cid=$output run_buildah run $cid awk '/open files/{print $4}' /proc/self/limits expect_output "300" "limits: open files (w/file limit)" - run_buildah run $cid awk '/processes/{print $3}' /proc/self/limits - expect_output ${maxpids} "limits: processes (w/file limit)" run_buildah rm $cid run_buildah from --quiet --ulimit nproc=100:200 --ulimit nofile=300:400 --pull=false $WITH_POLICY_JSON alpine